Skip to content

Commit 60d630f

Browse files
OSUpdate app: simplify and ensure UI is visible
1 parent 9fb042a commit 60d630f

File tree

2 files changed

+33
-64
lines changed

2 files changed

+33
-64
lines changed

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

Lines changed: 31 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ def __init__(self):
3333
self.current_state = UpdateState.IDLE
3434
self.connectivity_manager = None # Will be initialized in onStart
3535

36+
# This function gets called from both the main thread as the update_with_lvgl() thread
3637
def set_state(self, new_state):
3738
"""Change app state and update UI accordingly."""
3839
print(f"OSUpdate: state change {self.current_state} -> {new_state}")
3940
self.current_state = new_state
40-
self._update_ui_for_state()
41+
self.update_ui_threadsafe_if_foreground(self._update_ui_for_state) # Since called from both threads, be threadsafe
4142

4243
def onCreate(self):
4344
self.main_screen = lv.obj()
@@ -78,19 +79,6 @@ def onCreate(self):
7879
self.status_label.align_to(self.force_update, lv.ALIGN.OUT_BOTTOM_LEFT, 0, mpos.ui.pct_of_display_height(5))
7980
self.setContentView(self.main_screen)
8081

81-
def onStart(self, screen):
82-
# Get connectivity manager instance
83-
self.connectivity_manager = ConnectivityManager.get()
84-
85-
# Check if online and either start update check or wait for network
86-
if self.connectivity_manager.is_online():
87-
self.set_state(UpdateState.CHECKING_UPDATE)
88-
print("OSUpdate: Online, checking for updates...")
89-
self.show_update_info()
90-
else:
91-
self.set_state(UpdateState.WAITING_WIFI)
92-
print("OSUpdate: Offline, waiting for network...")
93-
9482
def _update_ui_for_state(self):
9583
"""Update UI elements based on current state."""
9684
if self.current_state == UpdateState.WAITING_WIFI:
@@ -112,10 +100,11 @@ def _update_ui_for_state(self):
112100
def onResume(self, screen):
113101
"""Register for connectivity callbacks when app resumes."""
114102
super().onResume(screen)
115-
if self.connectivity_manager:
116-
self.connectivity_manager.register_callback(self.network_changed)
117-
# Check current state
118-
self.network_changed(self.connectivity_manager.is_online())
103+
# Get connectivity manager instance
104+
self.connectivity_manager = ConnectivityManager.get()
105+
self.connectivity_manager.register_callback(self.network_changed)
106+
# Start, based on network state:
107+
self.network_changed(self.connectivity_manager.is_online())
119108

120109
def onPause(self, screen):
121110
"""Unregister connectivity callbacks when app pauses."""
@@ -138,17 +127,13 @@ def network_changed(self, online):
138127
pass
139128
elif self.current_state == UpdateState.CHECKING_UPDATE:
140129
# Was checking for updates when network dropped
141-
self.update_ui_threadsafe_if_foreground(
142-
self.set_state, UpdateState.WAITING_WIFI
143-
)
130+
self.set_state(UpdateState.WAITING_WIFI)
144131
else:
145132
# Went online
146-
if self.current_state == UpdateState.WAITING_WIFI:
133+
if self.current_state == UpdateState.IDLE or self.current_state == UpdateState.WAITING_WIFI:
147134
# Was waiting for network, now can check for updates
148-
self.update_ui_threadsafe_if_foreground(
149-
self.set_state, UpdateState.CHECKING_UPDATE
150-
)
151-
self.show_update_info()
135+
self.set_state(UpdateState.CHECKING_UPDATE)
136+
self.schedule_show_update_info()
152137
elif self.current_state == UpdateState.DOWNLOAD_PAUSED:
153138
# Download was paused, will auto-resume in download thread
154139
pass
@@ -191,8 +176,12 @@ def _get_user_friendly_error(self, error):
191176
else:
192177
return f"An error occurred:\n{str(error)}\n\nPlease try again."
193178

194-
def show_update_info(self):
195-
self.status_label.set_text("Checking for OS updates...")
179+
# Show update info with a delay, to ensure ordering of multiple lv.async_call()
180+
def schedule_show_update_info(self):
181+
timer = lv.timer_create(self.show_update_info, 150, None)
182+
timer.set_repeat_count(1)
183+
184+
def show_update_info(self, timer=None):
196185
hwid = mpos.info.get_hardware_id()
197186

198187
try:
@@ -212,6 +201,7 @@ def show_update_info(self):
212201
self.set_state(UpdateState.ERROR)
213202
self.status_label.set_text(self._get_user_friendly_error(e))
214203
except Exception as e:
204+
print(f"show_update_info got exception: {e}")
215205
# Unexpected error
216206
self.set_state(UpdateState.ERROR)
217207
self.status_label.set_text(self._get_user_friendly_error(e))
@@ -220,9 +210,7 @@ def handle_update_info(self, version, download_url, changelog):
220210
self.download_update_url = download_url
221211

222212
# Use UpdateChecker to determine if update is available
223-
is_newer = self.update_checker.is_update_available(
224-
version, mpos.info.CURRENT_OS_VERSION
225-
)
213+
is_newer = self.update_checker.is_update_available(version, mpos.info.CURRENT_OS_VERSION)
226214

