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

16

66

In the Android M we have ability to make status bar icons dark. To do that we can specify attribute in the theme's xml:

<item name="android:windowLightStatusBar">true</item>

OR we cat set it at runtime with this code:

View someView = findViewById(R.id.some_view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    someView.setSystemUiVisibility(someView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

And it actually works fine. But question is how to properly set a status bar mode to dark at runtime?

I already tried these variants:

// Makes status bar mode dark, but also hides it along with all navigation views. 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() | ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Also does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

So how it can be done the right way?

Voter answered 7/6, 2016 at 7:16 Comment(0)
I
65

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.

Ian answered 20/9, 2016 at 14:26 Comment(5)
Is it possible to restore the light status bar while the view is still on screen. For example, when changing the theme from light to dark mode?Schaaf
P.S., Why doesn't the restore operation occur immediately? Why is it tied to the view?Schaaf
how to manage for devices version below Build.VERSION_CODES.MCourtier
setSystemUiVisibility is deprecated in Api 30+Mitchel
how to replace setSystemUiVisibility ?Sorosis
C
52

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.

Casteel answered 11/7, 2016 at 16:57 Comment(4)
Why is setSystemUiVisibility() a method of View class?Mcadoo
@Mcadoo I think it is View view = window.getDecorView();.Kalina
@Kalina It really should be window.getDecorView(). If you try to set this on a random View in your fragment or Activity, it will not work.Padget
setSystemUiVisibility is deprecated in Api 30+Mitchel
Z
36

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

Zoraidazorana answered 26/7, 2018 at 6:41 Comment(6)
An XOR will also enable that flag if it isn't currently set, though. If you want to make sure that it is unset, you should use & ~.Fromma
@IanMacDonald why XOR can enable the flag we isn't current set? Can you give an example?Zoraidazorana
What if i want do it for below M versionsCourtier
@Courtier No can do ! Use Activity#getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) to add, from 4.4 to 5.1, a native black gradient to improve status bar icons visibility. And to unset, use clearFlags with same parameters. Prior to 4.4, status bar is usually always black.Thermoluminescent
With xor, the status bar toggle the theme. Using the & ~ worked perfectly.Mintz
why this does not work for me? I use the clear method but still the light bar remained.Complacent
T
11

systemUiVisibility - is deprecated now. You can use WindowInsetsControllerCompat instead.

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.let { window -> WindowInsetsControllerCompat(window, window.decorView) }
}

private fun setLightStatusBar(light: Boolean) {
    insetsController?.isAppearanceLightStatusBars = light
}

UPD: Above constructor for WindowInsetsControllerCompat is deprecated, so use the following instantiation instead:

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.decorView?.let(ViewCompat::getWindowInsetsController)
}
Transitive answered 8/2, 2022 at 15:44 Comment(0)
G
8

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.

Gaudet answered 20/7, 2021 at 22:58 Comment(0)
R
7

There is a slight change in API 30 of the SDK and now the light status bar appearance is controlled by WindowInsetsController, which can be obtained from a Window. Below is a sample method (within an Activity) in Kotlin, combining the new API with the previously used View.setSystemUiVisibility for older Android SDK versions. Bear in mind that this only changes the system icons appearance of the status bar and the actual color of the status bar can still be set by Window.setStatusBarColor.

@Suppress("DEPRECATION")
private fun setSystemUiLightStatusBar(isLightStatusBar: Boolean) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val systemUiAppearance = if (isLightStatusBar) {
                WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
            } else {
                0
            }
            window.insetsController?.setSystemBarsAppearance(systemUiAppearance,
                                                             WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
        } else {
            val systemUiVisibilityFlags = if (isLightStatusBar) {
                window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
            } else {
                window.decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
            }
            window.decorView.systemUiVisibility = systemUiVisibilityFlags
        }
    }
}
Rapeseed answered 11/11, 2020 at 13:4 Comment(2)
You could write else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { instead of two nested if.Hypothermal
This should be accepted answer and should have more up votes because it uses new and old API and code is cleaner than others. Thumbs up.Teakwood
K
5

I put together this simple utility object that allows you to change status bar color and light status bar on/off for within any fragment. However, this relies on using the Android Jetpack Navigation component for navigation (Kotlin):

object StatusBarUtil {
    fun changeStatusBarColor(activity: Activity, @ColorInt color: Int, lightStatusBar: Boolean) {
        activity.window?.let { win ->
            val nav = Navigation.findNavController(activity, R.id.your_nav_host_fragmen /* TODO: Use the ID of your nav host fragment */)
            val currentDest = nav.currentDestination?.id
            val oldColor = win.statusBarColor
            val oldFlags = win.decorView.systemUiVisibility
            win.statusBarColor = color

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                var flags = oldFlags
                flags = if (lightStatusBar) {
                    flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                } else {
                    flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
                }
                win.decorView.systemUiVisibility = flags
            }

            nav.addOnNavigatedListener { _, dest ->
                if (dest.id != currentDest) {
                    win.statusBarColor = oldColor
                    win.decorView.systemUiVisibility = oldFlags
                }
            }
        }
    }
}

