diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java index 37845bc0ec27..3fb824099ecc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java @@ -138,9 +138,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { if (mActivity != null) { Window window = mActivity.getWindow(); if (window != null) { - if (WindowUtilKt.isEdgeToEdgeFeatureFlagOn()) { - WindowUtilKt.enableEdgeToEdge(window); - } + WindowUtilKt.initEdgeToEdge(mActivity); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) { window.setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/util/AndroidVersion.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/util/AndroidVersion.kt index 05c9221bcee2..c6d00b5520df 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/util/AndroidVersion.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/util/AndroidVersion.kt @@ -21,10 +21,27 @@ internal object AndroidVersion { internal const val VERSION_CODE_VANILLA_ICE_CREAM: Int = 35 /** - * This is the version code for Android 16 (SDK Level 36). Delete it once we bump up the default - * compile SDK version to 36. + * This is the version code for Android 16 (SDK Level 36). Internally at Meta this code is also + * compiled against SDK 34, so we need to retain this constant instead of using + * [Build.VERSION_CODES.BAKLAVA] directly. + */ + internal const val VERSION_CODE_BAKLAVA: Int = 36 + + /** + * android.R.attr.windowOptOutEdgeToEdgeEnforcement added in API 35. Internally at Meta this code + * is compiled against an SDK that may not have this attribute defined. + * https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/res/res/values/public-final.xml;l=3848;drc=7fa5818fb7037c541807c3754882d98f6ea22d20 */ - private const val VERSION_CODE_BAKLAVA: Int = 36 + internal const val ATTR_WINDOW_OPT_OUT_EDGE_TO_EDGE_ENFORCEMENT: Int = 0x0101069a + + /** + * This method is used to check if the current device is running Android 15 (SDK Level 35) or + * higher and the app is targeting Android 15 (SDK Level 35) or higher. + */ + @JvmStatic + internal fun isAtLeastTargetSdk35(context: Context): Boolean = + Build.VERSION.SDK_INT >= VERSION_CODE_VANILLA_ICE_CREAM && + context.applicationInfo.targetSdkVersion >= VERSION_CODE_VANILLA_ICE_CREAM /** * This method is used to check if the current device is running Android 16 (SDK Level 36) or diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt index 0cff3bc458d8..a157467719e6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/WindowUtil.kt @@ -7,6 +7,7 @@ package com.facebook.react.views.view +import android.app.Activity import android.graphics.Color import android.os.Build import android.view.Window @@ -15,6 +16,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import com.facebook.react.util.AndroidVersion import com.facebook.react.views.common.UiModeUtils // The light scrim color used in the platform API 29+ @@ -37,6 +39,32 @@ public fun setEdgeToEdgeFeatureFlagOn() { isEdgeToEdgeFeatureFlagOn = true } +internal fun initEdgeToEdge(activity: Activity) { + // When the app targets SDK 35+, edge-to-edge may be enforced by the OS even if the + // feature flag wasn't explicitly set. In that case, turn the flag on to match. + if (AndroidVersion.isAtLeastTargetSdk35(activity)) { + if (Build.VERSION.SDK_INT >= AndroidVersion.VERSION_CODE_BAKLAVA) { + // The device is running Android 16+ (where edge-to-edge is always enforced) + isEdgeToEdgeFeatureFlagOn = true + } else { + val attributes = intArrayOf(AndroidVersion.ATTR_WINDOW_OPT_OUT_EDGE_TO_EDGE_ENFORCEMENT) + val typedArray = activity.theme.obtainStyledAttributes(attributes) + + // The device is running Android 15 with / without opting out + isEdgeToEdgeFeatureFlagOn = + try { + !typedArray.getBoolean(0, false) + } finally { + typedArray.recycle() + } + } + } + + if (isEdgeToEdgeFeatureFlagOn) { + activity.window.enableEdgeToEdge() + } +} + @Suppress("DEPRECATION") internal fun Window.setStatusBarTranslucency(isTranslucent: Boolean) { // If the status bar is translucent hook into the window insets calculations