Dark status bar on android 30 and above won't disappear

129 views Asked by At

I have an activity that's supposed to host a web view in fullscreen with the status and navigation bars fully immersive as I don't want to set a color for the status bar. I tried multiple snippets from S.O and I still have a dark status bar for Android 12 and above devices. I need a fully immersive status and navigation bar. With the implementation below, the navigation bar is displayed as expected; the status bar is still dark.

Here's the activity's theme that I apply to the activity:

<style name="FullScreenTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:colorBackground">@null</item>
    <item name="android:windowBackground">@null</item>
    <item name="background">@null</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:navigationBarColor">@android:color/transparent</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:enforceStatusBarContrast" tools:targetApi="q">false</item>
    <item name="android:enforceNavigationBarContrast"  tools:targetApi="q">false</item>
</style>

Here's the activity's onCreate() function:

  super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, false)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
        val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
        windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
    } else {
        @Suppress("DEPRECATION") // Older API support
        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE

                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN)
    }

After this I call the setContentView() and move forward. Any ideas as to what I'm missing exactly here ? Has any one faced a similar issue lately and has a (tested) workaround for this ?

3

There are 3 answers

3
Yurowitz On BEST ANSWER

The issue you're facing is probably related to the fact that you're not enabling cut-out mode (Enabling cut-out mode means that the full-screen mode will not reserve the space for the notch/camera and will just draw behind it as well, which will expand the screen periphery to the very outer boundaries).

Here's a helper method to enable or disable cut-out mode. Make sure you call it from an activity context (because it's an extension function)

    @TargetApi(Build.VERSION_CODES.P)
    fun ComponentActivity.cutoutMode(enable: Boolean) {
        if (enable) {
            window.attributes?.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
        } else {
            window.attributes?.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
        }
    }

Example usage:

[email protected](enable = true) 


//or simply
cutoutMode(enable = true)

If you want a rather static way to do it (in XML), you can edit your app's theme (in your themes.xml or styles.xml, wherever it is) to include this following property:

<item name="android:windowLayoutInDisplayCutoutMode">
    shortEdges <!-- default, shortEdges, or never -->
  </item>

You can find more about cut-out mode in the official docs: Google's Developer Android- Support display cutouts

If you're still having issues going full screen, you can use this helper method :

fun ComponentActivity.hideSystemUI(useDeprecated: Boolean) {
        runOnUiThread {
            if (!useDeprecated) {
                WindowCompat.setDecorFitsSystemWindows(window, false)
                WindowInsetsControllerCompat(window, window.decorView).let { controller ->
                    controller.hide(WindowInsetsCompat.Type.systemBars())
                    controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
                }
            } else {
                val decorView: View = window.decorView
                val uiOptions = decorView.systemUiVisibility
                var newUiOptions = uiOptions
                newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_LOW_PROFILE
                newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_FULLSCREEN
                newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_IMMERSIVE
                newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                decorView.systemUiVisibility = newUiOptions
                View.OnSystemUiVisibilityChangeListener { newmode ->
                    if (newmode != newUiOptions) {
                        hideSystemUI(false)
                    }
                }
                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)

            }
        }
    }

Please note that you can still use the deprecated way to go fullscreen (immersive mode). I personally found it easier and more stable even though it's deprecated ? That's why I don't do 'if-else' SDK version checks to choose one of the two ways, I only do:

hideSystemUI(useDeprecated = true)

even if I am on Android 14.

Hope this helps :)

0
hata On

As official guide explains, you should execute your code in OnApplyWindowInsetsListener:

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    window.decorView.setOnApplyWindowInsetsListener { view, windowInsets ->
        WindowCompat.setDecorFitsSystemWindows(window, false)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
            val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
            windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
        } else {
            @Suppress("DEPRECATION") // Older API support
            window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE

                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN)
        }

        view.onApplyWindowInsets(windowInsets)
    }
}

Alternatively, as of former official guide's explanation, execute your code in onWindowFocusChanged other than in onCreate:

override fun onWindowFocusChanged(hasFocus: Boolean) {
    super.onWindowFocusChanged(hasFocus)

    if (hasFocus) {
        WindowCompat.setDecorFitsSystemWindows(window, false)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
            val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
            windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
        } else {
            @Suppress("DEPRECATION") // Older API support
            window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE

                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN)
        }
    }
}
0
suresh madaparthi On
    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
    setContentView(R.layout.activity_main);
    Utility.darkenStatusBar(this, R.color.yourcolorname);

After Create Utility class

  public class Utility {
    public static void darkenStatusBar(Activity activity, int color) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

        activity.getWindow().setStatusBarColor(
                darkenColor(ContextCompat.getColor(activity, color)));
     }
  }
  private static int darkenColor(int color) {
    float[] hsv = new float[3];
    Color.colorToHSV(color, hsv);
    hsv[2] *= 0.8f;
    return Color.HSVToColor(hsv);
  }
}