Esempio n. 1
0
class HoldToConfirm(ui.Layout):
    DEFAULT_CONFIRM = "Hold To Confirm"
    DEFAULT_CONFIRM_STYLE = ButtonConfirm
    DEFAULT_LOADER_STYLE = LoaderDefault

    def __init__(
        self,
        content: ui.Component,
        confirm: str = DEFAULT_CONFIRM,
        confirm_style: ButtonStyleType = DEFAULT_CONFIRM_STYLE,
        loader_style: LoaderStyleType = DEFAULT_LOADER_STYLE,
    ):
        self.content = content

        self.loader = Loader(loader_style)
        self.loader.on_start = self._on_loader_start  # type: ignore

        self.button = Button(ui.grid(4, n_x=1), confirm, confirm_style)
        self.button.on_press_start = self._on_press_start  # type: ignore
        self.button.on_press_end = self._on_press_end  # type: ignore
        self.button.on_click = self._on_click  # type: ignore

    def _on_press_start(self) -> None:
        self.loader.start()

    def _on_press_end(self) -> None:
        self.loader.stop()

    def _on_loader_start(self) -> None:
        # Loader has either started growing, or returned to the 0-position.
        # In the first case we need to clear the content leftovers, in the latter
        # we need to render the content again.
        ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT - 58, ui.BG)
        self.content.dispatch(ui.REPAINT, 0, 0)

    def _on_click(self) -> None:
        if self.loader.elapsed_ms() >= self.loader.target_ms:
            self.on_confirm()

    def dispatch(self, event: int, x: int, y: int) -> None:
        if self.loader.start_ms is not None:
            self.loader.dispatch(event, x, y)
        else:
            self.content.dispatch(event, x, y)
        self.button.dispatch(event, x, y)

    def on_confirm(self) -> None:
        raise ui.Result(CONFIRMED)

    if __debug__:

        def read_content(self) -> List[str]:
            return self.content.read_content()

        def create_tasks(self) -> Tuple[loop.Task, ...]:
            return super().create_tasks() + (confirm_signal(), )
Esempio n. 2
0
class HoldToConfirm(ui.Layout):
    DEFAULT_CONFIRM = "Hold To Confirm"
    DEFAULT_CONFIRM_STYLE = ButtonConfirm
    DEFAULT_LOADER_STYLE = LoaderDefault

    def __init__(
        self,
        content,
        confirm=DEFAULT_CONFIRM,
        confirm_style=DEFAULT_CONFIRM_STYLE,
        loader_style=DEFAULT_LOADER_STYLE,
    ):
        self.content = content

        self.loader = Loader(loader_style)
        self.loader.on_start = self._on_loader_start

        self.button = Button(ui.grid(4, n_x=1), confirm, confirm_style)
        self.button.on_press_start = self._on_press_start
        self.button.on_press_end = self._on_press_end
        self.button.on_click = self._on_click

    def _on_press_start(self):
        self.loader.start()

    def _on_press_end(self):
        self.loader.stop()

    def _on_loader_start(self):
        # Loader has either started growing, or returned to the 0-position.
        # In the first case we need to clear the content leftovers, in the latter
        # we need to render the content again.
        ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT - 60, ui.BG)
        self.content.dispatch(ui.REPAINT, 0, 0)

    def _on_click(self):
        if self.loader.elapsed_ms() >= self.loader.target_ms:
            self.on_confirm()

    def dispatch(self, event, x, y):
        if self.loader.start_ms is not None:
            self.loader.dispatch(event, x, y)
        else:
            self.content.dispatch(event, x, y)
        self.button.dispatch(event, x, y)

    def on_confirm(self):
        raise ui.Result(CONFIRMED)