To use this, call the following from within any fragment's onViewCreated:

StatusBarUtil.changeStatusBarColor(requireActivity(), someDarkColor, false)
Knife answered 1/10, 2018 at 13:20 Comment(4)
wow just what I was looking for. Android really needs to add a whole new API for status bar control if they are going to push single activity apps :)Heyde
There is a problem with this code. If I pop back to this fragment, then the colors aren't set since currentDest is still the previous screen in onViewCreated. I've improved it here: gist.github.com/Chozzle/adf31f3bd709caec99c96cd996cd67ceHeyde
addOnNavigatedListener not exist!Bewray
Navigation.findNavController(activity, R.id.your_nav_host_fragment) - where to find this?Hightail
R
3

Based on @phan-van-linh answer, I wrote this class for Xamarin Android

public static class ActivityExtensions
{
    public static void SetLightStatusBar(this Activity activity)
    {
        int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
        flags |= (int)SystemUiFlags.LightStatusBar;   // add LIGHT_STATUS_BAR to flag
        activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
        //activity.Window.SetStatusBarColor(Color.GRAY); // optional
    }

    public static void ClearLightStatusBar(this Activity activity)
    {
        int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
        flags = flags ^ (int)SystemUiFlags.LightStatusBar; // use XOR here for remove LIGHT_STATUS_BAR from flags
        activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
        //activity.Window.setStatusBarColor(Color.GREEN); // optional
    }
}
Reputed answered 19/11, 2018 at 14:30 Comment(0)
R
3

To change to light status bar use:-

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
     activity?.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

To change back to dark status bar :-

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
     activity?.window?.decorView?.systemUiVisibility = 0
Reconciliatory answered 1/4, 2020 at 8:46 Comment(0)
A
2

i will make some changes in above answers.

make a class

 public class DarkStatusBar {
    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);
        }
    }
}

and Call it wherever you want like this

        Window window = getWindow();
        View view = window.getDecorView();
        DarkStatusBar.setLightStatusBar(view,this);
Armillda answered 23/5, 2018 at 4:13 Comment(1)
Thanks for sharing. No options above were working. Only yours.Decompose
S
1

It works for me

fun Activity.clearLightStatusBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val window = window
        window.statusBarColor = ContextCompat
            .getColor(this, R.color.ultramarine_blue)
    }
}
Superannuation answered 17/1, 2022 at 11:30 Comment(0)
S
0

Set blue background status bar with light text color kotlin version

fun setBlueStatusBarColor(window: Window, context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            window.statusBarColor = context.getColor(R.color.colorBlue)
        }else {
            window.statusBarColor = context.resources.getColor(R.color.colorBlue)
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            var flags: Int = window.decorView.systemUiVisibility
            flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
            window.decorView.systemUiVisibility = flags
        }
    }
}
Sideswipe answered 27/6, 2020 at 5:12 Comment(0)
B
0
/**
 * Changes color of the status bar icons
 * @param isLight if true - shows dark icons, light else
 */
fun setStatusBarUiTheme(activity: Activity?, isLight: Boolean) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        activity?.window?.decorView?.let {
            it.systemUiVisibility = if (isLight)
                it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR // dark icons
            else
                it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() // light icons
        }
    }
}
Bagnio answered 5/12, 2020 at 23:48 Comment(0)
H
0

In res/styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowLightStatusBar">true</item>
    .......
</style>

<style name="AppTheme.DarkStatus" parent="AppTheme" tools:targetApi="23" >
    <item name="android:windowLightStatusBar">false</item>
    <item name="android:statusBarColor" >@color/status_bar_color</item>
</style>

In code

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.AppTheme_DarkStatus);  //To set DarkStatusBar theme
    setContentView(R.layout.activity_drawer);
    ....
}
Havelock answered 28/1, 2021 at 14:41 Comment(0)
S
0

For people who doesn't have a Window instance. It's also possible to do with a View instance (For API 30) :

fun setLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)

fun clearLightStatusBar(view: View) = view.windowInsetsController?.setSystemBarsAppearance(
    0,
    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
Streamer answered 5/10, 2022 at 15:50 Comment(0)
C
0

To keep working older versions you would require to do something like this:

fun setLigthColors() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        activity?.window?.let {
            WindowCompat.setDecorFitsSystemWindows(it, true)
            WindowInsetsControllerCompat(it, view).isAppearanceLightStatusBars = true
        }
    } else { // deprecated method for older versions
        var flags: Int = view.systemUiVisibility
        flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        view.systemUiVisibility = flags
    }
}

fun setDarkColors() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        activity?.window?.let {
            WindowCompat.setDecorFitsSystemWindows(it, true)
            WindowInsetsControllerCompat(it, view).isAppearanceLightStatusBars = false
        }
    } else { // deprecated method for older versions
        var flags: Int = view.systemUiVisibility
        flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        view.systemUiVisibility = flags
    }
}
Corri answered 24/11, 2023 at 5:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.