From efe6b0e9f409fe1b7ea6b4a5d9e168045c0c5f02 Mon Sep 17 00:00:00 2001 From: Mark de Vocht Date: Mon, 13 Apr 2026 14:28:57 +0300 Subject: [PATCH 1/3] https://wix.atlassian.net/browse/WOAINFRA-3301 --- .../utils/SystemUiUtils.kt | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt index 35c74cb8bd..9d95809f8b 100644 --- a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +++ b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt @@ -69,11 +69,11 @@ object SystemUiUtils { * Initializes view-based system bar backgrounds for edge-to-edge. * Call from Activity.onPostCreate after the navigator content layout is set. * - * Status bar: reuses the system's android:id/statusBarBackground DecorView child. + * Status bar: reuses the system's android:id/statusBarBackground DecorView child + * when available. On API 35+ with EdgeToEdge, this view may not exist, so a + * manual view is created in the content layout, sized by status bar insets. * Navigation bar: creates a view in [contentLayout] sized by WindowInsets, * since the system's navigationBarBackground is not available with EdgeToEdge. - * - * Both fall back to deprecated window APIs when the views are unavailable. */ @JvmStatic fun setupSystemBarBackgrounds(activity: Activity, contentLayout: ViewGroup) { @@ -87,6 +87,27 @@ object SystemUiUtils { if (sbView != null) { sbView.setBackgroundColor(Color.BLACK) statusBarBackgroundView = sbView + } else { + val contentLayout = activity.findViewById(android.R.id.content) + val view = View(activity).apply { + setBackgroundColor(Color.BLACK) + } + val params = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.TOP + ) + contentLayout.addView(view, params) + statusBarBackgroundView = view + + ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> + val sbHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top + val lp = v.layoutParams + if (lp.height != sbHeight) { + lp.height = sbHeight + v.layoutParams = lp + } + insets + } + view.requestApplyInsets() } } From a3a3b16bf81bc152edea85779d0bbd4eb4340d3c Mon Sep 17 00:00:00 2001 From: Mark de Vocht Date: Mon, 13 Apr 2026 17:01:43 +0300 Subject: [PATCH 2/3] lazy loading --- .../utils/SystemUiUtils.kt | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt index 9d95809f8b..78c2375dff 100644 --- a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +++ b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt @@ -25,6 +25,7 @@ object SystemUiUtils { private const val THREE_BUTTON_NAV_BAR_OPACITY = 0.8f private var statusBarBackgroundView: View? = null + private var statusBarBackgroundActivity: java.lang.ref.WeakReference? = null private var navBarBackgroundView: View? = null @JvmStatic var isEdgeToEdgeActive = false @@ -88,27 +89,33 @@ object SystemUiUtils { sbView.setBackgroundColor(Color.BLACK) statusBarBackgroundView = sbView } else { - val contentLayout = activity.findViewById(android.R.id.content) - val view = View(activity).apply { - setBackgroundColor(Color.BLACK) - } - val params = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.TOP - ) - contentLayout.addView(view, params) - statusBarBackgroundView = view - - ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> - val sbHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top - val lp = v.layoutParams - if (lp.height != sbHeight) { - lp.height = sbHeight - v.layoutParams = lp - } - insets + statusBarBackgroundActivity = java.lang.ref.WeakReference(activity) + } + } + + private fun ensureStatusBarBackgroundView(): View? { + statusBarBackgroundView?.let { return it } + val activity = statusBarBackgroundActivity?.get() ?: return null + val contentLayout = activity.findViewById(android.R.id.content) ?: return null + val view = View(activity) + val params = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, 0, Gravity.TOP + ) + contentLayout.addView(view, params) + statusBarBackgroundView = view + statusBarBackgroundActivity = null + + ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> + val sbHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top + val lp = v.layoutParams + if (lp.height != sbHeight) { + lp.height = sbHeight + v.layoutParams = lp } - view.requestApplyInsets() + insets } + view.requestApplyInsets() + return view } private fun setupNavigationBarBackground(contentLayout: ViewGroup) { @@ -169,6 +176,7 @@ object SystemUiUtils { @JvmStatic fun tearDown() { statusBarBackgroundView = null + statusBarBackgroundActivity = null navBarBackgroundView = null isEdgeToEdgeActive = false isThreeButtonNav = false @@ -217,15 +225,25 @@ object SystemUiUtils { Color.green(color), Color.blue(color) ) - setStatusBarColor(window, opaqueColor) + applyStatusBarColor(window, opaqueColor) } /** - * Sets the status bar background color. - * Uses the view-based background when available (edge-to-edge), - * falls back to the deprecated window API on older configurations. + * Sets the status bar background color, lazily creating a manual view on API 35+ + * if the system view wasn't available at setup time. Use this for explicit app-level + * color requests (e.g. from MainActivity). */ fun setStatusBarColor(window: Window?, color: Int) { + val view = ensureStatusBarBackgroundView() + if (view != null) { + view.setBackgroundColor(color) + } else { + @Suppress("DEPRECATION") + window?.statusBarColor = color + } + } + + private fun applyStatusBarColor(window: Window?, color: Int) { statusBarBackgroundView?.setBackgroundColor(color) ?: run { @Suppress("DEPRECATION") window?.statusBarColor = color From 9baf307e97ba618e967bfd1a9e22c9a3cfc3dc33 Mon Sep 17 00:00:00 2001 From: Mark de Vocht Date: Tue, 14 Apr 2026 10:09:05 +0300 Subject: [PATCH 3/3] No more white statusbar in dark mode --- .../reactnativenavigation/NavigationActivity.java | 10 ++++++++++ .../com/reactnativenavigation/utils/SystemUiUtils.kt | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/reactnativenavigation/NavigationActivity.java b/android/src/main/java/com/reactnativenavigation/NavigationActivity.java index 0e5f70a8f9..4d6bdf4419 100644 --- a/android/src/main/java/com/reactnativenavigation/NavigationActivity.java +++ b/android/src/main/java/com/reactnativenavigation/NavigationActivity.java @@ -70,6 +70,16 @@ public void onPostCreate(@Nullable Bundle savedInstanceState) { ViewGroup contentLayout = findViewById(android.R.id.content); navigator.setContentLayout(contentLayout); SystemUiUtils.setupSystemBarBackgrounds(this, contentLayout); + applyThemeStatusBarColor(); + } + + private void applyThemeStatusBarColor() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM + && SystemUiUtils.needsManualStatusBarBackground()) { + //noinspection deprecation + getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT); + SystemUiUtils.setStatusBarColor(getWindow(), android.graphics.Color.TRANSPARENT); + } } @Override diff --git a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt index 78c2375dff..7df9a31d3e 100644 --- a/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt +++ b/android/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt @@ -86,7 +86,6 @@ object SystemUiUtils { if (statusBarBackgroundView != null) return val sbView = activity.window.decorView.findViewById(android.R.id.statusBarBackground) if (sbView != null) { - sbView.setBackgroundColor(Color.BLACK) statusBarBackgroundView = sbView } else { statusBarBackgroundActivity = java.lang.ref.WeakReference(activity) @@ -159,6 +158,14 @@ object SystemUiUtils { return Color.argb(alpha, Color.red(DEFAULT_NAV_BAR_COLOR), Color.green(DEFAULT_NAV_BAR_COLOR), Color.blue(DEFAULT_NAV_BAR_COLOR)) } + /** + * Returns true when the system statusBarBackground view was not found during setup, + * meaning a manual view will be lazily created on the first setStatusBarColor call. + * Use this to decide whether to apply a theme-based initial status bar color. + */ + @JvmStatic + fun needsManualStatusBarBackground(): Boolean = statusBarBackgroundActivity != null + /** * Marks edge-to-edge as active. Call after EdgeToEdge.enable() in the activity. * This flag controls whether navigation bar insets are forwarded to SafeAreaView @@ -231,8 +238,9 @@ object SystemUiUtils { /** * Sets the status bar background color, lazily creating a manual view on API 35+ * if the system view wasn't available at setup time. Use this for explicit app-level - * color requests (e.g. from MainActivity). + * color requests (e.g. from MainActivity or NavigationActivity). */ + @JvmStatic fun setStatusBarColor(window: Window?, color: Int) { val view = ensureStatusBarBackgroundView() if (view != null) {