diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 1e2f6d4f..5b3f22e8 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -15,7 +15,7 @@ from seedsigner.models.settings import SettingsConstants from seedsigner.models.singleton import Singleton from seedsigner.models.threads import BaseThread from seedsigner.views.screensaver import ScreensaverScreen -from seedsigner.views.view import Destination +from seedsigner.views.view import Destination, View logger = logging.getLogger(__name__) @@ -475,3 +475,18 @@ class Controller(Singleton): exception_msg, ] return Destination(UnhandledExceptionView, view_args={"error": error}, clear_history=True) + + + @property + def is_screensaver_start_allowed(self) -> bool: + """ + Determines whether the screensaver is allowed to start. + + The screensaver can start only if: + - It is not currently running. + - The current active view allows screensaver activity. + """ + from seedsigner.views import MainMenuView + # Confusingly, the top item in the `BackStack` is actually the *current* View + active_view = self.back_stack[-1].view if self.back_stack else MainMenuView() + return not self.is_screensaver_running and active_view.is_screensaver_allowed diff --git a/src/seedsigner/hardware/buttons.py b/src/seedsigner/hardware/buttons.py index aee21c25..abe18c5c 100644 --- a/src/seedsigner/hardware/buttons.py +++ b/src/seedsigner/hardware/buttons.py @@ -82,7 +82,7 @@ class HardwareButtons(Singleton): return HardwareButtonsConstants.OVERRIDE cur_time = int(time.time() * 1000) - if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.is_screensaver_running: + if cur_time - self.last_input_time > controller.screensaver_activation_ms and controller.is_screensaver_start_allowed: # Start the screensaver. Will block execution until input detected. controller.start_screensaver() diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index 15dad4a8..47958965 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -1552,7 +1552,8 @@ class SeedTranscribeSeedQRZoomedInView(View): self.seedqr_format = seedqr_format self.seed = self.controller.get_seed(seed_num) self.initial_zone_x = initial_zone_x - self.initial_zone_y = initial_zone_y + self.initial_zone_y = initial_zone_y + self.is_screensaver_allowed = False def run(self): diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index 08c56933..e2ac1ad0 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -71,6 +71,7 @@ class View: self.screen = None self._redirect: 'Destination' = None + self.is_screensaver_allowed = True def __init__(self): diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index 23b50671..fb872e6d 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -476,6 +476,25 @@ class TestSeedFlows(FlowTest): FlowStep(seed_views.SeedOptionsView), ]) + def test_transcribe_seedqr_screensaver_startable_status(self): + """ + The controller should return False for screensaver startable status when SeedTranscribeSeedQRZoomedInView + is active. + """ + # Load a finalized Seed into the Controller + mnemonic = ["abandon"] * 11 + ["about"] + self.controller.storage.set_pending_seed(Seed(mnemonic=mnemonic)) + self.controller.storage.finalize_pending_seed() + + self.run_sequence( + initial_destination_view_args={'num_modules': 21, 'seed_num': 0, 'seedqr_format': 'seed__seedqr'}, + sequence=[ + FlowStep(seed_views.SeedTranscribeSeedQRWholeQRView), + FlowStep(seed_views.SeedTranscribeSeedQRZoomedInView, is_redirect=True), # Live interactive screens are a bit weird; not sure why `is_redirect` is necessary here + ]) + + assert self.controller.is_screensaver_start_allowed == False + class TestMessageSigningFlows(FlowTest):