From 35042ff04ffc50e55ff59a385849442be4429d6b Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 19 Oct 2026 22:28:09 +0200 Subject: feat(ui): add the list, OTP detail, PIN entry, menu, info screens and glance List with live codes and countdowns, adaptive rows and lazy headers; OTP detail with ring or bar, next code and HOTP next; PIN keypad or button selector with create, unlock, change, lockout display and pending promotion; native menu; glance with the entry count; self-test after the first frame; option properties with defaults. Codes are computed one per timer event, apart from drawing, for the watchdog. A UI-test build (uitest.jungle) starts with fake entries, PIN 123456 and a frozen clock. Co-Authored-By: Claude Opus 5.5 --- source/app/OtpWalletApp.mc | 47 +++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) (limited to 'source/app/OtpWalletApp.mc') diff --git a/source/app/OtpWalletApp.mc b/source/app/OtpWalletApp.mc index 8858878..7419433 100644 --- a/source/app/OtpWalletApp.mc +++ b/source/app/OtpWalletApp.mc @@ -1,17 +1,19 @@ // Author: cirakg & claude ai -// Summary: Application entry point (Connect IQ AppBase): lifecycle and first view. +// Summary: Application entry point (Connect IQ AppBase): lifecycle, first view, glance, locking on exit. // -// OtpWalletApp: the application entry point (Connect IQ AppBase). +// Connect IQ lifecycle: initialize -> onStart -> getInitialView (or +// getGlanceView in glance mode) -> views run -> onStop. // -// M0 skeleton: the app shows a home screen with its name and version. Later -// milestones add the vault, settings processing, locking and the glance. +// The vault is opened in getInitialView, not onStart: onStart also runs in +// glance mode, which has too little memory for it. // -// Connect IQ lifecycle: initialize -> onStart -> getInitialView -> (views run) -// -> onStop. +// Security: onStop locks the vault (keys and decrypted codes are wiped). It +// runs when the user leaves the app, and also when Garmin's system passcode +// shuts running apps down. // -// Security note for later milestones: onStop is where every key and decrypted -// secret gets wiped. It runs when the user leaves the app, and also when -// Garmin's system passcode shuts running apps down. +// getGlanceView is (:glance), so the compiler checks this class against the +// glance scope too. The other methods never run in glance mode and use +// non-glance code, so they opt out with (:typecheck(disableGlanceCheck)). import Toybox.Application; import Toybox.Lang; @@ -19,22 +21,41 @@ import Toybox.WatchUi; class OtpWalletApp extends Application.AppBase { + // True once the full app (not the glance) has started. + private var _started as Boolean = false; + function initialize() { AppBase.initialize(); // Start of the start-up timer reported by `make perf` (debug builds only). Diag.markStart(); } - // Called after the app is loaded, before the first view is shown. function onStart(state as Dictionary?) as Void { } - // Called when the app exits, for any reason. + (:typecheck(disableGlanceCheck)) + // onStop also runs after the glance, where AppState was never loaded. function onStop(state as Dictionary?) as Void { + if (_started) { + AppState.lock(); + } } - // The first screen and its input handler. + // The list, or a full-screen message when the data can't be used. + (:typecheck(disableGlanceCheck)) function getInitialView() as [Views] or [Views, InputDelegates] { - return [new HomeView(), new HomeDelegate()]; + _started = true; + AppState.start(); + var error = AppState.startError; + if (error != null) { + return [new InfoView("OTP Wallet", [error], "error"), new InfoDelegate()]; + } + var list = new ListView(); + return [list, new ListDelegate(list)]; + } + + (:glance) + function getGlanceView() as [WatchUi.GlanceView] or [WatchUi.GlanceView, WatchUi.GlanceViewDelegate] or Null { + return [new OtpGlanceView()]; } } -- cgit v1.2.3