react-native-navigation 8.8.1 → 8.8.2-snapshot.2425
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.
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/reactnativenavigation/NavigationActivity.java +48 -1
- package/android/src/main/java/com/reactnativenavigation/react/modal/ModalHostLayout.kt +2 -2
- package/android/src/main/java/com/reactnativenavigation/utils/ReactTypefaceUtils.java +1 -2
- package/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +219 -54
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsController.java +3 -2
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +26 -6
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewController.java +6 -3
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/LayoutDirectionApplier.kt +1 -2
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/Presenter.java +3 -5
- package/android/src/main/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewController.java +2 -6
- package/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt +5 -0
- package/android/src/test/java/com/reactnativenavigation/BaseRobolectricTest.kt +1 -1
- package/android/src/test/java/com/reactnativenavigation/BaseTest.kt +3 -2
- package/android/src/test/java/com/reactnativenavigation/ShadowReactView.java +45 -0
- package/android/src/test/java/com/reactnativenavigation/ShadowSoLoader.java +28 -0
- package/android/src/test/java/com/reactnativenavigation/TestApplication.kt +2 -2
- package/android/src/test/java/com/reactnativenavigation/mocks/SimpleViewController.java +14 -7
- package/android/src/test/java/com/reactnativenavigation/presentation/PresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/utils/ButtonPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/utils/LayoutFactoryTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsControllerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/bottomtabs/BottomTabsPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/component/ComponentViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/externalcomponent/ExternalComponentViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalAnimatorTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalPresenterTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/modal/ModalStackTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/NavigatorTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/navigator/RootPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/overlay/OverlayManagerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/parent/ParentControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/sidemenu/SideMenuControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/BackButtonHelperTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/FloatingActionButtonTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackAnimatorTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackControllerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/StackPresenterTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TitleBarButtonControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TitleBarReactViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarButtonControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/stack/TopBarControllerTest.kt +0 -2
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/viewcontrollers/viewcontroller/ViewControllerTest.java +0 -1
- package/android/src/test/java/com/reactnativenavigation/views/TitleAndButtonsContainerTest.kt +0 -1
- package/android/src/test/java/com/reactnativenavigation/views/TitleSubTitleLayoutTest.kt +0 -1
- package/ios/BottomTabsAppearancePresenter.mm +17 -5
- package/ios/RNNBottomTabsController.mm +1 -4
- package/ios/RNNCommandsHandler.mm +29 -23
- package/ios/RNNOverlayManager.mm +2 -0
- package/lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/statusbar/StatusBarPresenter.kt +212 -0
- package/lib/module/events/EventsRegistry.test.js.map +1 -1
- package/package.json +2 -2
- package/src/events/EventsRegistry.test.tsx +3 -3
package/android/build.gradle
CHANGED
|
@@ -130,6 +130,7 @@ dependencies {
|
|
|
130
130
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
|
131
131
|
|
|
132
132
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
|
133
|
+
implementation 'androidx.activity:activity:1.9.0'
|
|
133
134
|
implementation 'androidx.annotation:annotation:1.2.0'
|
|
134
135
|
implementation 'com.google.android.material:material:1.2.0-alpha03'
|
|
135
136
|
|
|
@@ -7,6 +7,7 @@ import android.os.Build;
|
|
|
7
7
|
import android.os.Bundle;
|
|
8
8
|
import android.view.KeyEvent;
|
|
9
9
|
import android.view.View;
|
|
10
|
+
import android.view.ViewGroup;
|
|
10
11
|
|
|
11
12
|
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
|
12
13
|
import com.facebook.react.modules.core.PermissionAwareActivity;
|
|
@@ -17,10 +18,12 @@ import com.reactnativenavigation.viewcontrollers.viewcontroller.RootPresenter;
|
|
|
17
18
|
import com.reactnativenavigation.react.JsDevReloadHandler;
|
|
18
19
|
import com.reactnativenavigation.react.ReactGateway;
|
|
19
20
|
import com.reactnativenavigation.react.CommandListenerAdapter;
|
|
21
|
+
import com.reactnativenavigation.utils.SystemUiUtils;
|
|
20
22
|
import com.reactnativenavigation.viewcontrollers.child.ChildControllersRegistry;
|
|
21
23
|
import com.reactnativenavigation.viewcontrollers.modal.ModalStack;
|
|
22
24
|
import com.reactnativenavigation.viewcontrollers.navigator.Navigator;
|
|
23
25
|
|
|
26
|
+
import androidx.activity.EdgeToEdge;
|
|
24
27
|
import androidx.activity.OnBackPressedCallback;
|
|
25
28
|
import androidx.annotation.NonNull;
|
|
26
29
|
import androidx.annotation.Nullable;
|
|
@@ -36,6 +39,7 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
|
|
|
36
39
|
|
|
37
40
|
@Override
|
|
38
41
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
42
|
+
enableEdgeToEdge();
|
|
39
43
|
super.onCreate(savedInstanceState);
|
|
40
44
|
if (isFinishing()) {
|
|
41
45
|
return;
|
|
@@ -63,7 +67,19 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
|
|
|
63
67
|
@Override
|
|
64
68
|
public void onPostCreate(@Nullable Bundle savedInstanceState) {
|
|
65
69
|
super.onPostCreate(savedInstanceState);
|
|
66
|
-
|
|
70
|
+
ViewGroup contentLayout = findViewById(android.R.id.content);
|
|
71
|
+
navigator.setContentLayout(contentLayout);
|
|
72
|
+
SystemUiUtils.setupSystemBarBackgrounds(this, contentLayout);
|
|
73
|
+
applyThemeStatusBarColor();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private void applyThemeStatusBarColor() {
|
|
77
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
|
|
78
|
+
&& SystemUiUtils.needsManualStatusBarBackground()) {
|
|
79
|
+
//noinspection deprecation
|
|
80
|
+
getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
|
|
81
|
+
SystemUiUtils.setStatusBarColor(getWindow(), android.graphics.Color.TRANSPARENT);
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
@Override
|
|
@@ -88,6 +104,7 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
|
|
|
88
104
|
@Override
|
|
89
105
|
protected void onDestroy() {
|
|
90
106
|
super.onDestroy();
|
|
107
|
+
SystemUiUtils.tearDown();
|
|
91
108
|
if (navigator != null) {
|
|
92
109
|
navigator.destroy();
|
|
93
110
|
}
|
|
@@ -146,6 +163,36 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
|
|
|
146
163
|
navigator.destroyViews();
|
|
147
164
|
}
|
|
148
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Controls when edge-to-edge is enabled. Override to customize the decision logic.
|
|
168
|
+
* Call {@link #activateEdgeToEdge()} from your override to enable edge-to-edge.
|
|
169
|
+
* <p>
|
|
170
|
+
* The default implementation enables edge-to-edge only if the app theme sets
|
|
171
|
+
* {@code windowOptOutEdgeToEdgeEnforcement} to {@code false} (API 35+).
|
|
172
|
+
* Called at the start of onCreate, before super.onCreate.
|
|
173
|
+
*/
|
|
174
|
+
protected void enableEdgeToEdge() {
|
|
175
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
176
|
+
android.content.res.TypedArray a = getTheme().obtainStyledAttributes(
|
|
177
|
+
new int[]{android.R.attr.windowOptOutEdgeToEdgeEnforcement});
|
|
178
|
+
boolean optOut = a.getBoolean(0, true);
|
|
179
|
+
a.recycle();
|
|
180
|
+
if (!optOut) {
|
|
181
|
+
activateEdgeToEdge();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Enables edge-to-edge display and notifies the navigation framework.
|
|
188
|
+
* Call this from {@link #enableEdgeToEdge()} overrides instead of
|
|
189
|
+
* calling {@code EdgeToEdge.enable()} directly.
|
|
190
|
+
*/
|
|
191
|
+
protected void activateEdgeToEdge() {
|
|
192
|
+
EdgeToEdge.enable(this);
|
|
193
|
+
SystemUiUtils.activateEdgeToEdge();
|
|
194
|
+
}
|
|
195
|
+
|
|
149
196
|
protected void addDefaultSplashLayout() {
|
|
150
197
|
View view = new View(this);
|
|
151
198
|
setContentView(view);
|
|
@@ -34,8 +34,8 @@ open class ModalHostLayout(reactContext: ThemedReactContext) : ViewGroup(reactCo
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
@TargetApi(23)
|
|
37
|
-
override fun dispatchProvideStructure(structure: ViewStructure) {
|
|
38
|
-
mHostView.dispatchProvideStructure(
|
|
37
|
+
override fun dispatchProvideStructure(structure: ViewStructure?) {
|
|
38
|
+
structure?.let { mHostView.dispatchProvideStructure(it) }
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {}
|
|
@@ -18,9 +18,8 @@ import android.graphics.Typeface;
|
|
|
18
18
|
import android.text.TextUtils;
|
|
19
19
|
import androidx.annotation.Nullable;
|
|
20
20
|
import com.facebook.react.bridge.ReadableArray;
|
|
21
|
-
import com.facebook.react.common.ReactConstants;
|
|
22
21
|
import com.facebook.react.views.text.ReactFontManager;
|
|
23
|
-
import com.facebook.react.
|
|
22
|
+
import com.facebook.react.common.ReactConstants;
|
|
24
23
|
import java.util.ArrayList;
|
|
25
24
|
import java.util.List;
|
|
26
25
|
|
|
@@ -3,10 +3,14 @@ package com.reactnativenavigation.utils
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.graphics.Color
|
|
5
5
|
import android.graphics.Rect
|
|
6
|
+
import android.graphics.drawable.ColorDrawable
|
|
7
|
+
import android.view.Gravity
|
|
6
8
|
import android.view.View
|
|
9
|
+
import android.view.ViewGroup
|
|
7
10
|
import android.view.Window
|
|
11
|
+
import android.widget.FrameLayout
|
|
8
12
|
import androidx.annotation.ColorInt
|
|
9
|
-
import androidx.core.view.
|
|
13
|
+
import androidx.core.view.ViewCompat
|
|
10
14
|
import androidx.core.view.WindowInsetsCompat
|
|
11
15
|
import androidx.core.view.WindowInsetsControllerCompat
|
|
12
16
|
import kotlin.math.abs
|
|
@@ -16,8 +20,18 @@ object SystemUiUtils {
|
|
|
16
20
|
private const val STATUS_BAR_HEIGHT_M = 24
|
|
17
21
|
internal const val STATUS_BAR_HEIGHT_TRANSLUCENCY = 0.65f
|
|
18
22
|
private var statusBarHeight = -1
|
|
19
|
-
|
|
23
|
+
|
|
24
|
+
const val DEFAULT_NAV_BAR_COLOR = Color.BLACK
|
|
25
|
+
private const val THREE_BUTTON_NAV_BAR_OPACITY = 0.8f
|
|
26
|
+
|
|
27
|
+
private var statusBarBackgroundView: View? = null
|
|
28
|
+
private var statusBarBackgroundActivity: java.lang.ref.WeakReference<Activity>? = null
|
|
29
|
+
private var navBarBackgroundView: View? = null
|
|
30
|
+
@JvmStatic
|
|
31
|
+
var isEdgeToEdgeActive = false
|
|
20
32
|
private set
|
|
33
|
+
private var isThreeButtonNav = false
|
|
34
|
+
private var lastExplicitNavBarColor: Int? = null
|
|
21
35
|
|
|
22
36
|
@JvmStatic
|
|
23
37
|
fun getStatusBarHeight(activity: Activity?): Int {
|
|
@@ -45,98 +59,222 @@ object SystemUiUtils {
|
|
|
45
59
|
statusBarHeight = height
|
|
46
60
|
}
|
|
47
61
|
|
|
48
|
-
|
|
49
62
|
@JvmStatic
|
|
50
63
|
fun getStatusBarHeightDp(activity: Activity?): Int {
|
|
51
|
-
return UiUtils.pxToDp(activity, getStatusBarHeight(activity).toFloat())
|
|
52
|
-
.toInt()
|
|
64
|
+
return UiUtils.pxToDp(activity, getStatusBarHeight(activity).toFloat()).toInt()
|
|
53
65
|
}
|
|
54
66
|
|
|
67
|
+
// region Setup
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initializes view-based system bar backgrounds for edge-to-edge.
|
|
71
|
+
* Call from Activity.onPostCreate after the navigator content layout is set.
|
|
72
|
+
*
|
|
73
|
+
* Status bar: reuses the system's android:id/statusBarBackground DecorView child
|
|
74
|
+
* when available. On API 35+ with EdgeToEdge, this view may not exist, so a
|
|
75
|
+
* manual view is created in the content layout, sized by status bar insets.
|
|
76
|
+
* Navigation bar: creates a view in [contentLayout] sized by WindowInsets,
|
|
77
|
+
* since the system's navigationBarBackground is not available with EdgeToEdge.
|
|
78
|
+
*/
|
|
55
79
|
@JvmStatic
|
|
56
|
-
fun
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
fun setupSystemBarBackgrounds(activity: Activity, contentLayout: ViewGroup) {
|
|
81
|
+
setupStatusBarBackground(activity)
|
|
82
|
+
setupNavigationBarBackground(contentLayout)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private fun setupStatusBarBackground(activity: Activity) {
|
|
86
|
+
if (statusBarBackgroundView != null) return
|
|
87
|
+
val sbView = activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)
|
|
88
|
+
if (sbView != null) {
|
|
89
|
+
statusBarBackgroundView = sbView
|
|
90
|
+
} else {
|
|
91
|
+
statusBarBackgroundActivity = java.lang.ref.WeakReference(activity)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private fun ensureStatusBarBackgroundView(): View? {
|
|
96
|
+
statusBarBackgroundView?.let { return it }
|
|
97
|
+
val activity = statusBarBackgroundActivity?.get() ?: return null
|
|
98
|
+
val contentLayout = activity.findViewById<ViewGroup>(android.R.id.content) ?: return null
|
|
99
|
+
val view = View(activity)
|
|
100
|
+
val params = FrameLayout.LayoutParams(
|
|
101
|
+
FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.TOP
|
|
102
|
+
)
|
|
103
|
+
contentLayout.addView(view, params)
|
|
104
|
+
statusBarBackgroundView = view
|
|
105
|
+
statusBarBackgroundActivity = null
|
|
106
|
+
|
|
107
|
+
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
|
108
|
+
val sbHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
|
|
109
|
+
val lp = v.layoutParams
|
|
110
|
+
if (lp.height != sbHeight) {
|
|
111
|
+
lp.height = sbHeight
|
|
112
|
+
v.layoutParams = lp
|
|
62
113
|
}
|
|
114
|
+
insets
|
|
63
115
|
}
|
|
116
|
+
view.requestApplyInsets()
|
|
117
|
+
return view
|
|
64
118
|
}
|
|
65
119
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.navigationBars())
|
|
120
|
+
private fun setupNavigationBarBackground(contentLayout: ViewGroup) {
|
|
121
|
+
if (navBarBackgroundView != null) return
|
|
122
|
+
val view = View(contentLayout.context).apply {
|
|
123
|
+
setBackgroundColor(Color.BLACK)
|
|
71
124
|
}
|
|
125
|
+
val params = FrameLayout.LayoutParams(
|
|
126
|
+
FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.BOTTOM
|
|
127
|
+
)
|
|
128
|
+
contentLayout.addView(view, params)
|
|
129
|
+
navBarBackgroundView = view
|
|
130
|
+
|
|
131
|
+
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
|
132
|
+
val navBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
|
|
133
|
+
val tappableHeight = insets.getInsets(WindowInsetsCompat.Type.tappableElement()).bottom
|
|
134
|
+
val wasThreeButton = isThreeButtonNav
|
|
135
|
+
isThreeButtonNav = tappableHeight > 0
|
|
136
|
+
if (isThreeButtonNav != wasThreeButton) {
|
|
137
|
+
val color = lastExplicitNavBarColor ?: getDefaultNavBarColor()
|
|
138
|
+
v.setBackgroundColor(color)
|
|
139
|
+
}
|
|
140
|
+
val lp = v.layoutParams
|
|
141
|
+
if (lp.height != navBarHeight) {
|
|
142
|
+
lp.height = navBarHeight
|
|
143
|
+
v.layoutParams = lp
|
|
144
|
+
}
|
|
145
|
+
insets
|
|
146
|
+
}
|
|
147
|
+
view.requestApplyInsets()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Returns the default navigation bar color, applying 80% opacity for 3-button navigation.
|
|
152
|
+
* Gesture navigation gets a fully opaque color since the bar is minimal.
|
|
153
|
+
*/
|
|
154
|
+
@JvmStatic
|
|
155
|
+
fun getDefaultNavBarColor(): Int {
|
|
156
|
+
if (!isThreeButtonNav) return DEFAULT_NAV_BAR_COLOR
|
|
157
|
+
val alpha = (THREE_BUTTON_NAV_BAR_OPACITY * 255).toInt()
|
|
158
|
+
return Color.argb(alpha, Color.red(DEFAULT_NAV_BAR_COLOR), Color.green(DEFAULT_NAV_BAR_COLOR), Color.blue(DEFAULT_NAV_BAR_COLOR))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Returns true when the system statusBarBackground view was not found during setup,
|
|
163
|
+
* meaning a manual view will be lazily created on the first setStatusBarColor call.
|
|
164
|
+
* Use this to decide whether to apply a theme-based initial status bar color.
|
|
165
|
+
*/
|
|
166
|
+
@JvmStatic
|
|
167
|
+
fun needsManualStatusBarBackground(): Boolean = statusBarBackgroundActivity != null
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Marks edge-to-edge as active. Call after EdgeToEdge.enable() in the activity.
|
|
171
|
+
* This flag controls whether navigation bar insets are forwarded to SafeAreaView
|
|
172
|
+
* and whether the view-based nav bar background is used for color changes.
|
|
173
|
+
*/
|
|
174
|
+
@JvmStatic
|
|
175
|
+
fun activateEdgeToEdge() {
|
|
176
|
+
isEdgeToEdgeActive = true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Clears references to system bar background views.
|
|
181
|
+
* Call from Activity.onDestroy to avoid leaking views across activity recreation.
|
|
182
|
+
*/
|
|
183
|
+
@JvmStatic
|
|
184
|
+
fun tearDown() {
|
|
185
|
+
statusBarBackgroundView = null
|
|
186
|
+
statusBarBackgroundActivity = null
|
|
187
|
+
navBarBackgroundView = null
|
|
188
|
+
isEdgeToEdgeActive = false
|
|
189
|
+
isThreeButtonNav = false
|
|
190
|
+
lastExplicitNavBarColor = null
|
|
191
|
+
statusBarHeight = -1
|
|
72
192
|
}
|
|
73
193
|
|
|
194
|
+
// endregion
|
|
195
|
+
|
|
196
|
+
// region Status Bar
|
|
197
|
+
|
|
74
198
|
@JvmStatic
|
|
75
199
|
fun setStatusBarColorScheme(window: Window?, view: View, isDark: Boolean) {
|
|
76
200
|
window?.let {
|
|
77
201
|
WindowInsetsControllerCompat(window, view).isAppearanceLightStatusBars = isDark
|
|
78
|
-
// Workaround: on devices with api 30 status bar icons flickers or get hidden when removing view
|
|
79
|
-
//turns out it is a bug on such devices, fixed by using system flags until it is fixed.
|
|
80
|
-
var flags = view.systemUiVisibility
|
|
81
|
-
flags = if (isDark) {
|
|
82
|
-
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
|
83
|
-
} else {
|
|
84
|
-
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
view.systemUiVisibility = flags
|
|
88
202
|
}
|
|
89
203
|
}
|
|
90
204
|
|
|
91
205
|
@JvmStatic
|
|
92
206
|
fun setStatusBarTranslucent(window: Window?) {
|
|
93
|
-
window?.let {
|
|
94
|
-
setStatusBarColor(window,
|
|
207
|
+
getStatusBarColor(window)?.let { currentColor ->
|
|
208
|
+
setStatusBarColor(window, currentColor, true)
|
|
95
209
|
}
|
|
96
210
|
}
|
|
97
211
|
|
|
98
212
|
@JvmStatic
|
|
99
213
|
fun isTranslucent(window: Window?): Boolean {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
} ?: false
|
|
214
|
+
val color = getStatusBarColor(window) ?: return false
|
|
215
|
+
return Color.alpha(color) < 255
|
|
103
216
|
}
|
|
104
217
|
|
|
105
218
|
@JvmStatic
|
|
106
219
|
fun clearStatusBarTranslucency(window: Window?) {
|
|
107
|
-
window?.let {
|
|
108
|
-
setStatusBarColor(
|
|
220
|
+
getStatusBarColor(window)?.let { currentColor ->
|
|
221
|
+
setStatusBarColor(window, currentColor, false)
|
|
109
222
|
}
|
|
110
223
|
}
|
|
111
224
|
|
|
112
225
|
@JvmStatic
|
|
113
|
-
fun setStatusBarColor(
|
|
114
|
-
window: Window?,
|
|
115
|
-
@ColorInt color: Int,
|
|
116
|
-
translucent: Boolean
|
|
117
|
-
) {
|
|
226
|
+
fun setStatusBarColor(window: Window?, @ColorInt color: Int, translucent: Boolean) {
|
|
118
227
|
val colorAlpha = Color.alpha(color)
|
|
119
|
-
val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha/255.0f
|
|
120
|
-
val
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
228
|
+
val alpha = if (translucent && colorAlpha == 255) STATUS_BAR_HEIGHT_TRANSLUCENCY else colorAlpha / 255.0f
|
|
229
|
+
val opaqueColor = Color.argb(
|
|
230
|
+
ceil(alpha * 255).toInt(),
|
|
231
|
+
Color.red(color),
|
|
232
|
+
Color.green(color),
|
|
233
|
+
Color.blue(color)
|
|
234
|
+
)
|
|
235
|
+
applyStatusBarColor(window, opaqueColor)
|
|
125
236
|
}
|
|
126
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Sets the status bar background color, lazily creating a manual view on API 35+
|
|
240
|
+
* if the system view wasn't available at setup time. Use this for explicit app-level
|
|
241
|
+
* color requests (e.g. from MainActivity or NavigationActivity).
|
|
242
|
+
*/
|
|
243
|
+
@JvmStatic
|
|
127
244
|
fun setStatusBarColor(window: Window?, color: Int) {
|
|
128
|
-
|
|
245
|
+
val view = ensureStatusBarBackgroundView()
|
|
246
|
+
if (view != null) {
|
|
247
|
+
view.setBackgroundColor(color)
|
|
248
|
+
} else {
|
|
249
|
+
@Suppress("DEPRECATION")
|
|
250
|
+
window?.statusBarColor = color
|
|
251
|
+
}
|
|
129
252
|
}
|
|
130
253
|
|
|
254
|
+
private fun applyStatusBarColor(window: Window?, color: Int) {
|
|
255
|
+
statusBarBackgroundView?.setBackgroundColor(color) ?: run {
|
|
256
|
+
@Suppress("DEPRECATION")
|
|
257
|
+
window?.statusBarColor = color
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Gets the current status bar background color.
|
|
263
|
+
* Reads from the view-based background when available,
|
|
264
|
+
* falls back to the deprecated window API on older configurations.
|
|
265
|
+
*/
|
|
131
266
|
@JvmStatic
|
|
132
267
|
fun getStatusBarColor(window: Window?): Int? {
|
|
268
|
+
statusBarBackgroundView?.let { view ->
|
|
269
|
+
(view.background as? ColorDrawable)?.let { return it.color }
|
|
270
|
+
}
|
|
271
|
+
@Suppress("DEPRECATION")
|
|
133
272
|
return window?.statusBarColor
|
|
134
273
|
}
|
|
135
274
|
|
|
136
275
|
@JvmStatic
|
|
137
276
|
fun hideStatusBar(window: Window?, view: View) {
|
|
138
277
|
window?.let {
|
|
139
|
-
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
140
278
|
WindowInsetsControllerCompat(window, view).let { controller ->
|
|
141
279
|
controller.hide(WindowInsetsCompat.Type.statusBars())
|
|
142
280
|
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
|
@@ -147,22 +285,49 @@ object SystemUiUtils {
|
|
|
147
285
|
@JvmStatic
|
|
148
286
|
fun showStatusBar(window: Window?, view: View) {
|
|
149
287
|
window?.let {
|
|
150
|
-
WindowCompat.setDecorFitsSystemWindows(window, true)
|
|
151
288
|
WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.statusBars())
|
|
152
289
|
}
|
|
153
290
|
}
|
|
154
291
|
|
|
292
|
+
// endregion
|
|
293
|
+
|
|
294
|
+
// region Navigation Bar
|
|
295
|
+
|
|
155
296
|
@JvmStatic
|
|
156
|
-
fun
|
|
297
|
+
fun hideNavigationBar(window: Window?, view: View) {
|
|
157
298
|
window?.let {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
|
|
162
|
-
controller.isAppearanceLightNavigationBars = lightColor
|
|
299
|
+
WindowInsetsControllerCompat(window, view).let { controller ->
|
|
300
|
+
controller.hide(WindowInsetsCompat.Type.navigationBars())
|
|
301
|
+
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
|
163
302
|
}
|
|
164
|
-
window.navigationBarColor = color
|
|
165
303
|
}
|
|
166
304
|
}
|
|
167
305
|
|
|
306
|
+
@JvmStatic
|
|
307
|
+
fun showNavigationBar(window: Window?, view: View) {
|
|
308
|
+
window?.let {
|
|
309
|
+
WindowInsetsControllerCompat(window, view).show(WindowInsetsCompat.Type.navigationBars())
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Sets the navigation bar background color and icon appearance.
|
|
315
|
+
* Uses the view-based background when available (edge-to-edge),
|
|
316
|
+
* falls back to the deprecated window API on older configurations.
|
|
317
|
+
*/
|
|
318
|
+
@JvmStatic
|
|
319
|
+
fun setNavigationBarBackgroundColor(window: Window?, color: Int, lightColor: Boolean) {
|
|
320
|
+
lastExplicitNavBarColor = color
|
|
321
|
+
window?.let {
|
|
322
|
+
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars = lightColor
|
|
323
|
+
}
|
|
324
|
+
if (isEdgeToEdgeActive) {
|
|
325
|
+
navBarBackgroundView?.setBackgroundColor(color)
|
|
326
|
+
} else {
|
|
327
|
+
@Suppress("DEPRECATION")
|
|
328
|
+
window?.navigationBarColor = color
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// endregion
|
|
168
333
|
}
|
|
@@ -254,12 +254,13 @@ public class BottomTabsController extends ParentController<BottomTabsLayout> imp
|
|
|
254
254
|
|
|
255
255
|
@Override
|
|
256
256
|
public int getBottomInset(ViewController<?> child) {
|
|
257
|
-
return presenter.
|
|
257
|
+
return presenter.getChildrenBottomInset(resolveChildOptions(child)) + perform(getParentController(), 0, p -> p.getBottomInset(this));
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
@Override
|
|
261
261
|
public void applyBottomInset() {
|
|
262
|
-
presenter.
|
|
262
|
+
presenter.applyChildrenInset(getBottomInset());
|
|
263
|
+
presenter.applySelfInset(getBottomInset());
|
|
263
264
|
super.applyBottomInset();
|
|
264
265
|
}
|
|
265
266
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.reactnativenavigation.viewcontrollers.bottomtabs
|
|
2
2
|
|
|
3
3
|
import android.animation.Animator
|
|
4
|
+
import android.app.Activity
|
|
4
5
|
import android.graphics.Color
|
|
5
6
|
import android.view.ViewGroup
|
|
6
7
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|
@@ -12,6 +13,8 @@ import com.reactnativenavigation.RNNToggles.TAB_BAR_TRANSLUCENCE
|
|
|
12
13
|
import com.reactnativenavigation.options.Options
|
|
13
14
|
import com.reactnativenavigation.options.params.BottomTabsLayoutStyle
|
|
14
15
|
import com.reactnativenavigation.options.params.Fraction
|
|
16
|
+
import com.reactnativenavigation.utils.ColorUtils.isColorLight
|
|
17
|
+
import com.reactnativenavigation.utils.SystemUiUtils
|
|
15
18
|
import com.reactnativenavigation.utils.UiUtils
|
|
16
19
|
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController
|
|
17
20
|
import com.reactnativenavigation.views.bottomtabs.BottomTabs
|
|
@@ -92,7 +95,9 @@ class BottomTabsPresenter(
|
|
|
92
95
|
|
|
93
96
|
// Keep this before the translucent check below
|
|
94
97
|
if (bottomTabsOptions.backgroundColor.hasValue()) {
|
|
95
|
-
|
|
98
|
+
val color = bottomTabsOptions.backgroundColor.get()
|
|
99
|
+
bottomTabsContainer.setBackgroundColor(color)
|
|
100
|
+
syncNavigationBarColor(options, color)
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
if (RNNFeatureToggles.isEnabled(TAB_BAR_TRANSLUCENCE)) {
|
|
@@ -212,7 +217,9 @@ class BottomTabsPresenter(
|
|
|
212
217
|
bottomTabsContainer.enableBackgroundBlur()
|
|
213
218
|
} else {
|
|
214
219
|
bottomTabsContainer.disableBackgroundBlur()
|
|
215
|
-
|
|
220
|
+
val color = bottomTabsOptions.backgroundColor.get(Color.WHITE)!!
|
|
221
|
+
bottomTabsContainer.setBackgroundColor(color)
|
|
222
|
+
syncNavigationBarColor(options, color)
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
bottomTabs.setLayoutDirection(options.layout.direction)
|
|
@@ -282,13 +289,17 @@ class BottomTabsPresenter(
|
|
|
282
289
|
bottomTabs.setBehaviorTranslationEnabled(bottomTabsOptions.hideOnScroll[false])
|
|
283
290
|
}
|
|
284
291
|
|
|
285
|
-
fun
|
|
292
|
+
fun applyChildrenInset(bottomInset: Int) {
|
|
286
293
|
(bottomTabsContainer.layoutParams as ViewGroup.MarginLayoutParams).updateMargins(bottom = bottomInset)
|
|
287
294
|
bottomTabsContainer.requestLayout()
|
|
288
295
|
}
|
|
289
296
|
|
|
290
|
-
fun
|
|
291
|
-
return if (resolvedOptions.withDefaultOptions(defaultOptions).bottomTabsOptions.isHiddenOrDrawBehind) 0 else
|
|
297
|
+
fun getChildrenBottomInset(resolvedOptions: Options): Int {
|
|
298
|
+
return if (resolvedOptions.withDefaultOptions(defaultOptions).bottomTabsOptions.isHiddenOrDrawBehind) 0 else (bottomTabsContainer.height)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
fun applySelfInset(bottomInset: Int) {
|
|
302
|
+
bottomTabsContainer.setBottomInset(bottomInset)
|
|
292
303
|
}
|
|
293
304
|
|
|
294
305
|
fun getPushAnimation(appearingOptions: Options): Animator? {
|
|
@@ -351,7 +362,9 @@ class BottomTabsPresenter(
|
|
|
351
362
|
bottomTabsContainer.disableBackgroundBlur()
|
|
352
363
|
|
|
353
364
|
// TODO Change to bottomTabsContainer.setBackgroundColor()?
|
|
354
|
-
|
|
365
|
+
val color = bottomTabsOptions.backgroundColor.get(Color.WHITE)!!
|
|
366
|
+
bottomTabs.setBackgroundColor(color)
|
|
367
|
+
syncNavigationBarColor(options, color)
|
|
355
368
|
}
|
|
356
369
|
|
|
357
370
|
if (bottomTabsOptions.shadowOptions.hasValue()) {
|
|
@@ -376,4 +389,11 @@ class BottomTabsPresenter(
|
|
|
376
389
|
val margin = UiUtils.dpToPx(bottomTabsContainer.context, marginDp.toFloat()).roundToInt()
|
|
377
390
|
return margin
|
|
378
391
|
}
|
|
392
|
+
|
|
393
|
+
private fun syncNavigationBarColor(options: Options, tabsColor: Int) {
|
|
394
|
+
val resolved = options.copy().withDefaultOptions(defaultOptions)
|
|
395
|
+
if (resolved.navigationBar.backgroundColor.hasValue()) return
|
|
396
|
+
val window = (bottomTabsContainer.context as? Activity)?.window ?: return
|
|
397
|
+
SystemUiUtils.setNavigationBarBackgroundColor(window, tabsColor, isColorLight(tabsColor))
|
|
398
|
+
}
|
|
379
399
|
}
|
|
@@ -172,9 +172,12 @@ public class ComponentViewController extends ChildController<ComponentLayout> {
|
|
|
172
172
|
int systemWindowInsetTop = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top +
|
|
173
173
|
insets.getInsets(WindowInsetsCompat.Type.navigationBars()).top -
|
|
174
174
|
systemBarsInsets.top;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
|
|
176
|
+
int navBarBottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
|
|
177
|
+
int imeBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
|
|
178
|
+
int systemWindowInsetBottom = SystemUiUtils.isEdgeToEdgeActive()
|
|
179
|
+
? Math.max(imeBottom, navBarBottom)
|
|
180
|
+
: imeBottom;
|
|
178
181
|
|
|
179
182
|
WindowInsetsCompat finalInsets = new WindowInsetsCompat.Builder()
|
|
180
183
|
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime(),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package com.reactnativenavigation.viewcontrollers.viewcontroller
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import com.facebook.react.ReactInstanceManager
|
|
5
4
|
import com.facebook.react.modules.i18nmanager.I18nUtil
|
|
6
5
|
import com.reactnativenavigation.options.Options
|
|
7
6
|
|
|
@@ -16,4 +15,4 @@ class LayoutDirectionApplier {
|
|
|
16
15
|
I18nUtil.instance.forceRTL(currentContext, options.layout.direction.isRtl)
|
|
17
16
|
}
|
|
18
17
|
}
|
|
19
|
-
}
|
|
18
|
+
}
|
|
@@ -136,14 +136,12 @@ public class Presenter {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
private void setNavigationBarBackgroundColor(NavigationBarOptions navigationBar) {
|
|
139
|
-
int
|
|
140
|
-
navigationBarDefaultColor = navigationBarDefaultColor == -1 ? Color.BLACK : navigationBarDefaultColor;
|
|
139
|
+
int defaultColor = SystemUiUtils.getDefaultNavBarColor();
|
|
141
140
|
if (navigationBar.backgroundColor.canApplyValue()) {
|
|
142
|
-
int color = navigationBar.backgroundColor.get(
|
|
141
|
+
int color = navigationBar.backgroundColor.get(defaultColor);
|
|
143
142
|
SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(), color, isColorLight(color));
|
|
144
143
|
} else {
|
|
145
|
-
SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(),
|
|
146
|
-
|
|
144
|
+
SystemUiUtils.setNavigationBarBackgroundColor(activity.getWindow(), defaultColor, isColorLight(defaultColor));
|
|
147
145
|
}
|
|
148
146
|
}
|
|
149
147
|
|
|
@@ -430,17 +430,13 @@ public abstract class ViewController<T extends ViewGroup> implements ViewTreeObs
|
|
|
430
430
|
return false;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
-
public void applyTopInset() {
|
|
434
|
-
|
|
435
|
-
}
|
|
433
|
+
public void applyTopInset() {}
|
|
436
434
|
|
|
437
435
|
public int getTopInset() {
|
|
438
436
|
return 0;
|
|
439
437
|
}
|
|
440
438
|
|
|
441
|
-
public void applyBottomInset() {
|
|
442
|
-
|
|
443
|
-
}
|
|
439
|
+
public void applyBottomInset() {}
|
|
444
440
|
|
|
445
441
|
public int getBottomInset() {
|
|
446
442
|
return perform(parentController, 0, p -> p.getBottomInset(this));
|
package/android/src/main/java/com/reactnativenavigation/views/bottomtabs/BottomTabsContainer.kt
CHANGED
|
@@ -12,6 +12,7 @@ import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
|
|
|
12
12
|
import android.widget.LinearLayout
|
|
13
13
|
import androidx.annotation.RestrictTo
|
|
14
14
|
import androidx.core.graphics.ColorUtils
|
|
15
|
+
import androidx.core.view.updatePadding
|
|
15
16
|
import com.reactnativenavigation.options.params.Fraction
|
|
16
17
|
import com.reactnativenavigation.utils.UiUtils.dpToPx
|
|
17
18
|
import eightbitlab.com.blurview.BlurTarget
|
|
@@ -176,4 +177,8 @@ class BottomTabsContainer(context: Context, val bottomTabs: BottomTabs) : Shadow
|
|
|
176
177
|
fun setElevation(elevation: Fraction) {
|
|
177
178
|
setElevation(dpToPx(context, elevation.get().toFloat()))
|
|
178
179
|
}
|
|
180
|
+
|
|
181
|
+
fun setBottomInset(bottomInset: Int) {
|
|
182
|
+
blurringView.updatePadding(bottom = bottomInset)
|
|
183
|
+
}
|
|
179
184
|
}
|