diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index b294e30f..1848d6f9 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -101,11 +101,8 @@ def load_method(self): load_menu = Menu( self.ctx, [ - (t("Load from camera"), lambda: None), - ( - t("Load from SD card"), - None if not self.has_sd_card() else lambda: None, - ), + MenuItem(t("Load from camera"), lambda: None), + MenuItemSD(t("Load from SD card"), lambda: None), ], back_status=lambda: None, ) @@ -425,12 +422,7 @@ def has_sd_card(self): """Checks if the device has a SD card inserted""" self.ctx.display.clear() self.ctx.display.draw_centered_text(t("Checking for SD card..")) - try: - # Check for SD hot-plug - with SDHandler(): - return True - except: - return False + return SDHandler.sd_card_available() def shutdown(self): """Handler for the 'shutdown' menu item""" @@ -499,17 +491,15 @@ class Menu: def __init__( self, ctx, - menu, + menu_list, offset=None, disable_statusbar=False, back_label="Back", back_status=lambda: MENU_EXIT, ): self.ctx = ctx - self.menu = menu if back_label: - back_label = t("Back") if back_label == "Back" else back_label - self.menu += [("< " + back_label, back_status)] + menu_list += [MenuItem.back(back_label, back_status)] self.disable_statusbar = disable_statusbar if offset is None: @@ -521,16 +511,10 @@ def __init__( self.menu_offset = offset max_viewable = min( self.ctx.display.max_menu_lines(self.menu_offset), - len(self.menu), + len(menu_list), ) - self.menu_view = ListView(self.menu, max_viewable) - - def screensaver(self): - """Loads and starts screensaver""" - from .screensaver import ScreenSaver - - screen_saver = ScreenSaver(self.ctx) - screen_saver.start() + self.menu_view = ListView(menu_list, max_viewable) + self.back_index = len(menu_list) - 1 def run_loop(self, start_from_index=None): """Runs the menu loop until one of the menu items returns either a MENU_EXIT @@ -603,14 +587,16 @@ def run_loop(self, start_from_index=None): self.menu_view.move_backward() elif btn is None and self.menu_offset == STATUS_BAR_HEIGHT: # Activates screensaver if there's no info_box(other things draw on the screen) - self.screensaver() + from .screensaver import start_screen_saver + + start_screen_saver(self.ctx) def _clicked_item(self, selected_item_index): - if self.menu_view[selected_item_index][1] is None: + if not self.menu_view[selected_item_index].enabled(): return MENU_CONTINUE try: self.ctx.display.clear() - status = self.menu_view[selected_item_index][1]() + status = self.menu_view[selected_item_index].action() if status != MENU_CONTINUE: return status except Exception as e: @@ -731,7 +717,7 @@ def _draw_touch_menu(self, selected_item_index): offset_y = 0 Page.y_keypad_map = [offset_y] for menu_item in self.menu_view: - offset_y += len(self.ctx.display.to_lines(menu_item[0])) + 1 + offset_y += len(self.ctx.display.to_lines(menu_item.label)) + 1 Page.y_keypad_map.append(offset_y) height_multiplier = self.ctx.display.height() height_multiplier -= self.menu_offset # Top offset @@ -753,7 +739,7 @@ def _draw_touch_menu(self, selected_item_index): # draw centralized strings in regions for i, menu_item in enumerate(self.menu_view): - menu_item_lines = self.ctx.display.to_lines(menu_item[0]) + menu_item_lines = self.ctx.display.to_lines(menu_item.label) offset_y = Page.y_keypad_map[i + 1] - Page.y_keypad_map[i] offset_y -= len(menu_item_lines) * FONT_HEIGHT if i == len(self.menu_view) - 1: @@ -761,9 +747,7 @@ def _draw_touch_menu(self, selected_item_index): offset_y -= DEFAULT_PADDING offset_y //= 2 offset_y += Page.y_keypad_map[i] - fg_color = ( - theme.fg_color if menu_item[1] is not None else theme.disabled_color - ) + fg_color = theme.fg_color if menu_item.enabled() else theme.disabled_color if selected_item_index == i and self.ctx.input.buttons_active: self.ctx.display.fill_rectangle( 0, @@ -789,7 +773,7 @@ def _draw_menu(self, selected_item_index): extra_lines = 0 for menu_item in self.menu_view: # Count extra lines for multi-line menu items - extra_lines += len(self.ctx.display.to_lines(menu_item[0])) - 1 + extra_lines += len(self.ctx.display.to_lines(menu_item.label)) - 1 if self.menu_offset > STATUS_BAR_HEIGHT: offset_y = self.menu_offset + FONT_HEIGHT else: @@ -811,10 +795,8 @@ def _draw_menu(self, selected_item_index): # Limit padding to font height items_pad = min(items_pad, FONT_HEIGHT) for i, menu_item in enumerate(self.menu_view): - fg_color = ( - theme.fg_color if menu_item[1] is not None else theme.disabled_color - ) - menu_item_lines = self.ctx.display.to_lines(menu_item[0]) + fg_color = theme.fg_color if menu_item.enabled() else theme.disabled_color + menu_item_lines = self.ctx.display.to_lines(menu_item.label) delta_y = len(menu_item_lines) * FONT_HEIGHT delta_y += items_pad if selected_item_index == i: @@ -840,14 +822,36 @@ def _draw_menu(self, selected_item_index): offset_y += delta_y +class MenuItem: + """Handle items for the Menu""" + + def __init__(self, text, action, enabled=lambda: True): + self.label = text + self.action = action + self.enabled = enabled + + @staticmethod + def back(text="Back", action=lambda: MENU_EXIT): + """Create a standard back MenuItem""" + text = t("Back") if text == "Back" else text + return MenuItem("< " + text, action) + + +class MenuItemSD(MenuItem): + """Reusable MenuItem for the Menu that automatic disables when SD card not detected""" + + def __init__(self, text, action): + super().__init__(text, action, SDHandler.sd_card_available) + + def choose_len_mnemonic(ctx, double_mnemonic=False): """Reusable '12 or 24 words?" menu choice""" items = [ - (t("12 words"), lambda: 12), - (t("24 words"), lambda: 24), + MenuItem(t("12 words"), lambda: 12), + MenuItem(t("24 words"), lambda: 24), ] if double_mnemonic: - items += [(t("Double mnemonic"), lambda: 48)] + items += [MenuItem(t("Double mnemonic"), lambda: 48)] submenu = Menu( ctx, items, diff --git a/src/krux/pages/encryption_ui.py b/src/krux/pages/encryption_ui.py index 5b06b136..854d5d22 100644 --- a/src/krux/pages/encryption_ui.py +++ b/src/krux/pages/encryption_ui.py @@ -26,6 +26,8 @@ from . import ( Page, Menu, + MenuItem, + MenuItemSD, MENU_CONTINUE, ESC_KEY, LETTERS, @@ -49,8 +51,8 @@ def encryption_key(self): submenu = Menu( self.ctx, [ - (t("Type Key"), self.load_key), - (t("Scan Key QR Code"), self.load_qr_encryption_key), + MenuItem(t("Type Key"), self.load_key), + MenuItem(t("Scan Key QR Code"), self.load_qr_encryption_key), ], back_label=None, ) @@ -102,16 +104,11 @@ def encrypt_menu(self): """Menu with mnemonic encryption output options""" encrypt_outputs_menu = [ - (t("Store on Flash"), self.store_mnemonic_on_memory), - ( - t("Store on SD Card"), - ( - None - if not self.has_sd_card() - else lambda: self.store_mnemonic_on_memory(True) - ), + MenuItem(t("Store on Flash"), self.store_mnemonic_on_memory), + MenuItemSD( + t("Store on SD Card"), lambda: self.store_mnemonic_on_memory(True) ), - (t("Encrypted QR Code"), self.encrypted_qr_code), + MenuItem(t("Encrypted QR Code"), self.encrypted_qr_code), ] submenu = Menu(self.ctx, encrypt_outputs_menu) _, _ = submenu.run_loop() @@ -239,7 +236,7 @@ def load_from_storage(self, remove_opt=False): for mnemonic_id in mnemonics: mnemonic_ids_menu.append( - ( + MenuItem( mnemonic_id + "(flash)", lambda m_id=mnemonic_id: ( self._remove_encrypted_mnemonic(m_id) @@ -250,7 +247,7 @@ def load_from_storage(self, remove_opt=False): ) for mnemonic_id in sd_mnemonics: mnemonic_ids_menu.append( - ( + MenuItem( mnemonic_id + "(SD card)", lambda m_id=mnemonic_id: ( self._remove_encrypted_mnemonic(m_id, sd_card=True) @@ -261,7 +258,7 @@ def load_from_storage(self, remove_opt=False): ) submenu = Menu(self.ctx, mnemonic_ids_menu) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status diff --git a/src/krux/pages/file_manager.py b/src/krux/pages/file_manager.py index 1733ed6b..9611f044 100644 --- a/src/krux/pages/file_manager.py +++ b/src/krux/pages/file_manager.py @@ -22,7 +22,7 @@ import board import gc -from . import Page, Menu, MENU_EXIT, MENU_CONTINUE +from . import Page, Menu, MenuItem, MENU_EXIT, MENU_CONTINUE from ..sd_card import SDHandler from ..krux_settings import t from ..format import generate_thousands_separator, render_decimal_separator @@ -61,7 +61,7 @@ def select_file( if path != SD_ROOT_PATH: items.append("..") - menu_items.append(("../", lambda: MENU_EXIT)) + menu_items.append(MenuItem("../", lambda: MENU_EXIT)) # sorts by name ignorecase dir_files = sorted(os.listdir(path), key=str.lower) @@ -109,7 +109,7 @@ def select_file( ] ) menu_items.append( - ( + MenuItem( display_filename, lambda file=filename: select_file_handler( path + "/" + file diff --git a/src/krux/pages/home_pages/addresses.py b/src/krux/pages/home_pages/addresses.py index 2544394c..68fef82d 100644 --- a/src/krux/pages/home_pages/addresses.py +++ b/src/krux/pages/home_pages/addresses.py @@ -28,6 +28,7 @@ from .. import ( Page, Menu, + MenuItem, MENU_CONTINUE, MENU_EXIT, ) @@ -48,9 +49,9 @@ def addresses_menu(self): submenu = Menu( self.ctx, [ - (t("Scan Address"), self.pre_scan_address), - (t("Receive Addresses"), self.list_address_type), - (t("Change Addresses"), lambda: self.list_address_type(1)), + MenuItem(t("Scan Address"), self.pre_scan_address), + MenuItem(t("Receive Addresses"), self.list_address_type), + MenuItem(t("Change Addresses"), lambda: self.list_address_type(1)), ], ) submenu.run_loop() @@ -72,7 +73,7 @@ def list_address_type(self, addr_type=0): items = [] if address_index >= max_addresses: items.append( - ( + MenuItem( "%d..%d" % (address_index - max_addresses, address_index - 1), lambda: MENU_EXIT, ) @@ -87,7 +88,7 @@ def list_address_type(self, addr_type=0): pos_str = str(address_index) + "." + THIN_SPACE qr_title = pos_str + addr items.append( - ( + MenuItem( self.fit_to_line(addr, pos_str, fixed_chars=3), lambda address=addr, title=qr_title: self.show_address( address, title @@ -97,7 +98,7 @@ def list_address_type(self, addr_type=0): address_index += 1 items.append( - ( + MenuItem( "%d..%d" % (address_index, address_index + max_addresses - 1), lambda: MENU_EXIT, ) @@ -108,13 +109,14 @@ def list_address_type(self, addr_type=0): while stay_on_this_addr_menu: index, _ = submenu.run_loop() - if index == len(submenu.menu) - 1: # Back + if index == submenu.back_index: # Back del submenu, items gc.collect() return MENU_CONTINUE - if index == len(submenu.menu) - 2: # Next + + if index == submenu.back_index - 1: # Next stay_on_this_addr_menu = False - if index == 0 and address_index > max_addresses: # Prev + elif index == 0 and address_index > max_addresses: # Prev stay_on_this_addr_menu = False address_index -= 2 * max_addresses @@ -134,8 +136,8 @@ def pre_scan_address(self): submenu = Menu( self.ctx, [ - (t("Receive"), self.scan_address), - (t("Change"), lambda: self.scan_address(1)), + MenuItem(t("Receive"), self.scan_address), + MenuItem(t("Change"), lambda: self.scan_address(1)), ], ) submenu.run_loop() diff --git a/src/krux/pages/home_pages/home.py b/src/krux/pages/home_pages/home.py index 8152c80a..13ac116c 100644 --- a/src/krux/pages/home_pages/home.py +++ b/src/krux/pages/home_pages/home.py @@ -28,6 +28,8 @@ from .. import ( Page, Menu, + MenuItem, + MenuItemSD, MENU_CONTINUE, ESC_KEY, LOAD_FROM_CAMERA, @@ -41,24 +43,22 @@ class Home(Page): """Home is the main menu page of the app""" def __init__(self, ctx): + enabled = not Settings().security.hide_mnemonic super().__init__( ctx, Menu( ctx, [ - ( + MenuItem( t("Backup Mnemonic"), - ( - self.backup_mnemonic - if not Settings().security.hide_mnemonic - else None - ), + self.backup_mnemonic, + lambda: enabled, ), - (t("Extended Public Key"), self.public_key), - (t("Wallet"), self.wallet), - (t("Address"), self.addresses_menu), - (t("Sign"), self.sign), - (t("Shutdown"), self.shutdown), + MenuItem(t("Extended Public Key"), self.public_key), + MenuItem(t("Wallet"), self.wallet), + MenuItem(t("Address"), self.addresses_menu), + MenuItem(t("Sign"), self.sign), + MenuItem(t("Shutdown"), self.shutdown), ], back_label=None, ), @@ -165,10 +165,10 @@ def wallet(self): submenu = Menu( self.ctx, [ - (t("Wallet Descriptor"), self.wallet_descriptor), - (t("Passphrase"), self.passphrase), - (t("Customize"), self.customize), - ("BIP85", self.bip85), + MenuItem(t("Wallet Descriptor"), self.wallet_descriptor), + MenuItem(t("Passphrase"), self.passphrase), + MenuItem(t("Customize"), self.customize), + MenuItem("BIP85", self.bip85), ], ) submenu.run_loop() @@ -186,12 +186,12 @@ def sign(self): submenu = Menu( self.ctx, [ - ("PSBT", self.sign_psbt), - (t("Message"), self.sign_message), + MenuItem("PSBT", self.sign_psbt), + MenuItem(t("Message"), self.sign_message), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status @@ -226,11 +226,8 @@ def _sign_menu(self): sign_menu = Menu( self.ctx, [ - (t("Sign to QR code"), lambda: None), - ( - t("Sign to SD card"), - None if not self.has_sd_card() else lambda: None, - ), + MenuItem(t("Sign to QR code"), lambda: None), + MenuItemSD(t("Sign to SD card"), lambda: None), ], back_status=lambda: None, ) diff --git a/src/krux/pages/home_pages/mnemonic_backup.py b/src/krux/pages/home_pages/mnemonic_backup.py index 26b892a7..340373e9 100644 --- a/src/krux/pages/home_pages/mnemonic_backup.py +++ b/src/krux/pages/home_pages/mnemonic_backup.py @@ -26,6 +26,7 @@ from .. import ( Page, Menu, + MenuItem, MENU_CONTINUE, ) @@ -38,9 +39,9 @@ def mnemonic(self): submenu = Menu( self.ctx, [ - (t("QR Code"), self.qr_code_backup), - (t("Encrypted"), self.encrypt_mnemonic_menu), - (t("Other Formats"), self.other_backup_formats), + MenuItem(t("QR Code"), self.qr_code_backup), + MenuItem(t("Encrypted"), self.encrypt_mnemonic_menu), + MenuItem(t("Other Formats"), self.other_backup_formats), ], ) submenu.run_loop() @@ -51,10 +52,10 @@ def qr_code_backup(self): submenu = Menu( self.ctx, [ - (t("Plaintext QR"), self.display_standard_qr), - ("Compact SeedQR", lambda: self.display_seed_qr(True)), - ("SeedQR", self.display_seed_qr), - (t("Encrypted QR Code"), self.encrypt_qr_code), + MenuItem(t("Plaintext QR"), self.display_standard_qr), + MenuItem("Compact SeedQR", lambda: self.display_seed_qr(True)), + MenuItem("SeedQR", self.display_seed_qr), + MenuItem(t("Encrypted QR Code"), self.encrypt_qr_code), ], ) submenu.run_loop() @@ -65,15 +66,15 @@ def other_backup_formats(self): submenu = Menu( self.ctx, [ - ( + MenuItem( t("Words"), lambda: self.show_mnemonic( self.ctx.wallet.key.mnemonic, t("Mnemonic") ), ), - (t("Numbers"), self.display_mnemonic_numbers), - ("Stackbit 1248", self.stackbit), - ("Tiny Seed", self.tiny_seed), + MenuItem(t("Numbers"), self.display_mnemonic_numbers), + MenuItem("Stackbit 1248", self.stackbit), + MenuItem("Tiny Seed", self.tiny_seed), ], ) submenu.run_loop() @@ -118,7 +119,7 @@ def display_mnemonic_numbers(self): submenu = Menu( self.ctx, [ - ( + MenuItem( t("Decimal"), lambda: self.show_mnemonic( self.ctx.wallet.key.mnemonic, @@ -128,7 +129,7 @@ def display_mnemonic_numbers(self): ), ), ), - ( + MenuItem( t("Hexadecimal"), lambda: self.show_mnemonic( self.ctx.wallet.key.mnemonic, @@ -138,7 +139,7 @@ def display_mnemonic_numbers(self): ), ), ), - ( + MenuItem( t("Octal"), lambda: self.show_mnemonic( self.ctx.wallet.key.mnemonic, diff --git a/src/krux/pages/home_pages/pub_key_view.py b/src/krux/pages/home_pages/pub_key_view.py index ca668a99..da875228 100644 --- a/src/krux/pages/home_pages/pub_key_view.py +++ b/src/krux/pages/home_pages/pub_key_view.py @@ -25,6 +25,8 @@ from .. import ( Page, Menu, + MenuItem, + MenuItemSD, MENU_CONTINUE, MENU_EXIT, ) @@ -60,14 +62,7 @@ def _save_xpub_to_sd(version): def _pub_key_text(version): pub_text_menu_items = [ - ( - t("Save to SD card"), - ( - None - if not self.has_sd_card() - else lambda: _save_xpub_to_sd(version) - ), - ), + MenuItemSD(t("Save to SD card"), lambda: _save_xpub_to_sd(version)), ] full_pub_key = self.ctx.wallet.key.account_pubkey_str(version) menu_offset = 5 + len(self.ctx.display.to_lines(full_pub_key)) @@ -110,10 +105,14 @@ def _pub_key_qr(version): :WALLET_XPUB_START ].upper() pub_key_menu_items.append( - (title + " - " + t("Text"), lambda ver=version: _pub_key_text(ver)) + MenuItem( + title + " - " + t("Text"), lambda ver=version: _pub_key_text(ver) + ) ) pub_key_menu_items.append( - (title + " - " + t("QR Code"), lambda ver=version: _pub_key_qr(ver)) + MenuItem( + title + " - " + t("QR Code"), lambda ver=version: _pub_key_qr(ver) + ) ) pub_key_menu = Menu(self.ctx, pub_key_menu_items) while True: diff --git a/src/krux/pages/home_pages/sign_message_ui.py b/src/krux/pages/home_pages/sign_message_ui.py index 5641f36d..8e7f81e0 100644 --- a/src/krux/pages/home_pages/sign_message_ui.py +++ b/src/krux/pages/home_pages/sign_message_ui.py @@ -24,7 +24,7 @@ from embit import bip32, compact import hashlib import binascii -from .. import MENU_CONTINUE, LOAD_FROM_CAMERA, LOAD_FROM_SD, Menu +from .. import MENU_CONTINUE, LOAD_FROM_CAMERA, LOAD_FROM_SD, Menu, MenuItem, MenuItemSD from ...themes import theme from ...display import ( DEFAULT_PADDING, @@ -197,11 +197,8 @@ def sign_message(self): sign_menu = Menu( self.ctx, [ - (t("Sign to QR code"), lambda: None), - ( - t("Sign to SD card"), - None if not self.has_sd_card() else lambda: None, - ), + MenuItem(t("Sign to QR code"), lambda: None), + MenuItemSD(t("Sign to SD card"), lambda: None), ], back_status=lambda: None, ) diff --git a/src/krux/pages/login.py b/src/krux/pages/login.py index 4e8e0699..d1cff0b2 100644 --- a/src/krux/pages/login.py +++ b/src/krux/pages/login.py @@ -31,6 +31,7 @@ from . import ( Page, Menu, + MenuItem, MENU_CONTINUE, MENU_EXIT, ESC_KEY, @@ -55,12 +56,12 @@ def __init__(self, ctx): Menu( ctx, [ - (t("Load Mnemonic"), self.load_key), - (t("New Mnemonic"), self.new_key), - (t("Settings"), self.settings), - (t("Tools"), self.tools), - (t("About"), self.about), - (t("Shutdown"), self.shutdown), + MenuItem(t("Load Mnemonic"), self.load_key), + MenuItem(t("New Mnemonic"), self.new_key), + MenuItem(t("Settings"), self.settings), + MenuItem(t("Tools"), self.tools), + MenuItem(t("About"), self.about), + MenuItem(t("Shutdown"), self.shutdown), ], back_label=None, ), @@ -71,13 +72,13 @@ def load_key(self): submenu = Menu( self.ctx, [ - (t("Via Camera"), self.load_key_from_camera), - (t("Via Manual Input"), self.load_key_from_manual_input), - (t("From Storage"), self.load_mnemonic_from_storage), + MenuItem(t("Via Camera"), self.load_key_from_camera), + MenuItem(t("Via Manual Input"), self.load_key_from_manual_input), + MenuItem(t("From Storage"), self.load_mnemonic_from_storage), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status @@ -86,20 +87,22 @@ def load_key_from_camera(self): submenu = Menu( self.ctx, [ - (t("QR Code"), self.load_key_from_qr_code), - ("Tiny Seed", lambda: self.load_key_from_tiny_seed_image("Tiny Seed")), - ( + MenuItem(t("QR Code"), self.load_key_from_qr_code), + MenuItem( + "Tiny Seed", lambda: self.load_key_from_tiny_seed_image("Tiny Seed") + ), + MenuItem( "OneKey KeyTag", lambda: self.load_key_from_tiny_seed_image("OneKey KeyTag"), ), - ( + MenuItem( t("Binary Grid"), lambda: self.load_key_from_tiny_seed_image("Binary Grid"), ), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status @@ -108,14 +111,14 @@ def load_key_from_manual_input(self): submenu = Menu( self.ctx, [ - (t("Words"), self.load_key_from_text), - (t("Word Numbers"), self.pre_load_key_from_digits), - ("Tiny Seed (Bits)", self.load_key_from_tiny_seed), - ("Stackbit 1248", self.load_key_from_1248), + MenuItem(t("Words"), self.load_key_from_text), + MenuItem(t("Word Numbers"), self.pre_load_key_from_digits), + MenuItem("Tiny Seed (Bits)", self.load_key_from_tiny_seed), + MenuItem("Stackbit 1248", self.load_key_from_1248), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status @@ -134,14 +137,14 @@ def new_key(self): submenu = Menu( self.ctx, [ - (t("Via Camera"), self.new_key_from_snapshot), - (t("Via Words"), lambda: self.load_key_from_text(new=True)), - (t("Via D6"), self.new_key_from_dice), - (t("Via D20"), lambda: self.new_key_from_dice(True)), + MenuItem(t("Via Camera"), self.new_key_from_snapshot), + MenuItem(t("Via Words"), lambda: self.load_key_from_text(new=True)), + MenuItem(t("Via D6"), self.new_key_from_dice), + MenuItem(t("Via D20"), lambda: self.new_key_from_dice(True)), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status @@ -285,14 +288,14 @@ def _load_key_from_words(self, words, charset=LETTERS): submenu = Menu( self.ctx, [ - (t("Load Wallet"), lambda: None), - (t("Passphrase"), lambda: None), - (t("Customize"), lambda: None), + MenuItem(t("Load Wallet"), lambda: None), + MenuItem(t("Passphrase"), lambda: None), + MenuItem(t("Customize"), lambda: None), ], offset=info_len * FONT_HEIGHT + DEFAULT_PADDING, ) index, _ = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: if self.prompt(t("Are you sure?"), self.ctx.display.height() // 2): del key return MENU_CONTINUE @@ -597,13 +600,13 @@ def pre_load_key_from_digits(self): submenu = Menu( self.ctx, [ - (t("Decimal"), self.load_key_from_digits), - (t("Hexadecimal"), self.load_key_from_hexadecimal), - (t("Octal"), self.load_key_from_octal), + MenuItem(t("Decimal"), self.load_key_from_digits), + MenuItem(t("Hexadecimal"), self.load_key_from_hexadecimal), + MenuItem(t("Octal"), self.load_key_from_octal), ], ) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status diff --git a/src/krux/pages/new_mnemonic/dice_rolls.py b/src/krux/pages/new_mnemonic/dice_rolls.py index 0a74cfbd..bdeb6ae2 100644 --- a/src/krux/pages/new_mnemonic/dice_rolls.py +++ b/src/krux/pages/new_mnemonic/dice_rolls.py @@ -23,6 +23,7 @@ from .. import ( Page, Menu, + MenuItem, MENU_EXIT, ESC_KEY, choose_len_mnemonic, @@ -305,8 +306,8 @@ def delete_roll(buffer): submenu = Menu( self.ctx, [ - (t("Stats for Nerds"), lambda: MENU_EXIT), - (t("Generate Mnemonic"), lambda: MENU_EXIT), + MenuItem(t("Stats for Nerds"), lambda: MENU_EXIT), + MenuItem(t("Generate Mnemonic"), lambda: MENU_EXIT), ], offset=menu_offset, back_label=None, diff --git a/src/krux/pages/qr_view.py b/src/krux/pages/qr_view.py index d73358b4..a2ae7761 100644 --- a/src/krux/pages/qr_view.py +++ b/src/krux/pages/qr_view.py @@ -22,7 +22,7 @@ import qrcode from embit.wordlists.bip39 import WORDLIST -from . import Page, Menu, MENU_CONTINUE, MENU_EXIT, ESC_KEY +from . import Page, Menu, MenuItem, MenuItemSD, MENU_CONTINUE, MENU_EXIT, ESC_KEY from ..themes import theme, WHITE, BLACK from ..krux_settings import t from ..settings import THIN_SPACE @@ -400,14 +400,14 @@ def save_qr_image_menu(self): ) qr_menu = [] qr_menu.append( - ( + MenuItem( "%dx%d - PBM" % (size, size), lambda: self.save_pbm_image(suggested_file_name), ) ) for bmp_resolution in bmp_resolutions: qr_menu.append( - ( + MenuItem( "%dx%d - BMP" % (bmp_resolution, bmp_resolution), lambda res=bmp_resolution: self.save_bmp_image( suggested_file_name, res @@ -435,8 +435,10 @@ def display_qr(self, allow_export=False, transcript_tools=True, quick_exit=False label = self.title else: label = "" + if transcript_tools and self.ctx.input.touch is not None: label += "\n" + t("Swipe to change mode") + mode = 0 while True: button = None @@ -471,22 +473,26 @@ def toggle_brightness(): self.lr_index %= self.qr_size elif mode in (REGION_MODE, ZOOMED_R_MODE): self.lr_index %= self.columns * self.columns + if quick_exit: return MENU_CONTINUE + printer_func = self.print_qr if self.has_printer() else None qr_menu = [ - (t("Return to QR Viewer"), lambda: None), - (t("Toggle Brightness"), toggle_brightness), - ( - t("Save QR Image to SD Card"), - ( - self.save_qr_image_menu - if allow_export and self.has_sd_card() - else None - ), - ), - (t("Print to QR"), printer_func), + MenuItem(t("Return to QR Viewer"), lambda: None), + MenuItem(t("Toggle Brightness"), toggle_brightness), + MenuItem(t("Print to QR"), printer_func), ] + + if allow_export: + qr_menu.insert( + 2, + MenuItemSD( + t("Save QR Image to SD Card"), + self.save_qr_image_menu, + ), + ) + submenu = Menu(self.ctx, qr_menu, back_label=t("Back to Menu")) _, status = submenu.run_loop() if status == MENU_EXIT: diff --git a/src/krux/pages/screensaver.py b/src/krux/pages/screensaver.py index 0397933d..73f20c4f 100644 --- a/src/krux/pages/screensaver.py +++ b/src/krux/pages/screensaver.py @@ -20,42 +20,35 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# from . import Page -from ..display import SPLASH, FONT_HEIGHT, TOTAL_LINES -from ..themes import theme - -class ScreenSaver: - """Screensaver animation method""" - - def __init__(self, ctx): - self.ctx = ctx - - def start(self): - """Displays a screensaver until user presses a button or touch""" - anim_frame = 0 - initial_offset = (TOTAL_LINES - len(SPLASH)) // 2 - fg_color = theme.fg_color - bg_color = theme.bg_color - self.ctx.display.clear() - button_press = None - while button_press is None: - # show animation on the screeen - offset_y = anim_frame * FONT_HEIGHT - self.ctx.display.fill_rectangle( - 0, - offset_y, - self.ctx.display.width(), - FONT_HEIGHT, - bg_color, +def start_screen_saver(ctx): + """Displays a screensaver until user presses a button or touch""" + from ..display import SPLASH, FONT_HEIGHT, TOTAL_LINES + from ..themes import theme + + anim_frame = 0 + initial_offset = (TOTAL_LINES - len(SPLASH)) // 2 + fg_color = theme.fg_color + bg_color = theme.bg_color + ctx.display.clear() + button_press = None + while button_press is None: + # show animation on the screeen + offset_y = anim_frame * FONT_HEIGHT + ctx.display.fill_rectangle( + 0, + offset_y, + ctx.display.width(), + FONT_HEIGHT, + bg_color, + ) + if initial_offset <= anim_frame < len(SPLASH) + initial_offset: + ctx.display.draw_hcentered_text( + SPLASH[anim_frame - initial_offset], offset_y, fg_color, bg_color ) - if initial_offset <= anim_frame < len(SPLASH) + initial_offset: - self.ctx.display.draw_hcentered_text( - SPLASH[anim_frame - initial_offset], offset_y, fg_color, bg_color - ) - anim_frame += 1 - if anim_frame > len(SPLASH) + 2 * initial_offset: - anim_frame = 0 - bg_color, fg_color = fg_color, bg_color - # wait_duration(animation period) can be modified here - button_press = self.ctx.input.wait_for_button(block=False) + anim_frame += 1 + if anim_frame > len(SPLASH) + 2 * initial_offset: + anim_frame = 0 + bg_color, fg_color = fg_color, bg_color + # wait_duration(animation period) can be modified here + button_press = ctx.input.wait_for_button(block=False) diff --git a/src/krux/pages/settings_page.py b/src/krux/pages/settings_page.py index d9d143b9..f97fc5f9 100644 --- a/src/krux/pages/settings_page.py +++ b/src/krux/pages/settings_page.py @@ -47,6 +47,7 @@ from . import ( Page, Menu, + MenuItem, MENU_CONTINUE, MENU_EXIT, ESC_KEY, @@ -171,7 +172,7 @@ def handler(): setting_list = settings_namespace.setting_list() namespace_list = settings_namespace.namespace_list() items = [ - ( + MenuItem( settings_namespace.label(ns.namespace.split(".")[-1]), self.namespace(ns), ) @@ -179,7 +180,7 @@ def handler(): ] items.extend( [ - ( + MenuItem( settings_namespace.label(setting.attr), self.setting(settings_namespace, setting), ) @@ -190,17 +191,17 @@ def handler(): # If there is only one item in the namespace, don't show a submenu # and instead jump straight to the item's menu if len(items) == 1: - return items[0][1]() + return items[0].action() back_status = lambda: MENU_EXIT # pylint: disable=C3001 # Case for "Back" on the main Settings if settings_namespace.namespace == Settings.namespace: - items.append((t("Factory Settings"), self.restore_settings)) + items.append(MenuItem(t("Factory Settings"), self.restore_settings)) back_status = self._settings_exit_check submenu = Menu(self.ctx, items, back_status=back_status) index, status = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: return MENU_CONTINUE return status diff --git a/src/krux/pages/tools.py b/src/krux/pages/tools.py index dc1c5c1c..709d1349 100644 --- a/src/krux/pages/tools.py +++ b/src/krux/pages/tools.py @@ -28,6 +28,7 @@ from . import ( Page, Menu, + MenuItem, MENU_CONTINUE, ESC_KEY, LETTERS, @@ -48,12 +49,12 @@ def __init__(self, ctx): Menu( ctx, [ - (t("Check SD Card"), self.sd_check), - (t("Print Test QR"), self.print_test), - (t("Create QR Code"), self.create_qr), - (t("Descriptor Addresses"), self.descriptor_addresses), - (t("Remove Mnemonic"), self.rm_stored_mnemonic), - (t("Wipe Device"), self.wipe_device), + MenuItem(t("Check SD Card"), self.sd_check), + MenuItem(t("Print Test QR"), self.print_test), + MenuItem(t("Create QR Code"), self.create_qr), + MenuItem(t("Descriptor Addresses"), self.descriptor_addresses), + MenuItem(t("Remove Mnemonic"), self.rm_stored_mnemonic), + MenuItem(t("Wipe Device"), self.wipe_device), ], ), ) diff --git a/src/krux/pages/wallet_settings.py b/src/krux/pages/wallet_settings.py index e25dc2c7..3a50766b 100644 --- a/src/krux/pages/wallet_settings.py +++ b/src/krux/pages/wallet_settings.py @@ -27,6 +27,7 @@ from . import ( Page, Menu, + MenuItem, MENU_CONTINUE, MENU_EXIT, ESC_KEY, @@ -60,14 +61,15 @@ def load_passphrase_menu(self): submenu = Menu( self.ctx, [ - (t("Type BIP39 Passphrase"), self._load_passphrase), - (t("Scan BIP39 Passphrase"), self._load_qr_passphrase), + MenuItem(t("Type BIP39 Passphrase"), self._load_passphrase), + MenuItem(t("Scan BIP39 Passphrase"), self._load_qr_passphrase), ], disable_statusbar=True, ) _, passphrase = submenu.run_loop() if passphrase in (ESC_KEY, MENU_EXIT): return None + self.ctx.display.clear() if self.prompt( t("Passphrase") + ": " + passphrase, @@ -132,15 +134,15 @@ def customize_wallet(self, key): submenu = Menu( self.ctx, [ - (t("Network"), lambda: None), - ("Single/Multisig", lambda: None), - (t("Script Type"), (lambda: None) if not multisig else None), - (t("Account"), lambda: None), + MenuItem(t("Network"), lambda: None), + MenuItem("Single/Multisig", lambda: None), + MenuItem(t("Script Type"), lambda: None, lambda: not multisig), + MenuItem(t("Account"), lambda: None), ], offset=info_len * FONT_HEIGHT + DEFAULT_PADDING, ) index, _ = submenu.run_loop() - if index == len(submenu.menu) - 1: + if index == submenu.back_index: break if index == 0: network = self._coin_type() @@ -164,8 +166,8 @@ def _coin_type(self): submenu = Menu( self.ctx, [ - ("Mainnet", lambda: None), - ("Testnet", lambda: None), + MenuItem("Mainnet", lambda: None), + MenuItem("Testnet", lambda: None), ], disable_statusbar=True, back_label=None, @@ -178,8 +180,8 @@ def _multisig(self): submenu = Menu( self.ctx, [ - (t("Single-sig"), lambda: MENU_EXIT), - (t("Multisig"), lambda: MENU_EXIT), + MenuItem(t("Single-sig"), lambda: MENU_EXIT), + MenuItem(t("Multisig"), lambda: MENU_EXIT), ], disable_statusbar=True, back_label=None, @@ -192,10 +194,10 @@ def _script_type(self): submenu = Menu( self.ctx, [ - ("Legacy - 44", lambda: P2PKH), - ("Nested Segwit - 49", lambda: P2SH_P2WPKH), - ("Native Segwit - 84", lambda: P2WPKH), - ("Taproot - 86 (Experimental)", lambda: P2TR), + MenuItem("Legacy - 44", lambda: P2PKH), + MenuItem("Nested Segwit - 49", lambda: P2SH_P2WPKH), + MenuItem("Native Segwit - 84", lambda: P2WPKH), + MenuItem("Taproot - 86 (Experimental)", lambda: P2TR), ], disable_statusbar=True, back_label=None, diff --git a/src/krux/sd_card.py b/src/krux/sd_card.py index 8b52dbdd..5c4419eb 100644 --- a/src/krux/sd_card.py +++ b/src/krux/sd_card.py @@ -94,3 +94,13 @@ def file_exists(filename): return (os.stat(filename)[0] & 0x4000) == 0 except OSError: return False + + @staticmethod + def sd_card_available(): + """Return True if the device has a SD card inserted""" + try: + # Check for SD hot-plug + with SDHandler(): + return True + except: + return False diff --git a/tests/pages/home_pages/test_home.py b/tests/pages/home_pages/test_home.py index 35c0f3a2..84c41783 100644 --- a/tests/pages/home_pages/test_home.py +++ b/tests/pages/home_pages/test_home.py @@ -664,7 +664,8 @@ def test_sign_psbt(mocker, m5stickv, tdata): # case SD available if case[8] is not None: - mocker.patch.object(home, "has_sd_card", new=lambda: True) + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True mock_utils = mocker.patch("krux.pages.utils.Utils") mock_file = MockFile(case[2]) mocker.patch("builtins.open", mock_open(mock_file)) @@ -768,7 +769,8 @@ def test_psbt_warnings(mocker, m5stickv, tdata): mocker.spy(ctx.display, "draw_centered_text") # SD available - mocker.patch.object(home, "has_sd_card", new=lambda: True) + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True mock_utils = mocker.patch("krux.pages.utils.Utils") mock_utils.return_value.load_file.return_value = (PSBT_FILE_NAME, None) # Mock for reading from input file @@ -954,7 +956,8 @@ def test_sign_p2tr_zeroes_fingerprint(mocker, m5stickv, tdata): mocker.spy(ctx.display, "draw_centered_text") # SD available - mocker.patch.object(home, "has_sd_card", new=lambda: True) + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True mock_utils = mocker.patch("krux.pages.utils.Utils") mock_utils.return_value.load_file.return_value = (PSBT_FILE_NAME, None) # Mock for reading from input file diff --git a/tests/pages/home_pages/test_mnemonic_backup.py b/tests/pages/home_pages/test_mnemonic_backup.py index eee7af4d..73791ccb 100644 --- a/tests/pages/home_pages/test_mnemonic_backup.py +++ b/tests/pages/home_pages/test_mnemonic_backup.py @@ -254,7 +254,7 @@ def test_mnemonic_compact_qr(mocker, m5stickv, tdata): BUTTON_PAGE, # Move to Compact SeedQR BUTTON_ENTER, # Compact SeedQR BUTTON_ENTER, # Enter QR Menu - *([BUTTON_PAGE] * 3), # Move to Print + *([BUTTON_PAGE] * 2), # Move to Print BUTTON_ENTER, # Print BUTTON_ENTER, # Confirm Print BUTTON_ENTER, # Enter QR Menu again @@ -275,7 +275,7 @@ def test_mnemonic_compact_qr(mocker, m5stickv, tdata): BUTTON_PAGE, # Move to Compact SeedQR BUTTON_ENTER, # Compact SeedQR BUTTON_ENTER, # Enter QR Menu - *([BUTTON_PAGE] * 3), # Move to Print + *([BUTTON_PAGE] * 2), # Move to Print BUTTON_ENTER, # Print BUTTON_ENTER, # Confirm Print BUTTON_ENTER, # Enter QR Menu again @@ -296,7 +296,7 @@ def test_mnemonic_compact_qr(mocker, m5stickv, tdata): BUTTON_PAGE, # Move to Compact SeedQR BUTTON_ENTER, # Compact SeedQR BUTTON_ENTER, # Enter QR Menu - *([BUTTON_PAGE] * 3), # Move to Print + *([BUTTON_PAGE] * 2), # Move to Print BUTTON_ENTER, # Print BUTTON_PAGE, # Decline Print BUTTON_ENTER, # Enter QR Menu again @@ -317,7 +317,7 @@ def test_mnemonic_compact_qr(mocker, m5stickv, tdata): BUTTON_PAGE, # Move to Compact SeedQR BUTTON_ENTER, # Compact SeedQR BUTTON_ENTER, # Enter QR Menu - *([BUTTON_PAGE] * 3), # Move to Print + *([BUTTON_PAGE] * 2), # Move to Print BUTTON_ENTER, # Print BUTTON_PAGE, # Decline Print BUTTON_ENTER, # Enter QR Menu again diff --git a/tests/pages/home_pages/test_wallet_descriptor.py b/tests/pages/home_pages/test_wallet_descriptor.py index 2f43e6e5..2977d032 100644 --- a/tests/pages/home_pages/test_wallet_descriptor.py +++ b/tests/pages/home_pages/test_wallet_descriptor.py @@ -124,7 +124,7 @@ def test_wallet(mocker, m5stickv, tdata): None, [BUTTON_ENTER, BUTTON_ENTER, BUTTON_ENTER], ), - # 1 Load, from SD card, good data, accept + # 15 Load, from SD card, good data, accept ( False, tdata.SINGLESIG_12_WORD_KEY, @@ -144,7 +144,12 @@ def test_wallet(mocker, m5stickv, tdata): ctx = create_ctx(mocker, case[4], wallet, case[3]) wallet_descriptor = WalletDescriptor(ctx) - mocker.patch.object(wallet_descriptor, "has_sd_card", return_value=True) + + # case that uses SD card + if num == 15: + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True + mocker.patch.object( QRCodeCapture, "qr_capture_loop", new=lambda self: (case[2], FORMAT_PMOFN) ) diff --git a/tests/pages/test_menu.py b/tests/pages/test_menu.py index da1a0c50..135cc460 100644 --- a/tests/pages/test_menu.py +++ b/tests/pages/test_menu.py @@ -13,7 +13,7 @@ def test_init(mocker, m5stickv): def test_run_loop(mocker, m5stickv): - from krux.pages import Menu, MENU_CONTINUE, MENU_EXIT, MENU_SHUTDOWN + from krux.pages import Menu, MenuItem, MENU_CONTINUE, MENU_EXIT, MENU_SHUTDOWN from krux.input import BUTTON_ENTER, BUTTON_PAGE ctx = mock_context(mocker) @@ -24,10 +24,10 @@ def exception_raiser(): menu = Menu( ctx, [ - ("Option", lambda: MENU_CONTINUE), - ("Long Option", lambda: MENU_EXIT), - ("Longer Option", lambda: MENU_SHUTDOWN), - ("The Longest Option", exception_raiser), + MenuItem("Option", lambda: MENU_CONTINUE), + MenuItem("Long Option", lambda: MENU_EXIT), + MenuItem("Longer Option", lambda: MENU_SHUTDOWN), + MenuItem("The Longest Option", exception_raiser), ], back_label=None, ) @@ -64,7 +64,7 @@ def exception_raiser(): def test_run_loop_on_amigo_tft(mocker, amigo): - from krux.pages import Menu, MENU_CONTINUE, MENU_EXIT, MENU_SHUTDOWN + from krux.pages import Menu, MenuItem, MENU_CONTINUE, MENU_EXIT, MENU_SHUTDOWN from krux.input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV, BUTTON_TOUCH ctx = mock_context(mocker) @@ -75,10 +75,10 @@ def exception_raiser(): menu = Menu( ctx, [ - ("Option", lambda: MENU_CONTINUE), - ("Long Option", lambda: MENU_EXIT), - ("Longer Option", lambda: MENU_SHUTDOWN), - ("The Longest Option", exception_raiser), + MenuItem("Option", lambda: MENU_CONTINUE), + MenuItem("Long Option", lambda: MENU_EXIT), + MenuItem("Longer Option", lambda: MENU_SHUTDOWN), + MenuItem("The Longest Option", exception_raiser), ], back_label=None, ) diff --git a/tests/pages/test_qr_view.py b/tests/pages/test_qr_view.py index 1aa53496..887d326b 100644 --- a/tests/pages/test_qr_view.py +++ b/tests/pages/test_qr_view.py @@ -184,7 +184,8 @@ def test_save_qr_image_menu_pbm(amigo, mocker): ctx = create_ctx(mocker, BTN_SEQUENCE) data = TEST_DATA seed_qr_view = SeedQRView(ctx, data=data, title=TEST_TITLE) - mocker.patch.object(seed_qr_view, "has_sd_card", new=lambda: True) + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True mock_save_pbm_image = mocker.patch.object(seed_qr_view, "save_pbm_image") seed_qr_view.display_qr(allow_export=True) @@ -196,7 +197,7 @@ def test_save_qr_image_menu_pbm(amigo, mocker): ) # 10 is the max length for a suggested filename -def save_qr_image_menu_pbm(amigo, mocker): +def test_save_qr_image_menu_bmp(amigo, mocker): from krux.pages.qr_view import SeedQRView from krux.input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV @@ -205,25 +206,24 @@ def save_qr_image_menu_pbm(amigo, mocker): BUTTON_PAGE, BUTTON_PAGE, # Move to "Save QR image to SD card" BUTTON_ENTER, # Save QR image to SD card - BUTTON_PAGE_PREV, # On filename prompt, move to "Go" - BUTTON_ENTER, # Confirm BUTTON_PAGE, # Go to first resolution - BMP format BUTTON_ENTER, # Confirm first resolution - BMP format - BUTTON_ENTER, # Enter QR menu again - BUTTON_PAGE_PREV, # Move to "Back to Menu" + BUTTON_PAGE, + BUTTON_PAGE, # Move to "Back to Menu" BUTTON_ENTER, # Confirm ] ctx = create_ctx(mocker, BTN_SEQUENCE) data = TEST_DATA seed_qr_view = SeedQRView(ctx, data=data, title=TEST_TITLE) - mocker.patch.object(seed_qr_view, "has_sd_card", new=lambda: True) + mockSD = mocker.patch("krux.sd_card.SDHandler") + mockSD.return_value.sd_card_available.return_value = True mock_save_bmp_image = mocker.patch.object(seed_qr_view, "save_bmp_image") seed_qr_view.display_qr(allow_export=True) assert ctx.input.wait_for_button.call_count == len(BTN_SEQUENCE) - assert ctx.display.draw_qr_code.call_count == 2 # 1 for before, 1 for after saving + assert ctx.display.draw_qr_code.call_count == 1 mock_save_bmp_image.assert_called_once_with( - TEST_TITLE.replace(" ", "_")[:10] + TEST_TITLE.replace(" ", "_")[:10], 46 ) # 10 is the max length for a suggested filename diff --git a/tests/pages/test_screensaver.py b/tests/pages/test_screensaver.py index 7ed5aa03..5b498bde 100644 --- a/tests/pages/test_screensaver.py +++ b/tests/pages/test_screensaver.py @@ -6,7 +6,7 @@ def test_screensaver_m5stickv(m5stickv, mocker): from krux.themes import theme from krux.input import BUTTON_ENTER, QR_ANIM_PERIOD from krux.display import SPLASH - from krux.pages.screensaver import ScreenSaver + from krux.pages.screensaver import start_screen_saver import time SCREENSAVER_ANIMATION_TIME = QR_ANIM_PERIOD @@ -28,8 +28,7 @@ def test_screensaver_m5stickv(m5stickv, mocker): ctx = create_ctx(mocker, btn_seq) time.ticks_ms = mocker.MagicMock(side_effect=time_seq) - screen_saver = ScreenSaver(ctx) - screen_saver.start() + start_screen_saver(ctx) ctx.display.draw_hcentered_text.assert_any_call( SPLASH[10], 182, theme.fg_color, theme.bg_color