Skip to content

Commit b1614e2

Browse files
committed
Implement enableEdgeToEdge
1 parent 386a930 commit b1614e2

File tree

15 files changed

+193
-51
lines changed

15 files changed

+193
-51
lines changed

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateEntryPointTask.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ abstract class GenerateEntryPointTask : DefaultTask() {
7070
7171
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
7272
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
73+
import com.facebook.react.views.view.WindowUtilKt;
7374
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
7475
import com.facebook.soloader.SoLoader;
7576
@@ -93,6 +94,10 @@ abstract class GenerateEntryPointTask : DefaultTask() {
9394
if ({{packageName}}.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
9495
DefaultNewArchitectureEntryPoint.load();
9596
}
97+
98+
if ({{packageName}}.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
99+
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
100+
}
96101
}
97102
}
98103
"""

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
1111
import com.android.build.api.variant.LibraryAndroidComponentsExtension
1212
import com.android.build.gradle.LibraryExtension
1313
import com.facebook.react.ReactExtension
14+
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
1415
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
1516
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
1617
import java.io.File
@@ -39,6 +40,10 @@ internal object AgpConfiguratorUtils {
3940
project.isNewArchEnabled(extension).toString())
4041
ext.defaultConfig.buildConfigField(
4142
"boolean", "IS_HERMES_ENABLED", project.isHermesEnabled.toString())
43+
ext.defaultConfig.buildConfigField(
44+
"boolean",
45+
"IS_EDGE_TO_EDGE_ENABLED",
46+
project.isEdgeToEdgeEnabled.toString())
4247
}
4348
}
4449
project.pluginManager.withPlugin("com.android.application", action)

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import com.facebook.react.ReactExtension
1111
import com.facebook.react.model.ModelPackageJson
1212
import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
1313
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
14+
import com.facebook.react.utils.PropertyUtils.EDGE_TO_EDGE_ENABLED
1415
import com.facebook.react.utils.PropertyUtils.HERMES_ENABLED
1516
import com.facebook.react.utils.PropertyUtils.NEW_ARCH_ENABLED
1617
import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES
18+
import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED
1719
import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED
1820
import com.facebook.react.utils.PropertyUtils.SCOPED_NEW_ARCH_ENABLED
1921
import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES
@@ -59,6 +61,13 @@ internal object ProjectUtils {
5961
HERMES_FALLBACK
6062
}
6163

64+
internal val Project.isEdgeToEdgeEnabled: Boolean
65+
get() =
66+
(project.hasProperty(EDGE_TO_EDGE_ENABLED) &&
67+
project.property(EDGE_TO_EDGE_ENABLED).toString().toBoolean()) ||
68+
(project.hasProperty(SCOPED_EDGE_TO_EDGE_ENABLED) &&
69+
project.property(SCOPED_EDGE_TO_EDGE_ENABLED).toString().toBoolean())
70+
6271
internal val Project.useThirdPartyJSC: Boolean
6372
get() =
6473
(project.hasProperty(USE_THIRD_PARTY_JSC) &&

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ object PropertyUtils {
1414
const val NEW_ARCH_ENABLED = "newArchEnabled"
1515
const val SCOPED_NEW_ARCH_ENABLED = "react.newArchEnabled"
1616

17-
/** Public property that toggles the New Architecture */
17+
/** Public property that toggles Hermes */
1818
const val HERMES_ENABLED = "hermesEnabled"
1919
const val SCOPED_HERMES_ENABLED = "react.hermesEnabled"
2020

21+
/** Public property that toggles edge-to-edge */
22+
const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled"
23+
const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled"
24+
2125
/** Public property that excludes jsctooling from core */
2226
const val USE_THIRD_PARTY_JSC = "useThirdPartyJSC"
2327
const val SCOPED_USE_THIRD_PARTY_JSC = "react.useThirdPartyJSC"

packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GenerateEntryPointTaskTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class GenerateEntryPointTaskTest {
5555
5656
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
5757
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger;
58+
import com.facebook.react.views.view.WindowUtilKt;
5859
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
5960
import com.facebook.soloader.SoLoader;
6061
@@ -78,6 +79,10 @@ class GenerateEntryPointTaskTest {
7879
if (com.facebook.react.BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
7980
DefaultNewArchitectureEntryPoint.load();
8081
}
82+
83+
if (com.facebook.react.BuildConfig.IS_EDGE_TO_EDGE_ENABLED) {
84+
WindowUtilKt.setEdgeToEdgeFeatureFlagOn();
85+
}
8186
}
8287
}
8388
"""

packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.facebook.react.model.ModelCodegenConfig
1212
import com.facebook.react.model.ModelPackageJson
1313
import com.facebook.react.tests.createProject
1414
import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures
15+
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
1516
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
1617
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
1718
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
@@ -98,7 +99,7 @@ class ProjectUtilsTest {
9899
}
99100

100101
@Test
101-
fun isNewArchEnabled_withDisabledViaProperty_returnsFalse() {
102+
fun isHermesEnabled_withDisabledViaProperty_returnsFalse() {
102103
val project = createProject()
103104
project.extensions.extraProperties.set("hermesEnabled", "false")
104105
assertThat(project.isHermesEnabled).isFalse()
@@ -150,6 +151,32 @@ class ProjectUtilsTest {
150151
assertThat(project.isHermesEnabled).isTrue()
151152
}
152153

154+
@Test
155+
fun isEdgeToEdgeEnabled_returnsFalseByDefault() {
156+
assertThat(createProject().isEdgeToEdgeEnabled).isFalse()
157+
}
158+
159+
@Test
160+
fun isEdgeToEdgeEnabled_withDisabledViaProperty_returnsFalse() {
161+
val project = createProject()
162+
project.extensions.extraProperties.set("edgeToEdgeEnabled", "false")
163+
assertThat(project.isEdgeToEdgeEnabled).isFalse()
164+
}
165+
166+
@Test
167+
fun isEdgeToEdgeEnabled_withEnabledViaProperty_returnsTrue() {
168+
val project = createProject()
169+
project.extensions.extraProperties.set("edgeToEdgeEnabled", "true")
170+
assertThat(project.isEdgeToEdgeEnabled).isTrue()
171+
}
172+
173+
@Test
174+
fun isEdgeToEdgeEnabled_withInvalidViaProperty_returnsFalse() {
175+
val project = createProject()
176+
project.extensions.extraProperties.set("edgeToEdgeEnabled", "¯\\_(ツ)_/¯")
177+
assertThat(project.isEdgeToEdgeEnabled).isFalse()
178+
}
179+
153180
@Test
154181
fun needsCodegenFromPackageJson_withCodegenConfigInPackageJson_returnsTrue() {
155182
val project = createProject()

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.facebook.react.interfaces.fabric.ReactSurface;
2424
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags;
2525
import com.facebook.react.modules.core.PermissionListener;
26+
import com.facebook.react.views.view.WindowUtilKt;
2627
import com.facebook.systrace.Systrace;
2728

2829
/**
@@ -121,6 +122,9 @@ public void onCreate(Bundle savedInstanceState) {
121122
() -> {
122123
String mainComponentName = getMainComponentName();
123124
final Bundle launchOptions = composeLaunchOptions();
125+
if (WindowUtilKt.isEdgeToEdgeFeatureFlagOn() && mActivity != null) {
126+
WindowUtilKt.enableEdgeToEdge(mActivity.getWindow());
127+
}
124128
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isWideColorGamutEnabled()) {
125129
mActivity.getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
126130
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/deviceinfo/DeviceInfoModule.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import com.facebook.react.bridge.ReadableMap
1616
import com.facebook.react.module.annotations.ReactModule
1717
import com.facebook.react.uimanager.DisplayMetricsHolder.getDisplayMetricsWritableMap
1818
import com.facebook.react.uimanager.DisplayMetricsHolder.initDisplayMetricsIfNotInitialized
19+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
1920

2021
/** Module that exposes Android Constants to JS. */
2122
@ReactModule(name = NativeDeviceInfoSpec.NAME)
@@ -34,7 +35,11 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
3435

3536
// Cache the initial dimensions for later comparison in emitUpdateDimensionsEvent
3637
previousDisplayMetrics = displayMetrics.copy()
37-
return mapOf("Dimensions" to displayMetrics.toHashMap())
38+
39+
return mapOf(
40+
"Dimensions" to displayMetrics.toHashMap(),
41+
"isEdgeToEdge" to isEdgeToEdgeFeatureFlagOn,
42+
)
3843
}
3944

4045
override fun onHostResume() {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.facebook.react.common.ReactConstants
2323
import com.facebook.react.module.annotations.ReactModule
2424
import com.facebook.react.uimanager.DisplayMetricsHolder.getStatusBarHeightPx
2525
import com.facebook.react.uimanager.PixelUtil
26+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
2627
import com.facebook.react.views.view.setStatusBarTranslucency
2728
import com.facebook.react.views.view.setStatusBarVisibility
2829

@@ -54,6 +55,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
5455
"StatusBarModule: Ignored status bar change, current activity is null.")
5556
return
5657
}
58+
if (isEdgeToEdgeFeatureFlagOn) {
59+
FLog.w(
60+
ReactConstants.TAG,
61+
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
62+
return
63+
}
5764
UiThreadUtil.runOnUiThread(
5865
object : GuardedRunnable(reactApplicationContext) {
5966
override fun runGuarded() {
@@ -82,6 +89,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
8289
"StatusBarModule: Ignored status bar change, current activity is null.")
8390
return
8491
}
92+
if (isEdgeToEdgeFeatureFlagOn) {
93+
FLog.w(
94+
ReactConstants.TAG,
95+
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
96+
return
97+
}
8598
UiThreadUtil.runOnUiThread(
8699
object : GuardedRunnable(reactApplicationContext) {
87100
override fun runGuarded() {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.kt

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ import com.facebook.react.uimanager.events.EventDispatcher
5353
import com.facebook.react.views.common.ContextUtils
5454
import com.facebook.react.views.modal.ReactModalHostView.DialogRootViewGroup
5555
import com.facebook.react.views.view.ReactViewGroup
56+
import com.facebook.react.views.view.disableEdgeToEdge
57+
import com.facebook.react.views.view.enableEdgeToEdge
58+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
5659
import com.facebook.react.views.view.setStatusBarTranslucency
57-
import com.facebook.react.views.view.setSystemBarsTranslucency
5860
import com.facebook.yoga.annotations.DoNotStrip
5961

6062
/**
@@ -82,14 +84,22 @@ public class ReactModalHostView(context: ThemedReactContext) :
8284
public var onRequestCloseListener: OnRequestCloseListener? = null
8385
public var statusBarTranslucent: Boolean = false
8486
set(value) {
85-
field = value
86-
createNewDialog = true
87+
if (isEdgeToEdgeFeatureFlagOn) {
88+
field = true
89+
} else {
90+
field = value
91+
createNewDialog = true
92+
}
8793
}
8894

8995
public var navigationBarTranslucent: Boolean = false
9096
set(value) {
91-
field = value
92-
createNewDialog = true
97+
if (isEdgeToEdgeFeatureFlagOn) {
98+
field = true
99+
} else {
100+
field = value
101+
createNewDialog = true
102+
}
93103
}
94104

95105
public var animationType: String? = null
@@ -377,9 +387,10 @@ public class ReactModalHostView(context: ThemedReactContext) :
377387
}
378388

379389
// Navigation bar cannot be translucent without status bar being translucent too
380-
dialogWindow.setSystemBarsTranslucency(navigationBarTranslucent)
381-
382-
if (!navigationBarTranslucent) {
390+
if (navigationBarTranslucent) {
391+
dialogWindow.enableEdgeToEdge()
392+
} else {
393+
dialogWindow.disableEdgeToEdge()
383394
dialogWindow.setStatusBarTranslucency(statusBarTranslucent)
384395
}
385396

@@ -415,6 +426,13 @@ public class ReactModalHostView(context: ThemedReactContext) :
415426
val dialogWindowInsetsController =
416427
WindowInsetsControllerCompat(dialogWindow, dialogWindow.decorView)
417428

429+
if (isEdgeToEdgeFeatureFlagOn) {
430+
activityWindowInsetsController.systemBarsBehavior =
431+
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
432+
dialogWindowInsetsController.systemBarsBehavior =
433+
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
434+
}
435+
418436
dialogWindowInsetsController.isAppearanceLightStatusBars =
419437
activityWindowInsetsController.isAppearanceLightStatusBars
420438

0 commit comments

Comments
 (0)