core: consider lockscreen to be a separate homescreen

this involves some changes to the workflow defaults:

* workflow.start_default() takes no arguments
* workflow.set_default() (originally replace_default) configures the
  default that will be started by next call to start_default().
  The intended usecase is to set_default() first and then start it
  separately.
* apps.base.set_homescreen() factors out the logic originally in
  main.py, that decides which homescreen should be launched. This uses
  set_default() call. start_default() is then used explicitly in main.py
pull/971/head
matejcik 4 years ago committed by matejcik
parent d73480bc9d
commit 09af8aed4e

@ -112,14 +112,33 @@ async def handle_Ping(ctx: wire.Context, msg: Ping) -> Success:
ALLOW_WHILE_LOCKED = (MessageType.Initialize, MessageType.GetFeatures) ALLOW_WHILE_LOCKED = (MessageType.Initialize, MessageType.GetFeatures)
def set_homescreen() -> None:
if not config.is_unlocked():
from apps.homescreen.lockscreen import lockscreen
workflow.set_default(lockscreen)
elif storage.recovery.is_in_progress():
from apps.management.recovery_device.homescreen import recovery_homescreen
workflow.set_default(recovery_homescreen)
else:
from apps.homescreen.homescreen import homescreen
workflow.set_default(homescreen)
def lock_device() -> None: def lock_device() -> None:
config.lock() config.lock()
set_homescreen()
wire.find_handler = get_pinlocked_handler wire.find_handler = get_pinlocked_handler
async def unlock_device(ctx: wire.GenericContext = wire.DUMMY_CONTEXT) -> None: async def unlock_device(ctx: wire.GenericContext = wire.DUMMY_CONTEXT) -> None:
await verify_user_pin(ctx) await verify_user_pin(ctx)
# verify_user_pin will raise if the PIN was invalid # verify_user_pin will raise if the PIN was invalid
set_homescreen()
wire.find_handler = wire.find_registered_workflow_handler wire.find_handler = wire.find_registered_workflow_handler

