Lists
Full-screen entry reminders for the Android app
Summary
Designed and shipped per-entry reminders for the Android WebView shell (PR #3, feat/android -> main), plus a profile page layout pass and a main-branch .gitignore update.
Design: settled on RFC 5545 RRULE for recurrence after discussion; prototyped three pickers and three reminder screens as a static HTML page (kept on branch prototype/reminders). The user picked picker B (full-page form, Save fixed at the bottom, no raw-RRULE section) and screen C (big-button grid with a queue), with Done needing a double-tap, snooze 10/30/60 min, and Dismiss.
Built: backend migration 008 adds wall-clock remind_at (TIMESTAMP via a wallclock.Time type) and remind_rule; PUT /api/items/{id}/reminder and GET /api/reminders with an RRULE subset check. Web: reminder.ts rule model, ReminderPage picker at /lists/:id/remind/:entryId, android.ts bridge sync (Android-only via window.Android), reminder line on entries. Android: ReminderBridge (host-checked), Reminders.kt single exact alarm with lib-recur, ReminderReceiver (rings, re-arms on boot/update/clock/zone), ReminderActivity full-screen screen with bundled Noto Sans Mono, and an Open list action that counts as Dismiss.
Fixed during on-device testing (OnePlus CPH2691, Android 16, wireless adb): missing adb reverse tunnel; exact-alarm access not granted (prompts now repeat once per launch); background activity start blocked on Android 15+ (now an invisible 1px overlay while opening, and the notification always rings with a full-screen intent); OxygenOS replacing the monospace font; repeat/count fields that could not be cleared; duplicate days in the When row.
Profile page: grouped into Account (email + Log out in one row) and Developer settings (intro, MCP endpoint with Copy, keys, new-key form). Pushed main with graphify-out/ ignored (e750011).
Verified: go test, node frontend/reminder.check.ts, gradle unit tests (4/4), backend end to end with a throwaway user, emulator and phone checks of exact alarms, unlocked and locked full screen, queue, double-tap Done, snooze, DND silence, Open list. Not verified: ringing after a reboot, Done reaching the server from a real device, and the final phone-width render of the profile page.
Decisions
- One reminder per entry; RRULE for recurrence; wall-clock times with no zone
- Server only stores reminders; the phone schedules one exact alarm for the soonest reminder or snooze
- Reminder UI exists only inside the Android app (window.Android); the web shows none of it
- Date-only reminders default to 08:00; completing an entry pauses its reminder
- Under Do Not Disturb the reminder screen shows silently
- Open list on the reminder screen counts as Dismiss, not Done
- Missed reminders while the phone is off are backlog
Learnings
- Since Android 15, SYSTEM_ALERT_WINDOW only exempts background activity starts while the app has a visible overlay window
- OxygenOS overrides Typeface.MONOSPACE in native views; bundle a font for consistent rendering
- adb reverse is per device and drops on every wireless-debugging reconnect
- Two concurrent session restores spend Goauth’s rotating refresh cookie and sign the browser out
- Saved reminders are aligned to their first real occurrence so web preview and lib-recur agree