Add a native Android client that keeps working offline #6
Loading…
Reference in a new issue
No description provided.
Delete branch "android-app"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A Kotlin/Compose app in android/, talking to the same REST API the PWA
does, so a phone and a browser are interchangeable views of one instance.
The theme is a direct port of frontend/src/index.css — same colour tokens
and radii, DM Sans/DM Mono bundled as resources — and the icons are
redrawn from the same 24x24 paths the web components inline.
Offline is the reason this exists rather than the PWA. Reads cache as the
raw JSON each list endpoint returned: every screen here consumes whole
lists (GET /medications, /groups, /medications/history), so a keyed blob
cache buys what a relational store would without a second annotation
processor in the build. Writes the server never saw go to an outbox and
replay in order from a WorkManager job that survives the app closing.
Queued ops are also replayed optimistically over the cache, so a dose
logged with no signal moves the card out of Overdue immediately, and a
queued dose stores the instant it was taken rather than the instant it
uploads.
Adding a medication or group has no offline path on purpose — the server
mints the id every queued op refers to — and both say so rather than
failing quietly. Scanning and the assistant need the server for the same
reason it does: they call Claude through it.
Three things are absent by design, not oversight. Admin stays web-only.
OIDC sign-in would need a deep link the backend does not emit, since the
provider redirect lands on the web frontend; username/password works
alongside SSO as before. Web Push registers a browser endpoint an app
cannot claim on the user's behalf, so ntfy and SMS are the reminders
offered here, configured exactly as on the web.
Driven against a local backend in an emulator, which is what caught two
things worth naming: a bare host:port defaulted to HTTPS and so could
never reach a LAN address (private and loopback hosts now default to
HTTP, an explicit scheme always winning), and screens dropped the
optimistic overlay after a sync without re-fetching, briefly showing a
just-synced dose as still overdue.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com