diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-19 22:28:09 +0200 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-19 22:28:09 +0200 |
| commit | 35042ff04ffc50e55ff59a385849442be4429d6b (patch) | |
| tree | 3c142dd9bfb995b7aacdf39128e591dd3f9370de /source/util | |
| parent | 871aba901501302586895d266fe4569665d9fe70 (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'source/util')
| -rw-r--r-- | source/util/Clock.mc | 6 | ||||
| -rw-r--r-- | source/util/Diag.mc | 51 | ||||
| -rw-r--r-- | source/util/TestMode.mc | 30 |
3 files changed, 86 insertions, 1 deletions
diff --git a/source/util/Clock.mc b/source/util/Clock.mc index 4e2faf4..44a0e37 100644 --- a/source/util/Clock.mc +++ b/source/util/Clock.mc @@ -22,11 +22,17 @@ module Clock { // Current unix time (UTC seconds) plus the user's offset for clock skew. function nowUnix(offsetSeconds as Number) as Long { + if (TestMode.ON) { + return TestMode.NOW + offsetSeconds; + } return toUnsigned(Time.now().value()) + offsetSeconds; } // The trusted clock's unix time, or null when it isn't synced yet. function trustedUnix() as Long? { + if (TestMode.ON) { + return TestMode.NOW; + } try { return toUnsigned(Time.getCurrentTime({:currentTimeType => Time.CURRENT_TIME_RTC}).value()); } catch (e instanceof Time.RealTimeClockNotValidException) { diff --git a/source/util/Diag.mc b/source/util/Diag.mc index aea2a70..a09d3f8 100644 --- a/source/util/Diag.mc +++ b/source/util/Diag.mc @@ -1,5 +1,5 @@ // Author: cirakg & claude ai -// Summary: Debug-only diagnostics (start-up time, memory) for `make perf` and `make memtest`. +// Summary: Debug-only diagnostics: start-up time, memory, screen markers for UI scripts, and the performance overlay. // // Debug builds print one machine-readable line per event to the simulator // console: "DIAG", the event name, then key=value pairs, for example @@ -10,13 +10,21 @@ // (:release) versions instead, so a real watch running a release build never // logs anything. // +// UI scripts (docker/scripts/screenshots.sh) wait for "DIAG shown <screen>" +// lines before taking a screenshot, and read the PIN keypad's key positions +// from "DIAG pin_keys" to tap them. +// // Security rule: only timings and memory counters ever go through here, never // secrets, codes or entry names. This is the only file allowed to call // System.println outside tests (enforced by tools/lint_project.py). +import Toybox.Graphics; import Toybox.Lang; import Toybox.System; +// (:glance): the app object calls markStart() before the non-glance code is +// loaded (apps with a glance load the rest only for the full app). +(:glance) module Diag { // System.getTimer() value when the app object was created. @@ -47,4 +55,45 @@ module Diag { (:release) function firstFrame() as Void { } + + // A screen finished its first draw since it was shown. + (:debug) + function shown(screen as String) as Void { + System.println("DIAG shown " + screen); + } + + (:release) + function shown(screen as String) as Void { + } + + // Free-form key=value data for UI scripts (never secrets). + (:debug) + function data(event as String, values as String) as Void { + System.println("DIAG " + event + " " + values); + } + + (:release) + function data(event as String, values as String) as Void { + } + + // The performance overlay (toggled in the debug menu): draw time of the + // frame that started at startMs, and memory use. + (:debug) + var overlayOn as Boolean = false; + + (:debug) + function overlay(dc as Graphics.Dc, frameStartMs as Number) as Void { + if (!overlayOn) { + return; + } + var stats = System.getSystemStats(); + var text = (System.getTimer() - frameStartMs) + " ms " + (stats.usedMemory * 100 / stats.totalMemory) + "% mem"; + dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_BLACK); + dc.drawText(dc.getWidth() / 2, dc.getHeight() - dc.getFontHeight(Graphics.FONT_XTINY) - 2, Graphics.FONT_XTINY, + text, Graphics.TEXT_JUSTIFY_CENTER); + } + + (:release) + function overlay(dc as Graphics.Dc, frameStartMs as Number) as Void { + } } diff --git a/source/util/TestMode.mc b/source/util/TestMode.mc new file mode 100644 index 0000000..9b5c36f --- /dev/null +++ b/source/util/TestMode.mc @@ -0,0 +1,30 @@ +// Author: cirakg & claude ai +// Summary: Switch for the UI-test build: a frozen clock, a short auto-lock and a store reset to fake entries. +// +// Two versions of this module exist; each build keeps exactly one: +// * (:no_uitest), in every normal build (debug, release, unit tests): +// ON is false and nothing changes. +// * (:uitest), only with uitest.jungle (`make screenshots`): the clock is +// frozen at NOW, so codes and countdowns in screenshots never change; the +// store is wiped and filled with fake entries at every start +// (source/app/Fixtures.mc); auto-lock is short enough to test. +// The constant-condition checks on ON are resolved by the compiler, so the +// unused branch costs nothing. + +import Toybox.Lang; + +(:uitest) +module TestMode { + const ON = true; + // 2027-01-15 08:00:26 UTC: 4 s left in a 30 s period, so the detail + // screen shows the next code too. + const NOW = 1800000026l; + const AUTO_LOCK_MS = 10000; +} + +(:no_uitest) +module TestMode { + const ON = false; + const NOW = 0l; + const AUTO_LOCK_MS = 0; +} |
