Skip to content

Add light system bars #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 104 additions & 54 deletions application/ApplicationChrome.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,55 @@ public enum States {
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)

// Added in API 14 (Android 4.0.x): Status bar visible (the default)
private const int VIEW_SYSTEM_UI_FLAG_VISIBLE = 0;
// 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_LOW_PROFILE = 1;
// Added in API 14 (Android 4.0.x): Hides all navigation. Cleared when theres any user interaction.
private const int VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;
// 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_FULLSCREEN = 4;
// Added in API 16 (Android 4.1.x): ?
private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;
// 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_HIDE_NAVIGATION = 512;
// 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_LAYOUT_FULLSCREEN = 1024;
// 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 = 2048;
// Added in API 19 (Android 4.4): tells that HIDE_NAVIGATION and
// FULLSCREEN are interactive (also just a modifier)
private const int VIEW_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;
// Added in API 26 (Android 8.0). Deprecated in API 30 (Android 11)
// Makes navigation bar icons dark.
// Does not affect the "pill" when gesture navigation is enabled on Android 11+
private const int VIEW_SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16;
// Added in API 23 (Android 6.0). Deprecated in API 30 (Android 11)
// Makes status bar icons dark
private const int VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192;

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()
// 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()
private static int WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = -2147483648; // 0x80000000;

// Current values
private static int systemUiVisibilityValue;
private static int flagsValue;

#endif

// Properties
Expand All @@ -68,6 +96,9 @@ public enum States {

private static bool _dimmed;

private static bool _lightNavigationBar;
private static bool _lightStatusBar;


// ================================================================================================================
// INTERNAL INTERFACE ---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -99,7 +130,8 @@ private static void applyUIStatesAndroid() {
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;
if (_navigationBarColor != DEFAULT_BACKGROUND_COLOR || _statusBarColor != DEFAULT_BACKGROUND_COLOR)
newFlagsValue |= WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;

// Apply status bar values
switch (_statusBarState) {
Expand All @@ -114,7 +146,9 @@ private static void applyUIStatesAndroid() {
break;
case States.TranslucentOverContent:
_isStatusBarTranslucent = true;
newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN | WINDOW_FLAG_TRANSLUCENT_STATUS;
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:
Expand All @@ -132,20 +166,27 @@ private static void applyUIStatesAndroid() {
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;
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;
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;
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 (_lightNavigationBar) newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
if (_lightStatusBar) newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;

if (Screen.fullScreen) Screen.fullScreen = false;

// Applies everything natively
Expand All @@ -154,11 +195,9 @@ private static void applyUIStatesAndroid() {
}

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));
}
}
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) {
Expand All @@ -169,19 +208,15 @@ private static void setSystemUiVisibility(int value) {
}

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);
view.Call("setSystemUiVisibility", systemUiVisibilityValue);
}
}
}
}
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);
view.Call("setSystemUiVisibility", systemUiVisibilityValue);
}

private static void setFlags(int value) {
Expand All @@ -192,29 +227,23 @@ private static void setFlags(int value) {
}

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
}
}
}
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));
}
}
}
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
Expand All @@ -223,7 +252,7 @@ private static void applyUIColorsAndroidInThread() {
// ACCESSOR INTERFACE ---------------------------------------------------------------------------------------------

public static States navigationBarState {
get { return _navigationBarState; }
get => _navigationBarState;
set {
if (_navigationBarState != value) {
_navigationBarState = value;
Expand All @@ -233,7 +262,7 @@ public static States navigationBarState {
}

public static States statusBarState {
get { return _statusBarState; }
get => _statusBarState;
set {
if (_statusBarState != value) {
_statusBarState = value;
Expand All @@ -243,7 +272,7 @@ public static States statusBarState {
}

public static bool dimmed {
get { return _dimmed; }
get => _dimmed;
set {
if (_dimmed != value) {
_dimmed = value;
Expand All @@ -253,7 +282,7 @@ public static bool dimmed {
}

public static uint statusBarColor {
get { return _statusBarColor; }
get => _statusBarColor;
set {
if (_statusBarColor != value) {
_statusBarColor = value;
Expand All @@ -264,7 +293,7 @@ public static uint statusBarColor {
}

public static uint navigationBarColor {
get { return _navigationBarColor; }
get => _navigationBarColor;
set {
if (_navigationBarColor != value) {
_navigationBarColor = value;
Expand All @@ -273,4 +302,25 @@ public static uint navigationBarColor {
}
}
}

public static bool LightNavigationBar {
get => _lightNavigationBar;
set {
if (_lightNavigationBar != value) {
_lightNavigationBar = value;
applyUIStates();
}
}
}

public static bool LightStatusBar {
get => _lightStatusBar;
set {
if (_lightStatusBar != value) {
_lightStatusBar = value;
applyUIStates();
}
}
}

}