Skip to content

Commit 0515f30

Browse files
committed
Implement enableEdgeToEdge
1 parent 3bfbaa8 commit 0515f30

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
@@ -24,6 +24,7 @@ import com.facebook.react.bridge.UiThreadUtil
2424
import com.facebook.react.common.ReactConstants
2525
import com.facebook.react.module.annotations.ReactModule
2626
import com.facebook.react.uimanager.PixelUtil
27+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
2728
import com.facebook.react.views.view.setStatusBarTranslucency
2829
import com.facebook.react.views.view.setStatusBarVisibility
2930

@@ -70,6 +71,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
7071
"StatusBarModule: Ignored status bar change, current activity is null.")
7172
return
7273
}
74+
if (isEdgeToEdgeFeatureFlagOn) {
75+
FLog.w(
76+
ReactConstants.TAG,
77+
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
78+
return
79+
}
7380
UiThreadUtil.runOnUiThread(
7481
object : GuardedRunnable(reactApplicationContext) {
7582
override fun runGuarded() {
@@ -98,6 +105,12 @@ internal class StatusBarModule(reactContext: ReactApplicationContext?) :
98105
"StatusBarModule: Ignored status bar change, current activity is null.")
99106
return
100107
}
108+
if (isEdgeToEdgeFeatureFlagOn) {
109+
FLog.w(
110+
ReactConstants.TAG,
111+
"StatusBarModule: Ignored status bar change, current activity is edge-to-edge.")
112+
return
113+
}
101114
UiThreadUtil.runOnUiThread(
102115
object : GuardedRunnable(reactApplicationContext) {
103116
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
@@ -51,8 +51,10 @@ import com.facebook.react.uimanager.events.EventDispatcher
5151
import com.facebook.react.views.common.ContextUtils
5252
import com.facebook.react.views.modal.ReactModalHostView.DialogRootViewGroup
5353
import com.facebook.react.views.view.ReactViewGroup
54+
import com.facebook.react.views.view.disableEdgeToEdge
55+
import com.facebook.react.views.view.enableEdgeToEdge
56+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
5457
import com.facebook.react.views.view.setStatusBarTranslucency
55-
import com.facebook.react.views.view.setSystemBarsTranslucency
5658

5759
/**
5860
* ReactModalHostView is a view that sits in the view hierarchy representing a Modal view.
@@ -79,14 +81,22 @@ public class ReactModalHostView(context: ThemedReactContext) :
7981
public var onRequestCloseListener: OnRequestCloseListener? = null
8082
public var statusBarTranslucent: Boolean = false
8183
set(value) {
82-
field = value
83-
createNewDialog = true
84+
if (isEdgeToEdgeFeatureFlagOn) {
85+
field = true
86+
} else {
87+
field = value
88+
createNewDialog = true
89+
}
8490
}
8591

8692
public var navigationBarTranslucent: Boolean = false
8793
set(value) {
88-
field = value
89-
createNewDialog = true
94+
if (isEdgeToEdgeFeatureFlagOn) {
95+
field = true
96+
} else {
97+
field = value
98+
createNewDialog = true
99+
}
90100
}
91101

92102
public var animationType: String? = null
@@ -373,9 +383,10 @@ public class ReactModalHostView(context: ThemedReactContext) :
373383
}
374384

375385
// Navigation bar cannot be translucent without status bar being translucent too
376-
dialogWindow.setSystemBarsTranslucency(navigationBarTranslucent)
377-
378-
if (!navigationBarTranslucent) {
386+
if (navigationBarTranslucent) {
387+
dialogWindow.enableEdgeToEdge()
388+
} else {
389+
dialogWindow.disableEdgeToEdge()
379390
dialogWindow.setStatusBarTranslucency(statusBarTranslucent)
380391
}
381392

@@ -411,6 +422,13 @@ public class ReactModalHostView(context: ThemedReactContext) :
411422
val dialogWindowInsetsController =
412423
WindowInsetsControllerCompat(dialogWindow, dialogWindow.decorView)
413424

425+
if (isEdgeToEdgeFeatureFlagOn) {
426+
activityWindowInsetsController.systemBarsBehavior =
427+
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
428+
dialogWindowInsetsController.systemBarsBehavior =
429+
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
430+
}
431+
414432
dialogWindowInsetsController.isAppearanceLightStatusBars =
415433
activityWindowInsetsController.isAppearanceLightStatusBars
416434

0 commit comments

Comments
 (0)