diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
| commit | 773df56883b9961b607a1994920f5ef5299423b8 (patch) | |
| tree | e813e8f975e3cfa7a992222e5bf63ef615469916 /source | |
| parent | 2292bf700d28ade39cf2d64b45509e6db21034bc (diff) | |
feat(app): add minimal watch app with smoke test and per-size icons
The M0 placeholder app: AppBase entry point, home screen with name and
version, debug-only diagnostics (start-up time, memory) and a unit test
that runs in the headless simulator. Builds with strict type checking
and zero warnings for all 72 products; icons.jungle gives every product
a launcher icon of its exact size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source')
| -rw-r--r-- | source/app/AppInfo.mc | 16 | ||||
| -rw-r--r-- | source/app/OtpWalletApp.mc | 40 | ||||
| -rw-r--r-- | source/ui/HomeDelegate.mc | 16 | ||||
| -rw-r--r-- | source/ui/HomeView.mc | 42 | ||||
| -rw-r--r-- | source/util/Diag.mc | 50 |
5 files changed, 164 insertions, 0 deletions
diff --git a/source/app/AppInfo.mc b/source/app/AppInfo.mc new file mode 100644 index 0000000..5b2a586 --- /dev/null +++ b/source/app/AppInfo.mc @@ -0,0 +1,16 @@ +// Author: cirakg & claude ai +// Summary: Build-time facts about the app, such as its semantic version. +// +// AppInfo: build-time facts about the app. +// +// VERSION follows semantic versioning (README section 22). It is shown on the +// home screen now and in the About screen and settings later; keep it equal to +// the newest version in CHANGELOG.md once releases start. + +import Toybox.Lang; + +module AppInfo { + + // Semantic version of the app: MAJOR.MINOR.PATCH. + const VERSION = "0.1.0"; +} diff --git a/source/app/OtpWalletApp.mc b/source/app/OtpWalletApp.mc new file mode 100644 index 0000000..8858878 --- /dev/null +++ b/source/app/OtpWalletApp.mc @@ -0,0 +1,40 @@ +// Author: cirakg & claude ai +// Summary: Application entry point (Connect IQ AppBase): lifecycle and first view. +// +// OtpWalletApp: the application entry point (Connect IQ AppBase). +// +// M0 skeleton: the app shows a home screen with its name and version. Later +// milestones add the vault, settings processing, locking and the glance. +// +// Connect IQ lifecycle: initialize -> onStart -> getInitialView -> (views run) +// -> onStop. +// +// 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. + +import Toybox.Application; +import Toybox.Lang; +import Toybox.WatchUi; + +class OtpWalletApp extends Application.AppBase { + + 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. + function onStop(state as Dictionary?) as Void { + } + + // The first screen and its input handler. + function getInitialView() as [Views] or [Views, InputDelegates] { + return [new HomeView(), new HomeDelegate()]; + } +} diff --git a/source/ui/HomeDelegate.mc b/source/ui/HomeDelegate.mc new file mode 100644 index 0000000..fe23ae6 --- /dev/null +++ b/source/ui/HomeDelegate.mc @@ -0,0 +1,16 @@ +// Author: cirakg & claude ai +// Summary: Input handling for the home screen. +// +// HomeDelegate: input handling for the home screen. +// +// M0 has no actions yet. Inheriting BehaviorDelegate keeps the default +// behaviours, so BACK (button or swipe) leaves the app on every device. + +import Toybox.WatchUi; + +class HomeDelegate extends WatchUi.BehaviorDelegate { + + function initialize() { + BehaviorDelegate.initialize(); + } +} diff --git a/source/ui/HomeView.mc b/source/ui/HomeView.mc new file mode 100644 index 0000000..f73c783 --- /dev/null +++ b/source/ui/HomeView.mc @@ -0,0 +1,42 @@ +// Author: cirakg & claude ai +// Summary: M0 placeholder home screen showing the app name and version. +// +// HomeView: the M0 placeholder home screen. It draws the app name and version +// in the centre of the screen. Milestone M4 replaces it with the entry list. +// +// Performance note: everything that allocates (loading the name string, +// building the version text) happens once in initialize(), not in onUpdate(). + +import Toybox.Graphics; +import Toybox.Lang; +import Toybox.WatchUi; + +class HomeView extends WatchUi.View { + + private var _title as String; + private var _version as String; + // Set after the first complete draw, so start-up diagnostics run once. + private var _firstFrameDone as Boolean = false; + + function initialize() { + View.initialize(); + _title = WatchUi.loadResource(Rez.Strings.AppName) as String; + _version = "v" + AppInfo.VERSION; + } + + function onUpdate(dc as Graphics.Dc) as Void { + var centerX = dc.getWidth() / 2; + var centerY = dc.getHeight() / 2; + var justify = Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER; + + dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK); + dc.clear(); + dc.drawText(centerX, centerY, Graphics.FONT_MEDIUM, _title, justify); + dc.drawText(centerX, centerY + dc.getFontHeight(Graphics.FONT_MEDIUM), Graphics.FONT_XTINY, _version, justify); + + if (!_firstFrameDone) { + _firstFrameDone = true; + Diag.firstFrame(); + } + } +} diff --git a/source/util/Diag.mc b/source/util/Diag.mc new file mode 100644 index 0000000..aea2a70 --- /dev/null +++ b/source/util/Diag.mc @@ -0,0 +1,50 @@ +// Author: cirakg & claude ai +// Summary: Debug-only diagnostics (start-up time, memory) for `make perf` and `make memtest`. +// +// Debug builds print one machine-readable line per event to the simulator +// console: "DIAG", the event name, then key=value pairs, for example +// DIAG first_frame startup_ms=1 mem_used=6984 mem_total=781888 +// tools/check_budgets.py reads these lines and compares them with +// tools/budgets.json. Plain key=value pairs are used instead of JSON because +// the simulator console escapes quotes. Release builds compile the empty +// (:release) versions instead, so a real watch running a release build never +// logs anything. +// +// 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.Lang; +import Toybox.System; + +module Diag { + + // System.getTimer() value when the app object was created. + (:debug) + var startMs as Number = 0; + + // Start the start-up timer. Called from OtpWalletApp.initialize(). + (:debug) + function markStart() as Void { + startMs = System.getTimer(); + } + + (:release) + function markStart() as Void { + } + + // Report start-up time and memory use after the first complete frame. + // startup_ms covers app object creation to the end of the first draw. The + // time the system needs to load the program before that is invisible to + // the app; app-diag.sh measures it from outside (README section 18). + (:debug) + function firstFrame() as Void { + var stats = System.getSystemStats(); + System.println("DIAG first_frame startup_ms=" + (System.getTimer() - startMs) + + " mem_used=" + stats.usedMemory + " mem_total=" + stats.totalMemory); + } + + (:release) + function firstFrame() as Void { + } +} |
