diff --git a/gradle.properties b/gradle.properties index 12a6bcebf8b877..7b7c2b9bc5f361 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,8 @@ org.gradle.caching=true android.useAndroidX=true +edgeToEdgeEnabled=true + # Use this property to specify which architecture you want to build. # You can also override it from the CLI using # ./gradlew -PreactNativeArchitectures=x86_64 diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt index 61cd0f5107da46..836aa6764bccc1 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt @@ -14,7 +14,7 @@ object PropertyUtils { const val NEW_ARCH_ENABLED = "newArchEnabled" const val SCOPED_NEW_ARCH_ENABLED = "react.newArchEnabled" - /** Public property that toggles the New Architecture */ + /** Public property that toggles Hermes */ const val HERMES_ENABLED = "hermesEnabled" const val SCOPED_HERMES_ENABLED = "react.hermesEnabled" diff --git a/packages/helloworld/android/gradle.properties b/packages/helloworld/android/gradle.properties index eeb920d344cf18..0f939574cb16d5 100644 --- a/packages/helloworld/android/gradle.properties +++ b/packages/helloworld/android/gradle.properties @@ -10,3 +10,4 @@ android.useAndroidX=true reactNativeArchitectures=arm64-v8a newArchEnabled=true hermesEnabled=true +edgeToEdgeEnabled=false diff --git a/packages/react-native/Libraries/Modal/Modal.js b/packages/react-native/Libraries/Modal/Modal.js index 345191ab52850f..66ff718688a274 100644 --- a/packages/react-native/Libraries/Modal/Modal.js +++ b/packages/react-native/Libraries/Modal/Modal.js @@ -25,6 +25,7 @@ const AppContainer = require('../ReactNative/AppContainer'); const I18nManager = require('../ReactNative/I18nManager'); const {RootTagContext} = require('../ReactNative/RootTag'); const StyleSheet = require('../StyleSheet/StyleSheet'); +const Appearance = require('../Utilities/Appearance'); const Platform = require('../Utilities/Platform'); const React = require('react'); @@ -272,6 +273,8 @@ class Modal extends React.Component { return null; } + const isEdgeToEdge = Appearance.isEdgeToEdge(); + const containerStyles = { backgroundColor: this.props.transparent === true @@ -316,8 +319,10 @@ class Modal extends React.Component { onShow={this.props.onShow} onDismiss={onDismiss} visible={this.props.visible} - statusBarTranslucent={this.props.statusBarTranslucent} - navigationBarTranslucent={this.props.navigationBarTranslucent} + statusBarTranslucent={isEdgeToEdge || this.props.statusBarTranslucent} + navigationBarTranslucent={ + isEdgeToEdge || this.props.navigationBarTranslucent + } identifier={this._identifier} style={styles.modal} // $FlowFixMe[method-unbinding] added when improving typing for this parameters diff --git a/packages/react-native/Libraries/Utilities/Appearance.js b/packages/react-native/Libraries/Utilities/Appearance.js index d96047b1dda5f2..79ce0980939846 100644 --- a/packages/react-native/Libraries/Utilities/Appearance.js +++ b/packages/react-native/Libraries/Utilities/Appearance.js @@ -26,6 +26,8 @@ let lazyState: ?{ // Cache the color scheme to reduce the cost of reading it between changes. // NOTE: If `NativeAppearance` is null, this will always be null. appearance: ?Appearance, + // NOTE: If `NativeAppearance` is null, this will always be null. + edgeToEdge: ?boolean, // NOTE: This is non-nullable to make it easier for `onChangedListener` to // return a non-nullable `EventSubscription` value. This is not the common // path, so we do not have to over-optimize it. @@ -47,12 +49,14 @@ function getState(): $NonMaybeType { lazyState = { NativeAppearance: null, appearance: null, + edgeToEdge: null, eventEmitter, }; } else { const state: $NonMaybeType = { NativeAppearance, appearance: null, + edgeToEdge: null, eventEmitter, }; new NativeEventEmitter<{ @@ -111,6 +115,21 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void { } } +export function isEdgeToEdge(): boolean { + let edgeToEdge = false; + const state = getState(); + const {NativeAppearance} = state; + if (NativeAppearance != null) { + if (state.edgeToEdge == null) { + // Lazily initialize `state.edgeToEdge`. This should only + // happen once because we never reassign a null value to it. + state.edgeToEdge = NativeAppearance.isEdgeToEdge(); + } + edgeToEdge = state.edgeToEdge; + } + return edgeToEdge; +} + /** * Add an event handler that is fired when appearance preferences change. */ diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 68f5d000ef8ddf..dbbdbee99c5e58 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -8840,6 +8840,7 @@ declare export default typeof UTFSequence; exports[`public API should not change unintentionally Libraries/Utilities/Appearance.js 1`] = ` "declare export function getColorScheme(): ?ColorSchemeName; declare export function setColorScheme(colorScheme: ?ColorSchemeName): void; +declare export function isEdgeToEdge(): boolean; declare export function addChangeListener( listener: ({ colorScheme: ?ColorSchemeName }) => void ): EventSubscription; diff --git a/packages/react-native/React/CoreModules/RCTAppearance.mm b/packages/react-native/React/CoreModules/RCTAppearance.mm index 906cdf4d77d4b0..bf50a2fb07ec52 100644 --- a/packages/react-native/React/CoreModules/RCTAppearance.mm +++ b/packages/react-native/React/CoreModules/RCTAppearance.mm @@ -142,6 +142,11 @@ - (dispatch_queue_t)methodQueue return _currentColorScheme; } +RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSNumber *, isEdgeToEdge) +{ + return @(true); +} + - (void)appearanceChanged:(NSNotification *)notification { NSDictionary *userInfo = [notification userInfo]; diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index d2b0769a367611..eecd57cce9dce7 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -465,6 +465,11 @@ fun enableWarningsAsErrors(): Boolean { return value?.toString()?.toBoolean() ?: false } +fun isEdgeToEdgeEnabled(): Boolean { + val value = rootProject.properties["edgeToEdgeEnabled"] + return value?.toString()?.toBoolean() ?: false +} + val packageReactNdkLibsForBuck by tasks.registering(Copy::class) { dependsOn("mergeDebugNativeLibs") @@ -518,6 +523,7 @@ android { consumerProguardFiles("proguard-rules.pro") buildConfigField("boolean", "IS_INTERNAL_BUILD", "false") + buildConfigField("boolean", "IS_EDGE_TO_EDGE_ENABLED", isEdgeToEdgeEnabled().toString()) buildConfigField("int", "EXOPACKAGE_FLAGS", "0") buildConfigField("boolean", "UNSTABLE_ENABLE_FUSEBOX_RELEASE", "false") 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 1560b269b38cf1..1c010b6ea1e560 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 @@ -22,6 +22,7 @@ import com.facebook.react.common.annotations.DeprecatedInNewArchitecture; import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.modules.core.PermissionListener; +import com.facebook.react.views.view.WindowUtilKt; import com.facebook.systrace.Systrace; /** @@ -120,6 +121,9 @@ public void onCreate(Bundle savedInstanceState) { () -> { String mainComponentName = getMainComponentName(); final Bundle launchOptions = composeLaunchOptions(); + if (mActivity != null && BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { + WindowUtilKt.enableEdgeToEdge(mActivity.getWindow()); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) { mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt index b5fdfa79071e09..f092a09720ce80 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appearance/AppearanceModule.kt @@ -8,13 +8,15 @@ package com.facebook.react.modules.appearance import android.content.Context -import android.content.res.Configuration import androidx.appcompat.app.AppCompatDelegate import com.facebook.fbreact.specs.NativeAppearanceSpec +import com.facebook.react.BuildConfig +import com.facebook.react.ReactActivity import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.views.common.ContextUtils /** Module that exposes the user's preferred color scheme. */ @ReactModule(name = NativeAppearanceSpec.NAME) @@ -41,12 +43,9 @@ constructor( return overrideColorScheme.getScheme() } - val currentNightMode = - context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - return when (currentNightMode) { - Configuration.UI_MODE_NIGHT_NO -> "light" - Configuration.UI_MODE_NIGHT_YES -> "dark" - else -> "light" + return when (ContextUtils.isDarkMode(context)) { + true -> "dark" + false -> "light" } } @@ -69,6 +68,9 @@ constructor( } } + public override fun isEdgeToEdge(): Boolean = + BuildConfig.IS_EDGE_TO_EDGE_ENABLED + /** Stub */ public override fun addListener(eventName: String): Unit = Unit diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt index c5547ca8d3a542..09fed4c1bdae38 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt @@ -17,6 +17,8 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.facebook.common.logging.FLog import com.facebook.fbreact.specs.NativeStatusBarManagerAndroidSpec +import com.facebook.react.BuildConfig +import com.facebook.react.ReactActivity import com.facebook.react.bridge.GuardedRunnable import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext @@ -67,6 +69,12 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : "StatusBarModule: Ignored status bar change, current activity is null.") return } + if (BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { + FLog.w( + ReactConstants.TAG, + "StatusBarModule: Ignored status bar change, current activity is edge-to-edge.") + return + } UiThreadUtil.runOnUiThread( object : GuardedRunnable(reactApplicationContext) { override fun runGuarded() { @@ -96,6 +104,12 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : "StatusBarModule: Ignored status bar change, current activity is null.") return } + if (BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { + FLog.w( + ReactConstants.TAG, + "StatusBarModule: Ignored status bar change, current activity is edge-to-edge.") + return + } UiThreadUtil.runOnUiThread( object : GuardedRunnable(reactApplicationContext) { override fun runGuarded() { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index 7f773b6164629b..2bdc7d77a925b5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -26,6 +26,7 @@ import com.facebook.infer.annotation.ThreadConfined; import com.facebook.infer.annotation.ThreadSafe; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.BuildConfig; import com.facebook.react.MemoryPressureRouter; import com.facebook.react.ReactHost; import com.facebook.react.ReactInstanceEventListener; @@ -72,6 +73,7 @@ import com.facebook.react.uimanager.events.BlackHoleEventDispatcher; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper; +import com.facebook.react.views.view.WindowUtilKt; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; @@ -823,6 +825,19 @@ public void onNewIntent(Intent intent) { @ThreadConfined(UI) @Override public void onConfigurationChanged(Context updatedContext) { + if (BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + Activity currentActivity = getCurrentActivity(); + if (currentActivity != null) { + WindowUtilKt.enableEdgeToEdge(currentActivity.getWindow()); + } + } + }); + } + ReactContext currentReactContext = getCurrentReactContext(); if (currentReactContext != null) { AppearanceModule appearanceModule = diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/common/ContextUtils.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/common/ContextUtils.kt index 64ca9577f8f5a9..9904cdd96677cc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/common/ContextUtils.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/common/ContextUtils.kt @@ -9,12 +9,18 @@ package com.facebook.react.views.common import android.content.Context import android.content.ContextWrapper +import android.content.res.Configuration /** * Class containing static methods involving manipulations of Contexts and their related subclasses. */ public object ContextUtils { + @JvmStatic + public fun isDarkMode(context: Context): Boolean = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == + Configuration.UI_MODE_NIGHT_YES + /** * Returns the nearest context in the chain (as defined by ContextWrapper.getBaseContext()) which * is an instance of the specified type, or null if one could not be found diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt index 83d8bc287e3b6a..dd79c315623317 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt @@ -48,7 +48,8 @@ import com.facebook.react.uimanager.events.EventDispatcher import com.facebook.react.views.common.ContextUtils import com.facebook.react.views.view.ReactViewGroup import com.facebook.react.views.view.setStatusBarTranslucency -import com.facebook.react.views.view.setSystemBarsTranslucency +import com.facebook.react.views.view.enableEdgeToEdge +import com.facebook.react.views.view.disableEdgeToEdge import java.util.Objects /** @@ -343,9 +344,10 @@ public class ReactModalHostView(context: ThemedReactContext) : } // Navigation bar cannot be translucent without status bar being translucent too - dialogWindow.setSystemBarsTranslucency(navigationBarTranslucent) - - if (!navigationBarTranslucent) { + if (navigationBarTranslucent) { + dialogWindow.enableEdgeToEdge() + } else { + dialogWindow.disableEdgeToEdge() dialogWindow.setStatusBarTranslucency(statusBarTranslucent) } 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 7e6bb5186c42dc..f8c8b1dc397e7f 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,7 +7,6 @@ package com.facebook.react.views.view -import android.content.res.Configuration import android.graphics.Color import android.os.Build import android.view.Window @@ -15,6 +14,8 @@ import android.view.WindowManager import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat +import com.facebook.react.BuildConfig +import com.facebook.react.views.common.ContextUtils @Suppress("DEPRECATION") public fun Window.setStatusBarTranslucency(isTranslucent: Boolean) { @@ -45,7 +46,7 @@ public fun Window.setStatusBarVisibility(isHidden: Boolean) { @Suppress("DEPRECATION") private fun Window.statusBarHide() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { // Ensure the content extends into the cutout area attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES @@ -57,7 +58,7 @@ private fun Window.statusBarHide() { @Suppress("DEPRECATION") private fun Window.statusBarShow() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !BuildConfig.IS_EDGE_TO_EDGE_ENABLED) { attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT setDecorFitsSystemWindows(true) @@ -67,39 +68,39 @@ private fun Window.statusBarShow() { } @Suppress("DEPRECATION") -public fun Window.setSystemBarsTranslucency(isTranslucent: Boolean) { - WindowCompat.setDecorFitsSystemWindows(this, !isTranslucent) +public fun Window.enableEdgeToEdge() { + val isDarkMode = ContextUtils.isDarkMode(context) - if (isTranslucent) { - val isDarkMode = - context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == - Configuration.UI_MODE_NIGHT_YES - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isStatusBarContrastEnforced = false - isNavigationBarContrastEnforced = true - } + WindowCompat.setDecorFitsSystemWindows(this, false) - statusBarColor = Color.TRANSPARENT - navigationBarColor = - when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && !isDarkMode -> - Color.argb(0xe6, 0xFF, 0xFF, 0xFF) - else -> Color.argb(0x80, 0x1b, 0x1b, 0x1b) - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + isStatusBarContrastEnforced = false + isNavigationBarContrastEnforced = true + } - WindowInsetsControllerCompat(this, this.decorView).run { - isAppearanceLightNavigationBars = !isDarkMode + statusBarColor = Color.TRANSPARENT + navigationBarColor = + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDarkMode -> + Color.argb(0xe6, 0xFF, 0xFF, 0xFF) + else -> Color.argb(0x80, 0x1b, 0x1b, 0x1b) } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - attributes.layoutInDisplayCutoutMode = - when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS - else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - } - } + WindowInsetsControllerCompat(this, this.decorView).run { + isAppearanceLightNavigationBars = !isDarkMode } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + attributes.layoutInDisplayCutoutMode = + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + } +} + +public fun Window.disableEdgeToEdge() { + WindowCompat.setDecorFitsSystemWindows(this, true) } diff --git a/packages/react-native/src/private/specs/modules/NativeAppearance.js b/packages/react-native/src/private/specs/modules/NativeAppearance.js index d7bc36160eaf81..f1bbf75bbbe8dd 100644 --- a/packages/react-native/src/private/specs/modules/NativeAppearance.js +++ b/packages/react-native/src/private/specs/modules/NativeAppearance.js @@ -21,6 +21,7 @@ export type AppearancePreferences = { export interface Spec extends TurboModule { +getColorScheme: () => ?ColorSchemeName; +setColorScheme: (colorScheme: ColorSchemeName) => void; + +isEdgeToEdge: () => boolean; // RCTEventEmitter +addListener: (eventName: string) => void; diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt index e1394a77e0fbe5..d5fbb3d3317bfe 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.kt @@ -7,6 +7,7 @@ package com.facebook.react.uiapp +import android.content.res.Configuration import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle @@ -16,8 +17,10 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.facebook.react.FBRNTesterEndToEndHelper import com.facebook.react.ReactActivity +import com.facebook.react.bridge.UiThreadUtil import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.facebook.react.views.common.ContextUtils import java.io.FileDescriptor import java.io.PrintWriter @@ -43,10 +46,23 @@ internal class RNTesterActivity : ReactActivity() { if (this::initialProps.isInitialized) initialProps else Bundle() } + // set background color so it will show below transparent system bars on forced edge-to-edge + private fun maybeUpdateBackgroundColor() { + val color = when(ContextUtils.isDarkMode(this)) { + true -> Color.parseColor("#0b0600") + false -> Color.parseColor("#f3f8ff") + } + + UiThreadUtil.runOnUiThread { + this.window?.setBackgroundDrawable(ColorDrawable(color)) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // set background color so it will show below transparent system bars on forced edge-to-edge - this.window?.setBackgroundDrawable(ColorDrawable(Color.BLACK)) + + maybeUpdateBackgroundColor() + // register insets listener to update margins on the ReactRootView to avoid overlap w/ system // bars getReactDelegate()?.getReactRootView()?.let { rootView -> @@ -66,6 +82,11 @@ internal class RNTesterActivity : ReactActivity() { } } + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + maybeUpdateBackgroundColor() + } + override fun createReactActivityDelegate() = RNTesterActivityDelegate(this, mainComponentName) override fun getMainComponentName() = "RNTesterApp"