From 055b29b22625627a3f129ef52d550a654fa77901 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 4 Mar 2026 19:37:24 -0800 Subject: [PATCH] updater: better flow (#37560) * better update flow * clean up * clean up * cmt * clean up * todo --- common/filter_simple.py | 2 +- system/ui/mici_updater.py | 24 ++++++++++++++---------- system/ui/widgets/nav_widget.py | 9 +++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/common/filter_simple.py b/common/filter_simple.py index 212e1a8f4..b28c3d68f 100644 --- a/common/filter_simple.py +++ b/common/filter_simple.py @@ -28,7 +28,7 @@ class BounceFilter(FirstOrderFilter): scale = self.dt / (1.0 / 60.0) # tuned at 60 fps self.velocity.x += (x - self.x) * self.bounce * scale * self.dt self.velocity.update(0.0) - if abs(self.velocity.x) < 1e-5: + if abs(self.velocity.x) < 1e-3: self.velocity.x = 0.0 self.x += self.velocity.x return self.x diff --git a/system/ui/mici_updater.py b/system/ui/mici_updater.py index 8e6f0a195..42aa1090b 100755 --- a/system/ui/mici_updater.py +++ b/system/ui/mici_updater.py @@ -8,7 +8,7 @@ from openpilot.common.realtime import config_realtime_process, set_core_affinity from openpilot.system.hardware import HARDWARE, TICI from openpilot.common.swaglog import cloudlog from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.widgets import Widget +from openpilot.system.ui.widgets.nav_widget import NavWidget from openpilot.system.ui.widgets.scroller import Scroller from openpilot.system.ui.widgets.label import UnifiedLabel from openpilot.system.ui.mici_setup import (NetworkSetupPage, FailedPage, NetworkConnectivityMonitor, @@ -22,7 +22,7 @@ class UpdaterNetworkSetupPage(NetworkSetupPage): self._continue_button.set_green(False) -class ProgressPage(Widget): +class ProgressPage(NavWidget): def __init__(self): super().__init__() @@ -32,12 +32,16 @@ class ProgressPage(Widget): font_weight=FontWeight.ROMAN, alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_BOTTOM) + def _back_enabled(self) -> bool: + return False + def set_progress(self, text: str, value: int): self._progress_title_label.set_text(text.replace("_", "_\n") + "...") self._progress_percent_label.set_text(f"{value}%") def show_event(self): super().show_event() + self._nav_bar._alpha = 0.0 # not dismissable self.set_progress("downloading", 0) def _render(self, rect: rl.Rectangle): @@ -82,7 +86,7 @@ class Updater(Scroller): self._continue_button.set_click_callback(lambda: gui_app.push_widget(self._network_setup_page)) self._scroller.add_widgets([ - GreyBigButton("update required", "The download size is\napproximately 1 GB", + GreyBigButton("update required", "the download size\nis approximately 1 GB", gui_app.texture("icons_mici/offroad_alerts/green_wheel.png", 64, 64)), self._continue_button, ]) @@ -101,19 +105,19 @@ class Updater(Scroller): if self._update_failed: self._update_failed = False self.show_event() - gui_app.pop_widgets_to(self, instant=True) - gui_app.push_widget(self._failed_page) + gui_app.pop_widgets_to(self, lambda: gui_app.push_widget(self._failed_page)) def install_update(self): self.progress_value = 0 self.progress_text = "downloading" - gui_app.pop_widgets_to(self, instant=True) - gui_app.push_widget(self._progress_page) + def start_update(): + self.update_thread = threading.Thread(target=self._run_update_process, daemon=True) + self.update_thread.start() - # Start the update process in a separate thread - self.update_thread = threading.Thread(target=self._run_update_process, daemon=True) - self.update_thread.start() + # Start the update process in a separate thread *after* show animation completes + self._progress_page.set_shown_callback(start_update) + gui_app.push_widget(self._progress_page) def _run_update_process(self): # TODO: just import it and run in a thread without a subprocess diff --git a/system/ui/widgets/nav_widget.py b/system/ui/widgets/nav_widget.py index 3292c53ce..5cf8715c0 100644 --- a/system/ui/widgets/nav_widget.py +++ b/system/ui/widgets/nav_widget.py @@ -65,6 +65,8 @@ class NavWidget(Widget, abc.ABC): self._back_callback: Callable[[], None] | None = None # persistent callback for user-initiated back navigation self._dismiss_callback: Callable[[], None] | None = None # transient callback for programmatic dismiss + # TODO: add this functionality to push_widget + self._shown_callback: Callable[[], None] | None = None # transient callback fired after show animation completes # TODO: move this state into NavBar self._nav_bar = NavBar() @@ -79,6 +81,9 @@ class NavWidget(Widget, abc.ABC): def set_back_callback(self, callback: Callable[[], None]) -> None: self._back_callback = callback + def set_shown_callback(self, callback: Callable[[], None] | None) -> None: + self._shown_callback = callback + def _handle_mouse_event(self, mouse_event: MouseEvent) -> None: super()._handle_mouse_event(mouse_event) @@ -147,6 +152,10 @@ class NavWidget(Widget, abc.ABC): if abs(new_y) < 1 and self._y_pos_filter.velocity.x == 0.0: new_y = self._y_pos_filter.x = 0.0 + if self._shown_callback is not None: + self._shown_callback() + self._shown_callback = None + if new_y > self._rect.height + DISMISS_PUSH_OFFSET - 10: gui_app.pop_widget()