From 0a0ef2ef8de25caf54b3e6b774e8f8bd6c9de290 Mon Sep 17 00:00:00 2001 From: Krzysztof Ligarski Date: Mon, 22 Sep 2025 14:10:38 +0200 Subject: [PATCH] change cutout inset handling in SAV and CustomToolbar --- .../com/swmansion/rnscreens/CustomToolbar.kt | 17 ++------- .../rnscreens/safearea/SafeAreaView.kt | 36 +++++++++++++------ .../com/swmansion/rnscreens/utils/InsetsKt.kt | 5 ++- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt b/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt index ed43348f99..dd38afb3e0 100644 --- a/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +++ b/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt @@ -91,21 +91,10 @@ open class CustomToolbar( // 3. edge-to-edge with translucent navigation buttons bar. // // Additionally we need to gracefully handle possible display cutouts. - - // We use rootWindowInsets in lieu of insets or unhandledInsets here, - // because cutout sometimes (only in certain scenarios, e.g. with headerLeft view present) - // happen to be Insets.ZERO and is not reliable. - val rootWindowInsets = rootWindowInsets val cutoutInsets = - resolveInsetsOrZero(WindowInsetsCompat.Type.displayCutout(), rootWindowInsets) + resolveInsetsOrZero(WindowInsetsCompat.Type.displayCutout(), unhandledInsets) val systemBarInsets = - resolveInsetsOrZero(WindowInsetsCompat.Type.systemBars(), rootWindowInsets) - val statusBarInsetsStable = - resolveInsetsOrZero( - WindowInsetsCompat.Type.systemBars(), - rootWindowInsets, - ignoreVisibility = true, - ) + resolveInsetsOrZero(WindowInsetsCompat.Type.systemBars(), unhandledInsets) // This seems to work fine in all tested configurations, because cutout & system bars overlap // only in portrait mode & top inset is controlled separately, therefore we don't count @@ -124,7 +113,7 @@ open class CustomToolbar( val verticalInsets = InsetsCompat.of( 0, - max(cutoutInsets.top, if (shouldApplyTopInset) statusBarInsetsStable.top else 0), + max(cutoutInsets.top, if (shouldApplyTopInset) systemBarInsets.top else 0), 0, max(cutoutInsets.bottom, 0), ) diff --git a/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt b/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt index cb31893f68..13bf4666cf 100644 --- a/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt +++ b/android/src/main/java/com/swmansion/rnscreens/safearea/SafeAreaView.kt @@ -3,6 +3,7 @@ package com.swmansion.rnscreens.safearea import android.annotation.SuppressLint +import android.os.Build import android.util.Log import android.view.View import android.view.ViewTreeObserver @@ -114,28 +115,41 @@ class SafeAreaView( } } - return WindowInsetsCompat + var shouldConsumeDisplayCutout = false + var consumedInsets = WindowInsetsCompat .Builder(insets) .apply { if (insetType.containsSystem()) { + val consumedSystemBarsInsets = getConsumedInsetsFromSelectedEdges( + insets.getInsets( + WindowInsetsCompat.Type.systemBars(), + )) + + val consumedDisplayCutoutInsets = getConsumedInsetsFromSelectedEdges( + insets.getInsets( + WindowInsetsCompat.Type.displayCutout() + ) + ) + shouldConsumeDisplayCutout = consumedDisplayCutoutInsets == Insets.NONE + setInsets( WindowInsetsCompat.Type.systemBars(), - getConsumedInsetsFromSelectedEdges( - insets.getInsets( - WindowInsetsCompat.Type.systemBars(), - ), - ), + consumedSystemBarsInsets ) setInsets( WindowInsetsCompat.Type.displayCutout(), - getConsumedInsetsFromSelectedEdges( - insets.getInsets( - WindowInsetsCompat.Type.displayCutout(), - ), - ), + consumedDisplayCutoutInsets ) } }.build() + + // On Android versions prior to R, setInsets(WindowInsetsCompat.Type.displayCutout(), ...) + // does not work. We need to use previous API. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R && shouldConsumeDisplayCutout) { + consumedInsets = consumedInsets.consumeDisplayCutout() + } + + return consumedInsets } private fun updateInsetsIfNeeded(): Boolean { diff --git a/android/src/main/java/com/swmansion/rnscreens/utils/InsetsKt.kt b/android/src/main/java/com/swmansion/rnscreens/utils/InsetsKt.kt index 6e19705c09..dd312d7fb1 100644 --- a/android/src/main/java/com/swmansion/rnscreens/utils/InsetsKt.kt +++ b/android/src/main/java/com/swmansion/rnscreens/utils/InsetsKt.kt @@ -22,7 +22,10 @@ internal fun View.resolveInsetsOrZero( return InsetsCompat.NONE } - val windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(sourceWindowInsets, this) + // We don't use root view-aware WindowInsetsCompat to make sure we get information about display + // cutout inset being consumed by one of the ancestor views. Refer to WindowInsetsCompat + // `Impl20` implementation of getInsetsForType (case Type.DISPLAY_CUTOUT). + val windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(sourceWindowInsets) return if (!ignoreVisibility) { windowInsetsCompat.getInsets(insetType) } else {