ApplicationChrome.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #if UNITY_ANDROID && !UNITY_EDITOR
  2. #define USE_ANDROID
  3. #endif
  4. using System;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. /**
  8. * @author zeh fernando
  9. */
  10. class ApplicationChrome {
  11. /**
  12. * Manipulates the system application chrome to change the way the status bar and navigation bar work
  13. *
  14. * References:
  15. * . http://developer.android.com/reference/android/view/View.html#setSystemUiVisibility(int)
  16. * . http://forum.unity3d.com/threads/calling-setsystemuivisibility.139445/#post-952946
  17. * . http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_LAYOUT_IN_SCREEN
  18. **/
  19. // Enums
  20. public enum States {
  21. Unknown,
  22. Visible,
  23. VisibleOverContent,
  24. TranslucentOverContent,
  25. Hidden
  26. }
  27. // Constants
  28. private const uint DEFAULT_BACKGROUND_COLOR = 0xff000000;
  29. #if USE_ANDROID
  30. // Original Android flags
  31. private const int VIEW_SYSTEM_UI_FLAG_VISIBLE = 0; // Added in API 14 (Android 4.0.x): Status bar visible (the default)
  32. 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)
  33. 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.
  34. 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)
  35. private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // Added in API 16 (Android 4.1.x): ?
  36. 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)
  37. 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)
  38. 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)
  39. 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)
  40. private static int WINDOW_FLAG_FULLSCREEN = 0x00000400;
  41. private static int WINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
  42. private static int WINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100;
  43. private static int WINDOW_FLAG_TRANSLUCENT_STATUS = 0x04000000;
  44. private static int WINDOW_FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
  45. 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()
  46. // Current values
  47. private static int systemUiVisibilityValue;
  48. private static int flagsValue;
  49. #endif
  50. // Properties
  51. private static States _statusBarState;
  52. private static States _navigationBarState;
  53. private static uint _statusBarColor = DEFAULT_BACKGROUND_COLOR;
  54. private static uint _navigationBarColor = DEFAULT_BACKGROUND_COLOR;
  55. private static bool _isStatusBarTranslucent; // Just so we know whether its translucent when hidden or not
  56. private static bool _isNavigationBarTranslucent;
  57. private static bool _dimmed;
  58. // ================================================================================================================
  59. // INTERNAL INTERFACE ---------------------------------------------------------------------------------------------
  60. static ApplicationChrome() {
  61. applyUIStates();
  62. applyUIColors();
  63. }
  64. private static void applyUIStates() {
  65. #if USE_ANDROID
  66. applyUIStatesAndroid();
  67. #endif
  68. }
  69. private static void applyUIColors() {
  70. #if USE_ANDROID
  71. applyUIColorsAndroid();
  72. #endif
  73. }
  74. #if USE_ANDROID
  75. private static void applyUIStatesAndroid() {
  76. int newFlagsValue = 0;
  77. int newSystemUiVisibilityValue = 0;
  78. // Apply dim values
  79. if (_dimmed) newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LOW_PROFILE;
  80. // Apply color values
  81. if (_navigationBarColor != DEFAULT_BACKGROUND_COLOR || _statusBarColor != DEFAULT_BACKGROUND_COLOR) newFlagsValue |= WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
  82. // Apply status bar values
  83. switch (_statusBarState) {
  84. case States.Visible:
  85. _isStatusBarTranslucent = false;
  86. newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN;
  87. break;
  88. case States.VisibleOverContent:
  89. _isStatusBarTranslucent = false;
  90. newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
  91. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
  92. break;
  93. case States.TranslucentOverContent:
  94. _isStatusBarTranslucent = true;
  95. newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN | WINDOW_FLAG_TRANSLUCENT_STATUS;
  96. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
  97. break;
  98. case States.Hidden:
  99. newFlagsValue |= WINDOW_FLAG_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
  100. if (_isStatusBarTranslucent) newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_STATUS;
  101. break;
  102. }
  103. // Applies navigation values
  104. switch (_navigationBarState) {
  105. case States.Visible:
  106. _isNavigationBarTranslucent = false;
  107. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE;
  108. break;
  109. case States.VisibleOverContent:
  110. // TODO: Side effect: forces status bar over content if set to VISIBLE
  111. _isNavigationBarTranslucent = false;
  112. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE | VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
  113. break;
  114. case States.TranslucentOverContent:
  115. // TODO: Side effect: forces status bar over content if set to VISIBLE
  116. _isNavigationBarTranslucent = true;
  117. newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_NAVIGATION;
  118. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE | VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
  119. break;
  120. case States.Hidden:
  121. newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_FULLSCREEN | VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION | VIEW_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
  122. if (_isNavigationBarTranslucent) newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_NAVIGATION;
  123. break;
  124. }
  125. if (Screen.fullScreen) Screen.fullScreen = false;
  126. // Applies everything natively
  127. setFlags(newFlagsValue);
  128. setSystemUiVisibility(newSystemUiVisibilityValue);
  129. }
  130. private static void runOnAndroidUiThread(Action target) {
  131. using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
  132. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
  133. activity.Call("runOnUiThread", new AndroidJavaRunnable(target));
  134. }
  135. }
  136. }
  137. private static void setSystemUiVisibility(int value) {
  138. if (systemUiVisibilityValue != value) {
  139. systemUiVisibilityValue = value;
  140. runOnAndroidUiThread(setSystemUiVisibilityInThread);
  141. }
  142. }
  143. private static void setSystemUiVisibilityInThread() {
  144. using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
  145. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
  146. using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
  147. using (var view = window.Call<AndroidJavaObject>("getDecorView")) {
  148. // We also remove the existing listener. It seems Unity uses it internally
  149. // to detect changes to the visibility flags, and re-apply its own changes.
  150. // For example, if we hide the navigation bar, it shows up again 1 sec later.
  151. view.Call("setOnSystemUiVisibilityChangeListener", null);
  152. view.Call("setSystemUiVisibility", systemUiVisibilityValue);
  153. }
  154. }
  155. }
  156. }
  157. }
  158. private static void setFlags(int value) {
  159. if (flagsValue != value) {
  160. flagsValue = value;
  161. runOnAndroidUiThread(setFlagsInThread);
  162. }
  163. }
  164. private static void setFlagsInThread() {
  165. using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
  166. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
  167. using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
  168. window.Call("setFlags", flagsValue, -1); // (int)0x7FFFFFFF
  169. }
  170. }
  171. }
  172. }
  173. private static void applyUIColorsAndroid() {
  174. runOnAndroidUiThread(applyUIColorsAndroidInThread);
  175. }
  176. private static void applyUIColorsAndroidInThread() {
  177. using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
  178. using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
  179. using (var window = activity.Call<AndroidJavaObject>("getWindow")) {
  180. //Debug.Log("Colors SET: " + _statusBarColor);
  181. window.Call("setStatusBarColor", unchecked((int)_statusBarColor));
  182. window.Call("setNavigationBarColor", unchecked((int)_navigationBarColor));
  183. }
  184. }
  185. }
  186. }
  187. #endif
  188. // ================================================================================================================
  189. // ACCESSOR INTERFACE ---------------------------------------------------------------------------------------------
  190. public static States navigationBarState {
  191. get { return _navigationBarState; }
  192. set {
  193. if (_navigationBarState != value) {
  194. _navigationBarState = value;
  195. applyUIStates();
  196. }
  197. }
  198. }
  199. public static States statusBarState {
  200. get { return _statusBarState; }
  201. set {
  202. if (_statusBarState != value) {
  203. _statusBarState = value;
  204. applyUIStates();
  205. }
  206. }
  207. }
  208. public static bool dimmed {
  209. get { return _dimmed; }
  210. set {
  211. if (_dimmed != value) {
  212. _dimmed = value;
  213. applyUIStates();
  214. }
  215. }
  216. }
  217. public static uint statusBarColor {
  218. get { return _statusBarColor; }
  219. set {
  220. if (_statusBarColor != value) {
  221. _statusBarColor = value;
  222. applyUIColors();
  223. applyUIStates();
  224. }
  225. }
  226. }
  227. public static uint navigationBarColor {
  228. get { return _navigationBarColor; }
  229. set {
  230. if (_navigationBarColor != value) {
  231. _navigationBarColor = value;
  232. applyUIColors();
  233. applyUIStates();
  234. }
  235. }
  236. }
  237. }