androidstatusbarandroid-navigation-barandroid-windowandroid-api-30

Switch Status Bar Text Color / setAppearanceLightStatusBars of WindowController is not working as expected


I am using two different backgrounds for my app depending on which screen is active. In general the status bar is transparent and the background should lay behind it. Depending on the background I need the color of my status bar text to be either light or dark.

So far I used this theme attributes to get the status bar transparent (well its translucent really, but if anyone knows how to get it transparent it would be appreciated)

<item name="android:windowTranslucentStatus">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>

I am using Jetpack navigation to navigate and pass arguments to the destination whenever the background should switch like this

mNavController.addOnDestinationChangedListener((controller, destination, arguments) -> {
        if (arguments != null) {
            setUseSecondaryBackground(arguments.getBoolean("bg_line_style", false));
        }
});

This works really good. Now the part that does not work is the switching of the status bar text color:

private void setUseSecondaryBackground(boolean useLineBackground) {
        if (useLineBackground) {
            //Stuff to switch backgrounds
            windowController.setAppearanceLightStatusBars(false); //Works fine
        } else {
            //Stuff to switch backgrounds

            //Does not work!!! If enabled the whole background is shifted downward by the status bar hight - see pictures
            windowController.setAppearanceLightStatusBars(true);
        }
}

This is the used controller, created in onCreate() of my MainActivity

windowController = new WindowInsetsControllerCompat(this.getWindow(), this.getWindow().getDecorView());

Here you see that the status bar is green because the background of the mainActivity lays behind the transculent status bar. windowController.setAppearanceLightStatusBars(true); is commented for this version Working version

Here you see that the status bar is white, the text is white (because it is still the green background) but it got shifted down and replaced by some kind of default white status bar background. Look at where the battery icon is now. windowController.setAppearanceLightStatusBars(true); Is not commented and was executed somewhere along the road Broken version

UPDATE:

Following this I was able to get the desired result on my testing device running Android 10.

On the Emulator Running API 30 I now have one additional Problem:

WindowCompat.setDecorFitsSystemWindows(window, false);

lets us draw behind the status bar but also behind the navigation bar. Now the status bar works as intended but the navigation bar hides part of the UI. I need the navigation bar in its old state and not to be drawn under. Any ideas?

For completeness

compileSdkVersion 31
minSdkVersion 23
targetSdkVersion 31

Solution

  • Following this I was able to get the desired result on my testing device running Android 10.

    On the Emulator Running API 30 I now have one additional Problem:

    WindowCompat.setDecorFitsSystemWindows(window, false);
    

    lets us draw behind the status bar but also behind the navigation bar. Now the status bar works as intended but the navigation bar hides part of the UI. I need the navigation bar in its old state and not to be drawn under. Any ideas?

    This is right, because setDecorFitsSystemWindows() implements edge-to-edge to your app (i.e. the app will cover both the system status & navigation bars)

    But, As per documentation:

    You can address overlaps by reacting to insets, which specify which parts of the screen intersect with system UI such as the navigation bar or the status bar. Intersecting can mean simply being displayed above the content, but it can also inform your app about system gestures, too.

    So, we need to handle the System bars insets for API level 30+ to avoid your app overlapping with the navigation bar:

    This requires the top root ViewGroup of your activity layout, and accordingly you need to cast the LayoutParams appropriately.

    Here I am using FrameLayout.LayoutParams:

    /*
    *  Making the Navigation system bar not overlapping with the activity
    */
    if (Build.VERSION.SDK_INT >= 30) {
    
        // Root ViewGroup of my activity
        val root = findViewById<ConstraintLayout>(R.id.root)
    
        ViewCompat.setOnApplyWindowInsetsListener(root) { view, windowInsets ->
    
            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
    
            // Apply the insets as a margin to the view. Here the system is setting
            // only the bottom, left, and right dimensions, but apply whichever insets are
            // appropriate to your layout. You can also update the view padding
            // if that's more appropriate.
    
            view.layoutParams =  (view.layoutParams as FrameLayout.LayoutParams).apply {
                leftMargin = insets.left
                bottomMargin = insets.bottom
                rightMargin = insets.right
            }
    
            // Return CONSUMED if you don't want want the window insets to keep being
            // passed down to descendant views.
            WindowInsetsCompat.CONSUMED
        }
    
    }