Android Status bar icons color in fullscreen mode

I tried everything but I cannot able to change the color of the Android status bar icons color. I want the icons to be black with transparent background when I’m in fullscreen immersive mode, but they’re always white. I’m using an old StatusBarChanger.cs code to hide/show the status bar at runtime (in my case show only when I have devices with notch), but there isn’t any option to change the color. My app has white background, so I need the icons to be dark.
Any suggestions?

I post my solution, because I struggled many hours to find it…
I had to modify the AndroidManifest declaring a custom theme like this:

android:theme=" @ /UnityTransparentStatusBarTheme">

and then I had to create a values-v23.xml file inside Plugin->Android->res->values-v23 folder.

The xml is this:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="UnityTransparentStatusBarTheme" parent="UnityThemeSelector">
      <item name="android:windowLightStatusBar">true</item>
      <item name="android:colorPrimaryDark">#00BB86FC</item>
  </style>
</resources>

android:windowLightStatusBar (only available from sdk 23) make the status bar icons dark
android:colorPrimaryDark defines the color of the background, #00 is full transparency

what unity version did you use? I’m struggling to get it working since unity says that declaring resources in res/values is deprecated. Do you have any solution?

Weird because it is not solved. Now i am also looking for its solution

Any update about this? I have the same problem, i need dark icons on a white background… I manage to change the background to white using https://github.com/zeh/unity-tidbits/blob/master/application/ApplicationChrome.cs, but icons are still white.

i tried this snippet of code(Android code), that i found on https://copyprogramming.com/howto/system-ui-flag-light-status-bar-and-flag-translucent-status-is-deprecated#system_ui_flag_light_status_bar-and-flag_translucent_status-is-deprecated

Window window = getWindow();
View decorView = window.getDecorView();
WindowInsetsControllerCompat wic = new WindowInsetsControllerCompat(window, decorView);
wic.setAppearanceLightStatusBars(true); // true or false as desired.

using unity calls to android, adding this code in ApplicationChrome.cs and calling it in another script

private static void applyLightNavbarThemeAndroid()
{
          runOnAndroidUiThread(applyLightNavbarThemeAndroidInThread);
}

private static void applyLightNavbarThemeAndroidInThread() {
   using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) 
   {
       using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) 
       {
           using (var window = activity.Call<AndroidJavaObject>("getWindow")) 
           {
               using(var decorView = window.Call<AndroidJavaObject>("getDecorView")) 
               {
                   using(var wic = new AndroidJavaObject("WindowInsetControllerCompat", window, decorView)) {
                        wic.Call<AndroidJavaObject>("setAppearanceLightStatusBars", true);
                   }
               }
           }
       }
   }
}

but doesn’t work…

I’m using Unity 2021.3.18f1 and minimum android API level 30

Any update? Still not resolved.

So after a lot of research… I managed to change the status bar icons color from black to white and from white to black by modifying the ApplicationChrome script. Here is the full code:

#if UNITY_ANDROID && !UNITY_EDITOR
#define USE_ANDROID
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
/**
 * @author zeh fernando
 */
