react-native-navigation 7.23.1-snapshot.412 → 7.23.1-snapshot.440

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/lib/android/app/build.gradle +9 -9
  2. package/lib/android/app/src/main/java/com/reactnativenavigation/options/HardwareBackButtonOptions.kt +35 -2
  3. package/lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java +5 -2
  4. package/lib/android/app/src/main/java/com/reactnativenavigation/react/modal/ModalFrameLayout.kt +3 -3
  5. package/lib/android/app/src/main/java/com/reactnativenavigation/react/modal/ModalViewManager.kt +11 -8
  6. package/lib/android/app/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +168 -0
  7. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java +42 -2
  8. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/child/ChildController.java +12 -16
  9. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewController.java +18 -11
  10. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewController.java +2 -2
  11. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenter.java +33 -18
  12. package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +52 -71
  13. package/lib/android/app/src/main/java/com/reactnativenavigation/views/component/ComponentLayout.java +2 -0
  14. package/lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ReactImageMatrixAnimator.kt +2 -3
  15. package/lib/android/app/src/main/java/com/reactnativenavigation/views/stack/topbar/TopBar.java +1 -2
  16. package/lib/android/app/src/test/java/com/reactnativenavigation/BaseTest.java +7 -6
  17. package/lib/android/app/src/test/java/com/reactnativenavigation/TestActivity.java +5 -0
  18. package/lib/android/app/src/test/java/com/reactnativenavigation/mocks/ImageLoaderMock.kt +5 -4
  19. package/lib/android/app/src/test/java/com/reactnativenavigation/mocks/Mocks.kt +3 -2
  20. package/lib/android/app/src/test/java/com/reactnativenavigation/mocks/TypefaceLoaderMock.kt +1 -1
  21. package/lib/android/app/src/test/java/com/reactnativenavigation/options/TransitionAnimationOptionsTest.kt +2 -3
  22. package/lib/android/app/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java +25 -10
  23. package/lib/android/app/src/test/java/com/reactnativenavigation/utils/MotionEventTest.kt +1 -1
  24. package/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiThreadTest.java +2 -2
  25. package/lib/android/app/src/test/java/com/reactnativenavigation/utils/UiUtilsTest.java +1 -1
  26. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt +572 -0
  27. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt +1 -1
  28. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/child/ChildControllerTest.java +2 -1
  29. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java +5 -5
  30. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/fakes/FakeParentController.kt +1 -1
  31. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt +1 -1
  32. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java +3 -3
  33. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java +4 -3
  34. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt +5 -5
  35. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java +4 -0
  36. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuControllerTest.java +6 -2
  37. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt +1 -1
  38. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt +3 -3
  39. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +4 -4
  40. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +1 -1
  41. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewControllerTest.java +4 -1
  42. package/lib/android/app/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +2 -2
  43. package/lib/android/app/src/test/java/com/reactnativenavigation/views/animations/BaseViewAnimatorTest.kt +4 -4
  44. package/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainerTest.kt +1 -1
  45. package/lib/android/app/src/test/java/com/reactnativenavigation/views/bottomtabs/BottomTabsTest.kt +4 -4
  46. package/lib/dist/interfaces/Options.d.ts +4 -0
  47. package/lib/ios/BottomTabPresenter.h +0 -3
  48. package/lib/ios/BottomTabPresenter.m +0 -8
  49. package/lib/ios/RNNBasePresenter.h +0 -4
  50. package/lib/ios/RNNBasePresenter.m +0 -12
  51. package/lib/ios/RNNBottomTabsController.m +8 -8
  52. package/lib/ios/RNNComponentPresenter.m +0 -4
  53. package/lib/ios/RNNComponentViewController.m +0 -4
  54. package/lib/ios/RNNExternalViewController.m +0 -4
  55. package/lib/ios/RNNSideMenuChildVC.m +0 -4
  56. package/lib/ios/RNNSideMenuController.m +0 -4
  57. package/lib/ios/RNNSplitViewController.m +0 -4
  58. package/lib/ios/RNNStackController.m +0 -4
  59. package/lib/src/interfaces/Options.ts +5 -0
  60. package/package.json +2 -2
  61. package/lib/android/app/src/main/java/com/reactnativenavigation/utils/StatusBarUtils.kt +0 -39
  62. package/lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.java +0 -511
@@ -172,13 +172,13 @@ allprojects { p ->
172
172
  }
