Hand-written Kotlin behind ONE MethodChannel, never a plugin: AGP 9.0.1 /
Gradle 9.1 / Kotlin 2.3.20 is bleeding edge and a plugin that will not compile
breaks the build outright rather than degrading.
- AlarmScheduler: setAlarmClock() (Doze-exempt, no SCHEDULE_EXACT_ALARM
prompt), persists the time so boot and dismiss can re-arm.
- AlarmReceiver: hands straight to the service — a receiver gets ~10s and no
wakelock, so nothing that must outlive it belongs there.
- AlarmService: foreground service, PARTIAL_WAKE_LOCK, USAGE_ALARM
(= STREAM_ALARM, which is what ignores DND and silent mode), looping
ringtone, vibration, full-screen intent over the lockscreen.
- AlarmActivity: showWhenLocked + turnScreenOn, routes to the Dart ring
screen, back is a no-op so the alarm cannot be escaped.
- BootReceiver: re-arms on BOOT_COMPLETED; AlarmManager drops alarms across a
reboot.
- StepCounter: TYPE_STEP_COUNTER baseline for item 3's fallback proof, also
hand-written to keep plugin risk off this path.
Details that are a hard crash or a silent no-ring rather than a warning:
FLAG_IMMUTABLE on every PendingIntent (mandatory since API 31);
foregroundServiceType declared on the <service> AND passed to
startForeground(); explicit android:exported on every component;
USE_FULL_SCREEN_INTENT declared (missing from the plan's list) and
canUseFullScreenIntent() exposed so the Dart side can gate rather than warn.
Every permission verified present in android-36's api-versions.xml rather
than from recall. Compiles green with `flutter build apk --debug`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EckWmyfetYq3Mi8MAnUudh
All three were delegated to the Flutter Gradle plugin, so there was no floor
to reason about — and full-screen-intent and foreground-service semantics both
changed at API 34/35, which is exactly the code the alarm path needs.
- compileSdk/targetSdk = 36, matching the target phone.
- minSdk = 28: the floor for android:showWhenLocked / android:turnScreenOn as
plain manifest attributes (no legacy branch), which also clears
flutter_secure_storage's API 23 and Flutter's own default of 24.
Verified with `flutter build apk --debug` (exit 0).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EckWmyfetYq3Mi8MAnUudh
Groundwork for the alarm-hardening work. No behaviour change.
- .pre-commit-config.yaml pinned gatelock-v0.4.0 while pyproject.toml and
requirements.txt pinned v0.4.1, so pylint type-checked against a different
gatelock than production runs. All three now agree on v0.4.1.
- AGENTS.md claimed ALARM_DAYS is "Mon, Fri, Sat, Sun"; _constants.py:9 is
frozenset({0..6}), i.e. every day. Corrected.
- Complete the CLAUDE.md -> AGENTS.md symlink migration that was left
uncommitted in the working tree.
- DOCS-speaker-recommendation.md: item 7 of the hardening work. Prose only.
Researched from official manuals/vendor FAQs. The deciding spec turns out
to be the power switch, not the LED: _alarm.py already cuts plug mains on
every exit path, so a speaker needs a latching mechanical switch to come
back without a button press. Anything shipping a remote has a standby
microcontroller that a mains cycle cannot clear (confirmed by Edifier's own
support article), which disqualifies it.
- Drop the stale "TODO: Specify your own unique Application ID" template
comment; applicationId has been set to com.kuhy.wake_alarm_sync all along.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EckWmyfetYq3Mi8MAnUudh
wake-alarm had no APK build at all; releases were not produced. Adds a
standalone release-apk.yml plus the release signing config the app was
missing.
Signing follows the pattern proven in home_inventory: gradle reads
android/key.properties when present and falls back to the debug key when
absent, so local `flutter run --release` is unaffected. CI writes that
file from repository secrets and shreds it afterwards, and apksigner
fails the build closed if a debug-signed APK ever appears.
This repo has no Flutter analyze/test job, so the release job is not
gated on tests — a broken commit still publishes, with the build failing
as the only backstop. Add `needs:` if a Flutter CI job is added later.
versionCode is github.run_number, which is per-workflow-file — this file
must never be renamed, or the counter resets and updates stop installing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dzgcZxmpS3VtmLci2sbxj
pubspec.yaml's adaptive_icon_background is a separate literal from
python_pkg/app_icons' own style.BACKGROUND -- the icon regen updated
every generated PNG but missed this one, so the actual on-device home
screen icon (Android's adaptive icon, not the legacy mipmap) stayed on
the old cool charcoal. Caught on the physical device, not by file checks.
testsAndMisc's generator now syncs this automatically going forward.
Picks up gatelock's warm-tinted unified-design-system palette + the new
on_fill token. wake_alarm's own UI wasn't audited for the on-fill
contrast bug this session (out of the 3-pilot scope that found it in
diet-guard/screen-locker/dufs-cloud) -- worth a follow-up pass.
Verified: wake_alarm pytest 300/300 @ 100% coverage against the new pin.
Adopt the shared icon family (python_pkg/app_icons in testsAndMisc): a
charcoal #1B1D21 field carrying an orange clock glyph, orange being this
app's own theme seedColor.
The clock is deliberately drawn without bell "ears" - at launcher size they
read as noise rather than as an alarm clock.
Adds the adaptive-icon layers the project never had, including an Android 13
monochrome layer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New Flutter app (com.kuhy.wake_alarm_sync) that sets the phone alarm and
writes the wake time to a GitHub Gist for cross-device sync: Gist service,
GitHub device-flow auth, sync settings, home/settings screens. Source only;
build/.dart_tool/.idea artifacts excluded by the app's .gitignore.
Not yet gated by CI (wake-alarm CI is Python-only) and pending on-device
verification.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM