55import _thread
66
77from mpos .apps import Activity
8- from mpos import PackageManager
8+ from mpos import PackageManager , ConnectivityManager
99import mpos .info
1010import mpos .ui
1111
@@ -28,10 +28,10 @@ class OSUpdate(Activity):
2828 def __init__ (self ):
2929 super ().__init__ ()
3030 # Initialize business logic components with dependency injection
31- self .network_monitor = NetworkMonitor ()
3231 self .update_checker = UpdateChecker ()
33- self .update_downloader = UpdateDownloader (network_monitor = self . network_monitor )
32+ self .update_downloader = UpdateDownloader ()
3433 self .current_state = UpdateState .IDLE
34+ self .connectivity_manager = None # Will be initialized in onStart
3535
3636 def set_state (self , new_state ):
3737 """Change app state and update UI accordingly."""
@@ -79,16 +79,17 @@ def onCreate(self):
7979 self .setContentView (self .main_screen )
8080
8181 def onStart (self , screen ):
82- # Check wifi and either start update check or wait for wifi
83- if not self .network_monitor .is_connected ():
84- self .set_state (UpdateState .WAITING_WIFI )
85- # Start wifi monitoring in background
86- _thread .stack_size (mpos .apps .good_stack_size ())
87- _thread .start_new_thread (self ._wifi_wait_thread , ())
88- else :
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 ():
8987 self .set_state (UpdateState .CHECKING_UPDATE )
90- print ("Showing update info ..." )
88+ print ("OSUpdate: Online, checking for updates ..." )
9189 self .show_update_info ()
90+ else :
91+ self .set_state (UpdateState .WAITING_WIFI )
92+ print ("OSUpdate: Offline, waiting for network..." )
9293
9394 def _update_ui_for_state (self ):
9495 """Update UI elements based on current state."""
@@ -108,32 +109,49 @@ def _update_ui_for_state(self):
108109 # Show "Check Again" button on errors
109110 self .check_again_button .remove_flag (lv .obj .FLAG .HIDDEN )
110111
111- def _wifi_wait_thread (self ):
112- """Background thread that waits for wifi connection."""
113- print ("OSUpdate: waiting for wifi..." )
114- check_interval = 5 # Check every 5 seconds
115- max_wait_time = 300 # 5 minutes timeout
116- elapsed = 0
117-
118- while elapsed < max_wait_time and self .has_foreground ():
119- if self .network_monitor .is_connected ():
120- print ("OSUpdate: wifi connected, checking for updates" )
121- # Switch to checking state and start update check
112+ def onResume (self , screen ):
113+ """Register for connectivity callbacks when app resumes."""
114+ 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 ())
119+
120+ def onPause (self , screen ):
121+ """Unregister connectivity callbacks when app pauses."""
122+ if self .connectivity_manager :
123+ self .connectivity_manager .unregister_callback (self .network_changed )
124+ super ().onPause (screen )
125+
126+ def network_changed (self , online ):
127+ """Callback when network connectivity changes.
128+
129+ Args:
130+ online: True if network is online, False if offline
131+ """
132+ print (f"OSUpdate: network_changed, now: { 'ONLINE' if online else 'OFFLINE' } " )
133+
134+ if not online :
135+ # Went offline
136+ if self .current_state == UpdateState .DOWNLOADING :
137+ # Download will automatically pause due to connectivity check
138+ pass
139+ elif self .current_state == UpdateState .CHECKING_UPDATE :
140+ # Was checking for updates when network dropped
141+ self .update_ui_threadsafe_if_foreground (
142+ self .set_state , UpdateState .WAITING_WIFI
143+ )
144+ else :
145+ # Went online
146+ if self .current_state == UpdateState .WAITING_WIFI :
147+ # Was waiting for network, now can check for updates
122148 self .update_ui_threadsafe_if_foreground (
123149 self .set_state , UpdateState .CHECKING_UPDATE
124150 )
125151 self .show_update_info ()
126- return
127-
128- time .sleep (check_interval )
129- elapsed += check_interval
130-
131- # Timeout or user navigated away
132- if self .has_foreground ():
133- self .update_ui_threadsafe_if_foreground (
134- self .status_label .set_text ,
135- "WiFi connection timeout.\n Please check your network and restart the app."
136- )
152+ elif self .current_state == UpdateState .DOWNLOAD_PAUSED :
153+ # Download was paused, will auto-resume in download thread
154+ pass
137155
138156 def _get_user_friendly_error (self , error ):
139157 """Convert technical errors into user-friendly messages with guidance."""
@@ -299,13 +317,15 @@ def update_with_lvgl(self, url):
299317 )
300318
301319 # Wait for wifi to return
302- check_interval = 5 # Check every 5 seconds
320+ # ConnectivityManager will notify us via callback when network returns
321+ print ("OSUpdate: Waiting for network to return..." )
322+ check_interval = 2 # Check every 2 seconds
303323 max_wait = 300 # 5 minutes timeout
304324 elapsed = 0
305325
306326 while elapsed < max_wait and self .has_foreground ():
307- if self .network_monitor . is_connected ():
308- print ("OSUpdate: WiFi reconnected, resuming download" )
327+ if self .connectivity_manager . is_online ():
328+ print ("OSUpdate: Network reconnected, resuming download" )
309329 self .update_ui_threadsafe_if_foreground (
310330 self .set_state , UpdateState .DOWNLOADING
311331 )
@@ -315,15 +335,18 @@ def update_with_lvgl(self, url):
315335 elapsed += check_interval
316336
317337 if elapsed >= max_wait :
318- # Timeout waiting for wifi
319- msg = f"WiFi timeout during download.\n { bytes_written } /{ total_size } bytes written.\n Press Update to retry."
338+ # Timeout waiting for network
339+ msg = f"Network timeout during download.\n { bytes_written } /{ total_size } bytes written.\n Press ' Update OS' to retry."
320340 self .update_ui_threadsafe_if_foreground (self .status_label .set_text , msg )
321341 self .update_ui_threadsafe_if_foreground (
322342 self .install_button .remove_state , lv .STATE .DISABLED
323343 )
344+ self .update_ui_threadsafe_if_foreground (
345+ self .set_state , UpdateState .ERROR
346+ )
324347 return
325348
326- # If we're here, wifi is back - continue to next iteration to resume
349+ # If we're here, network is back - continue to next iteration to resume
327350
328351 else :
329352 # Update failed with error (not pause)
@@ -378,57 +401,20 @@ class UpdateState:
378401 COMPLETED = "completed"
379402 ERROR = "error"
380403
381- class NetworkMonitor :
382- """Monitors network connectivity status."""
383-
384- def __init__ (self , network_module = None ):
385- """Initialize with optional dependency injection for testing.
386-
387- Args:
388- network_module: Network module (defaults to network if available)
389- """
390- self .network_module = network_module
391- if self .network_module is None :
392- try :
393- import network
394- self .network_module = network
395- except ImportError :
396- # Desktop/simulation mode - no network module
397- self .network_module = None
398-
399- def is_connected (self ):
400- """Check if WiFi is currently connected.
401-
402- Returns:
403- bool: True if connected, False otherwise
404- """
405- if self .network_module is None :
406- # No network module available (desktop mode)
407- # Assume connected for testing purposes
408- return True
409-
410- try :
411- wlan = self .network_module .WLAN (self .network_module .STA_IF )
412- return wlan .isconnected ()
413- except Exception as e :
414- print (f"NetworkMonitor: Error checking connection: { e } " )
415- return False
416-
417-
418404class UpdateDownloader :
419405 """Handles downloading and installing OS updates."""
420406
421- def __init__ (self , requests_module = None , partition_module = None , network_monitor = None ):
407+ def __init__ (self , requests_module = None , partition_module = None , connectivity_manager = None ):
422408 """Initialize with optional dependency injection for testing.
423409
424410 Args:
425411 requests_module: HTTP requests module (defaults to requests)
426412 partition_module: ESP32 Partition module (defaults to esp32.Partition if available)
427- network_monitor: NetworkMonitor instance for checking wifi during download
413+ connectivity_manager: ConnectivityManager instance for checking network during download
428414 """
429415 self .requests = requests_module if requests_module else requests
430416 self .partition_module = partition_module
431- self .network_monitor = network_monitor
417+ self .connectivity_manager = connectivity_manager
432418 self .simulate = False
433419
434420 # Download state for pause/resume
@@ -514,9 +500,18 @@ def download_and_install(self, url, progress_callback=None, should_continue_call
514500 response .close ()
515501 return result
516502
517- # Check wifi connection (if monitoring enabled)
518- if self .network_monitor and not self .network_monitor .is_connected ():
519- print ("UpdateDownloader: WiFi lost, pausing download" )
503+ # Check network connection (if monitoring enabled)
504+ if self .connectivity_manager :
505+ is_online = self .connectivity_manager .is_online ()
506+ elif ConnectivityManager ._instance :
507+ # Use global instance if available
508+ is_online = ConnectivityManager ._instance .is_online ()
509+ else :
510+ # No connectivity checking available
511+ is_online = True
512+
513+ if not is_online :
514+ print ("UpdateDownloader: Network lost, pausing download" )
520515 self .is_paused = True
521516 self .bytes_written_so_far = bytes_written
522517 result ['paused' ] = True
0 commit comments