Skip to content

Commit c0ef2e8

Browse files
committed
Don't hold windowDisplayMetrics
1 parent 6de6ba8 commit c0ef2e8

6 files changed

Lines changed: 56 additions & 41 deletions

File tree

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,13 +3283,12 @@ public abstract class com/facebook/react/uimanager/BaseViewManagerDelegate : com
32833283

32843284
public final class com/facebook/react/uimanager/DisplayMetricsHolder {
32853285
public static final field INSTANCE Lcom/facebook/react/uimanager/DisplayMetricsHolder;
3286-
public static final fun getDisplayMetricsWritableMap (D)Lcom/facebook/react/bridge/WritableMap;
3286+
public static final fun getDisplayMetricsWritableMap (Landroid/util/DisplayMetrics;D)Lcom/facebook/react/bridge/WritableMap;
32873287
public static final fun getScreenDisplayMetrics ()Landroid/util/DisplayMetrics;
3288-
public static final fun getWindowDisplayMetrics ()Landroid/util/DisplayMetrics;
3288+
public static final fun getWindowDisplayMetrics (Landroid/content/Context;Landroid/app/Activity;)Landroid/util/DisplayMetrics;
32893289
public static final fun initDisplayMetrics (Landroid/content/Context;)V
32903290
public static final fun initDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
32913291
public static final fun setScreenDisplayMetrics (Landroid/util/DisplayMetrics;)V
3292-
public static final fun setWindowDisplayMetrics (Landroid/util/DisplayMetrics;)V
32933292
}
32943293

32953294
public final class com/facebook/react/uimanager/FloatUtil {

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT;
1515

1616
import android.annotation.SuppressLint;
17+
import android.app.Activity;
1718
import android.content.Context;
1819
import android.graphics.BlendMode;
1920
import android.graphics.Canvas;
@@ -1018,8 +1019,13 @@ private void checkForKeyboardEventsLegacy() {
10181019
}
10191020
}
10201021
}
1022+
1023+
final ReactContext reactContext = getCurrentReactContext();
1024+
final Activity currentActivity =
1025+
reactContext != null ? reactContext.getCurrentActivity() : null;
1026+
10211027
final int heightDiff =
1022-
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels
1028+
DisplayMetricsHolder.getWindowDisplayMetrics(getContext(), currentActivity).heightPixels
10231029
- mVisibleViewArea.bottom
10241030
+ notchHeight;
10251031

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger
1515
import com.facebook.react.bridge.ReadableMap
1616
import com.facebook.react.module.annotations.ReactModule
1717
import com.facebook.react.uimanager.DisplayMetricsHolder.getDisplayMetricsWritableMap
18+
import com.facebook.react.uimanager.DisplayMetricsHolder.getWindowDisplayMetrics
1819
import com.facebook.react.uimanager.DisplayMetricsHolder.initDisplayMetricsIfNotInitialized
1920
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
2021

@@ -31,7 +32,9 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
3132
}
3233

