Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager;
import com.facebook.react.devsupport.interfaces.RedBoxHandler;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.interfaces.TaskInterface;
import com.facebook.react.internal.AndroidChoreographerProvider;
import com.facebook.react.internal.ChoreographerProvider;
import com.facebook.react.modules.appearance.AppearanceModule;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.packagerconnection.RequestHandler;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.ReactRoot;
Expand Down Expand Up @@ -859,6 +861,27 @@ public void onConfigurationChanged(Context updatedContext, @Nullable Configurati

ReactContext currentReactContext = getCurrentReactContext();
if (currentReactContext != null) {
boolean didDisplayMetricsChange =
DisplayMetricsHolder.updateDisplayMetricsIfChanged(updatedContext);
if (didDisplayMetricsChange) {
@Nullable UIManager uiManager = UIManagerHelper.getUIManager(currentReactContext, FABRIC);
if (uiManager instanceof FabricUIManager) {
((FabricUIManager) uiManager).updateDisplayMetricDensity();
}

synchronized (mAttachedReactRoots) {
for (ReactRoot reactRoot : mAttachedReactRoots) {
reactRoot.getRootViewGroup().requestLayout();
}
}

DeviceInfoModule deviceInfoModule =
currentReactContext.getNativeModule(DeviceInfoModule.class);
if (deviceInfoModule != null) {
deviceInfoModule.emitUpdateDimensionsEvent();
}
}

AppearanceModule appearanceModule =
currentReactContext.getNativeModule(AppearanceModule.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private void init() {
setClipChildren(false);

if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
DisplayMetricsHolder.initDisplayMetrics(getContext());
}
}

Expand Down Expand Up @@ -916,7 +916,7 @@ private class CustomGlobalLayoutListener implements ViewTreeObserver.OnGlobalLay
private int mDeviceRotation = 0;

/* package */ CustomGlobalLayoutListener() {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext());
mVisibleViewArea = new Rect();
}

Expand Down Expand Up @@ -991,7 +991,7 @@ private void checkForDeviceOrientationChanges() {
return;
}
mDeviceRotation = rotation;
DisplayMetricsHolder.initDisplayMetrics(getContext().getApplicationContext());
DisplayMetricsHolder.initDisplayMetrics(getContext());
emitOrientationChanged(rotation);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ public <T extends View> int startSurface(
UiThreadUtil.isOnUiThread() ? RootViewUtil.getViewportOffset(rootView) : new Point(0, 0);

Assertions.assertNotNull(mBinding, "Binding in FabricUIManager is null");
mBinding.setPixelDensity(context.getResources().getDisplayMetrics().density);
mBinding.startSurfaceWithConstraints(
rootTag,
moduleName,
Expand Down Expand Up @@ -1033,6 +1034,12 @@ void setBinding(FabricUIManagerBinding binding) {
mBinding = binding;
}

public void updateDisplayMetricDensity() {
if (mBinding != null) {
mBinding.setPixelDensity(PixelUtil.getDisplayMetricDensity());
}
}

/**
* Updates the layout metrics of the root view based on the Measure specs received by parameters.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatur
import com.facebook.react.modules.appearance.AppearanceModule
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.facebook.react.modules.deviceinfo.DeviceInfoModule
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers
import com.facebook.react.runtime.internal.bolts.Task
import com.facebook.react.runtime.internal.bolts.TaskCompletionSource
import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
import com.facebook.react.uimanager.DisplayMetricsHolder
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.events.BlackHoleEventDispatcher
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
Expand Down Expand Up @@ -736,16 +736,14 @@ public class ReactHostImpl(
override fun onConfigurationChanged(context: Context) {
val currentReactContext = this.currentReactContext
if (currentReactContext != null) {
if (ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()) {
val previousFontScale = PixelUtil.toPixelFromSP(1.0)
DisplayMetricsHolder.initDisplayMetrics(currentReactContext)
val newFontScale = PixelUtil.toPixelFromSP(1.0)

if (previousFontScale != newFontScale) {
synchronized(attachedSurfaces) {
attachedSurfaces.forEach { surface -> surface.view?.requestLayout() }
}
val didDisplayMetricsChange = DisplayMetricsHolder.updateDisplayMetricsIfChanged(context)
if (didDisplayMetricsChange) {
synchronized(attachedSurfaces) {
attachedSurfaces.forEach { surface -> surface.view?.requestLayout() }
}

val deviceInfoModule = currentReactContext.getNativeModule(DeviceInfoModule::class.java)
deviceInfoModule?.emitUpdateDimensionsEvent()
}

val appearanceModule = currentReactContext.getNativeModule(AppearanceModule::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ public object DisplayMetricsHolder {
@SuppressLint("DeprecatedMethod") // for Android Lint
@Suppress("DEPRECATION") // for Kotlin compiler
public fun initDisplayMetrics(context: Context) {
screenDisplayMetrics = getDisplayMetrics(context)
}

@JvmStatic
@SuppressLint("DeprecatedMethod") // for Android Lint
@Suppress("DEPRECATION") // for Kotlin compiler
public fun updateDisplayMetricsIfChanged(context: Context): Boolean {
val oldMetrics = screenDisplayMetrics
val newMetrics = getDisplayMetrics(context)
val didChange =
oldMetrics == null ||
oldMetrics.widthPixels != newMetrics.widthPixels ||
oldMetrics.heightPixels != newMetrics.heightPixels ||
oldMetrics.density != newMetrics.density ||
oldMetrics.scaledDensity != newMetrics.scaledDensity ||
oldMetrics.densityDpi != newMetrics.densityDpi
screenDisplayMetrics = newMetrics
return didChange
}

@SuppressLint("DeprecatedMethod") // for Android Lint
@Suppress("DEPRECATION") // for Kotlin compiler
private fun getDisplayMetrics(context: Context): DisplayMetrics {
val displayMetrics = context.resources.displayMetrics
val screenDisplayMetrics = DisplayMetrics()
screenDisplayMetrics.setTo(displayMetrics)
Expand All @@ -65,7 +88,7 @@ public object DisplayMetricsHolder {
// physical display metrics without the system font scale setting.
// This is needed for proper text scaling when fontScale < 1.0
screenDisplayMetrics.scaledDensity = displayMetrics.scaledDensity
DisplayMetricsHolder.screenDisplayMetrics = screenDisplayMetrics
return screenDisplayMetrics
}

internal fun getStatusBarHeightPx(activity: Activity?): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,61 @@ class DisplayMetricsHolderTest {
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull()
}

@Test
fun updateDisplayMetricsIfChanged_returnsTrueAndUpdatesMetricsWhenMetricsChange() {
val originalMetrics = DisplayMetrics().apply {
density = 2.0f
scaledDensity = 2.0f
widthPixels = 1000
heightPixels = 2000
densityDpi = DisplayMetrics.DENSITY_XHIGH
}
val updatedMetrics = DisplayMetrics().apply {
density = 1.5f
scaledDensity = 1.5f
widthPixels = 1200
heightPixels = 1800
densityDpi = DisplayMetrics.DENSITY_HIGH
}
val mockContext: Context = mock()
val mockResources: android.content.res.Resources = mock()

DisplayMetricsHolder.setScreenDisplayMetrics(originalMetrics)
whenever(mockContext.resources).thenReturn(mockResources)
whenever(mockResources.displayMetrics).thenReturn(updatedMetrics)
whenever(mockContext.getSystemService(Context.WINDOW_SERVICE))
.thenThrow(IllegalStateException("non-visual context"))

val didChange = DisplayMetricsHolder.updateDisplayMetricsIfChanged(mockContext)

assertThat(didChange).isTrue()
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics().density).isEqualTo(1.5f)
assertThat(DisplayMetricsHolder.getScreenDisplayMetrics().scaledDensity).isEqualTo(1.5f)
}

@Test
fun updateDisplayMetricsIfChanged_returnsFalseWhenMetricsMatch() {
val metrics = DisplayMetrics().apply {
density = 2.0f
scaledDensity = 2.0f
widthPixels = 1000
heightPixels = 2000
densityDpi = DisplayMetrics.DENSITY_XHIGH
}
val mockContext: Context = mock()
val mockResources: android.content.res.Resources = mock()

DisplayMetricsHolder.setScreenDisplayMetrics(DisplayMetrics().apply { setTo(metrics) })
whenever(mockContext.resources).thenReturn(mockResources)
whenever(mockResources.displayMetrics).thenReturn(metrics)
whenever(mockContext.getSystemService(Context.WINDOW_SERVICE))
.thenThrow(IllegalStateException("non-visual context"))

val didChange = DisplayMetricsHolder.updateDisplayMetricsIfChanged(mockContext)

assertThat(didChange).isFalse()
}

@Test
fun initDisplayMetricsIfNotInitialized_onlyInitializesOnce() {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
Expand Down
Loading