From cc485ccf7dddbcbdcfb03457fd2a48569fab5820 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Fri, 31 May 2024 08:57:41 -0700 Subject: [PATCH] Fix status bar height calculation for all cutout sizes (#44697) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44697 Google has discouraged attempting to read the `status_bar_height` resource [since 2017](https://youtu.be/_mGDMVRO3iE?si=qGQd7gLa_qTmfLGL&t=1079). With the introduction of display cutouts there can be a mismatch between the resource value and the true status bar size (and issues like [this one](https://github.com/facebook/react-native/issues/33612) popped up). The recommended approach is to instead call `getInsets` with the proper status bar and navigation flags provided by `WindowInsets`. On older APIs where `getInsets` is not supported, we have access to `systemWindowInsetTop`. Changelog: [Android][Fixed] - Fixed StatusBar.currentHeight calculations to honor all cutout sizes Reviewed By: cipolleschi, alanleedev Differential Revision: D57878119 fbshipit-source-id: 9fadd33d5f9b617a70a052c98dbd53fd29281650 --- .../modules/statusbar/StatusBarModule.kt | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt index 14857b9f288..08401927f55 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/statusbar/StatusBarModule.kt @@ -12,9 +12,9 @@ import android.animation.ValueAnimator import android.animation.ValueAnimator.AnimatorUpdateListener import android.os.Build import android.view.View +import android.view.WindowInsets import android.view.WindowInsetsController import android.view.WindowManager -import android.view.WindowManager.LayoutParams import androidx.core.view.ViewCompat import com.facebook.common.logging.FLog import com.facebook.fbreact.specs.NativeStatusBarManagerAndroidSpec @@ -33,31 +33,40 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : @Suppress("DEPRECATION") override fun getTypedExportedConstants(): Map { - val context = getReactApplicationContext() - val heightResId = context.resources.getIdentifier("status_bar_height", "dimen", "android") - val height = - heightResId - .takeIf { it > 0 } - ?.let { - PixelUtil.toDIPFromPixel(context.resources.getDimensionPixelSize(it).toFloat()) - } ?: 0 - var statusBarColorString = "black" - val statusBarColor = getCurrentActivity()?.window?.statusBarColor + val statusBarColor = currentActivity?.window?.statusBarColor if (statusBarColor != null) { statusBarColorString = String.format("#%06X", 0xFFFFFF and statusBarColor) } return mapOf( - HEIGHT_KEY to height, + HEIGHT_KEY to PixelUtil.toDIPFromPixel(getStatusBarHeightPx()), DEFAULT_BACKGROUND_COLOR_KEY to statusBarColorString, ) } + @Suppress("DEPRECATION") + private fun getStatusBarHeightPx(): Float { + val activity = currentActivity ?: return 0f + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val window = activity.window ?: return 0f + val insets = + window.decorView.rootWindowInsets.getInsets( + WindowInsets.Type.statusBars() or + WindowInsets.Type.navigationBars() or + WindowInsets.Type.displayCutout()) + insets.top.toFloat() + } else { + val window = activity.window ?: return 0f + val insets = window.decorView.rootWindowInsets + insets.systemWindowInsetTop.toFloat() + } + } + @Suppress("DEPRECATION") override fun setColor(colorDouble: Double, animated: Boolean) { val color = colorDouble.toInt() - val activity = getCurrentActivity() + val activity = currentActivity if (activity == null) { FLog.w( ReactConstants.TAG, @@ -65,7 +74,7 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : return } UiThreadUtil.runOnUiThread( - object : GuardedRunnable(getReactApplicationContext()) { + object : GuardedRunnable(reactApplicationContext) { override fun runGuarded() { val window = activity.window ?: return window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) @@ -89,7 +98,7 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : @Suppress("DEPRECATION") override fun setTranslucent(translucent: Boolean) { - val activity = getCurrentActivity() + val activity = currentActivity if (activity == null) { FLog.w( ReactConstants.TAG, @@ -97,7 +106,7 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : return } UiThreadUtil.runOnUiThread( - object : GuardedRunnable(getReactApplicationContext()) { + object : GuardedRunnable(reactApplicationContext) { override fun runGuarded() { // If the status bar is translucent hook into the window insets calculations // and consume all the top insets so no padding will be added under the status bar. @@ -122,7 +131,7 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : @Suppress("DEPRECATION") override fun setHidden(hidden: Boolean) { - val activity = getCurrentActivity() + val activity = currentActivity if (activity == null) { FLog.w( ReactConstants.TAG, @@ -144,7 +153,7 @@ public class StatusBarModule(reactContext: ReactApplicationContext?) : @Suppress("DEPRECATION") override fun setStyle(style: String?) { - val activity = getCurrentActivity() + val activity = currentActivity if (activity == null) { FLog.w( ReactConstants.TAG,