3334
public override fun getTypedExportedConstants(): Map<String, Any> {
34-
val displayMetrics = getDisplayMetricsWritableMap(fontScale.toDouble())
35+
val windowDisplayMetrics =
36+
getWindowDisplayMetrics(reactApplicationContext, reactApplicationContext.currentActivity)
37+
val displayMetrics = getDisplayMetricsWritableMap(windowDisplayMetrics, fontScale.toDouble())
3538

3639
// Cache the initial dimensions for later comparison in emitUpdateDimensionsEvent
3740
previousDisplayMetrics = displayMetrics.copy()
@@ -58,7 +61,13 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
5861
reactApplicationContext.let { context ->
5962
if (context.hasActiveReactInstance()) {
6063
// Don't emit an event to JS if the dimensions haven't changed
61-
val displayMetrics = getDisplayMetricsWritableMap(fontScale.toDouble())
64+
val windowDisplayMetrics =
65+
getWindowDisplayMetrics(
66+
reactApplicationContext,
67+
reactApplicationContext.currentActivity,
68+
)
69+
val displayMetrics =
70+
getDisplayMetricsWritableMap(windowDisplayMetrics, fontScale.toDouble())
6271
if (previousDisplayMetrics == null) {
6372
previousDisplayMetrics = displayMetrics.copy()
6473
} else if (displayMetrics != previousDisplayMetrics) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import android.util.DisplayMetrics
1414
import android.view.WindowManager
1515
import androidx.core.view.ViewCompat
1616
import androidx.core.view.WindowInsetsCompat
17+
import androidx.window.layout.WindowMetricsCalculator
1718
import com.facebook.react.bridge.WritableMap
1819
import com.facebook.react.bridge.WritableNativeMap
1920
import com.facebook.react.uimanager.PixelUtil.pxToDp
21+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
2022

2123
/**
2224
* Holds an instance of the current DisplayMetrics so we don't have to thread it through all the
@@ -26,19 +28,25 @@ public object DisplayMetricsHolder {
2628
private const val INITIALIZATION_MISSING_MESSAGE =
2729
"DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or initDisplayMetrics"
2830

29-
@JvmStatic private var windowDisplayMetrics: DisplayMetrics? = null
3031
@JvmStatic private var screenDisplayMetrics: DisplayMetrics? = null
3132

3233
/** The metrics of the window associated to the Context used to initialize ReactNative */
3334
@JvmStatic
34-
public fun getWindowDisplayMetrics(): DisplayMetrics {
35-
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
36-
return windowDisplayMetrics as DisplayMetrics
37-
}
35+
public fun getWindowDisplayMetrics(context: Context, activity: Activity?): DisplayMetrics {
36+
val displayMetrics = context.resources.displayMetrics
37+
val windowDisplayMetrics = DisplayMetrics()
38+
windowDisplayMetrics.setTo(displayMetrics)
39+
40+
if (isEdgeToEdgeFeatureFlagOn) {
41+
activity?.let { activity ->
42+
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity).let {
43+
windowDisplayMetrics.widthPixels = it.bounds.width()
44+
windowDisplayMetrics.heightPixels = it.bounds.height()
45+
}
46+
}
47+
}
3848

39-
@JvmStatic
40-
public fun setWindowDisplayMetrics(displayMetrics: DisplayMetrics?) {
41-
windowDisplayMetrics = displayMetrics
49+
return windowDisplayMetrics
4250
}
4351

4452
/** Screen metrics returns the metrics of the default screen on the device. */
@@ -62,11 +70,10 @@ public object DisplayMetricsHolder {
6270
}
6371

6472
@JvmStatic
65-
@SuppressLint("DeprecatedMethod") // for Andriod Lint
73+
@SuppressLint("DeprecatedMethod") // for Android Lint
6674
@Suppress("DEPRECATION") // for Kotlin compiler
6775
public fun initDisplayMetrics(context: Context) {
6876
val displayMetrics = context.resources.displayMetrics
69-
windowDisplayMetrics = displayMetrics
7077
val screenDisplayMetrics = DisplayMetrics()
7178
screenDisplayMetrics.setTo(displayMetrics)
7279
try {
@@ -85,14 +92,16 @@ public object DisplayMetricsHolder {
8592
}
8693

8794
@JvmStatic
88-
public fun getDisplayMetricsWritableMap(fontScale: Double): WritableMap {
89-
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
95+
public fun getDisplayMetricsWritableMap(
96+
windowDisplayMetrics: DisplayMetrics,
97+
fontScale: Double,
98+
): WritableMap {
9099
checkNotNull(screenDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
91100

92101
return WritableNativeMap().apply {
93102
putMap(
94103
"windowPhysicalPixels",
95-
getPhysicalPixelsWritableMap(windowDisplayMetrics as DisplayMetrics, fontScale),
104+
getPhysicalPixelsWritableMap(windowDisplayMetrics, fontScale),
96105
)
97106
putMap(
98107
"screenPhysicalPixels",

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/deviceinfo/DeviceInfoModuleTest.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package com.facebook.react.modules.deviceinfo
1111

12+
import android.util.DisplayMetrics
1213
import com.facebook.react.bridge.BridgeReactContext
1314
import com.facebook.react.bridge.JavaOnlyMap
1415
import com.facebook.react.bridge.ReactContext
@@ -111,8 +112,15 @@ class DeviceInfoModuleTest : TestCase() {
111112
}
112113

113114
private fun givenDisplayMetricsHolderContains(fakeDisplayMetrics: WritableMap?) {
115+
val windowDisplayMetrics = DisplayMetrics()
116+
117+
displayMetricsHolder
118+
.`when`<DisplayMetrics> { DisplayMetricsHolder.getWindowDisplayMetrics(reactContext, null) }
119+
.thenAnswer { windowDisplayMetrics }
114120
displayMetricsHolder
115-
.`when`<WritableMap> { DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0) }
121+
.`when`<WritableMap> {
122+
DisplayMetricsHolder.getDisplayMetricsWritableMap(windowDisplayMetrics, 1.0)
123+
}
116124
.thenAnswer { fakeDisplayMetrics }
117125
}
118126

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,19 @@ class DisplayMetricsHolderTest {
5555
fun setUp() {
5656
context = RuntimeEnvironment.getApplication()
5757
displayMetrics = context.resources.displayMetrics
58-
DisplayMetricsHolder.setWindowDisplayMetrics(null)
5958
DisplayMetricsHolder.setScreenDisplayMetrics(null)
6059
}
6160

6261
@After
6362
fun tearDown() {
64-
DisplayMetricsHolder.setWindowDisplayMetrics(null)
6563
DisplayMetricsHolder.setScreenDisplayMetrics(null)
6664
}
6765

68-
@Test(expected = IllegalStateException::class)
69-
fun getWindowDisplayMetrics_failsIfDisplayMetricsIsNotInitialized() {
70-
DisplayMetricsHolder.getWindowDisplayMetrics()
71-
}
72-
7366
@Test(expected = IllegalStateException::class)
7467
fun getScreenDisplayMetrics_failsIfDisplayMetricsIsNotInitialized() {
7568
DisplayMetricsHolder.getScreenDisplayMetrics()
7669
}
7770

78-
@Test
79-
fun setAndGetWindowDisplayMetrics_returnsSetValue() {
80-
DisplayMetricsHolder.setWindowDisplayMetrics(displayMetrics)
81-
val result = DisplayMetricsHolder.getWindowDisplayMetrics()
82-
assertThat(result).isEqualTo(displayMetrics)
83-
}
84-
8571
@Test
8672
fun setAndGetScreenDisplayMetrics_returnsSetValue() {
8773
DisplayMetricsHolder.setScreenDisplayMetrics(displayMetrics)
@@ -92,33 +78,32 @@ class DisplayMetricsHolderTest {
9278
@Test
9379
fun initDisplayMetrics_setsMetrics() {
9480
DisplayMetricsHolder.initDisplayMetrics(context)
95-
assertThat(DisplayMetricsHolder.getWindowDisplayMetrics()).isNotNull()
9681
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull()
9782
}
9883

9984
@Test
10085
fun initDisplayMetricsIfNotInitialized_onlyInitializesOnce() {
10186
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
102-
val firstWindow = DisplayMetricsHolder.getWindowDisplayMetrics()
10387
val firstScreen = DisplayMetricsHolder.getScreenDisplayMetrics()
10488
// Should not reinitialize
10589
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
106-
val secondWindow = DisplayMetricsHolder.getWindowDisplayMetrics()
10790
val secondScreen = DisplayMetricsHolder.getScreenDisplayMetrics()
108-
assertThat(secondWindow).isEqualTo(firstWindow)
10991
assertThat(secondScreen).isEqualTo(firstScreen)
11092
}
11193

11294
@Test(expected = IllegalStateException::class)
11395
fun getDisplayMetricsWritableMap_failsIfNotInitialized() {
114-
DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0)
96+
val windowDisplayMetrics = DisplayMetrics()
97+
DisplayMetricsHolder.getDisplayMetricsWritableMap(windowDisplayMetrics, 1.0)
11598
}
11699

117100
@Test
118101
fun getDisplayMetricsWritableMap_returnsCorrectMap() {
119102
// Use the official initialization method to ensure both metrics are set
120103
DisplayMetricsHolder.initDisplayMetrics(context)
121-
val map: WritableMap = DisplayMetricsHolder.getDisplayMetricsWritableMap(1.0)
104+
val windowDisplayMetrics = DisplayMetrics()
105+
val map: WritableMap =
106+
DisplayMetricsHolder.getDisplayMetricsWritableMap(windowDisplayMetrics, 1.0)
122107
assertThat(map.hasKey("windowPhysicalPixels")).isTrue()
123108
assertThat(map.hasKey("screenPhysicalPixels")).isTrue()
124109
val windowMap = map.getMap("windowPhysicalPixels")
@@ -204,7 +189,6 @@ class DisplayMetricsHolderTest {
204189
DisplayMetricsHolder.initDisplayMetrics(mockContext)
205190

206191
// Metrics should still be set from resource display metrics
207-
assertThat(DisplayMetricsHolder.getWindowDisplayMetrics()).isNotNull()
208192
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull()
209193
}
210194
}

0 commit comments

Comments
 (0)