88# All icons took: 1457ms
99# All icons took: 1250ms
1010# Most of this time is actually spent reading and parsing manifests.
11-
12- import uos
1311import lvgl as lv
14-
1512import mpos .apps
1613import mpos .ui
1714from mpos .content .pm import PackageManager
1815from mpos import Activity
16+ import time
17+ import uhashlib
18+ import ubinascii
19+
1920
2021class Launcher (Activity ):
22+ def __init__ (self ):
23+ super ().__init__ ()
24+ # Cache of the last app list + a quick hash of the icons
25+ self ._last_app_list = None # list of tuples (name, path, icon_hash)
26+ self ._last_ui_built = False # was UI built at least once?
2127
2228 def onCreate (self ):
2329 print ("launcher.py onCreate()" )
2430 main_screen = lv .obj ()
2531 main_screen .set_style_border_width (0 , lv .PART .MAIN )
2632 main_screen .set_style_radius (0 , 0 )
27- main_screen .set_pos (0 , mpos .ui .topmenu .NOTIFICATION_BAR_HEIGHT ) # leave some margin for the notification bar
28- #main_screen.set_size(lv.pct(100), lv.pct(100))
33+ main_screen .set_pos (0 , mpos .ui .topmenu .NOTIFICATION_BAR_HEIGHT )
2934 main_screen .set_style_pad_hor (mpos .ui .pct_of_display_width (2 ), 0 )
3035 main_screen .set_style_pad_ver (mpos .ui .topmenu .NOTIFICATION_BAR_HEIGHT , 0 )
3136 main_screen .set_flex_flow (lv .FLEX_FLOW .ROW_WRAP )
3237 self .setContentView (main_screen )
3338
39+ # ------------------------------------------------------------------
40+ # Helper: compute a cheap hash of a file (or return None if missing)
41+ @staticmethod
42+ def _hash_file (path ):
43+ try :
44+ with open (path , "rb" ) as f :
45+ h = uhashlib .sha1 ()
46+ while True :
47+ data = f .read (1024 )
48+ if not data :
49+ break
50+ h .update (data )
51+ return ubinascii .hexlify (h .digest ()).decode ()
52+ except Exception :
53+ return None
54+
55+ # ------------------------------------------------------------------
3456 def onResume (self , screen ):
35- # Grid parameters
36- icon_size = 64 # Adjust based on your display
37- label_height = 24
38- iconcont_width = icon_size + label_height
39- iconcont_height = icon_size + label_height
57+ # ------------------------------------------------------------------
58+ # 1. Build a *compact* representation of the current app list
59+ current_apps = []
60+ for app in PackageManager .get_app_list ():
61+ if app .category == "launcher" :
62+ continue
63+ icon_path = f"{ app .installed_path } /res/mipmap-mdpi/icon_64x64.png"
64+ icon_hash = Launcher ._hash_file (icon_path ) # cheap SHA-1 of the icon file
65+ current_apps .append ((app .name , app .installed_path , icon_hash ))
4066
41- import time
67+ # ------------------------------------------------------------------
68+ # 2. Compare with the cached list – if identical we skip UI rebuild
4269 start = time .ticks_ms ()
70+ rebuild_needed = True
71+
72+ if (self ._last_app_list is not None and
73+ len (self ._last_app_list ) == len (current_apps )):
74+ # element-wise compare (name, path, icon_hash)
75+ if all (a == b for a , b in zip (self ._last_app_list , current_apps )):
76+ rebuild_needed = False
77+
78+ if not rebuild_needed :
79+ end = time .ticks_ms ()
80+ print (f"Redraw icons took: { end - start } ms (cached – no change)" )
81+ return
4382
83+ # ------------------------------------------------------------------
84+ # 3. UI needs (re)building – clear screen and create widgets
4485 screen .clean ()
4586
46- # Get the group for focusable objects
4787 focusgroup = lv .group_get_default ()
4888 if not focusgroup :
4989 print ("WARNING: could not get default focusgroup" )
5090
51- # Create UI for each app
91+ # Grid parameters
92+ icon_size = 64
93+ label_height = 24
94+ iconcont_width = icon_size + label_height
95+ iconcont_height = icon_size + label_height
96+
5297 for app in PackageManager .get_app_list ():
5398 if app .category == "launcher" :
54- print ("Skipping launcher app from launcher apps..." )
5599 continue
100+
56101 app_name = app .name
57102 app_dir_fullpath = app .installed_path
58103 print (f"Adding app { app_name } from { app_dir_fullpath } " )
59- # Create container for each app (icon + label)
104+
105+ # ----- container ------------------------------------------------
60106 app_cont = lv .obj (screen )
61107 app_cont .set_size (iconcont_width , iconcont_height )
62108 app_cont .set_style_border_width (0 , lv .PART .MAIN )
63109 app_cont .set_style_pad_all (0 , 0 )
64- app_cont .set_style_bg_opa (lv .OPA .TRANSP ,0 ) # prevent default style from adding slight gray to this container
110+ app_cont .set_style_bg_opa (lv .OPA .TRANSP , 0 )
65111 app_cont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
66- # Load and display icon
112+
113+ # ----- icon ----------------------------------------------------
67114 icon_path = f"{ app_dir_fullpath } /res/mipmap-mdpi/icon_64x64.png"
68115 image = lv .image (app_cont )
69116 try :
70117 image .set_src (Launcher .load_icon (icon_path ))
71118 except Exception as e :
72- print (f"Error loading icon { icon_path } : { e } - loading default icon " )
119+ print (f"Error loading icon { icon_path } : { e } - loading default" )
73120 icon_path = "builtin/res/mipmap-mdpi/default_icon_64x64.png"
74121 try :
75122 image .set_src (Launcher .load_icon (icon_path ))
76123 except Exception as e :
77- print (f"Error loading default icon { icon_path } : { e } - using symbol" )
124+ print (f"Error loading default { icon_path } : { e } - using symbol" )
78125 image .set_src (lv .SYMBOL .STOP )
126+
79127 image .align (lv .ALIGN .TOP_MID , 0 , 0 )
80128 image .set_size (icon_size , icon_size )
129+
130+ # ----- label ---------------------------------------------------
81131 label = lv .label (app_cont )
82- label .set_text (app_name ) # Use app_name directly
132+ label .set_text (app_name )
83133 label .set_long_mode (lv .label .LONG_MODE .WRAP )
84134 label .set_width (iconcont_width )
85135 label .align (lv .ALIGN .BOTTOM_MID , 0 , 0 )
86136 label .set_style_text_align (lv .TEXT_ALIGN .CENTER , 0 )
87- app_cont .add_event_cb (lambda e , fullname = app .fullname : mpos .apps .start_app (fullname ), lv .EVENT .CLICKED , None )
88- app_cont .add_event_cb (lambda e , app_cont = app_cont : self .focus_app_cont (app_cont ),lv .EVENT .FOCUSED ,None )
89- app_cont .add_event_cb (lambda e , app_cont = app_cont : self .defocus_app_cont (app_cont ),lv .EVENT .DEFOCUSED ,None )
137+
138+ # ----- events --------------------------------------------------
139+ app_cont .add_event_cb (
140+ lambda e , fullname = app .fullname : mpos .apps .start_app (fullname ),
141+ lv .EVENT .CLICKED , None )
142+ app_cont .add_event_cb (
143+ lambda e , cont = app_cont : self .focus_app_cont (cont ),
144+ lv .EVENT .FOCUSED , None )
145+ app_cont .add_event_cb (
146+ lambda e , cont = app_cont : self .defocus_app_cont (cont ),
147+ lv .EVENT .DEFOCUSED , None )
148+
90149 if focusgroup :
91150 focusgroup .add_obj (app_cont )
92-
151+
152+ # ------------------------------------------------------------------
153+ # 4. Store the new representation for the next resume
154+ self ._last_app_list = current_apps
155+ self ._last_ui_built = True
156+
93157 end = time .ticks_ms ()
94- print (f"Redraw icons took: { end - start } ms" )
158+ print (f"Redraw icons took: { end - start } ms (full rebuild) " )
95159
160+ # ------------------------------------------------------------------
96161 @staticmethod
97162 def load_icon (icon_path ):
98163 with open (icon_path , 'rb' ) as f :
@@ -103,12 +168,11 @@ def load_icon(icon_path):
103168 })
104169 return image_dsc
105170
171+ # ------------------------------------------------------------------
106172 def focus_app_cont (self , app_cont ):
107- #print(f"app_cont {app_cont} focused, setting border...")
108- app_cont .set_style_border_color (lv .theme_get_color_primary (None ),lv .PART .MAIN )
173+ app_cont .set_style_border_color (lv .theme_get_color_primary (None ), lv .PART .MAIN )
109174 app_cont .set_style_border_width (1 , lv .PART .MAIN )
110- app_cont .scroll_to_view (True ) # scroll to bring it into view
175+ app_cont .scroll_to_view (True )
111176
112177 def defocus_app_cont (self , app_cont ):
113- #print(f"app_cont {app_cont} defocused, unsetting border...")
114178 app_cont .set_style_border_width (0 , lv .PART .MAIN )
0 commit comments