Esempio n. 3
0
class HoldToConfirmDialog(Widget):
    def __init__(
        self,
        content,
        hold="Hold to confirm",
        button_style=ui.BTN_CONFIRM,
        loader_style=ui.LDR_DEFAULT,
    ):
        self.content = content
        self.button = Button(ui.grid(4, n_x=1), hold, style=button_style)
        self.loader = Loader(style=loader_style)

        if content.__class__.__iter__ is not Widget.__iter__:
            raise TypeError(
                "HoldToConfirmDialog does not support widgets with custom event loop"
            )

    def taint(self):
        super().taint()
        self.button.taint()
        self.content.taint()

    def render(self):
        self.button.render()
        if not self.loader.is_active():
            self.content.render()

    def touch(self, event, pos):
        button = self.button
        was_active = button.state == BTN_ACTIVE
        button.touch(event, pos)
        is_active = button.state == BTN_ACTIVE
        if is_active and not was_active:
            ui.display.clear()
            self.loader.start()
            return _STARTED
        if was_active and not is_active:
            self.content.taint()
            if self.loader.stop():
                return CONFIRMED
            else:
                return _STOPPED

    async def __iter__(self):
        result = None
        while result is None or result < 0:  # _STARTED or _STOPPED
            if self.loader.is_active():
                if __debug__:
                    result = await loop.spawn(self.loader,
                                              super().__iter__(),
                                              confirm_signal)
                else:
                    result = await loop.spawn(self.loader, super().__iter__())
            else:
                if __debug__:
                    result = await loop.spawn(super().__iter__(),
                                              confirm_signal)
                else:
                    result = await super().__iter__()
        return result
Esempio n. 4
0
class HoldToConfirmDialog(Widget):
    def __init__(
        self,
        content,
        hold="Hold to confirm",
        button_style=ui.BTN_CONFIRM,
        loader_style=ui.LDR_DEFAULT,
    ):
        self.content = content
        self.button = Button(ui.grid(4, n_x=1), hold, style=button_style)
        self.loader = Loader(style=loader_style)

    def taint(self):
        super().taint()
        self.button.taint()
        self.content.taint()

    def render(self):
        self.button.render()

    def touch(self, event, pos):
        button = self.button
        was_active = button.state == BTN_ACTIVE
        button.touch(event, pos)
        is_active = button.state == BTN_ACTIVE
        if is_active and not was_active:
            ui.display.clear()
            self.loader.start()
            return _STARTED
        if was_active and not is_active:
            self.content.taint()
            if self.loader.stop():
                return CONFIRMED
            else:
                return _STOPPED

    async def __iter__(self):
        result = None
        while result is None or result < 0:  # _STARTED or _STOPPED
            if self.loader.is_active():
                content_loop = self.loader
            else:
                content_loop = self.content
            confirm_loop = super().__iter__()  # default loop (render on touch)
            if __debug__:
                result = await loop.spawn(content_loop, confirm_loop,
                                          confirm_signal)
            else:
                result = await loop.spawn(content_loop, confirm_loop)
        return result
Esempio n. 5
0
class HoldToConfirmDialog(Widget):
    def __init__(self, content, hold='Hold to confirm', *args, **kwargs):
        self.content = content
        self.button = Button(ui.grid(4, n_x=1),
                             hold,
                             normal_style=ui.BTN_CONFIRM,
                             active_style=ui.BTN_CONFIRM_ACTIVE)
        self.loader = Loader(*args, **kwargs)

    def render(self):
        self.button.render()

    def touch(self, event, pos):
        button = self.button
        was_started = button.state & BTN_STARTED and button.state & BTN_ACTIVE
        button.touch(event, pos)
        is_started = button.state & BTN_STARTED and button.state & BTN_ACTIVE
        if is_started and not was_started:
            ui.display.clear()
            self.loader.start()
            return _STARTED
        if was_started and not is_started:
            if self.loader.stop():
                return CONFIRMED
            else:
                return _STOPPED

    async def __iter__(self):
        result = None
        while result is None or result < 0:  # _STARTED or _STOPPED
            if self.loader.is_active():
                content_loop = self.loader
            else:
                content_loop = self.content
            confirm_loop = super().__iter__()  # default loop (render on touch)
            result = await loop.wait(content_loop, confirm_loop)
        return result