class ApplicationChrome
{
    /**
    * Manipulates the system application chrome to change the way the status bar and navigation bar work
    *
    * References:
    * . http://developer.android.com/reference/android/view/View.html#setSystemUiVisibility(int)
    * . http://forum.unity3d.com/threads/calling-setsystemuivisibility.139445/#post-952946
    * . http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_LAYOUT_IN_SCREEN
    **/
    // Enums
    public enum States
    {
        Unknown,
        Visible,
        VisibleOverContent,
        TranslucentOverContent,
        Hidden
    }
    // Constants
    private const uint DEFAULT_BACKGROUND_COLOR = 0xff000000;
#if USE_ANDROID
        // Original Android flags
        private const int VIEW_SYSTEM_UI_FLAG_VISIBLE = 0;                    // Added in API 14 (Android 4.0.x): Status bar visible (the default)
        private const int VIEW_SYSTEM_UI_FLAG_LOW_PROFILE = 1;                // Added in API 14 (Android 4.0.x): Low profile for games, book readers, and video players; the status bar and/or navigation icons are dimmed out (if visible)
        private const int VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;            // Added in API 14 (Android 4.0.x): Hides all navigation. Cleared when theres any user interaction.
        private const int VIEW_SYSTEM_UI_FLAG_FULLSCREEN = 4;                // Added in API 16 (Android 4.1.x): Hides status bar. Does nothing in Unity (already hidden if "status bar hidden" is checked)
        private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;            // Added in API 16 (Android 4.1.x): ?
        private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512;    // Added in API 16 (Android 4.1.x): like HIDE_NAVIGATION, but for layouts? it causes the layout to be drawn like that, even if the whole view isn't (to avoid artifacts in animation)
        private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024;        // Added in API 16 (Android 4.1.x): like FULLSCREEN, but for layouts? it causes the layout to be drawn like that, even if the whole view isn't (to avoid artifacts in animation)
        private const int VIEW_SYSTEM_UI_FLAG_IMMERSIVE = 2048;                // Added in API 19 (Android 4.4): like HIDE_NAVIGATION, but interactive (it's a modifier for HIDE_NAVIGATION, needs to be used with it)
        private const int VIEW_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;        // Added in API 19 (Android 4.4): tells that HIDE_NAVIGATION and FULSCREEN are interactive (also just a modifier)
        private static int WINDOW_FLAG_FULLSCREEN = 0x00000400;
        private static int WINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
        private static int WINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100;
        private static int WINDOW_FLAG_TRANSLUCENT_STATUS = 0x04000000;
        private static int WINDOW_FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
        private static int WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = -2147483648; // 0x80000000; // Added in API 21 (Android 5.0): tells the Window is responsible for drawing the background for the system bars. If set, the system bars are drawn with a transparent background and the corresponding areas in this window are filled with the colors specified in getStatusBarColor() and getNavigationBarColor()
        // Add the SYSTEM_UI_FLAG_LIGHT_STATUS_BAR flag
        private const int VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; 
        // Current values
        private static int systemUiVisibilityValue;
        private static int flagsValue;
#endif
    // Properties
    private static States _statusBarState;
    private static States _navigationBarState;
    private static uint _statusBarColor = DEFAULT_BACKGROUND_COLOR;
    private static uint _navigationBarColor = DEFAULT_BACKGROUND_COLOR;
    private static bool _isStatusBarTranslucent; // Just so we know whether its translucent when hidden or not
    private static bool _isNavigationBarTranslucent;
    private static bool _dimmed;
    private static bool _statusbar_light_mode;
    // ================================================================================================================
    // INTERNAL INTERFACE ---------------------------------------------------------------------------------------------
    static ApplicationChrome()
    {
        applyUIStates();
        applyUIColors();
    }
    private static void applyUIStates()
    {
#if USE_ANDROID
            applyUIStatesAndroid();
#endif
    }
    private static void applyUIColors()
    {
#if USE_ANDROID
            applyUIColorsAndroid();
#endif
    }
#if USE_ANDROID
        private static void applyUIStatesAndroid() {
            int newFlagsValue = 0;
            int newSystemUiVisibilityValue = 0;
            // Apply dim values
            if (_dimmed) newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LOW_PROFILE;
            // Apply color values
            if (_navigationBarColor != DEFAULT_BACKGROUND_COLOR || _statusBarColor != DEFAULT_BACKGROUND_COLOR) newFlagsValue |= WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
            // Apply status bar values
            switch (_statusBarState) {
                case States.Visible:
                    _isStatusBarTranslucent = false;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN;
                    break;
                case States.VisibleOverContent:
                    _isStatusBarTranslucent = false;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                    break;
                case States.TranslucentOverContent:
                    _isStatusBarTranslucent = true;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN | WINDOW_FLAG_TRANSLUCENT_STATUS;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                    break;
                case States.Hidden:
                    newFlagsValue |= WINDOW_FLAG_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
                    if (_isStatusBarTranslucent) newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_STATUS;
                    break;
            }
            if (_statusbar_light_mode)
                newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            // Applies navigation values
            switch (_navigationBarState) {
                case States.Visible:
                    _isNavigationBarTranslucent = false;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE;
                    break;
                case States.VisibleOverContent:
                    // TODO: Side effect: forces status bar over content if set to VISIBLE
                    _isNavigationBarTranslucent = false;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE | VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
                    break;
                case States.TranslucentOverContent:
                    // TODO: Side effect: forces status bar over content if set to VISIBLE
                    _isNavigationBarTranslucent = true;
                    newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_NAVIGATION;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE | VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
                    break;
                case States.Hidden:
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_FULLSCREEN | VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION | VIEW_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
                    if (_isNavigationBarTranslucent) newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_NAVIGATION;
                    break;
            }
            if (Screen.fullScreen) Screen.fullScreen = false;
            // Applies everything natively
            setFlags(newFlagsValue);
            setSystemUiVisibility(newSystemUiVisibilityValue);
        }
        private static void runOnAndroidUiThread(Action target) {
            using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
                using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
                    activity.Call("runOnUiThread", new AndroidJavaRunnable(target));
                }
            }
        }
        private static void setSystemUiVisibility(int value) {
            if (systemUiVisibilityValue != value) {
                systemUiVisibilityValue = value;
                runOnAndroidUiThread(setSystemUiVisibilityInThread);
            }
        }
        private static void setSystemUiVisibilityInThread() {
            using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
                using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
                    using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
                        using (var view = window.Call<AndroidJavaObject>("getDecorView")) {
                            // We also remove the existing listener. It seems Unity uses it internally
                            // to detect changes to the visibility flags, and re-apply its own changes.
                            // For example, if we hide the navigation bar, it shows up again 1 sec later.
                            //view.Call("setOnSystemUiVisibilityChangeListener", null);
                            int currentSystemUiVisibility = view.Call<int>("getSystemUiVisibility");
                            // Set the SYSTEM_UI_FLAG_LIGHT_STATUS_BAR flag
                            //if (statusbar_light_mode) {
                            //    view.Call("setSystemUiVisibility", currentSystemUiVisibility | VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 
                            //    Debug.Log("light mode");
                            //    }
                            //else
                                //view.Call("setSystemUiVisibility", systemUiVisibilityValue);
                                //| VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                            view.Call("setSystemUiVisibility", systemUiVisibilityValue);
                        }
                    }
                }
            }
        }
        private static void setFlags(int value) {
            if (flagsValue != value) {
                flagsValue = value;
                runOnAndroidUiThread(setFlagsInThread);
            }
        }
        private static void setFlagsInThread() {
            using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
                using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
                    using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
                        window.Call("setFlags", flagsValue, -1); // (int)0x7FFFFFFF
                    }
                }
            }
        }
        private static void applyUIColorsAndroid() {
            runOnAndroidUiThread(applyUIColorsAndroidInThread);
        }
        private static void applyUIColorsAndroidInThread() {
            using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
                using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
                    using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
                        //Debug.Log("Colors SET: " + _statusBarColor);
                        window.Call("setStatusBarColor", unchecked((int)_statusBarColor));
                        window.Call("setNavigationBarColor", unchecked((int)_navigationBarColor));
                    }
                }
            }
        }