173
173
 
174
174
  dependencies {
175
- implementation "androidx.core:core-ktx:1.3.2"
175
+ implementation "androidx.core:core-ktx:1.6.0"
176
176
  implementation "org.jetbrains.kotlin:$kotlinStdlib:$kotlinVersion"
177
177
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesCore"
178
178
  implementation "androidx.constraintlayout:constraintlayout:2.0.4"
179
179
 
180
- implementation 'androidx.appcompat:appcompat:1.1.0'
181
- implementation 'androidx.annotation:annotation:1.1.0'
180
+ implementation 'androidx.appcompat:appcompat:1.3.1'
181
+ implementation 'androidx.annotation:annotation:1.2.0'
182
182
  implementation 'com.google.android.material:material:1.2.0-alpha03'
183
183
 
184
184
  implementation 'com.github.wix-playground:ahbottomnavigation:3.3.0'
@@ -190,12 +190,12 @@ dependencies {
190
190
  implementation 'com.facebook.react:react-native:+'
191
191
 
192
192
  // tests
193
- testImplementation 'junit:junit:4.12'
194
- testImplementation "org.robolectric:robolectric:4.4"
195
- testImplementation 'org.assertj:assertj-core:3.8.0'
196
- testImplementation 'com.squareup.assertj:assertj-android:1.1.1'
197
- testImplementation 'org.mockito:mockito-core:3.4.0'
193
+ testImplementation 'junit:junit:4.13.2'
194
+ testImplementation "org.robolectric:robolectric:4.7.2"
195
+ testImplementation 'org.assertj:assertj-core:3.11.1'
196
+ testImplementation 'org.mockito:mockito-core:4.0.0'
197
+ testImplementation 'com.squareup.assertj:assertj-android:1.2.0'
198
198
  testImplementation 'org.mockito:mockito-inline:3.4.0'
199
- testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
199
+ testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
200
200
  testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion"
201
201
  }
@@ -6,10 +6,40 @@ import com.reactnativenavigation.options.parsers.BoolParser
6
6
  import org.json.JSONObject
7
7
 
8
8
 
9
+ sealed class HwBackBottomTabsBehaviour {
10
+ object Undefined : HwBackBottomTabsBehaviour() {
11
+ override fun hasValue(): Boolean = false
12
+ }
13
+
14
+ object Exit : HwBackBottomTabsBehaviour()
15
+ object PrevSelection : HwBackBottomTabsBehaviour()
16
+ object JumpToFirst : HwBackBottomTabsBehaviour()
17
+
18
+ open fun hasValue(): Boolean = true
19
+
20
+ companion object {
21
+ private const val BEHAVIOUR_EXIT = "exit"
22
+ private const val BEHAVIOUR_PREV = "previous"
23
+ private const val BEHAVIOUR_FIRST = "first"
24
+ fun fromString(behaviour: String?): HwBackBottomTabsBehaviour {
25
+ return when (behaviour) {
26
+ BEHAVIOUR_PREV -> PrevSelection
27
+ BEHAVIOUR_FIRST -> JumpToFirst
28
+ BEHAVIOUR_EXIT -> Exit
29
+ else -> Undefined
30
+ }
31
+ }
32
+ }
33
+ }
34
+
9
35
  open class HardwareBackButtonOptions(json: JSONObject? = null) {
10
36
 
11
- @JvmField var dismissModalOnPress: Bool = NullBool()
12
- @JvmField var popStackOnPress: Bool = NullBool()
37
+ @JvmField
38
+ var dismissModalOnPress: Bool = NullBool()
39
+
40
+ @JvmField
41
+ var popStackOnPress: Bool = NullBool()
42
+ var bottomTabOnPress: HwBackBottomTabsBehaviour = HwBackBottomTabsBehaviour.Undefined
13
43
 
14
44
  init {
15
45
  parse(json)
@@ -18,16 +48,19 @@ open class HardwareBackButtonOptions(json: JSONObject? = null) {
18
48
  fun mergeWith(other: HardwareBackButtonOptions) {
19
49
  if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress
20
50
  if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress
51
+ if (other.bottomTabOnPress.hasValue()) bottomTabOnPress = other.bottomTabOnPress
21
52
  }
22
53
 
23
54
  fun mergeWithDefault(defaultOptions: HardwareBackButtonOptions) {
24
55
  if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaultOptions.dismissModalOnPress
25
56
  if (!popStackOnPress.hasValue()) popStackOnPress = defaultOptions.popStackOnPress
57
+ if (!bottomTabOnPress.hasValue()) bottomTabOnPress = defaultOptions.bottomTabOnPress
26
58
  }
27
59
 
28
60
  private fun parse(json: JSONObject?) {
29
61
  json ?: return
30
62
  dismissModalOnPress = BoolParser.parse(json, "dismissModalOnPress")
31
63
  popStackOnPress = BoolParser.parse(json, "popStackOnPress")
64
+ bottomTabOnPress = HwBackBottomTabsBehaviour.fromString(json.optString("bottomTabsOnPress"))
32
65
  }
33
66
  }
@@ -23,7 +23,7 @@ import com.reactnativenavigation.options.parsers.TypefaceLoader;
23
23
  import com.reactnativenavigation.react.events.EventEmitter;
24
24
  import com.reactnativenavigation.utils.LaunchArgsParser;
25
25
  import com.reactnativenavigation.utils.Now;
26
- import com.reactnativenavigation.utils.StatusBarUtils;
26
+ import com.reactnativenavigation.utils.SystemUiUtils;
27
27
  import com.reactnativenavigation.utils.UiThread;
28
28
  import com.reactnativenavigation.utils.UiUtils;
29
29
  import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
@@ -34,6 +34,8 @@ import java.util.Objects;
34
34
 
35
35
  import static com.reactnativenavigation.utils.UiUtils.pxToDp;
36
36
 
37
+ import android.app.Activity;
38
+
37
39
  public class NavigationModule extends ReactContextBaseJavaModule {
38
40
  private static final String NAME = "RNNBridgeModule";
39
41
 
@@ -88,10 +90,11 @@ public class NavigationModule extends ReactContextBaseJavaModule {
88
90
 
89
91
  private WritableMap createNavigationConstantsMap() {
90
92
  ReactApplicationContext ctx = getReactApplicationContext();
93
+ final Activity currentActivity = ctx.getCurrentActivity();
91
94
  WritableMap constants = Arguments.createMap();
92
95
  constants.putString(Constants.BACK_BUTTON_JS_KEY, Constants.BACK_BUTTON_ID);
93
96
  constants.putDouble(Constants.BOTTOM_TABS_HEIGHT_KEY, pxToDp(ctx, UiUtils.getBottomTabsHeight(ctx)));
94
- constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, pxToDp(ctx, StatusBarUtils.getStatusBarHeight(ctx)));
97
+ constants.putDouble(Constants.STATUS_BAR_HEIGHT_KEY, pxToDp(ctx, SystemUiUtils.getStatusBarHeight(currentActivity)));
95
98
  constants.putDouble(Constants.TOP_BAR_HEIGHT_KEY, pxToDp(ctx, UiUtils.getTopBarHeight(ctx)));
96
99
  return constants;
97
100
  }
@@ -2,7 +2,7 @@ package com.reactnativenavigation.react.modal
2
2
 
3
3
  import android.widget.FrameLayout
4
4
  import com.facebook.react.bridge.ReactContext
5
- import com.reactnativenavigation.utils.StatusBarUtils
5
+ import com.reactnativenavigation.utils.SystemUiUtils
6
6
 
7
7
  class ModalFrameLayout(context: ReactContext) : FrameLayout(context) {
8
8
  val modalContentLayout = ModalContentLayout(context)
@@ -11,9 +11,9 @@ class ModalFrameLayout(context: ReactContext) : FrameLayout(context) {
11
11
  addView(modalContentLayout, MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT)
12
12
  .apply {
13
13
  val translucent = context.currentActivity?.window?.let {
14
- StatusBarUtils.isTranslucent(it)
14
+ SystemUiUtils.isTranslucent(it)
15
15
  } ?: false
16
- topMargin = if (translucent) 0 else StatusBarUtils.getStatusBarHeight(context)
16
+ topMargin = if (translucent) 0 else SystemUiUtils.getStatusBarHeight(context.currentActivity)
17
17
  })
18
18
  }
19
19
  }
@@ -1,5 +1,6 @@
1
1
  package com.reactnativenavigation.react.modal
2
2
 
3
+ import android.app.Activity
3
4
  import android.content.Context
4
5
  import android.graphics.Point
5
6
  import android.view.WindowManager
@@ -21,7 +22,7 @@ import com.reactnativenavigation.options.parseTransitionAnimationOptions
21
22
  import com.reactnativenavigation.options.parsers.JSONParser
22
23
  import com.reactnativenavigation.react.CommandListener
23
24
  import com.reactnativenavigation.react.CommandListenerAdapter
24
- import com.reactnativenavigation.utils.StatusBarUtils
25
+ import com.reactnativenavigation.utils.SystemUiUtils
25
26
  import com.reactnativenavigation.viewcontrollers.navigator.Navigator
26
27
 
27
28
  private const val MODAL_MANAGER_NAME = "RNNModalViewManager"
@@ -107,18 +108,18 @@ class ModalViewManager(val reactContext: ReactContext) : ViewGroupManager<ModalH
107
108
  }
108
109
  }
109
110
 
110
- private fun getModalHostSize(context: Context): Point {
111
+ private fun getModalHostSize(activity: Activity): Point {
111
112
  val MIN_POINT = Point()
112
113
  val MAX_POINT = Point()
113
114
  val SIZE_POINT = Point()
114
- val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
115
+ val wm = activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager
115
116
  val display = Assertions.assertNotNull(wm).defaultDisplay
116
117
  // getCurrentSizeRange will return the min and max width and height that the window can be
117
118
  display.getCurrentSizeRange(MIN_POINT, MAX_POINT)
118
119
  // getSize will return the dimensions of the screen in its current orientation
119
120
  display.getSize(SIZE_POINT)
120
121
  val attrs = intArrayOf(android.R.attr.windowFullscreen)
121
- val theme = context.theme
122
+ val theme = activity.theme
122
123
  val ta = theme.obtainStyledAttributes(attrs)
123
124
  val windowFullscreen = ta.getBoolean(0, false)
124
125
 
@@ -126,7 +127,7 @@ private fun getModalHostSize(context: Context): Point {
126
127
  // because Display.getCurrentSizeRange doesn't include it.
127
128
  var statusBarHeight = 0
128
129
  if (windowFullscreen) {
129
- statusBarHeight = StatusBarUtils.getStatusBarHeight(context)
130
+ statusBarHeight = SystemUiUtils.getStatusBarHeight(activity)
130
131
  }
131
132
  return if (SIZE_POINT.x < SIZE_POINT.y) {
132
133
  // If we are vertical the width value comes from min width and height comes from max height
@@ -140,8 +141,10 @@ private fun getModalHostSize(context: Context): Point {
140
141
  private class ModalHostShadowNode : LayoutShadowNode() {
141
142
  override fun addChildAt(child: ReactShadowNodeImpl, i: Int) {
142
143
  super.addChildAt(child, i)
143
- val modalSize = getModalHostSize(themedContext)
144
- child.setStyleWidth(modalSize.x.toFloat())
145
- child.setStyleHeight(modalSize.y.toFloat())
144
+ themedContext?.currentActivity?.let {
145
+ val modalSize = getModalHostSize(it)
146
+ child.setStyleWidth(modalSize.x.toFloat())
147
+ child.setStyleHeight(modalSize.y.toFloat())
148
+ }
146
149
  }
147
150
  }
@@ -0,0 +1,168 @@
1
+ package com.reactnativenavigation.utils
2
+
3
+ import android.app.Activity
4
+ import android.graphics.Color
5
+ import android.graphics.Rect
6
+ import android.os.Build
7
+ import android.view.View
8
+ import android.view.Window
9
+ import androidx.annotation.ColorInt
10
+ import androidx.core.view.WindowCompat
11
+ import androidx.core.view.WindowInsetsCompat
12
+ import androidx.core.view.WindowInsetsControllerCompat
13
+ import kotlin.math.abs
14
+ import kotlin.math.ceil
15
+
16
+
17
+ object SystemUiUtils {
18
+ private const val STATUS_BAR_HEIGHT_M = 24
19
+ private const val STATUS_BAR_HEIGHT_L = 25
20
+ private const val STATUS_BAR_HEIGHT_TRANSLUCENCY = 0.65f
21
+ private var statusBarHeight = -1
22
+ var navigationBarDefaultColor = -1
23
+ private set
24
+
25
+
26
+ @JvmStatic
27
+ fun getStatusBarHeight(activity: Activity?): Int {
28
+ val res = if (statusBarHeight > 0) {
29
+ statusBarHeight
30
+ } else {
31
+ statusBarHeight = activity?.let {
32
+ val rectangle = Rect()
33
+ val window: Window = activity.window
34
+ window.decorView.getWindowVisibleDisplayFrame(rectangle)
35
+ val statusBarHeight: Int = rectangle.top
36
+ val contentView = window.findViewById<View>(Window.ID_ANDROID_CONTENT)
37
+ contentView?.let {
38
+ val contentViewTop = contentView.top
39
+ abs(contentViewTop - statusBarHeight)
40
+ }
41
+ } ?: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) STATUS_BAR_HEIGHT_M else STATUS_BAR_HEIGHT_L
42
+ statusBarHeight
43
+ }
44
+ return res
45
+ }
46
+
47
+ @JvmStatic
48
+ fun saveStatusBarHeight(height: Int) {
49
+ statusBarHeight = height
50
+ }
51
+
52
+
53
+ @JvmStatic
54
+ fun getStatusBarHeightDp(activity: Activity?): Int {
55
+ return UiUtils.pxToDp(activity, getStatusBarHeight(activity).toFloat())
56
+ .toInt()
57
+ }
58
+
59
+ @JvmStatic
60
+ fun hideNavigationBar(window: Window?, view: View) {
61
+ window?.let {
62
+ WindowCompat.setDecorFitsSystemWindows(window, false)
63
+ WindowInsetsControllerCompat(window, view).let { controller ->
64
+ controller.hide(WindowInsetsCompat.Type.navigationBars())
65
+ controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
66
+ }
67
+ }
68
+ }
69
+
70
+ @JvmStatic
71
+ fun showNavigationBar(window: Window?, view: View) {
72
+ window?.let {
73
+ WindowCompat.setDecorFitsSystemWindows(window, true)
74
+ WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.navigationBars())
75
+ }
76
+ }
77
+
78
+ @JvmStatic
79
+ fun setStatusBarColorScheme(window: Window?, view: View, isDark: Boolean) {
80
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
81
+
82
+ window?.let {
83
+ WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = isDark
84
+ // Workaround: on devices with api 30 status bar icons flickers or get hidden when removing view
85
+ //turns out it is a bug on such devices, fixed by using system flags until it is fixed.
86
+ var flags = view.systemUiVisibility
87
+ flags = if (isDark) {
88
+ flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
89
+ } else {
90
+ flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
91
+ }
92
+
93
+ view.systemUiVisibility = flags
94
+ }
95
+ }
96
+
97
+ @JvmStatic
98
+ fun setStatusBarTranslucent(window: Window?) {
99
+ window?.let {
100
+ setStatusBarColor(window, window.statusBarColor, true)
101
+ }
102
+ }
103
+
104
+ @JvmStatic
105
+ fun isTranslucent(window: Window?): Boolean {
106
+ return window?.let {
107
+ Color.alpha(it.statusBarColor) < 255
108
+ } ?: false
109
+ }
110
+
111
+ @JvmStatic
112
+ fun clearStatusBarTranslucency(window: Window?) {
113
+ window?.let {
114
+ setStatusBarColor(it, it.statusBarColor, false)
115
+ }
116
+ }
117
+
118
+ @JvmStatic
119
+ fun setStatusBarColor(
120
+ window: Window?,
121
+ @ColorInt color: Int,
122
+ translucent: Boolean
123
+ ) {
124
+ val opaqueColor = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
125
+ Color.BLACK
126
+ }else{
127
+ val alpha = if (translucent) STATUS_BAR_HEIGHT_TRANSLUCENCY else 1f
128
+ val red: Int = Color.red(color)
129
+ val green: Int = Color.green(color)
130
+ val blue: Int = Color.blue(color)
131
+ Color.argb(ceil(alpha * 255).toInt(), red, green, blue)
132
+ }
133
+ window?.statusBarColor = opaqueColor
134
+ }
135
+
136
+ @JvmStatic
137
+ fun hideStatusBar(window: Window?, view: View) {
138
+ window?.let {
139
+ WindowCompat.setDecorFitsSystemWindows(window, false)
140
+ WindowInsetsControllerCompat(window, view).let { controller ->
141
+ controller.hide(WindowInsetsCompat.Type.statusBars())
142
+ controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
143
+ }
144
+ }
145
+ }
146
+
147
+ @JvmStatic
148
+ fun showStatusBar(window: Window?, view: View) {
149
+ window?.let {
150
+ WindowCompat.setDecorFitsSystemWindows(window, true)
151
+ WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.statusBars())
152
+ }
153
+ }
154
+
155
+ @JvmStatic
156
+ fun setNavigationBarBackgroundColor(window: Window?, color: Int, lightColor: Boolean) {
157
+ window?.let {
158
+ if (navigationBarDefaultColor == -1) {
159
+ navigationBarDefaultColor = window.navigationBarColor
160
+ }
161
+ WindowInsetsControllerCompat(window, window.decorView).let { controller ->
162
+ controller.isAppearanceLightNavigationBars = lightColor
163
+ }
164
+ window.navigationBarColor = color
165
+ }
166
+ }
167
+
168
+ }
@@ -13,6 +13,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
13
13
  import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
14
14
  import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
15
15
  import com.reactnativenavigation.options.BottomTabOptions;
16
+ import com.reactnativenavigation.options.HwBackBottomTabsBehaviour;
16
17
  import com.reactnativenavigation.options.Options;
17
18
  import com.reactnativenavigation.react.CommandListener;
18
19
  import com.reactnativenavigation.react.CommandListenerAdapter;
@@ -29,6 +30,8 @@ import com.reactnativenavigation.views.bottomtabs.BottomTabsContainer;
29
30
  import com.reactnativenavigation.views.bottomtabs.BottomTabsLayout;
30
31
 
31
32
  import java.util.Collection;
33
+ import java.util.Deque;
34
+ import java.util.LinkedList;
32
35
  import java.util.List;
33
36
 
34
37
  import static com.reactnativenavigation.utils.CollectionUtils.forEach;
@@ -39,6 +42,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
39
42
 
40
43
  private BottomTabsContainer bottomTabsContainer;
41
44
  private BottomTabs bottomTabs;
45
+ private final Deque<Integer> selectionStack;
42
46
  private final List<ViewController<?>> tabs;
43
47
  private final EventEmitter eventEmitter;
44
48
  private final ImageLoader imageLoader;
@@ -66,6 +70,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
66
70
  this.presenter = bottomTabsPresenter;
67
71
  this.tabPresenter = bottomTabPresenter;
68
72
  forEach(tabs, tab -> tab.setParentController(this));
73
+ selectionStack = new LinkedList<>();
69
74
  }
70
75
 
71
76
  @Override
@@ -156,7 +161,27 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
156
161
 
157
162
  @Override
158
163
  public boolean handleBack(CommandListener listener) {
159
- return !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener);
164
+ final boolean childBack = !tabs.isEmpty() && tabs.get(bottomTabs.getCurrentItem()).handleBack(listener);
165
+ final Options options = resolveCurrentOptions();
166
+ if (!childBack) {
167
+ if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection) {
168
+ if (!selectionStack.isEmpty()) {
169
+ final int prevSelectedTabIndex = selectionStack.poll();
170
+ selectTab(prevSelectedTabIndex, false);
171
+ return true;
172
+ }
173
+ } else if (options.hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.JumpToFirst) {
174
+ if (getSelectedIndex() != 0) {
175
+ selectTab(0, false);
176
+ return true;
177
+ } else {
178
+ return false;
179
+ }
180
+ } else {
181
+ return false;
182
+ }
183
+ }
184
+ return childBack;
160
185
  }
