mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add edge-to-edge opt-in support (#52088)
Summary: This follows https://github.com/facebook/react-native/pull/47554 Compared to the initial proposal, I had to remove the `edgeToEdgeEnabled` property from the root `gradle.properties` and put it in the app `gradle.properties` instead (explaining the `AgpConfiguratorUtils.kt` / `GenerateEntryPointTask.kt` / `ProjectUtils.kt` / `PropertyUtils.kt` changes) This PR: - Enable edge-to-edge for `MainActivity` (when `edgeToEdgeEnabled` is set to `true`) - Disable `StatusBar` `backgroundColor` and `translucent` (when `edgeToEdgeEnabled` is set to `true`) - Enforce `statusBarTranslucent` and `navigationBarTranslucent` on `Modal` when edge-to-edge is enabled - Add an `isEdgeToEdge` constant to `DeviceInfoModule` for [`react-native-is-edge-to-edge`](https://github.com/zoontek/react-native-edge-to-edge/tree/main/react-native-is-edge-to-edge) detection ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> - [Android] [Added] - Add edge-to-edge opt-in support Pull Request resolved: https://github.com/facebook/react-native/pull/52088 Test Plan: - Update `enableEdgeToEdge` value in `packages/rn-tester/android/app/gradle.properties` - Recompile https://github.com/user-attachments/assets/4c6beb98-fa88-427c-b62d-a42ffe5330f0 Rollback Plan: Reviewed By: cortinico Differential Revision: D76834213 Pulled By: alanleedev fbshipit-source-id: c39b2cff1a5e94e31306e3b35651aa2de83d2fe6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
78c9671c24
commit
09ef774ff6
+5
@@ -70,6 +70,7 @@ abstract class GenerateEntryPointTask : DefaultTask() {
|
||||
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
|
||||
import com.facebook.react.views.view.WindowUtilKt;
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
@@ -93,6 +94,10 @@ abstract class GenerateEntryPointTask : DefaultTask() {
|
||||
if ({{packageName}}.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
|
||||
if ({{packageName}}.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
|
||||
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import com.facebook.react.ReactExtension
|
||||
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
|
||||
import java.io.File
|
||||
@@ -39,6 +40,8 @@ internal object AgpConfiguratorUtils {
|
||||
project.isNewArchEnabled(extension).toString())
|
||||
ext.defaultConfig.buildConfigField(
|
||||
"boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString())
|
||||
ext.defaultConfig.buildConfigField(
|
||||
"boolean", "IS_EDGE_TO_EDGE_ENABLED", project.isEdgeToEdgeEnabled.toString())
|
||||
}
|
||||
}
|
||||
project.pluginManager.withPlugin("com.android.application", action)
|
||||
|
||||
+9
@@ -11,9 +11,11 @@ import com.facebook.react.ReactExtension
|
||||
import com.facebook.react.model.ModelPackageJson
|
||||
import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
|
||||
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
|
||||
import com.facebook.react.utils.PropertyUtils.EDGE_TO_EDGE_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.HERMES_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.NEW_ARCH_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
|
||||
import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.SCOPED_NEW_ARCH_ENABLED
|
||||
import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
|
||||
@@ -59,6 +61,13 @@ internal object ProjectUtils {
|
||||
HERMES_FALLBACK
|
||||
}
|
||||
|
||||
internal val Project.isEdgeToEdgeEnabled: Boolean
|
||||
get() =
|
||||
(project.hasProperty(EDGE_TO_EDGE_ENABLED) &&
|
||||
project.property(EDGE_TO_EDGE_ENABLED).toString().toBoolean()) ||
|
||||
(project.hasProperty(SCOPED_EDGE_TO_EDGE_ENABLED) &&
|
||||
project.property(SCOPED_EDGE_TO_EDGE_ENABLED).toString().toBoolean())
|
||||
|
||||
internal val Project.useThirdPartyJSC: Boolean
|
||||
get() =
|
||||
(project.hasProperty(USE_THIRD_PARTY_JSC) &&
|
||||
|
||||
+5
-1
@@ -14,10 +14,14 @@ 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"
|
||||
|
||||
/** Public property that toggles edge-to-edge */
|
||||
const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled"
|
||||
const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled"
|
||||
|
||||
/** Public property that excludes jsctooling from core */
|
||||
const val USE_THIRD_PARTY_JSC = "useThirdPartyJSC"
|
||||
const val SCOPED_USE_THIRD_PARTY_JSC = "react.useThirdPartyJSC"
|
||||
|
||||
+5
@@ -55,6 +55,7 @@ class GenerateEntryPointTaskTest {
|
||||
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
|
||||
import com.facebook.react.views.view.WindowUtilKt;
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
@@ -78,6 +79,10 @@ class GenerateEntryPointTaskTest {
|
||||
if (com.facebook.react.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
DefaultNewArchitectureEntryPoint.load();
|
||||
}
|
||||
|
||||
if (com.facebook.react.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
|
||||
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
+28
-1
@@ -12,6 +12,7 @@ import com.facebook.react.model.ModelCodegenConfig
|
||||
import com.facebook.react.model.ModelPackageJson
|
||||
import com.facebook.react.tests.createProject
|
||||
import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures
|
||||
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
|
||||
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
|
||||
@@ -98,7 +99,7 @@ class ProjectUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNewArchEnabled_withDisabledViaProperty_returnsFalse() {
|
||||
fun isHermesEnabled_withDisabledViaProperty_returnsFalse() {
|
||||
val project = createProject()
|
||||
project.extensions.extraProperties.set("hermesEnabled", "false")
|
||||
assertThat(project.isHermesEnabled).isFalse()
|
||||
@@ -150,6 +151,32 @@ class ProjectUtilsTest {
|
||||
assertThat(project.isHermesEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEdgeToEdgeEnabled_returnsFalseByDefault() {
|
||||
assertThat(createProject().isEdgeToEdgeEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEdgeToEdgeEnabled_withDisabledViaProperty_returnsFalse() {
|
||||
val project = createProject()
|
||||
project.extensions.extraProperties.set("edgeToEdgeEnabled", "false")
|
||||
assertThat(project.isEdgeToEdgeEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEdgeToEdgeEnabled_withEnabledViaProperty_returnsTrue() {
|
||||
val project = createProject()
|
||||
project.extensions.extraProperties.set("edgeToEdgeEnabled", "true")
|
||||
assertThat(project.isEdgeToEdgeEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEdgeToEdgeEnabled_withInvalidViaProperty_returnsFalse() {
|
||||
val project = createProject()
|
||||
project.extensions.extraProperties.set("edgeToEdgeEnabled", "¯\\_(ツ)_/¯")
|
||||
assertThat(project.isEdgeToEdgeEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun needsCodegenFromPackageJson_withCodegenConfigInPackageJson_returnsTrue() {
|
||||
val project = createProject()
|
||||
|
||||
@@ -6673,3 +6673,8 @@ public class com/facebook/react/views/view/ReactViewManager : com/facebook/react
|
||||
public final class com/facebook/react/views/view/ReactViewManager$Companion {
|
||||
}
|
||||
|
||||
public final class com/facebook/react/views/view/WindowUtilKt {
|
||||
public static final fun isEdgeToEdgeFeatureFlagOn ()Z
|
||||
public static final fun setEdgeToEdgeFeatureFlagOn ()V
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -15,6 +15,7 @@ import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Window;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
@@ -23,6 +24,7 @@ import com.facebook.react.common.annotations.DeprecatedInNewArchitecture;
|
||||
import com.facebook.react.interfaces.fabric.ReactSurface;
|
||||
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import com.facebook.react.views.view.WindowUtilKt;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
||||
/**
|
||||
@@ -57,7 +59,7 @@ public class ReactActivityDelegate {
|
||||
|
||||
/**
|
||||
* Public API to populate the launch options that will be passed to React. Here you can customize
|
||||
* the values that will be passed as `initialProperties` to the Renderer.
|
||||
* the values that will be passed as 'initialProperties' to the Renderer.
|
||||
*
|
||||
* @return Either null or a key-value map as a Bundle
|
||||
*/
|
||||
@@ -121,8 +123,16 @@ public class ReactActivityDelegate {
|
||||
() -> {
|
||||
String mainComponentName = getMainComponentName();
|
||||
final Bundle launchOptions = composeLaunchOptions();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) {
|
||||
mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
|
||||
if (mActivity != null) {
|
||||
Window window = mActivity.getWindow();
|
||||
if (window != null) {
|
||||
if (WindowUtilKt.isEdgeToEdgeFeatureFlagOn()) {
|
||||
WindowUtilKt.enableEdgeToEdge(window);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) {
|
||||
window.setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) {
|
||||
mReactDelegate =
|
||||
|
||||
+6
-1
@@ -16,6 +16,7 @@ import com.facebook.react.bridge.ReadableMap
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder.getDisplayMetricsWritableMap
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder.initDisplayMetricsIfNotInitialized
|
||||
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
|
||||
|
||||
/** Module that exposes Android Constants to JS. */
|
||||
@ReactModule(name = NativeDeviceInfoSpec.NAME)
|
||||
@@ -34,7 +35,11 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
|
||||
|
||||
// Cache the initial dimensions for later comparison in emitUpdateDimensionsEvent
|
||||
previousDisplayMetrics = displayMetrics.copy()
|
||||
return mapOf("Dimensions" to displayMetrics.toHashMap())
|
||||
|
||||
return mapOf(
|
||||
"Dimensions" to displayMetrics.toHashMap(),
|
||||
"isEdgeToEdge" to isEdgeToEdgeFeatureFlagOn,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onHostResume() {
|
||||
|
||||
+13
@@ -23,6 +23,7 @@ import com.facebook.react.common.ReactConstants
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder.getStatusBarHeightPx
|
||||
import com.facebook.react.uimanager.PixelUtil
|
||||
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
|
||||
import com.facebook.react.views.view.setStatusBarTranslucency
|
||||
import com.facebook.react.views.view.setStatusBarVisibility
|
||||
|
||||
@@ -54,6 +55,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
|
||||
"StatusBarModule: Ignored status bar change, current activity is null.")
|
||||
return
|
||||
}
|
||||
if (isEdgeToEdgeFeatureFlagOn) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
|
||||
return
|
||||
}
|
||||
UiThreadUtil.runOnUiThread(
|
||||
object : GuardedRunnable(reactApplicationContext) {
|
||||
override fun runGuarded() {
|
||||
@@ -82,6 +89,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
|
||||
"StatusBarModule: Ignored status bar change, current activity is null.")
|
||||
return
|
||||
}
|
||||
if (isEdgeToEdgeFeatureFlagOn) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
|
||||
return
|
||||
}
|
||||
UiThreadUtil.runOnUiThread(
|
||||
object : GuardedRunnable(reactApplicationContext) {
|
||||
override fun runGuarded() {
|
||||
|
||||
+19
-6
@@ -53,8 +53,10 @@ import com.facebook.react.uimanager.events.EventDispatcher
|
||||
import com.facebook.react.views.common.ContextUtils
|
||||
import com.facebook.react.views.modal.ReactModalHostView.DialogRootViewGroup
|
||||
import com.facebook.react.views.view.ReactViewGroup
|
||||
import com.facebook.react.views.view.disableEdgeToEdge
|
||||
import com.facebook.react.views.view.enableEdgeToEdge
|
||||
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
|
||||
import com.facebook.react.views.view.setStatusBarTranslucency
|
||||
import com.facebook.react.views.view.setSystemBarsTranslucency
|
||||
import com.facebook.yoga.annotations.DoNotStrip
|
||||
|
||||
/**
|
||||
@@ -81,16 +83,19 @@ public class ReactModalHostView(context: ThemedReactContext) :
|
||||
public var transparent: Boolean = false
|
||||
public var onShowListener: DialogInterface.OnShowListener? = null
|
||||
public var onRequestCloseListener: OnRequestCloseListener? = null
|
||||
|
||||
public var statusBarTranslucent: Boolean = false
|
||||
get() = field || isEdgeToEdgeFeatureFlagOn
|
||||
set(value) {
|
||||
field = value
|
||||
createNewDialog = true
|
||||
createNewDialog = !isEdgeToEdgeFeatureFlagOn
|
||||
}
|
||||
|
||||
public var navigationBarTranslucent: Boolean = false
|
||||
get() = field || isEdgeToEdgeFeatureFlagOn
|
||||
set(value) {
|
||||
field = value
|
||||
createNewDialog = true
|
||||
createNewDialog = !isEdgeToEdgeFeatureFlagOn
|
||||
}
|
||||
|
||||
public var animationType: String? = null
|
||||
@@ -378,9 +383,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)
|
||||
}
|
||||
|
||||
@@ -416,6 +422,13 @@ public class ReactModalHostView(context: ThemedReactContext) :
|
||||
val dialogWindowInsetsController =
|
||||
WindowInsetsControllerCompat(dialogWindow, dialogWindow.decorView)
|
||||
|
||||
if (isEdgeToEdgeFeatureFlagOn) {
|
||||
activityWindowInsetsController.systemBarsBehavior =
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
dialogWindowInsetsController.systemBarsBehavior =
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
}
|
||||
|
||||
dialogWindowInsetsController.isAppearanceLightStatusBars =
|
||||
activityWindowInsetsController.isAppearanceLightStatusBars
|
||||
|
||||
|
||||
+76
-40
@@ -13,9 +13,30 @@ import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.facebook.react.views.common.UiModeUtils
|
||||
|
||||
// The light scrim color used in the platform API 29+
|
||||
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/com/android/internal/policy/DecorView.java;drc=6ef0f022c333385dba2c294e35b8de544455bf19;l=142
|
||||
internal val LightNavigationBarColor = Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
|
||||
|
||||
// The dark scrim color used in the platform.
|
||||
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/color/system_bar_background_semi_transparent.xml
|
||||
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/remote_color_resources_res/values/colors.xml;l=67
|
||||
internal val DarkNavigationBarColor = Color.argb(0x80, 0x1b, 0x1b, 0x1b)
|
||||
|
||||
/**
|
||||
* This does not enable or apply edge-to-edge behavior, it simply tracks whether it has been flagged
|
||||
* as enabled elsewhere in the application.
|
||||
*/
|
||||
public var isEdgeToEdgeFeatureFlagOn: Boolean = false
|
||||
private set
|
||||
|
||||
public fun setEdgeToEdgeFeatureFlagOn() {
|
||||
isEdgeToEdgeFeatureFlagOn = true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
internal fun Window.setStatusBarTranslucency(isTranslucent: Boolean) {
|
||||
// If the status bar is translucent hook into the window insets calculations
|
||||
@@ -45,59 +66,74 @@ internal fun Window.setStatusBarVisibility(isHidden: Boolean) {
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun Window.statusBarHide() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Ensure the content extends into the cutout area
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
setDecorFitsSystemWindows(false)
|
||||
if (isEdgeToEdgeFeatureFlagOn) {
|
||||
WindowInsetsControllerCompat(this, decorView).run {
|
||||
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
hide(WindowInsetsCompat.Type.statusBars())
|
||||
}
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Ensure the content extends into the cutout area
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
setDecorFitsSystemWindows(false)
|
||||
}
|
||||
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
|
||||
}
|
||||
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun Window.statusBarShow() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
|
||||
setDecorFitsSystemWindows(true)
|
||||
if (isEdgeToEdgeFeatureFlagOn) {
|
||||
WindowInsetsControllerCompat(this, decorView).run {
|
||||
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
show(WindowInsetsCompat.Type.statusBars())
|
||||
}
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
|
||||
setDecorFitsSystemWindows(true)
|
||||
}
|
||||
addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
internal fun Window.setSystemBarsTranslucency(isTranslucent: Boolean) {
|
||||
WindowCompat.setDecorFitsSystemWindows(this, !isTranslucent)
|
||||
internal fun Window.enableEdgeToEdge() {
|
||||
WindowCompat.setDecorFitsSystemWindows(this, false)
|
||||
|
||||
if (isTranslucent) {
|
||||
val isDarkMode = UiModeUtils.isDarkMode(context)
|
||||
val isDarkMode = UiModeUtils.isDarkMode(context)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
isStatusBarContrastEnforced = false
|
||||
isNavigationBarContrastEnforced = true
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
isStatusBarContrastEnforced = false
|
||||
isNavigationBarContrastEnforced = true
|
||||
}
|
||||
|
||||
statusBarColor = Color.TRANSPARENT
|
||||
navigationBarColor =
|
||||
statusBarColor = Color.TRANSPARENT
|
||||
navigationBarColor =
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDarkMode -> LightNavigationBarColor
|
||||
else -> DarkNavigationBarColor
|
||||
}
|
||||
|
||||
WindowInsetsControllerCompat(this, decorView).run {
|
||||
isAppearanceLightNavigationBars = !isDarkMode
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
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)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Window.disableEdgeToEdge() {
|
||||
WindowCompat.setDecorFitsSystemWindows(this, true)
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ using DimensionsPayload = NativeDeviceInfoDimensionsPayload<
|
||||
std::optional<DisplayMetricsAndroid>,
|
||||
std::optional<DisplayMetricsAndroid>>;
|
||||
|
||||
using DeviceInfoConstants =
|
||||
NativeDeviceInfoDeviceInfoConstants<DimensionsPayload, std::optional<bool>>;
|
||||
using DeviceInfoConstants = NativeDeviceInfoDeviceInfoConstants<
|
||||
DimensionsPayload,
|
||||
std::optional<bool>,
|
||||
std::optional<bool>>;
|
||||
|
||||
template <>
|
||||
struct Bridging<DisplayMetrics>
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ export type DimensionsPayload = {
|
||||
|
||||
export type DeviceInfoConstants = {
|
||||
+Dimensions: DimensionsPayload,
|
||||
+isEdgeToEdge?: boolean,
|
||||
+isIPhoneX_deprecated?: boolean,
|
||||
};
|
||||
|
||||
|
||||
@@ -7,3 +7,5 @@ android.useAndroidX=true
|
||||
newArchEnabled=true
|
||||
# RN-Tester is running with Hermes always enabled
|
||||
hermesEnabled=true
|
||||
# RN-Tester is running with EdgeToEdge always enabled
|
||||
edgeToEdgeEnabled=true
|
||||
|
||||
+5
@@ -33,6 +33,7 @@ import com.facebook.react.uiapp.component.MyNativeViewManager
|
||||
import com.facebook.react.uiapp.component.ReportFullyDrawnViewManager
|
||||
import com.facebook.react.uimanager.ReactShadowNode
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
import com.facebook.react.views.view.setEdgeToEdgeFeatureFlagOn
|
||||
import com.facebook.soloader.SoLoader
|
||||
|
||||
internal class RNTesterApplication : Application(), ReactApplication {
|
||||
@@ -135,5 +136,9 @@ internal class RNTesterApplication : Application(), ReactApplication {
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
load()
|
||||
}
|
||||
|
||||
if (BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
|
||||
setEdgeToEdgeFeatureFlagOn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,3 +10,4 @@ android.useAndroidX=true
|
||||
reactNativeArchitectures=arm64-v8a
|
||||
newArchEnabled=true
|
||||
hermesEnabled=true
|
||||
edgeToEdgeEnabled=false
|
||||
|
||||
Reference in New Issue
Block a user