Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Activity>? = null
private var navBarBackgroundView: View? = null
@JvmStatic
var isEdgeToEdgeActive = false
Expand Down Expand Up @@ -69,11 +70,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) {
Expand All @@ -85,9 +86,35 @@ object SystemUiUtils {
if (statusBarBackgroundView != null) return
val sbView = activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)
if (sbView != null) {
sbView.setBackgroundColor(Color.BLACK)
statusBarBackgroundView = sbView
} else {
statusBarBackgroundActivity = java.lang.ref.WeakReference(activity)
}
}

private fun ensureStatusBarBackgroundView(): View? {
statusBarBackgroundView?.let { return it }
val activity = statusBarBackgroundActivity?.get() ?: return null
val contentLayout = activity.findViewById<ViewGroup>(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
}
insets
}
view.requestApplyInsets()
return view
}

private fun setupNavigationBarBackground(contentLayout: ViewGroup) {
Expand Down Expand Up @@ -131,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
Expand All @@ -148,6 +183,7 @@ object SystemUiUtils {
@JvmStatic
fun tearDown() {
statusBarBackgroundView = null
statusBarBackgroundActivity = null
navBarBackgroundView = null
isEdgeToEdgeActive = false
isThreeButtonNav = false
Expand Down Expand Up @@ -196,15 +232,26 @@ 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 or NavigationActivity).
*/
@JvmStatic
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
Expand Down