161
186
 
162
187
  @Override
@@ -203,7 +228,7 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
203
228
  });
204
229
  }
205
230
 
206
- int getSelectedIndex() {
231
+ public int getSelectedIndex() {
207
232
  return bottomTabs.getCurrentItem();
208
233
  }
209
234
 
@@ -239,6 +264,12 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
239
264
 
240
265
  @Override
241
266
  public void selectTab(final int newIndex) {
267
+ final boolean enableSelectionHistory = resolveCurrentOptions().hardwareBack.getBottomTabOnPress() instanceof HwBackBottomTabsBehaviour.PrevSelection;
268
+ selectTab(newIndex, enableSelectionHistory);
269
+ }
270
+
271
+ private void selectTab(int newIndex, boolean enableSelectionHistory) {
272
+ saveTabSelection(newIndex, enableSelectionHistory);
242
273
  tabsAttacher.onTabSelected(tabs.get(newIndex));
243
274
  getCurrentView().setVisibility(View.INVISIBLE);
244
275
  bottomTabs.setCurrentItem(newIndex, false);
@@ -246,6 +277,15 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
246
277
  getCurrentChild().onViewDidAppear();
247
278
  }
248
279
 
280
+ private void saveTabSelection(int newIndex, boolean enableSelectionHistory) {
281
+ if (enableSelectionHistory) {
282
+ if (selectionStack.isEmpty()
283
+ || selectionStack.peek() != newIndex
284
+ || bottomTabs.getCurrentItem() != newIndex)
285
+ selectionStack.offerFirst(bottomTabs.getCurrentItem());
286
+ }
287
+ }
288
+
249
289
  @NonNull
250
290
  private ViewGroup getCurrentView() {
251
291
  return tabs.get(bottomTabs.getCurrentItem()).getView();
@@ -2,12 +2,16 @@ package com.reactnativenavigation.viewcontrollers.child;
2
2
 
3
3
  import android.app.Activity;
4
4
  import android.content.res.Configuration;
5
+ import android.os.Build;
6
+ import android.util.Log;
5
7
  import android.view.View;
6
8
  import android.view.ViewGroup;
9
+ import android.view.WindowInsets;
7
10
 
8
11
  import com.reactnativenavigation.options.Options;
12
+ import com.reactnativenavigation.utils.LogKt;
13
+ import com.reactnativenavigation.viewcontrollers.parent.ParentController;
9
14
  import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
10
- import com.reactnativenavigation.utils.StatusBarUtils;
11
15
  import com.reactnativenavigation.viewcontrollers.viewcontroller.NoOpYellowBoxDelegate;
12
16
  import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
13
17
  import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController;
@@ -15,7 +19,9 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.overlay.ViewCont
15
19
  import com.reactnativenavigation.views.component.Component;
16
20
 
17
21
  import androidx.annotation.CallSuper;
22
+ import androidx.core.graphics.Insets;
18
23
  import androidx.core.view.ViewCompat;
24
+ import androidx.core.view.WindowCompat;
19
25
  import androidx.core.view.WindowInsetsCompat;
20
26
 
21
27
  public abstract class ChildController<T extends ViewGroup> extends ViewController<T> {
@@ -61,7 +67,7 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
61
67
  }
62
68
 
63
69
  public void onViewBroughtToFront() {
64
- presenter.onViewBroughtToFront(resolveCurrentOptions());
70
+ presenter.onViewBroughtToFront(this, resolveCurrentOptions());
65
71
  }
66
72
 
67
73
  @Override
@@ -73,7 +79,7 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
73
79
  @Override
74
80
  public void mergeOptions(Options options) {
75
81
  if (options == Options.EMPTY) return;
76
- if (isViewShown()) presenter.mergeOptions(getView(), options);
82
+ if (isViewShown()) presenter.mergeOptions(this, options);
77
83
  super.mergeOptions(options);
78
84
  performOnParentController(parentController -> parentController.mergeChildOptions(options, this));
79
85
  }
@@ -93,23 +99,13 @@ public abstract class ChildController<T extends ViewGroup> extends ViewControlle
93
99
  getView().getParent() != null;
94
100
  }
95
101
 
96
- private WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
97
- StatusBarUtils.saveStatusBarHeight(insets.getSystemWindowInsetTop());
98
- return applyWindowInsets(findController(view), insets);
99
- }
100
-
101
- protected WindowInsetsCompat applyWindowInsets(ViewController<?> view, WindowInsetsCompat insets) {
102
- return insets.replaceSystemWindowInsets(
103
- insets.getSystemWindowInsetLeft(),
104
- 0,
105
- insets.getSystemWindowInsetRight(),
106
- insets.getSystemWindowInsetBottom()
107
- );
102
+ protected WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
103
+ return insets;
108
104
  }
