androidandroid-statusbarandroid-fullscreenandroid-navigation-barandroid-immersive

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


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 ?


Solution

  • 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:

    this@MainActivity.cutoutMode(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 :)