@ -6,10 +6,9 @@ if False:
class HomescreenBase(ui.Layout): class HomescreenBase(ui.Layout):
def __init__(self, lock_label = "Locked") -> None: def __init__(self) -> None:
self.repaint = True self.repaint = True
self.lock_label = lock_label
self.label = storage.device.get_label() or "My Trezor" self.label = storage.device.get_label() or "My Trezor"
self.image = storage.device.get_homescreen() or res.load( self.image = storage.device.get_homescreen() or res.load(
"apps/homescreen/res/bg.toif" "apps/homescreen/res/bg.toif"
@ -19,18 +18,6 @@ class HomescreenBase(ui.Layout):
ui.display.avatar(48, 48 - 10, self.image, ui.WHITE, ui.BLACK) 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) ui.display.text_center(ui.WIDTH // 2, 220, self.label, ui.BOLD, ui.FG, ui.BG)
def render_lock(self) -> None:
ui.display.bar_radius(40, 100, 160, 40, ui.TITLE_GREY, ui.BG, 4)
ui.display.bar_radius(42, 102, 156, 36, ui.BG, ui.TITLE_GREY, 4)
ui.display.text_center(
ui.WIDTH // 2, 128, self.lock_label, ui.BOLD, ui.TITLE_GREY, ui.BG
)
ui.display.text_center(
ui.WIDTH // 2 + 10, 220, "Tap to unlock", ui.BOLD, ui.TITLE_GREY, ui.BG
)
ui.display.icon(45, 202, res.load(ui.ICON_CLICK), ui.TITLE_GREY, ui.BG)
def dispatch(self, event: int, x: int, y: int) -> None: def dispatch(self, event: int, x: int, y: int) -> None:
if event is ui.RENDER and self.repaint: if event is ui.RENDER and self.repaint:
self.repaint = False self.repaint = False

@ -12,7 +12,7 @@ async def homescreen() -> None:
class Homescreen(HomescreenBase): class Homescreen(HomescreenBase):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
if config.is_unlocked() and not storage.is_initialized(): if not storage.is_initialized():
self.label = "Go to trezor.io/start" self.label = "Go to trezor.io/start"
def render_warning(self) -> None: def render_warning(self) -> None:
@ -30,5 +30,3 @@ class Homescreen(HomescreenBase):
def on_render(self) -> None: def on_render(self) -> None:
self.render_warning() self.render_warning()
self.render_homescreen() self.render_homescreen()
if not config.is_unlocked():
self.render_lock()

@ -1,9 +1,35 @@
from trezor import ui from trezor import res, ui
from . import HomescreenBase from . import HomescreenBase
async def lockscreen() -> None:
from apps.common.request_pin import can_lock_device
from apps.base import unlock_device
if can_lock_device():
await Lockscreen()
await unlock_device()
class Lockscreen(HomescreenBase): class Lockscreen(HomescreenBase):
def __init__(self, lock_label: str = "Locked") -> None:
self.lock_label = lock_label
super().__init__()
def render_lock(self) -> None:
ui.display.bar_radius(40, 100, 160, 40, ui.TITLE_GREY, ui.BG, 4)
ui.display.bar_radius(42, 102, 156, 36, ui.BG, ui.TITLE_GREY, 4)
ui.display.text_center(
ui.WIDTH // 2, 128, self.lock_label, ui.BOLD, ui.TITLE_GREY, ui.BG
)
ui.display.text_center(
ui.WIDTH // 2 + 10, 220, "Tap to unlock", ui.BOLD, ui.TITLE_GREY, ui.BG
)
ui.display.icon(45, 202, res.load(ui.ICON_CLICK), ui.TITLE_GREY, ui.BG)
def on_render(self) -> None: def on_render(self) -> None:
self.render_homescreen() self.render_homescreen()
self.render_lock() self.render_lock()

@ -63,7 +63,7 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
storage.recovery.set_in_progress(True) storage.recovery.set_in_progress(True)
storage.recovery.set_dry_run(bool(msg.dry_run)) storage.recovery.set_dry_run(bool(msg.dry_run))
workflow.replace_default(recovery_homescreen) workflow.set_default(recovery_homescreen)
return await recovery_process(ctx) return await recovery_process(ctx)

@ -24,7 +24,7 @@ if False:
async def recovery_homescreen() -> None: async def recovery_homescreen() -> None:
if not storage.recovery.is_in_progress(): if not storage.recovery.is_in_progress():
workflow.replace_default(homescreen) workflow.set_default(homescreen)
return return
# recovery process does not communicate on the wire # recovery process does not communicate on the wire

@ -6,7 +6,6 @@ import boot # noqa: F401
# prepare the USB interfaces, but do not connect to the host yet # prepare the USB interfaces, but do not connect to the host yet
import usb import usb
import storage.recovery
from trezor import loop, utils, wire, workflow from trezor import loop, utils, wire, workflow
# start the USB # start the USB
@ -57,14 +56,8 @@ def _boot_apps() -> None:
apps.debug.boot() apps.debug.boot()
# run main event loop and specify which screen is the default # run main event loop and specify which screen is the default
if storage.recovery.is_in_progress(): apps.base.set_homescreen()
from apps.management.recovery_device.homescreen import recovery_homescreen workflow.start_default()
workflow.start_default(recovery_homescreen)
else:
from apps.homescreen.homescreen import homescreen
workflow.start_default(homescreen)
# initialize the wire codec # initialize the wire codec

@ -42,7 +42,7 @@ def on_close(workflow: loop.Task) -> None:
if not tasks and default_constructor: if not tasks and default_constructor:
# If no workflows are running, we should create a new default workflow # If no workflows are running, we should create a new default workflow
# and run it. # and run it.
start_default(default_constructor) start_default()
if __debug__: if __debug__:
# In debug builds, we dump a memory info right after a workflow is # In debug builds, we dump a memory info right after a workflow is
# finished. # finished.
@ -50,18 +50,19 @@ def on_close(workflow: loop.Task) -> None:
micropython.mem_info() micropython.mem_info()
def start_default(constructor: Callable[[], loop.Task]) -> None: def start_default() -> None:
"""Start a default workflow, created from `constructor`. """Start a default workflow.
If a default task is already running, nothing will happen. Use `replace_default` Use `set_default` to set the default workflow constructor.
to set up a new default task for the next run. If a default task is already running, nothing will happen.
""" """
global default_task global default_task
global default_constructor global default_constructor
assert default_constructor is not None
if not default_task: if not default_task:
default_constructor = constructor default_task = default_constructor()
default_task = constructor()
if __debug__: if __debug__:
log.debug(__name__, "start default: %s", default_task) log.debug(__name__, "start default: %s", default_task)
# Schedule the default task. Because the task can complete on its own, # Schedule the default task. Because the task can complete on its own,
@ -72,7 +73,7 @@ def start_default(constructor: Callable[[], loop.Task]) -> None:
log.debug(__name__, "default already started") log.debug(__name__, "default already started")
def replace_default(constructor: Callable[[], loop.Task]) -> None: def set_default(constructor: Callable[[], loop.Task]) -> None:
"""Configure a default workflow, which will be started next time it is needed.""" """Configure a default workflow, which will be started next time it is needed."""
global default_constructor global default_constructor
if __debug__: if __debug__:
@ -111,7 +112,7 @@ def _finalize_default(task: loop.Task, value: Any) -> None:
# finalizer, so when this function finished, nothing will be running. # finalizer, so when this function finished, nothing will be running.
# We must schedule a new instance of the default now. # We must schedule a new instance of the default now.
if default_constructor is not None: if default_constructor is not None:
start_default(default_constructor) start_default()
else: else:
raise RuntimeError # no tasks and no default constructor raise RuntimeError # no tasks and no default constructor

Loading…
Cancel
Save