Skip to content

Commit bf09cbf

Browse files
Launcher: only redraw if app list changed
1 parent 394908f commit bf09cbf

File tree

1 file changed

+93
-29
lines changed
  • internal_filesystem/builtin/apps/com.micropythonos.launcher/assets

1 file changed

+93
-29
lines changed

internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py

Lines changed: 93 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,91 +8,156 @@
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
1311
import lvgl as lv
14-
1512
import mpos.apps
1613
import mpos.ui
1714
from mpos.content.pm import PackageManager
1815
from mpos import Activity
16+
import time
17+
import uhashlib
18+
import ubinascii
19+
1920

2021
class 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

Comments
 (0)