#endif
    // ================================================================================================================
    // ACCESSOR INTERFACE ---------------------------------------------------------------------------------------------
    public static States navigationBarState
    {
        get { return _navigationBarState; }
        set
        {
            if (_navigationBarState != value)
            {
                _navigationBarState = value;
                applyUIStates();
            }
        }
    }
    public static States statusBarState
    {
        get { return _statusBarState; }
        set
        {
            if (_statusBarState != value)
            {
                _statusBarState = value;
                applyUIStates();
            }
        }
    }
    public static bool dimmed
    {
        get { return _dimmed; }
        set
        {
            if (_dimmed != value)
            {
                _dimmed = value;
                applyUIStates();
            }
        }
    }
    public static bool statusbar_light_mode
    {
        get { return _statusbar_light_mode; }
        set
        {
            if (_statusbar_light_mode != value)
            {
                _statusbar_light_mode = value;
                applyUIStates();
            }
        }
    }
    public static uint statusBarColor
    {
        get { return _statusBarColor; }
        set
        {
            if (_statusBarColor != value)
            {
                _statusBarColor = value;
                applyUIColors();
                applyUIStates();
            }
        }
    }
    public static uint navigationBarColor
    {
        get { return _navigationBarColor; }
        set
        {
            if (_navigationBarColor != value)
            {
                _navigationBarColor = value;
                applyUIColors();
                applyUIStates();
            }
        }
    }
}

In your Controller script just call to ApplicationChrome.statusbar_light_mode = true; to change the icons color to black, and put it to false to chage them back to white.