227215
if is_newer:
228216
label = "New"
@@ -270,7 +258,7 @@ def check_again_click(self):
270258
print("OSUpdate: Check Again button clicked")
271259
self.check_again_button.add_flag(lv.obj.FLAG.HIDDEN)
272260
self.set_state(UpdateState.CHECKING_UPDATE)
273-
self.show_update_info()
261+
self.schedule_show_update_info()
274262

275263
def progress_callback(self, percent):
276264
print(f"OTA Update: {percent:.1f}%")
@@ -296,12 +284,9 @@ def update_with_lvgl(self, url):
296284

297285
if result['success']:
298286
# Update succeeded - set boot partition and restart
299-
self.update_ui_threadsafe_if_foreground(
300-
self.status_label.set_text,
301-
"Update finished! Restarting..."
302-
)
287+
self.update_ui_threadsafe_if_foreground(self.status_label.set_text,"Update finished! Restarting...")
303288
# Small delay to show the message
304-
time.sleep_ms(500)
289+
time.sleep_ms(2000)
305290
self.update_downloader.set_boot_partition_and_restart()
306291
return
307292

@@ -312,9 +297,7 @@ def update_with_lvgl(self, url):
312297
percent = (bytes_written / total_size * 100) if total_size > 0 else 0
313298

314299
print(f"OSUpdate: Download paused at {percent:.1f}% ({bytes_written}/{total_size} bytes)")
315-
self.update_ui_threadsafe_if_foreground(
316-
self.set_state, UpdateState.DOWNLOAD_PAUSED
317-
)
300+
self.set_state(UpdateState.DOWNLOAD_PAUSED)
318301

319302
# Wait for wifi to return
320303
# ConnectivityManager will notify us via callback when network returns
@@ -326,9 +309,7 @@ def update_with_lvgl(self, url):
326309
while elapsed < max_wait and self.has_foreground():
327310
if self.connectivity_manager.is_online():
328311
print("OSUpdate: Network reconnected, resuming download")
329-
self.update_ui_threadsafe_if_foreground(
330-
self.set_state, UpdateState.DOWNLOADING
331-
)
312+
self.set_state(UpdateState.DOWNLOADING)
332313
break # Exit wait loop and retry download
333314

334315
time.sleep(check_interval)
@@ -338,12 +319,8 @@ def update_with_lvgl(self, url):
338319
# Timeout waiting for network
339320
msg = f"Network timeout during download.\n{bytes_written}/{total_size} bytes written.\nPress 'Update OS' to retry."
340321
self.update_ui_threadsafe_if_foreground(self.status_label.set_text, msg)
341-
self.update_ui_threadsafe_if_foreground(
342-
self.install_button.remove_state, lv.STATE.DISABLED
343-
)
344-
self.update_ui_threadsafe_if_foreground(
345-
self.set_state, UpdateState.ERROR
346-
)
322+
self.update_ui_threadsafe_if_foreground(self.install_button.remove_state, lv.STATE.DISABLED)
323+
self.set_state(UpdateState.ERROR)
347324
return
348325

349326
# If we're here, network is back - continue to next iteration to resume
@@ -366,26 +343,16 @@ def update_with_lvgl(self, url):
366343
progress_info += "\n\nPress 'Update OS' to resume."
367344
msg = friendly_msg + progress_info
368345

369-
self.update_ui_threadsafe_if_foreground(
370-
self.set_state, UpdateState.ERROR
371-
)
346+
self.set_state(UpdateState.ERROR)
372347
self.update_ui_threadsafe_if_foreground(self.status_label.set_text, msg)
373-
self.update_ui_threadsafe_if_foreground(
374-
self.install_button.remove_state, lv.STATE.DISABLED
375-
) # allow retry
348+
self.update_ui_threadsafe_if_foreground(self.install_button.remove_state, lv.STATE.DISABLED) # allow retry
376349
return
377350

378351
except Exception as e:
379352
msg = self._get_user_friendly_error(e) + "\n\nPress 'Update OS' to retry."
380-
self.update_ui_threadsafe_if_foreground(
381-
self.set_state, UpdateState.ERROR
382-
)
383-
self.update_ui_threadsafe_if_foreground(
384-
self.status_label.set_text, msg
385-
)
386-
self.update_ui_threadsafe_if_foreground(
387-
self.install_button.remove_state, lv.STATE.DISABLED
388-
) # allow retry
353+
self.set_state(UpdateState.ERROR)
354+
self.update_ui_threadsafe_if_foreground(self.status_label.set_text, msg)
355+
self.update_ui_threadsafe_if_foreground(self.install_button.remove_state, lv.STATE.DISABLED) # allow retry
389356

390357
# Business Logic Classes:
391358

internal_filesystem/lib/mpos/app/activity.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def if_foreground(self, func, *args, **kwargs):
8484

8585
# Update the UI in a threadsafe way if the Activity is in the foreground
8686
# The call may get throttled, unless important=True is added to it.
87+
# The order of these update_ui calls are not guaranteed, so a UI update might be overwritten by an "earlier" update.
88+
# To avoid this, use lv.timer_create() with .set_repeat_count(1) as examplified in osupdate.py
8789
def update_ui_threadsafe_if_foreground(self, func, *args, important=False, **kwargs):
8890
self.throttle_async_call_counter += 1
8991
if not important and self.throttle_async_call_counter > 100: # 250 seems to be okay, so 100 is on the safe side

0 commit comments

Comments
 (0)