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 ead31bf3b0e1..b901406e398a 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 @@ -7,6 +7,7 @@ package com.facebook.react.uimanager +import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.util.DisplayMetrics @@ -61,19 +62,21 @@ public object DisplayMetricsHolder { } @JvmStatic - @Suppress("DEPRECATION") + @SuppressLint("DeprecatedMethod") // for Andriod Lint + @Suppress("DEPRECATION") // for Kotlin compiler public fun initDisplayMetrics(context: Context) { val displayMetrics = context.resources.displayMetrics windowDisplayMetrics = displayMetrics val screenDisplayMetrics = DisplayMetrics() screenDisplayMetrics.setTo(displayMetrics) - val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager - // Get the real display metrics if we are using API level 17 or higher. - // The real metrics include system decor elements (e.g. soft menu bar). - // - // See: - // http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics) - wm.defaultDisplay.getRealMetrics(screenDisplayMetrics) + try { + val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + // getRealMetrics includes system decor (e.g. nav bar) excluded from resource metrics. + wm.defaultDisplay.getRealMetrics(screenDisplayMetrics) + } catch (_: Exception) { + // Non-visual contexts (e.g. Application) may throw on API 30+. + // Falls back to resource display metrics copied via setTo() above. + } // 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/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/DisplayMetricsHolderTest.kt index 92a2ce3fe1c2..6b2137c1ba5d 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 @@ -10,13 +10,13 @@ package com.facebook.react.uimanager -import android.annotation.TargetApi import android.app.Activity import android.content.Context import android.util.DisplayMetrics import android.view.View import android.view.Window import android.view.WindowInsets +import androidx.annotation.RequiresApi import com.facebook.react.bridge.WritableMap import com.facebook.testutils.shadows.ShadowNativeLoader import com.facebook.testutils.shadows.ShadowNativeMap @@ -133,7 +133,7 @@ class DisplayMetricsHolderTest { } @Test - @TargetApi(30) + @RequiresApi(30) fun getEncodedScreenSizeWithoutVerticalInsets_returnsEncodedValue() { DisplayMetricsHolder.initDisplayMetrics(context) @@ -183,4 +183,28 @@ class DisplayMetricsHolderTest { assertThat(screenMetrics.scaledDensity).isEqualTo(customScaledDensity) } + + @Test + fun initDisplayMetrics_doesNotCrashWithNonVisualContext() { + val mockContext: Context = mock() + val mockResources: android.content.res.Resources = mock() + val metrics = DisplayMetrics() + metrics.density = 2.0f + metrics.scaledDensity = 2.0f + metrics.widthPixels = 1080 + metrics.heightPixels = 1920 + metrics.densityDpi = DisplayMetrics.DENSITY_XHIGH + + whenever(mockContext.resources).thenReturn(mockResources) + whenever(mockResources.displayMetrics).thenReturn(metrics) + whenever(mockContext.getSystemService(Context.WINDOW_SERVICE)) + .thenThrow(IllegalStateException("non-visual context")) + + // Should not throw + DisplayMetricsHolder.initDisplayMetrics(mockContext) + + // Metrics should still be set from resource display metrics + assertThat(DisplayMetricsHolder.getWindowDisplayMetrics()).isNotNull() + assertThat(DisplayMetricsHolder.getScreenDisplayMetrics()).isNotNull() + } }