Skip to content

Commit aa34674

Browse files
committed
fix(android): use the context's display for display metrics, not the device default
DisplayMetricsHolder.initDisplayMetrics populated screenDisplayMetrics by calling getRealMetrics on WindowManager.defaultDisplay, which is always the device's primary display regardless of which display the activity is running on. On a secondary display (Samsung DeX, desktop mode, external monitor, freeform window) that reports the primary display's density and dimensions, so PixelUtil's dp <-> px conversion — and therefore Fabric's layout — scales content for the wrong display. The visible region ends up clipped to a fraction of the activity window and text renders at sub-pixel positions. Use Context.getDisplay() (API 30+) so the metrics come from the display the context is actually associated with, falling back to defaultDisplay on older API levels. Also pass the view's own context (not the application context) from ReactRootView, so the context is associated with the activity's display. Fixes #56894 (also tracked in #55659). Changelog: [ANDROID] [FIXED] - Display metrics now reflect the activity's display instead of the device's default display, fixing layout scaling on secondary displays / desktop mode / freeform windows.
1 parent 60d8b28 commit aa34674

2 files changed

Lines changed: 17 additions & 5 deletions

File tree

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ private void init() {
137137
setClipChildren(false);
138138

139139
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
140-
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
140+
DisplayMetricsHolder.initDisplayMetrics(getContext());
141141
}
142142
}
143143

@@ -932,7 +932,7 @@ private class CustomGlobalLayoutListener implements ViewTreeObserver.OnGlobalLay
932932
private int mDeviceRotation = 0;
933933

934934
/* package */ CustomGlobalLayoutListener() {
935-
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
935+
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext());
936936
mVisibleViewArea = new Rect();
937937
}
938938

@@ -1007,7 +1007,7 @@ private void checkForDeviceOrientationChanges() {
10071007
return;
10081008
}
10091009
mDeviceRotation = rotation;
1010-
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
1010+
DisplayMetricsHolder.initDisplayMetrics(getContext());
10111011
emitOrientationChanged(rotation);
10121012
}
10131013

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ package com.facebook.react.uimanager
1010
import android.annotation.SuppressLint
1111
import android.app.Activity
1212
import android.content.Context
13+
import android.os.Build
1314
import android.util.DisplayMetrics
15+
import android.view.Display
1416
import android.view.WindowManager
1517
import androidx.core.view.ViewCompat
1618
import androidx.core.view.WindowInsetsCompat
@@ -54,9 +56,19 @@ public object DisplayMetricsHolder {
5456
val screenDisplayMetrics = DisplayMetrics()
5557
screenDisplayMetrics.setTo(displayMetrics)
5658
try {
57-
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
59+
// Use the display the context is associated with, not the device's
60+
// default display. They differ when the activity is running on a
61+
// secondary display (Samsung DeX, desktop mode, external monitor,
62+
// freeform window) — using the default display there reports the
63+
// primary display's density/dimensions and breaks layout scaling.
64+
val display: Display? =
65+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
66+
context.display
67+
} else {
68+
(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
69+
}
5870
// getRealMetrics includes system decor (e.g. nav bar) excluded from resource metrics.
59-
wm.defaultDisplay.getRealMetrics(screenDisplayMetrics)
71+
display?.getRealMetrics(screenDisplayMetrics)
6072
} catch (_: Exception) {
6173
// Non-visual contexts (e.g. Application) may throw on API 30+.
6274
// Falls back to resource display metrics copied via setTo() above.

0 commit comments

Comments
 (0)