Skip to content

Commit 47d8ed5

Browse files
committed
Fix Dimensions window values on Android < 15
1 parent d1a1020 commit 47d8ed5

11 files changed

Lines changed: 90 additions & 34 deletions

File tree

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3358,8 +3358,10 @@ public final class com/facebook/react/uimanager/DisplayMetricsHolder {
33583358
public static final fun getDisplayMetricsWritableMap (D)Lcom/facebook/react/bridge/WritableMap;
33593359
public static final fun getScreenDisplayMetrics ()Landroid/util/DisplayMetrics;
33603360
public static final fun getWindowDisplayMetrics ()Landroid/util/DisplayMetrics;
3361-
public static final fun initDisplayMetrics (Landroid/content/Context;)V
3362-
public static final fun initDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
3361+
public static final fun initScreenDisplayMetrics (Landroid/content/Context;)V
3362+
public static final fun initWindowDisplayMetrics (Landroid/content/Context;)V
3363+
public static final fun initScreenDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
3364+
public static final fun initWindowDisplayMetricsIfNotInitialized (Landroid/content/Context;)V
33633365
public static final fun setScreenDisplayMetrics (Landroid/util/DisplayMetrics;)V
33643366
public static final fun setWindowDisplayMetrics (Landroid/util/DisplayMetrics;)V
33653367
}

