Android M Light and Dark status bar programmatically - how to make it dark again?

I base on @Aracem and @Carlos Hernández Gil but I think it will easy to understand if we use bitwise XOR (^ operator in Java)

private void setLightStatusBar(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
        flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;   // add LIGHT_STATUS_BAR to flag
        activity.getWindow().getDecorView().setSystemUiVisibility(flags); 
        activity.getWindow().setStatusBarColor(Color.GRAY); // optional
    }
}

private void clearLightStatusBar(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
        flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // use XOR here for remove LIGHT_STATUS_BAR from flags
        activity.getWindow().getDecorView().setSystemUiVisibility(flags);
        activity.getWindow().setStatusBarColor(Color.GREEN); // optional
    }
}

Explain

First, look at SYSTEM_UI_FLAG_LIGHT_STATUS_BAR and setSystemUiVisibility

/**
 * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
 * is compatible with light status bar backgrounds.
 */
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;

public void setSystemUiVisibility(int visibility) {
    if (visibility != mSystemUiVisibility) {
        mSystemUiVisibility = visibility;
        ...
    }
}

I think 2 lines code below is quite hard to understand

flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for clear light status bar

At first look, I just think we can use simple like

flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
flags = 0; // for clear light status bar (0 <=> LIGHT_STATUS_BAR <=> default systemUiVisibility)

But we should use | and ^ because
Example, we want to set both status bar and navigationbar to light, then we will use

flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

When we don't want status bar is light anymore, we can use

flags = View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

OR

flags = activity.getWindow().getDecorView().getSystemUiVisibility();
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
activity.getWindow().getDecorView().setSystemUiVisibility(flags);

To know more why we use | and ^, I think the tutorial below may help https://medium.com/@JakobUlbrich/flag-attributes-in-android-how-to-use-them-ac4ec8aee7d1 Here is my understand. Hope this help


According to Nick Butcher's project "Plaid"

public static void clearLightStatusBar(@NonNull View view) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        int flags = view.getSystemUiVisibility();
        flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
        view.setSystemUiVisibility(flags);
    }
}

You can find this file here.


The way I switched light and dark for APIs 23-30 was a little different than these. This is a kotlin version

Since I was using Compose with the Crossfade animation to change themes, in some cases would call this function twice, hence making xor undo itself. An alternative is an inverse or operation. My light theme switcher-thing ended up looking like this

@Suppress("DEPRECATION")
fun invertInsets(darkTheme: Boolean, window: Window) {
    if (Build.VERSION.SDK_INT >= 30) {
        //Correct way of doing things
        val statusBar = APPEARANCE_LIGHT_STATUS_BARS
        val navBar = APPEARANCE_LIGHT_NAVIGATION_BARS
        if (!darkTheme) {
            window.insetsController?.setSystemBarsAppearance(statusBar, statusBar)
            window.insetsController?.setSystemBarsAppearance(navBar, navBar)
        } else {
            window.insetsController?.setSystemBarsAppearance(0, statusBar)
            window.insetsController?.setSystemBarsAppearance(0, navBar)
        }
    } else {
        // Does bitwise operations (or to add, inverse or to remove)
        // This is depreciated but the new version is API 30+ so I should have this here
        val flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
            if (Build.VERSION.SDK_INT >= 26) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0

        if (!darkTheme) {
            window.decorView.systemUiVisibility = 
                window.decorView.systemUiVisibility or flags
        } else {
            window.decorView.systemUiVisibility = 
                (window.decorView.systemUiVisibility.inv() or flags).inv()
        }
    }
}

The bit for API 30+ is what's not depreciated, but realistically not many phones are at API 30 so there's also the bit for lower APIs

It just calculates flags (since setting LIGHT_NAVIGATION_BARS is API 26+) beforehand for conciseness and then either definitively sets or resets those exact flags. No and or xor funny buisiness. or will always set the flags to 1, and the inverse or thing will always set the flags to 0. This is only possible because both SYSTEM_UI_FLAG_LIGHT_STATUS_BAR and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR are one bit, however. Otherwise it would probably need to use xor.


The solution posted by @Aracem is valid but, doesn't work if you try change also the background color of the status bar. In my case I do it in the following way.

To enable windowLightStatusBar(programatically,inside a Utils class for example):

 public static void setLightStatusBar(View view,Activity activity){


            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                int flags = view.getSystemUiVisibility();
                flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                view.setSystemUiVisibility(flags);
                activity.getWindow().setStatusBarColor(Color.WHITE); 
            }
}

To restore to StatusBar to the previous state:

  public static void clearLightStatusBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = activity.getWindow();
            window.setStatusBarColor(ContextCompat
                 .getColor(activity,R.color.colorPrimaryDark)); 
        }
    }

Restoring the color of the status bar is enough, it restores also the icons colors. VERY IMPORTANT: The restore operation will not occur until the view used in setLightStatusBar(View view..) dissapears(that is, view.getVisibility()==GONE|INVISIBLE) from the screen.