Esempio n. 6
0
class Homescreen(HomescreenBase):
    def __init__(self) -> None:
        super().__init__()
        if not storage.device.is_initialized():
            self.label = "Go to trezor.io/start"

        self.loader = Loader(
            style=LoaderNeutral,
            target_ms=_LOADER_TOTAL_MS - _LOADER_DELAY_MS,
            offset_y=-10,
            reverse_speedup=3,
        )
        self.touch_ms: Optional[int] = None

    def on_render(self) -> None:
        if not self.repaint:
            return

        # warning bar on top
        if storage.device.is_initialized() and storage.device.no_backup():
            ui.header_error("SEEDLESS")
        elif storage.device.is_initialized() and storage.device.unfinished_backup():
            ui.header_error("BACKUP FAILED!")
        elif storage.device.is_initialized() and storage.device.needs_backup():
            ui.header_warning("NEEDS BACKUP!")
        elif storage.device.is_initialized() and not config.has_pin():
            ui.header_warning("PIN NOT SET!")
        elif storage.device.get_experimental_features():
            ui.header_warning("EXPERIMENTAL MODE!")
        else:
            ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT, ui.BG)

        # homescreen with shifted avatar and text on bottom
        ui.display.avatar(48, 48 - 10, self.image, ui.WHITE, ui.BLACK)
        ui.display.text_center(ui.WIDTH // 2, 220, self.label, ui.BOLD, ui.FG, ui.BG)

        self.repaint = False

    def on_touch_start(self, _x: int, _y: int) -> None:
        if self.loader.start_ms is not None:
            self.loader.start()
        elif config.has_pin():
            self.touch_ms = utime.ticks_ms()

    def on_touch_end(self, _x: int, _y: int) -> None:
        if self.loader.start_ms is not None:
            self.repaint = True
        self.loader.stop()
        self.touch_ms = None

        # raise here instead of self.loader.on_finish so as not to send TOUCH_END to the lockscreen
        if self.loader.elapsed_ms() >= self.loader.target_ms:
            raise ui.Result(None)

    def _loader_start(self) -> None:
        ui.display.clear()
        ui.display.text_center(ui.WIDTH // 2, 35, "Hold to lock", ui.BOLD, ui.FG, ui.BG)
        self.loader.start()

    def dispatch(self, event: int, x: int, y: int) -> None:
        if (
            self.touch_ms is not None
            and self.touch_ms + _LOADER_DELAY_MS < utime.ticks_ms()
        ):
            self.touch_ms = None
            self._loader_start()

        if event is ui.RENDER and self.loader.start_ms is not None:
            self.loader.dispatch(event, x, y)
        else:
            super().dispatch(event, x, y)
Esempio n. 7
0
class HoldToConfirm(ui.Layout):
    DEFAULT_CONFIRM = "Hold to confirm"
    DEFAULT_CONFIRM_STYLE = ButtonConfirm
    DEFAULT_LOADER_STYLE = LoaderDefault

    def __init__(
        self,
        content: ui.Component,
        confirm: str = DEFAULT_CONFIRM,
        confirm_style: ButtonStyleType = DEFAULT_CONFIRM_STYLE,
        loader_style: LoaderStyleType = DEFAULT_LOADER_STYLE,
        cancel: bool = True,
    ):
        super().__init__()
        self.content = content

        self.loader = Loader(loader_style)
        self.loader.on_start = self._on_loader_start  # type: ignore

        if cancel:
            self.confirm = Button(ui.grid(17, n_x=4, cells_x=3), confirm,
                                  confirm_style)
        else:
            self.confirm = Button(ui.grid(4, n_x=1), confirm, confirm_style)
        self.confirm.on_press_start = self._on_press_start  # type: ignore
        self.confirm.on_press_end = self._on_press_end  # type: ignore
        self.confirm.on_click = self._on_click  # type: ignore

        self.cancel = None
        if cancel:
            self.cancel = Button(ui.grid(16, n_x=4), res.load(ui.ICON_CANCEL),
                                 ButtonAbort)
            self.cancel.on_click = self.on_cancel  # type: ignore

    def _on_press_start(self) -> None:
        self.loader.start()

    def _on_press_end(self) -> None:
        self.loader.stop()

    def _on_loader_start(self) -> None:
        # Loader has either started growing, or returned to the 0-position.
        # In the first case we need to clear the content leftovers, in the latter
        # we need to render the content again.
        ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT - 58, ui.BG)
        self.content.dispatch(ui.REPAINT, 0, 0)

    def _on_click(self) -> None:
        if self.loader.elapsed_ms() >= self.loader.target_ms:
            self.on_confirm()

    def dispatch(self, event: int, x: int, y: int) -> None:
        if self.loader.start_ms is not None:
            if utils.DISABLE_ANIMATION:
                self.on_confirm()
            self.loader.dispatch(event, x, y)
        else:
            self.content.dispatch(event, x, y)
        self.confirm.dispatch(event, x, y)
        if self.cancel:
            self.cancel.dispatch(event, x, y)

    def on_confirm(self) -> None:
        raise ui.Result(CONFIRMED)

    def on_cancel(self) -> None:
        raise ui.Result(CANCELLED)

    if __debug__:

        def read_content(self) -> list[str]:
            return self.content.read_content()

        def create_tasks(self) -> tuple[loop.Task, ...]:
            from apps.debug import confirm_signal

            return super().create_tasks() + (confirm_signal(), )
Esempio n. 8
0
class Homescreen(HomescreenBase):
    RENDER_INDICATOR = storage.cache.HOMESCREEN_ON

    def __init__(self) -> None:
        super().__init__()
        if not storage.device.is_initialized():
            self.label = "Go to trezor.io/start"

        self.loader = Loader(
            style=LoaderNeutral,
            target_ms=_LOADER_TOTAL_MS - _LOADER_DELAY_MS,
            offset_y=-10,
            reverse_speedup=3,
        )
        self.touch_ms: int | None = None

    def do_render(self) -> None:
        # warning bar on top
        if storage.device.is_initialized() and storage.device.no_backup():
            ui.header_error("SEEDLESS")
        elif storage.device.is_initialized(
        ) and storage.device.unfinished_backup():
            ui.header_error("BACKUP FAILED!")
        elif storage.device.is_initialized() and storage.device.needs_backup():
            ui.header_warning("NEEDS BACKUP!")
        elif storage.device.is_initialized() and not config.has_pin():
            ui.header_warning("PIN NOT SET!")
        elif storage.device.get_experimental_features():
            ui.header_warning("EXPERIMENTAL MODE!")
        else:
            ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT, ui.BG)

        # homescreen with shifted avatar and text on bottom
        # Differs for each model

        # TODO: support homescreen avatar change for R and 1
        if utils.MODEL in ("T", ):
            ui.display.avatar(48, 48 - 10, self.get_image(), ui.WHITE,
                              ui.BLACK)
        elif utils.MODEL in ("R", ):
            icon = "trezor/res/homescreen_model_r.toif"  # 92x92 px
            ui.display.icon(18, 18, ui.res.load(icon), ui.style.FG,
                            ui.style.BG)
        elif utils.MODEL in ("1", ):
            icon = "trezor/res/homescreen_model_1.toif"  # 64x36 px
            ui.display.icon(33, 14, ui.res.load(icon), ui.style.FG,
                            ui.style.BG)

        label_heights = {"1": 60, "R": 120, "T": 220}
        ui.display.text_center(ui.WIDTH // 2, label_heights[utils.MODEL],
                               self.label, ui.BOLD, ui.FG, ui.BG)

    def on_touch_start(self, _x: int, _y: int) -> None:
        if self.loader.start_ms is not None:
            self.loader.start()
        elif config.has_pin():
            self.touch_ms = utime.ticks_ms()

    def on_touch_end(self, _x: int, _y: int) -> None:
        if self.loader.start_ms is not None:
            self.set_repaint(True)
        self.loader.stop()
        self.touch_ms = None

        # raise here instead of self.loader.on_finish so as not to send TOUCH_END to the lockscreen
        if self.loader.elapsed_ms() >= self.loader.target_ms:
            raise ui.Result(None)

    def _loader_start(self) -> None:
        ui.display.clear()
        ui.display.text_center(ui.WIDTH // 2, 35, "Hold to lock", ui.BOLD,
                               ui.FG, ui.BG)
        self.loader.start()

    def dispatch(self, event: int, x: int, y: int) -> None:
        if (self.touch_ms is not None
                and self.touch_ms + _LOADER_DELAY_MS < utime.ticks_ms()):
            self.touch_ms = None
            self._loader_start()

        if event is ui.RENDER and self.loader.start_ms is not None:
            self.loader.dispatch(event, x, y)
        else:
            super().dispatch(event, x, y)