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/Options.mc | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 source/app/Options.mc (limited to 'source/app/Options.mc') diff --git a/source/app/Options.mc b/source/app/Options.mc new file mode 100644 index 0000000..0b4715c --- /dev/null +++ b/source/app/Options.mc @@ -0,0 +1,78 @@ +// Author: cirakg & claude ai +// Summary: The user's options (README 10.2, Options group), read from app properties with safe defaults. +// +// The properties are defined in resources/settings/properties.xml (reading +// an undefined one stops the app with an uncatchable system error); M7 adds +// the phone form. A value of the wrong type or out of range falls back to +// the default below. + +import Toybox.Application; +import Toybox.Lang; + +class Options { + + static const CODE_NORMAL = 0; + static const CODE_LARGE = 1; + static const CODE_HUGE = 2; + + static const THEME_DARK = 0; + static const THEME_LIGHT = 1; + + // optAutoLock list values, in ms: Immediately, 15 s, 30 s, 1 min, 2 min, 5 min. + static const AUTO_LOCK_CHOICES = [0, 15000, 30000, 60000, 120000, 300000]; + static const AUTO_LOCK_DEFAULT = 3; + static const MAX_TIME_OFFSET_S = 120; + + var autoLockMs as Number = 60000; + var eraseAfterFailures as Boolean = false; + var timeOffset as Number = 0; + var codeSize as Number = CODE_LARGE; + var theme as Number = THEME_DARK; + var showGroups as Boolean = true; + var encryptNames as Boolean = false; + var glanceCode as Boolean = false; + var fullBrightness as Boolean = true; + + function initialize() { + } + + // Read every option; anything missing or invalid keeps its default. + function load() as Void { + var autoLock = choice("optAutoLock", AUTO_LOCK_DEFAULT, AUTO_LOCK_CHOICES.size() - 1); + autoLockMs = AUTO_LOCK_CHOICES[autoLock] as Number; + eraseAfterFailures = flag("optWipe", false); + var offset = number("optTimeOffset"); + timeOffset = (offset != null && offset >= -MAX_TIME_OFFSET_S && offset <= MAX_TIME_OFFSET_S) ? offset : 0; + codeSize = choice("optCodeSize", CODE_LARGE, CODE_HUGE); + theme = choice("optTheme", THEME_DARK, THEME_LIGHT); + showGroups = flag("optGroups", true); + encryptNames = flag("optEncryptNames", false); + glanceCode = flag("optGlanceCode", false); + fullBrightness = flag("optBrightness", true); + } + + // A property, or null when it doesn't exist (before M7) or can't be read. + private function raw(key as String) as Application.Properties.ValueType or Null { + try { + return Application.Properties.getValue(key); + } catch (e instanceof Lang.Exception) { + return null; + } + } + + private function number(key as String) as Number? { + var v = raw(key); + return v instanceof Number ? v : null; + } + + // A list index in 0..max, or the default. + private function choice(key as String, defaultValue as Number, max as Number) as Number { + var v = number(key); + return (v != null && v >= 0 && v <= max) ? v : defaultValue; + } + + private function flag(key as String, defaultValue as Boolean) as Boolean { + var v = raw(key); + return v instanceof Boolean ? v : defaultValue; + } +} -- cgit v1.2.3