109
105
 
110
106
  @Override
111
107
  public void onConfigurationChanged(Configuration newConfig) {
112
108
  super.onConfigurationChanged(newConfig);
113
- presenter.onConfigurationChanged(this,options);
109
+ presenter.onConfigurationChanged(this, options);
114
110
  }
115
111
  }
@@ -4,10 +4,11 @@ import android.app.Activity;
4
4
  import android.content.res.Configuration;
5
5
  import android.view.View;
6
6
 
7
+ import com.reactnativenavigation.utils.LogKt;
7
8
  import com.reactnativenavigation.viewcontrollers.viewcontroller.ScrollEventListener;
8
9
  import com.reactnativenavigation.options.Options;
9
10
  import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
10
- import com.reactnativenavigation.utils.StatusBarUtils;
11
+ import com.reactnativenavigation.utils.SystemUiUtils;
11
12
  import com.reactnativenavigation.viewcontrollers.viewcontroller.ReactViewCreator;
12
13
  import com.reactnativenavigation.viewcontrollers.child.ChildController;
13
14
  import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
@@ -127,8 +128,9 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
127
128
 
128
129
  @Override
129
130
  public int getTopInset() {
130
- int statusBarInset = resolveCurrentOptions(presenter.defaultOptions).statusBar.isHiddenOrDrawBehind() ? 0 : StatusBarUtils.getStatusBarHeight(getActivity());
131
- return statusBarInset + perform(getParentController(), 0, p -> p.getTopInset(this));
131
+ int statusBarInset = resolveCurrentOptions(presenter.defaultOptions).statusBar.isHiddenOrDrawBehind() ? 0 : SystemUiUtils.getStatusBarHeight(getActivity());
132
+ final Integer perform = perform(getParentController(), 0, p -> p.getTopInset(this));
133
+ return statusBarInset + perform;
132
134
  }
