1- from android .runnable import Runnable
1+ from android import mActivity
2+ from android .runnable import run_on_ui_thread
23from jnius import autoclass , java_method , PythonJavaClass
34from typing import Literal
45
5- __all__ = ('update_system_ui' )
6+ __all__ = ("update_system_ui" )
7+
8+
9+ Color = autoclass ("android.graphics.Color" )
10+ Build_VERSION = autoclass ("android.os.Build$VERSION" )
11+ WindowInsetsType = autoclass ("android.view.WindowInsets$Type" )
12+ View = autoclass ("android.view.View" )
13+ window = mActivity .getWindow ()
14+ decor_view = window .getDecorView ()
15+ content_view = window .findViewById (autoclass ("android.R$id" ).content )
16+
17+
18+ def parse_color (value ):
19+ if isinstance (value , str ):
20+ return Color .parseColor (value )
21+ elif isinstance (value , (list , tuple )) and len (value ) == 4 :
22+ r , g , b , a = value
23+ return Color .argb (a , r , g , b )
24+ else :
25+ raise ValueError ("Color must be hex string or RGBA tuple" )
26+
27+
28+ # Oops!! android 15+ needs a listener
29+ if Build_VERSION .SDK_INT >= 35 :
30+
31+ class InsetsListener (PythonJavaClass ):
32+ __javainterfaces__ = [
33+ "android/view/View$OnApplyWindowInsetsListener"
34+ ]
35+ __javacontext__ = "app"
36+
37+ def __init__ (self , status_color , navigation_color , pad_status , pad_nav ):
38+ super ().__init__ ()
39+ self .status_color = status_color
40+ self .navigation_color = navigation_color
41+ self .pad_status = pad_status
42+ self .pad_nav = pad_nav
43+
44+ @java_method (
45+ "(Landroid/view/View;Landroid/view/WindowInsets;)Landroid/view/WindowInsets;"
46+ )
47+ def onApplyWindowInsets (self , view , insets ):
48+ try :
49+ status_insets = insets .getInsets (
50+ WindowInsetsType .statusBars ()
51+ )
52+ nav_insets = insets .getInsets (
53+ WindowInsetsType .navigationBars ()
54+ )
55+
56+ top_pad = status_insets .top if self .pad_status else 0
57+ bottom_pad = nav_insets .bottom if self .pad_nav else 0
58+
59+ content_view .setPadding (0 , top_pad , 0 , bottom_pad )
60+ content_view .setBackgroundColor (self .status_color )
661
62+ window .setNavigationBarColor (self .navigation_color )
63+ except Exception as e :
64+ print ("Insets error:" , e )
65+ import traceback
66+ traceback .print_exc ()
67+ return insets
768
69+
70+ @run_on_ui_thread
871def update_system_ui (
972 status_bar_color : list [float ] | str ,
1073 navigation_bar_color : list [float ] | str ,
@@ -20,132 +83,69 @@ def update_system_ui(
2083 IF `icon_style` IS `Dark` THE ICONS WILL BE DARK.
2184 IF `icon_style` IS `Light` THE ICONS WILL BE LIGHT.
2285
23- Original code at https://github.com/CarbonKivy/CarbonKivy/blob/39e360314a3885f3b462add4475e6c609b5bef53/carbonkivy/utils.py#L43 (subject to active changes ahead).
86+ Adapted from https://github.com/CarbonKivy/CarbonKivy/blob/39e360314a3885f3b462add4475e6c609b5bef53/carbonkivy/utils.py#L43
2487 """
2588
26- Color = autoclass ("android.graphics.Color" )
27- Build_VERSION = autoclass ("android.os.Build$VERSION" )
28- WindowInsetsType = autoclass ("android.view.WindowInsets$Type" )
29- PythonActivity = autoclass ("org.kivy.android.PythonActivity" )
30- View = autoclass ("android.view.View" )
31-
32- activity = PythonActivity .mActivity
33- window = activity .getWindow ()
34- decor_view = window .getDecorView ()
35- content_view = window .findViewById (autoclass ("android.R$id" ).content )
36-
3789 try :
3890 WindowCompat = autoclass ("androidx.core.view.WindowCompat" )
3991 inset_controller = WindowCompat .getInsetsController (window , decor_view )
4092 except Exception :
4193 inset_controller = None
4294
43- def parse_color (value ):
44- if isinstance (value , str ):
45- return Color .parseColor (value )
46- elif isinstance (value , (list , tuple )) and len (value ) == 4 :
47- r , g , b , a = value
48- return Color .argb (a , r , g , b )
49- else :
50- raise ValueError ("Color must be hex string or RGBA tuple" )
51-
52- def apply_system_bars ():
53- status_color_int = parse_color (status_bar_color )
54- navigation_color_int = parse_color (navigation_bar_color )
55-
56- # Beleive me, I once drew `dark icons over dark` and `light icons over light` but this won't happen ever again!
57- if (Build_VERSION .SDK_INT >= 30 ):
58- # API 30+ (Android 10+)
59- if inset_controller and "WindowInsetsControllerCompat" in str (type (inset_controller )):
60- # Compat wrapper (AndroidX)
61- # I suggest to include androidx in builds, it actually helps!
62- if icon_style == "Dark" :
63- inset_controller .setAppearanceLightStatusBars (False )
64- inset_controller .setAppearanceLightNavigationBars (False )
65- else :
66- inset_controller .setAppearanceLightStatusBars (True )
67- inset_controller .setAppearanceLightNavigationBars (True )
95+ status_color_int = parse_color (status_bar_color )
96+ navigation_color_int = parse_color (navigation_bar_color )
97+
98+ # Beleive me, I once drew `dark icons over dark` and `light icons over light` but this won't happen ever again!
99+ if (Build_VERSION .SDK_INT >= 30 ):
100+ # API 30+ (Android 10+)
101+ if inset_controller and "WindowInsetsControllerCompat" in str (type (inset_controller )):
102+ # Compat wrapper (AndroidX)
103+ # I suggest to include androidx in builds, it actually helps!
104+ if icon_style == "Light" :
105+ inset_controller .setAppearanceLightStatusBars (False )
106+ inset_controller .setAppearanceLightNavigationBars (False )
68107 else :
69- # Platform controller
70- controller = inset_controller or window .getInsetsController ()
71- WindowInsetsController = autoclass ("android.view.WindowInsetsController" )
72- if icon_style == "Dark" :
73- controller .setSystemBarsAppearance (
74- 0 ,
75- WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
76- | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
77- )
78- else :
79- controller .setSystemBarsAppearance (
80- WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
81- | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
82- WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
83- | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
84- )
85-
108+ inset_controller .setAppearanceLightStatusBars (True )
109+ inset_controller .setAppearanceLightNavigationBars (True )
86110 else :
87- # Legacy flags for API 23–29
88- # Yepp, python3.14 with ndk 28c doesn't support building for android <= 11 with 32 bit armeabi-v7a cpu so this may never be called but who knows??
89- visibility_flags = decor_view .getSystemUiVisibility ()
90-
91- if icon_style == "Dark" :
92- visibility_flags &= ~ View .SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
93- if Build_VERSION .SDK_INT >= 26 :
94- visibility_flags &= ~ View .SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
111+ # Platform controller
112+ controller = window .getInsetsController ()
113+ WindowInsetsController = autoclass ("android.view.WindowInsetsController" )
114+ if icon_style == "Light" :
115+ controller .setSystemBarsAppearance (
116+ 0 ,
117+ WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
118+ | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
119+ )
95120 else :
96- visibility_flags |= View .SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
97- if Build_VERSION .SDK_INT >= 26 :
98- visibility_flags |= View .SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
99-
100- decor_view .setSystemUiVisibility (visibility_flags )
101-
102- # Oops!! android 15+ needs a listener
103- if Build_VERSION .SDK_INT >= 35 :
104-
105- class InsetsListener (PythonJavaClass ):
106- __javainterfaces__ = [
107- "android/view/View$OnApplyWindowInsetsListener"
108- ]
109- __javacontext__ = "app"
110-
111- def __init__ (self , status_color , navigation_color ):
112- super ().__init__ ()
113- self .status_color = status_color
114- self .navigation_color = navigation_color
115-
116- @java_method (
117- "(Landroid/view/View;Landroid/view/WindowInsets;)Landroid/view/WindowInsets;"
121+ controller .setSystemBarsAppearance (
122+ WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
123+ | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
124+ WindowInsetsController .APPEARANCE_LIGHT_STATUS_BARS
125+ | WindowInsetsController .APPEARANCE_LIGHT_NAVIGATION_BARS ,
118126 )
119- def onApplyWindowInsets (self , view , insets ):
120- try :
121- status_insets = insets .getInsets (
122- WindowInsetsType .statusBars ()
123- )
124- nav_insets = insets .getInsets (
125- WindowInsetsType .navigationBars ()
126- )
127-
128- top_pad = status_insets .top if pad_status else 0
129- bottom_pad = nav_insets .bottom if pad_nav else 0
130-
131- content_view .setPadding (0 , top_pad , 0 , bottom_pad )
132- content_view .setBackgroundColor (self .status_color )
133-
134- window .setNavigationBarColor (self .navigation_color )
135- except Exception as e :
136- print ("Insets error:" , e )
137- import traceback
138- traceback .print_exc ()
139- return insets
140-
141- listener = InsetsListener (status_color_int , navigation_color_int )
142- # I don't know why but sometimes pyjnius failed to find invoke, maybe due to garbage collection and so I made a reference
143- activity ._system_ui_listener = listener
144- decor_view .setOnApplyWindowInsetsListener (listener )
145- decor_view .requestApplyInsets ()
127+ else :
128+ # Legacy flags for API 23–29
129+ # Yepp, python3.14 with ndk 28c doesn't support building for android <= 11 with 32 bit armeabi-v7a cpu so this may never be called but who knows??
130+ visibility_flags = decor_view .getSystemUiVisibility ()
131+
132+ if icon_style == "Light" :
133+ visibility_flags &= ~ View .SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
134+ if Build_VERSION .SDK_INT >= 26 :
135+ visibility_flags &= ~ View .SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
146136 else :
147- window .setStatusBarColor (status_color_int )
148- window .setNavigationBarColor (navigation_color_int )
149-
150- # even if it fails it fails in a separate thread
151- Runnable (apply_system_bars )()
137+ visibility_flags |= View .SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
138+ if Build_VERSION .SDK_INT >= 26 :
139+ visibility_flags |= View .SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
140+
141+ decor_view .setSystemUiVisibility (visibility_flags )
142+
143+ if Build_VERSION .SDK_INT >= 35 :
144+ listener = InsetsListener (status_color_int , navigation_color_int , pad_status , pad_nav )
145+ # I don't know why but sometimes pyjnius failed to find invoke, maybe due to garbage collection and so I made a reference
146+ mActivity ._system_ui_listener = listener
147+ decor_view .setOnApplyWindowInsetsListener (listener )
148+ decor_view .requestApplyInsets ()
149+ else :
150+ window .setStatusBarColor (status_color_int )
151+ window .setNavigationBarColor (navigation_color_int )
0 commit comments