packages/react-native/ReactAndroid/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ dependencies {
665665
api(libs.androidx.autofill)
666666
api(libs.androidx.swiperefreshlayout)
667667
api(libs.androidx.tracing)
668+
api(libs.androidx.window)
668669

669670
api(libs.fbjni)
670671
api(libs.fresco)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@ public static ReactInstanceManagerBuilder builder() {
264264
FLog.d(TAG, "ReactInstanceManager.ctor()");
265265
initializeSoLoaderIfNecessary(applicationContext);
266266

267-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
267+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(applicationContext);
268+
269+
if (currentActivity != null) {
270+
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
271+
}
268272

269273
// See {@code ReactInstanceManagerBuilder} for description of all flags here.
270274
mApplicationContext = applicationContext;
@@ -929,6 +933,13 @@ public void onConfigurationChanged(Context updatedContext, @Nullable Configurati
929933

930934
ReactContext currentReactContext = getCurrentReactContext();
931935
if (currentReactContext != null) {
936+
DisplayMetricsHolder.initScreenDisplayMetrics(currentReactContext);
937+
Activity currentActivity = currentReactContext.getCurrentActivity();
938+
939+
if (currentActivity != null) {
940+
DisplayMetricsHolder.initWindowDisplayMetrics(currentActivity);
941+
}
942+
932943
AppearanceModule appearanceModule =
933944
currentReactContext.getNativeModule(AppearanceModule.class);
934945

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,8 @@ private void init() {
136136
setRootViewTag(ReactRootViewTagGenerator.getNextRootViewTag());
137137
setClipChildren(false);
138138

139-
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
140-
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
141-
}
139+
DisplayMetricsHolder.initScreenDisplayMetrics(getContext());
140+
DisplayMetricsHolder.initWindowDisplayMetrics(getContext());
142141
}
143142

144143
@Override
@@ -883,7 +882,8 @@ private class CustomGlobalLayoutListener implements ViewTreeObserver.OnGlobalLay
883882
private int mDeviceRotation = 0;
884883

885884
/* package */ CustomGlobalLayoutListener() {
886-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
885+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(getContext());
886+
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(getContext());
887887
mVisibleViewArea = new Rect();
888888
mMinKeyboardHeightDetected = (int) PixelUtil.toPixelFromDIP(60);
889889
}
@@ -1006,7 +1006,8 @@ private void checkForDeviceOrientationChanges() {
10061006
return;
10071007
}
10081008
mDeviceRotation = rotation;
1009-
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
1009+
DisplayMetricsHolder.initScreenDisplayMetrics(getContext());
1010+
DisplayMetricsHolder.initWindowDisplayMetrics(getContext());
10101011
emitOrientationChanged(rotation);
10111012
}
10121013

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ 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.initDisplayMetricsIfNotInitialized
18+
import com.facebook.react.uimanager.DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized
19+
import com.facebook.react.uimanager.DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized
1920
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
2021

2122
/** Module that exposes Android Constants to JS. */
@@ -26,7 +27,8 @@ internal class DeviceInfoModule(reactContext: ReactApplicationContext) :
2627
private var previousDisplayMetrics: ReadableMap? = null
2728

2829
init {
29-
initDisplayMetricsIfNotInitialized(reactContext)
30+
initScreenDisplayMetricsIfNotInitialized(reactContext)
31+
reactContext.currentActivity?.let { initWindowDisplayMetricsIfNotInitialized(it) }
3032
reactContext.addLifecycleEventListener(this)
3133
}
3234

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,9 +655,8 @@ public class ReactHostImpl(
655655
override fun onConfigurationChanged(context: Context) {
656656
val currentReactContext = this.currentReactContext
657657
if (currentReactContext != null) {
658-
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
659-
DisplayMetricsHolder.initDisplayMetrics(currentReactContext)
660-
}
658+
DisplayMetricsHolder.initScreenDisplayMetrics(currentReactContext)
659+
currentReactContext.currentActivity?.let { DisplayMetricsHolder.initWindowDisplayMetrics(it) }
661660

662661
val appearanceModule = currentReactContext.getNativeModule(AppearanceModule::class.java)
663662
appearanceModule?.onConfigurationChanged(context)
@@ -967,6 +966,7 @@ public class ReactHostImpl(
967966
val instance =
968967
ReactInstance(
969968
reactContext,
969+
currentActivity,
970970
reactHostDelegate,
971971
componentFactory,
972972
devSupportManager,

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.kt

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

88
package com.facebook.react.runtime
99

10+
import android.app.Activity
1011
import android.content.res.AssetManager
1112
import android.view.View
1213
import com.facebook.common.logging.FLog
@@ -88,6 +89,7 @@ import kotlin.jvm.JvmStatic
8889
@UnstableReactNativeAPI
8990
internal class ReactInstance(
9091
private val context: BridgelessReactContext,
92+
private val activity: Activity?,
9193
delegate: ReactHostDelegate,
9294
componentFactory: ComponentFactory,
9395
devSupportManager: DevSupportManager,
@@ -250,7 +252,8 @@ internal class ReactInstance(
250252
FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)
251253

252254
// Misc initialization that needs to be done before Fabric init
253-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
255+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(context)
256+
activity?.let { DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(it) }
254257

255258
val binding = FabricUIManagerBinding()
256259
binding.register(

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

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,29 @@ import android.util.DisplayMetrics
1313
import android.view.WindowManager
1414
import androidx.core.view.ViewCompat
1515
import androidx.core.view.WindowInsetsCompat
16+
import androidx.window.layout.WindowMetricsCalculator
1617
import com.facebook.react.bridge.WritableMap
1718
import com.facebook.react.bridge.WritableNativeMap
1819
import com.facebook.react.uimanager.PixelUtil.pxToDp
20+
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
1921

2022
/**
2123
* Holds an instance of the current DisplayMetrics so we don't have to thread it through all the
2224
* classes that need it.
2325
*/
2426
public object DisplayMetricsHolder {
25-
private const val INITIALIZATION_MISSING_MESSAGE =
26-
"DisplayMetricsHolder must be initialized with initDisplayMetricsIfNotInitialized or initDisplayMetrics"
27+
private const val SCREEN_INITIALIZATION_MISSING_MESSAGE =
28+
"DisplayMetricsHolder must be initialized with initScreenDisplayMetricsIfNotInitialized or initScreenDisplayMetrics"
29+
private const val WINDOW_INITIALIZATION_MISSING_MESSAGE =
30+
"DisplayMetricsHolder must be initialized with initWindowDisplayMetricsIfNotInitialized or initWindowDisplayMetrics"
2731

2832
@JvmStatic private var windowDisplayMetrics: DisplayMetrics? = null
2933
@JvmStatic private var screenDisplayMetrics: DisplayMetrics? = null
3034

3135
/** The metrics of the window associated to the Context used to initialize ReactNative */
3236
@JvmStatic
3337
public fun getWindowDisplayMetrics(): DisplayMetrics {
34-
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
38+
checkNotNull(windowDisplayMetrics) { WINDOW_INITIALIZATION_MISSING_MESSAGE }
3539
return windowDisplayMetrics as DisplayMetrics
3640
}
3741

@@ -43,7 +47,7 @@ public object DisplayMetricsHolder {
4347
/** Screen metrics returns the metrics of the default screen on the device. */
4448
@JvmStatic
4549
public fun getScreenDisplayMetrics(): DisplayMetrics {
46-
checkNotNull(screenDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
50+
checkNotNull(screenDisplayMetrics) { SCREEN_INITIALIZATION_MISSING_MESSAGE }
4751
return screenDisplayMetrics as DisplayMetrics
4852
}
4953

@@ -53,33 +57,53 @@ public object DisplayMetricsHolder {
5357
}
5458

5559
@JvmStatic
56-
public fun initDisplayMetricsIfNotInitialized(context: Context) {
57-
if (screenDisplayMetrics != null) {
58-
return
60+
public fun initScreenDisplayMetricsIfNotInitialized(context: Context) {
61+
if (screenDisplayMetrics == null) {
62+
initScreenDisplayMetrics(context)
5963
}
60-
initDisplayMetrics(context)
6164
}
6265

6366
@JvmStatic
64-
public fun initDisplayMetrics(context: Context) {
65-
val displayMetrics = context.resources.displayMetrics
66-
windowDisplayMetrics = displayMetrics
67-
val screenDisplayMetrics = DisplayMetrics()
68-
screenDisplayMetrics.setTo(displayMetrics)
67+
public fun initWindowDisplayMetricsIfNotInitialized(context: Context) {
68+
if (windowDisplayMetrics == null) {
69+
initWindowDisplayMetrics(context)
70+
}
71+
}
72+
73+
@JvmStatic
74+
public fun initScreenDisplayMetrics(context: Context) {
75+
val displayMetrics = DisplayMetrics()
76+
displayMetrics.setTo(context.resources.displayMetrics)
77+
6978
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
7079
// Get the real display metrics if we are using API level 17 or higher.
7180
// The real metrics include system decor elements (e.g. soft menu bar).
7281
//
7382
// See:
7483
// http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
75-
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(screenDisplayMetrics)
76-
DisplayMetricsHolder.screenDisplayMetrics = screenDisplayMetrics
84+
@Suppress("DEPRECATION") wm.defaultDisplay.getRealMetrics(displayMetrics)
85+
screenDisplayMetrics = displayMetrics
86+
}
87+
88+
@JvmStatic
89+
public fun initWindowDisplayMetrics(context: Context) {
90+
val displayMetrics = DisplayMetrics()
91+
displayMetrics.setTo(context.resources.displayMetrics)
92+
93+
if (isEdgeToEdgeFeatureFlagOn) {
94+
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context).let {
95+
displayMetrics.widthPixels = it.bounds.width()
96+
displayMetrics.heightPixels = it.bounds.height()
97+
}
98+
}
99+
100+
windowDisplayMetrics = displayMetrics
77101
}
78102

79103
@JvmStatic
80104
public fun getDisplayMetricsWritableMap(fontScale: Double): WritableMap {
81-
checkNotNull(windowDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
82-
checkNotNull(screenDisplayMetrics) { INITIALIZATION_MISSING_MESSAGE }
105+
checkNotNull(windowDisplayMetrics) { WINDOW_INITIALIZATION_MISSING_MESSAGE }
106+
checkNotNull(screenDisplayMetrics) { SCREEN_INITIALIZATION_MISSING_MESSAGE }
83107

84108
return WritableNativeMap().apply {
85109
putMap(

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
1313
import static com.facebook.react.uimanager.common.UIManagerType.LEGACY;
1414

15+
import android.app.Activity;
1516
import android.content.ComponentCallbacks2;
1617
import android.content.res.Configuration;
1718
import android.view.View;
@@ -128,7 +129,11 @@ public UIManagerModule(
128129
ViewManagerResolver viewManagerResolver,
129130
int minTimeLeftInFrameForNonBatchedOperationMs) {
130131
super(reactContext);
131-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
132+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext);
133+
Activity currentActivity = reactContext.getCurrentActivity();
134+
if (currentActivity != null) {
135+
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
136+
}
132137
mEventDispatcher = new EventDispatcherImpl(reactContext);
133138
mModuleConstants = createConstants(viewManagerResolver);
134139
mCustomDirectEvents = UIManagerModuleConstants.directEventTypeConstants;
@@ -148,7 +153,11 @@ public UIManagerModule(
148153
List<ViewManager> viewManagersList,
149154
int minTimeLeftInFrameForNonBatchedOperationMs) {
150155
super(reactContext);
151-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
156+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext);
157+
Activity currentActivity = reactContext.getCurrentActivity();
158+
if (currentActivity != null) {
159+
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(currentActivity);
160+
}
152161
mEventDispatcher = new EventDispatcherImpl(reactContext);
153162
mCustomDirectEvents = MapBuilder.newHashMap();
154163
mModuleConstants = createConstants(viewManagersList, null, mCustomDirectEvents);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ class RootViewTest {
7171
reactContext = spy(BridgeReactContext(RuntimeEnvironment.getApplication()))
7272
reactContext.initializeWithInstance(catalystInstanceMock)
7373

74-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext)
74+
DisplayMetricsHolder.initScreenDisplayMetricsIfNotInitialized(reactContext)
75+
DisplayMetricsHolder.initWindowDisplayMetricsIfNotInitialized(reactContext)
7576
val uiManagerModuleMock: UIManagerModule = mock()
7677
whenever(catalystInstanceMock.getNativeModule(UIManagerModule::class.java))
7778
.thenReturn(uiManagerModuleMock)

0 commit comments

Comments
 (0)