133
135
 
134
136
  @Override
@@ -137,14 +139,19 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
137
139
  }
138
140
 
139
141
  @Override
140
- protected WindowInsetsCompat applyWindowInsets(ViewController<?> view, WindowInsetsCompat insets) {
141
- final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder();
142
- final WindowInsetsCompat finalInsets = builder.setSystemWindowInsets(Insets.of(insets.getSystemWindowInsetLeft(),
143
- Math.max(insets.getSystemWindowInsetTop() - getTopInset(), 0),
144
- insets.getSystemWindowInsetRight(),
145
- Math.max(insets.getSystemWindowInsetBottom() - getBottomInset(), 0))).build();
146
- ViewCompat.onApplyWindowInsets(view.getView(), finalInsets);
147
- return finalInsets;
142
+ protected WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
143
+ ViewController<?> viewController = findController(view);
144
+ if (viewController == null || viewController.getView() == null) return insets;
145
+ final Insets keyboardInsets = insets.getInsets( WindowInsetsCompat.Type.ime());
146
+ final Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() );
147
+ final int visibleNavBar = resolveCurrentOptions(presenter.defaultOptions).navigationBar.isVisible.isTrueOrUndefined()?1:0;
148
+ final WindowInsetsCompat finalInsets = new WindowInsetsCompat.Builder().setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime(),
149
+ Insets.of(systemBarsInsets.left,
150
+ 0,
151
+ systemBarsInsets.right,
152
+ Math.max(visibleNavBar*systemBarsInsets.bottom,keyboardInsets.bottom))
153
+ ).build();
154
+ return ViewCompat.onApplyWindowInsets(viewController.getView(), finalInsets);
148
155
  }
