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
This commit is contained in:
Peter Abbondanzo
2024-05-31 08:57:41 -07:00
committed by Facebook GitHub Bot
parent db1043dfbf
commit cc485ccf7d
@@ -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<String, Any> {
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,