diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 0342cf267f2f..0c879d6415f9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -564,6 +564,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun useTurboModuleInterop(): Boolean = accessor.useTurboModuleInterop() + /** + * Keeps the window density in the screen display metrics instead of the physical display density, so text is measured at the density the window renders at. + */ + @JvmStatic + public fun useWindowDensityForDisplayMetricsAndroid(): Boolean = accessor.useWindowDensityForDisplayMetricsAndroid() + /** * Outset the culling context frame with the provided ratio. The culling context frame size will be outset by width * ratio on the left and right, and height * ratio on the top and bottom. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index bfc64b6a1339..28329721fab8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0107593110db613063b6f4ceb50d8e58>> + * @generated SignedSource<<71d0554dc45df7d5365f81b7ec8c3d5b>> */ /** @@ -109,6 +109,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var useSharedAnimatedBackendCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null + private var useWindowDensityForDisplayMetricsAndroidCache: Boolean? = null private var viewCullingOutsetRatioCache: Double? = null private var viewTransitionEnabledCache: Boolean? = null private var viewTransitionUseHardwareBitmapAndroidCache: Boolean? = null @@ -915,6 +916,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun useWindowDensityForDisplayMetricsAndroid(): Boolean { + var cached = useWindowDensityForDisplayMetricsAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useWindowDensityForDisplayMetricsAndroid() + useWindowDensityForDisplayMetricsAndroidCache = cached + } + return cached + } + override fun viewCullingOutsetRatio(): Double { var cached = viewCullingOutsetRatioCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 93e688128426..6f83f1cc3dbd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<312cec99bd429d22e25f4e82d7fc4ee7>> */ /** @@ -206,6 +206,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun useTurboModuleInterop(): Boolean + @DoNotStrip @JvmStatic public external fun useWindowDensityForDisplayMetricsAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun viewCullingOutsetRatio(): Double @DoNotStrip @JvmStatic public external fun viewTransitionEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index c593a036b4e7..f4998cb8b416 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<768077a22c110aa98ab02bc96eec695b>> + * @generated SignedSource<<669f16e131d4a5ddc026ffecc6893575>> */ /** @@ -201,6 +201,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useTurboModuleInterop(): Boolean = false + override fun useWindowDensityForDisplayMetricsAndroid(): Boolean = false + override fun viewCullingOutsetRatio(): Double = 0.0 override fun viewTransitionEnabled(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 961e8c94ddc7..536ee78905d7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<86ebc7307e7ebe2023e909a868ed3117>> + * @generated SignedSource<> */ /** @@ -113,6 +113,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var useSharedAnimatedBackendCache: Boolean? = null private var useTraitHiddenOnAndroidCache: Boolean? = null private var useTurboModuleInteropCache: Boolean? = null + private var useWindowDensityForDisplayMetricsAndroidCache: Boolean? = null private var viewCullingOutsetRatioCache: Double? = null private var viewTransitionEnabledCache: Boolean? = null private var viewTransitionUseHardwareBitmapAndroidCache: Boolean? = null @@ -1008,6 +1009,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun useWindowDensityForDisplayMetricsAndroid(): Boolean { + var cached = useWindowDensityForDisplayMetricsAndroidCache + if (cached == null) { + cached = currentProvider.useWindowDensityForDisplayMetricsAndroid() + accessedFeatureFlags.add("useWindowDensityForDisplayMetricsAndroid") + useWindowDensityForDisplayMetricsAndroidCache = cached + } + return cached + } + override fun viewCullingOutsetRatio(): Double { var cached = viewCullingOutsetRatioCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 73121e261bb3..0143013e3ccd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<18267103ef4f3eb22f23a15b808528c2>> */ /** @@ -201,6 +201,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun useTurboModuleInterop(): Boolean + @DoNotStrip public fun useWindowDensityForDisplayMetricsAndroid(): Boolean + @DoNotStrip public fun viewCullingOutsetRatio(): Double @DoNotStrip public fun viewTransitionEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt index 7dd31eb758d5..76827dec40fa 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/DisplayMetricsHolder.kt @@ -14,6 +14,7 @@ import android.util.DisplayMetrics import android.view.WindowManager import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.uimanager.PixelUtil.pxToDp /** @@ -61,6 +62,13 @@ public object DisplayMetricsHolder { // Non-visual contexts (e.g. Application) may throw on API 30+. // Falls back to resource display metrics copied via setTo() above. } + if ( + displayMetrics.densityDpi != screenDisplayMetrics.densityDpi && + ReactNativeFeatureFlags.useWindowDensityForDisplayMetricsAndroid() + ) { + screenDisplayMetrics.density = displayMetrics.density + screenDisplayMetrics.densityDpi = displayMetrics.densityDpi + } // Preserve fontScale from the configuration because getRealMetrics() returns // physical display metrics without the system font scale setting. // This is needed for proper text scaling when fontScale < 1.0 diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 6bff0ece1398..526398c18c11 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<200266835197634d13de07182d80a2c1>> + * @generated SignedSource<<568d6e4175fb51e09bc3d4441ffa9741>> */ /** @@ -573,6 +573,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool useWindowDensityForDisplayMetricsAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useWindowDensityForDisplayMetricsAndroid"); + return method(javaProvider_); + } + double viewCullingOutsetRatio() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("viewCullingOutsetRatio"); @@ -1046,6 +1052,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useTurboModuleInterop( return ReactNativeFeatureFlags::useTurboModuleInterop(); } +bool JReactNativeFeatureFlagsCxxInterop::useWindowDensityForDisplayMetricsAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useWindowDensityForDisplayMetricsAndroid(); +} + double JReactNativeFeatureFlagsCxxInterop::viewCullingOutsetRatio( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::viewCullingOutsetRatio(); @@ -1364,6 +1375,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "useTurboModuleInterop", JReactNativeFeatureFlagsCxxInterop::useTurboModuleInterop), + makeNativeMethod( + "useWindowDensityForDisplayMetricsAndroid", + JReactNativeFeatureFlagsCxxInterop::useWindowDensityForDisplayMetricsAndroid), makeNativeMethod( "viewCullingOutsetRatio", JReactNativeFeatureFlagsCxxInterop::viewCullingOutsetRatio), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index e87b24228769..5f3fdda386cd 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<13aa248dae556b06b31c25a28e448460>> + * @generated SignedSource<<2e0f936c003aafc456ab3f960ca4b2f3>> */ /** @@ -297,6 +297,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool useTurboModuleInterop( facebook::jni::alias_ref); + static bool useWindowDensityForDisplayMetricsAndroid( + facebook::jni::alias_ref); + static double viewCullingOutsetRatio( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt index 54bc46108f8a..8b409f185d70 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt @@ -17,6 +17,9 @@ import android.view.View import android.view.Window import android.view.WindowInsets import androidx.annotation.RequiresApi +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests import com.facebook.testutils.shadows.ShadowNativeLoader import com.facebook.testutils.shadows.ShadowNativeMap import com.facebook.testutils.shadows.ShadowReadableNativeMap @@ -52,6 +55,7 @@ class DisplayMetricsHolderTest { @Before fun setUp() { + ReactNativeFeatureFlagsForTests.setUp() context = RuntimeEnvironment.getApplication() displayMetrics = context.resources.displayMetrics DisplayMetricsHolder.setScreenDisplayMetrics(null) @@ -60,6 +64,7 @@ class DisplayMetricsHolderTest { @After fun tearDown() { DisplayMetricsHolder.setScreenDisplayMetrics(null) + ReactNativeFeatureFlags.dangerouslyReset() } @Test(expected = IllegalStateException::class) @@ -142,6 +147,41 @@ class DisplayMetricsHolderTest { assertThat(screenMetrics.scaledDensity).isEqualTo(customScaledDensity) } + @Test + fun initDisplayMetrics_withWindowDensityFlag_usesTheWindowDensity() { + overrideWindowDensityFlag(true) + setWindowDensityTo400Dpi() + + DisplayMetricsHolder.initDisplayMetrics(context) + + val screenMetrics = DisplayMetricsHolder.getScreenDisplayMetrics() + assertThat(screenMetrics.density).isEqualTo(2.5f) + assertThat(screenMetrics.densityDpi).isEqualTo(DisplayMetrics.DENSITY_400) + assertThat(PixelUtil.toDIPFromPixel(PixelUtil.toPixelFromSP(16f))).isEqualTo(16f) + } + + @Test + fun initDisplayMetrics_withoutWindowDensityFlag_usesTheDisplayDensity() { + DisplayMetricsHolder.initDisplayMetrics(context) + val displayDensityDpi = DisplayMetricsHolder.getScreenDisplayMetrics().densityDpi + overrideWindowDensityFlag(false) + setWindowDensityTo400Dpi() + + DisplayMetricsHolder.initDisplayMetrics(context) + + assertThat(DisplayMetricsHolder.getScreenDisplayMetrics().densityDpi) + .isEqualTo(displayDensityDpi) + .isNotEqualTo(DisplayMetrics.DENSITY_400) + } + + @Test + fun initDisplayMetrics_withMatchingDensities_doesNotReadFeatureFlags() { + DisplayMetricsHolder.initDisplayMetrics(context) + overrideWindowDensityFlag(true) + + assertThat(ReactNativeFeatureFlags.useWindowDensityForDisplayMetricsAndroid()).isTrue() + } + @Test fun initDisplayMetrics_doesNotCrashWithNonVisualContext() { val mockContext: Context = mock() @@ -164,4 +204,19 @@ class DisplayMetricsHolderTest { // Metrics should still be set from resource display metrics assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull() } + + private fun overrideWindowDensityFlag(enabled: Boolean) { + ReactNativeFeatureFlags.override( + object : ReactNativeFeatureFlagsDefaults() { + override fun useWindowDensityForDisplayMetricsAndroid(): Boolean = enabled + }, + ) + } + + private fun setWindowDensityTo400Dpi() { + val resourceMetrics = context.resources.displayMetrics + resourceMetrics.density = 2.5f + resourceMetrics.densityDpi = DisplayMetrics.DENSITY_400 + resourceMetrics.scaledDensity = 2.5f + } } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/PixelUtilTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/PixelUtilTest.kt index 46ae613601d3..766392383397 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/PixelUtilTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/PixelUtilTest.kt @@ -14,6 +14,8 @@ import android.content.Context import android.content.res.Configuration import android.content.res.Resources import android.util.DisplayMetrics +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests import com.facebook.testutils.shadows.ShadowNativeLoader import com.facebook.testutils.shadows.ShadowSoLoader import kotlin.math.abs @@ -36,6 +38,7 @@ class PixelUtilTest { @Before fun setUp() { + ReactNativeFeatureFlagsForTests.setUp() context = RuntimeEnvironment.getApplication() DisplayMetricsHolder.setScreenDisplayMetrics(null) } @@ -43,6 +46,7 @@ class PixelUtilTest { @After fun tearDown() { DisplayMetricsHolder.setScreenDisplayMetrics(null) + ReactNativeFeatureFlags.dangerouslyReset() } @Test diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 1744ea854459..adc3531518ef 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -382,6 +382,10 @@ bool ReactNativeFeatureFlags::useTurboModuleInterop() { return getAccessor().useTurboModuleInterop(); } +bool ReactNativeFeatureFlags::useWindowDensityForDisplayMetricsAndroid() { + return getAccessor().useWindowDensityForDisplayMetricsAndroid(); +} + double ReactNativeFeatureFlags::viewCullingOutsetRatio() { return getAccessor().viewCullingOutsetRatio(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index c5195f19bd59..cd4d40533d5f 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1a9a8a587b477b6aa0d3aa57e30272c2>> + * @generated SignedSource<<62dbb584d6aaf595314636a81346e3e5>> */ /** @@ -486,6 +486,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool useTurboModuleInterop(); + /** + * Keeps the window density in the screen display metrics instead of the physical display density, so text is measured at the density the window renders at. + */ + RN_EXPORT static bool useWindowDensityForDisplayMetricsAndroid(); + /** * Outset the culling context frame with the provided ratio. The culling context frame size will be outset by width * ratio on the left and right, and height * ratio on the top and bottom. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 3d332f2976f7..93ef1cc62ace 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -1631,6 +1631,24 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::useWindowDensityForDisplayMetricsAndroid() { + auto flagValue = useWindowDensityForDisplayMetricsAndroid_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(89, "useWindowDensityForDisplayMetricsAndroid"); + + flagValue = currentProvider_->useWindowDensityForDisplayMetricsAndroid(); + useWindowDensityForDisplayMetricsAndroid_ = flagValue; + } + + return flagValue.value(); +} + double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { auto flagValue = viewCullingOutsetRatio_.load(); @@ -1640,7 +1658,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewCullingOutsetRatio"); + markFlagAsAccessed(90, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1658,7 +1676,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "viewTransitionEnabled"); + markFlagAsAccessed(91, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1676,7 +1694,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(91, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(92, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1694,7 +1712,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(92, "virtualViewPrerenderRatio"); + markFlagAsAccessed(93, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index fc7efe0b5c7e..5c05c3bc5427 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<486b4aac1c83d8bced6a3f5393a03756>> + * @generated SignedSource<<074e6ad1d773f289f93ce1d16656ec6e>> */ /** @@ -123,6 +123,7 @@ class ReactNativeFeatureFlagsAccessor { bool useSharedAnimatedBackend(); bool useTraitHiddenOnAndroid(); bool useTurboModuleInterop(); + bool useWindowDensityForDisplayMetricsAndroid(); double viewCullingOutsetRatio(); bool viewTransitionEnabled(); bool viewTransitionUseHardwareBitmapAndroid(); @@ -138,7 +139,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 93> accessedFeatureFlags_; + std::array, 94> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -229,6 +230,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> useSharedAnimatedBackend_; std::atomic> useTraitHiddenOnAndroid_; std::atomic> useTurboModuleInterop_; + std::atomic> useWindowDensityForDisplayMetricsAndroid_; std::atomic> viewCullingOutsetRatio_; std::atomic> viewTransitionEnabled_; std::atomic> viewTransitionUseHardwareBitmapAndroid_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 92e1157fefbf..82208e3861b9 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<52db1adfed5fd99e4ca145b50dc413a0>> */ /** @@ -385,6 +385,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool useWindowDensityForDisplayMetricsAndroid() override { + return false; + } + double viewCullingOutsetRatio() override { return 0.0; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index a49d8e7e919a..4d40ac107fc4 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2a5ad585617d77035a43b255d3e30eb3>> + * @generated SignedSource<<8d27c5f3feb4c8000922012882701b7e>> */ /** @@ -848,6 +848,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::useTurboModuleInterop(); } + bool useWindowDensityForDisplayMetricsAndroid() override { + auto value = values_["useWindowDensityForDisplayMetricsAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::useWindowDensityForDisplayMetricsAndroid(); + } + double viewCullingOutsetRatio() override { auto value = values_["viewCullingOutsetRatio"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index eed6e5e8a51c..3ba8405c14ff 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8daa3cebd554caa27139d96604fcc368>> + * @generated SignedSource<<7e5839223cfce81a42708f876c4c77ee>> */ /** @@ -116,6 +116,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool useSharedAnimatedBackend() = 0; virtual bool useTraitHiddenOnAndroid() = 0; virtual bool useTurboModuleInterop() = 0; + virtual bool useWindowDensityForDisplayMetricsAndroid() = 0; virtual double viewCullingOutsetRatio() = 0; virtual bool viewTransitionEnabled() = 0; virtual bool viewTransitionUseHardwareBitmapAndroid() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 55a535f5be9d..f2d4210f8d23 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<00cccf7098052c51f5855949375ea0f9>> + * @generated SignedSource<> */ /** @@ -489,6 +489,11 @@ bool NativeReactNativeFeatureFlags::useTurboModuleInterop( return ReactNativeFeatureFlags::useTurboModuleInterop(); } +bool NativeReactNativeFeatureFlags::useWindowDensityForDisplayMetricsAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useWindowDensityForDisplayMetricsAndroid(); +} + double NativeReactNativeFeatureFlags::viewCullingOutsetRatio( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::viewCullingOutsetRatio(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 459619f34276..4aafb51f00fc 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<84b944e162fc53799326c584a5edd17d>> */ /** @@ -216,6 +216,8 @@ class NativeReactNativeFeatureFlags bool useTurboModuleInterop(jsi::Runtime& runtime); + bool useWindowDensityForDisplayMetricsAndroid(jsi::Runtime& runtime); + double viewCullingOutsetRatio(jsi::Runtime& runtime); bool viewTransitionEnabled(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 08f37bd4f127..0a76242a2c75 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -1005,6 +1005,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'canary', }, + useWindowDensityForDisplayMetricsAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2026-10-11', + description: + 'Keeps the window density in the screen display metrics instead of the physical display density, so text is measured at the density the window renders at.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, viewCullingOutsetRatio: { defaultValue: 0, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index d8626a635e6b..1f474567697e 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<4eb26b4190d023d26a50f54b6e3ea5eb>> * @flow strict * @noformat */ @@ -139,6 +139,7 @@ export type ReactNativeFeatureFlags = Readonly<{ useSharedAnimatedBackend: Getter, useTraitHiddenOnAndroid: Getter, useTurboModuleInterop: Getter, + useWindowDensityForDisplayMetricsAndroid: Getter, viewCullingOutsetRatio: Getter, viewTransitionEnabled: Getter, viewTransitionUseHardwareBitmapAndroid: Getter, @@ -580,6 +581,10 @@ export const useTraitHiddenOnAndroid: Getter = createNativeFlagGetter(' * In Bridgeless mode, should legacy NativeModules use the TurboModule system? */ export const useTurboModuleInterop: Getter = createNativeFlagGetter('useTurboModuleInterop', false); +/** + * Keeps the window density in the screen display metrics instead of the physical display density, so text is measured at the density the window renders at. + */ +export const useWindowDensityForDisplayMetricsAndroid: Getter = createNativeFlagGetter('useWindowDensityForDisplayMetricsAndroid', false); /** * Outset the culling context frame with the provided ratio. The culling context frame size will be outset by width * ratio on the left and right, and height * ratio on the top and bottom. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index b73154a74852..673dc0ed3bf8 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3972c873d92371d3dc67eaa3a6217427>> + * @generated SignedSource<<40cc7b87d7658fb5aa3d33f468eff3fc>> * @flow strict * @noformat */ @@ -114,6 +114,7 @@ export interface Spec extends TurboModule { readonly useSharedAnimatedBackend?: () => boolean; readonly useTraitHiddenOnAndroid?: () => boolean; readonly useTurboModuleInterop?: () => boolean; + readonly useWindowDensityForDisplayMetricsAndroid?: () => boolean; readonly viewCullingOutsetRatio?: () => number; readonly viewTransitionEnabled?: () => boolean; readonly viewTransitionUseHardwareBitmapAndroid?: () => boolean;