149
156
 
150
157
  @Override
@@ -10,7 +10,7 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.Presenter;
10
10
  import com.reactnativenavigation.react.events.ComponentType;
11
11
  import com.reactnativenavigation.react.events.EventEmitter;
12
12
  import com.reactnativenavigation.utils.CoordinatorLayoutUtils;
13
- import com.reactnativenavigation.utils.StatusBarUtils;
13
+ import com.reactnativenavigation.utils.SystemUiUtils;
14
14
  import com.reactnativenavigation.viewcontrollers.child.ChildController;
15
15
  import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
16
16
  import com.reactnativenavigation.views.BehaviourDelegate;
@@ -72,7 +72,7 @@ public class ExternalComponentViewController extends ChildController<ExternalCom
72
72
 
73
73
  @Override
74
74
  public int getTopInset() {
75
- int statusBarInset = resolveCurrentOptions().statusBar.drawBehind.isTrue() ? 0 : StatusBarUtils.getStatusBarHeight(getActivity());
75
+ int statusBarInset = resolveCurrentOptions().statusBar.drawBehind.isTrue() ? 0 : SystemUiUtils.getStatusBarHeight(getActivity());
76
76
  return statusBarInset + perform(getParentController(), 0, p -> p.getTopInset(this));
77
77
  }
78
78