feat: self-contained Android push via the Subscribe stream #87
No reviewers
Labels
No labels
adr
android
area/calendar
area/design-system
area/i18n
area/jobs
area/offline
area/server
area/testing
bug
ci
duplicate
enhancement
help wanted
invalid
notifications
question
reliability
security
severity/low
severity/medium
tracking
web
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
eagraiclainne/app!87
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/self-contained-android-push"
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?
Closes #58. ADR-0030.
Why
Android push depended on two external things, both against system rule 3: a separate ntfy relay and a third-party UnifiedPush distributor app each member had to install. The main server already streams notifications (
NotificationService.Subscribe), and the web client already consumes it — Android didn't.What
InboxRepository.live()already heldSubscribeopen (to keep the inbox fresh); it now takes anonNotificationcallback. The signed-in shell passes one that raises each streamedNotificationas a system notification via the existingNotifier, on its own per-kind channel, keyed on uid.org.unifiedpush.android:connectordependency and its pin,PushRegistrar.kt,AppPushService.kt, the manifest service, and thepush_endpointstorage inSessionStore(incl. its sign-out/switch clearing).InboxPollWorker(15-min) as the closed-app fallback — its old guard (skip when a distributor endpoint exists) is gone, so it always runs when the app is closed.ApiClient.pushclient:PushService.SendTestdoes Web Push to subscribed devices, which Android no longer is. Settings copy now states the real delivery model.Transport
The existing Connect server-streaming RPC — stays proto-first (rule 4), typed Kotlin client already in
gen/android, no hand-rolled reader. No Web Push encryption needed: the app holds its own TLS+JWT connection, no relay to protect. (Alternatives — embedding ntfy, a raw SSE endpoint — rejected in the ADR.)Kept (out of scope)
internal/push, VAPID,push_subscription, thePushServiceRPCs — these still serve web Web Push, unchanged. Web is untouched. Per-kind preferences unchanged.Known interim regression (accepted, see ADR)
A self-held connection lives only while the app process does. Until the follow-up foreground service (#59), closed-app push falls back to the 15-minute poll. For a family install with the connector being dropped anyway, this is acceptable.
Deliberate surface divergence (rule 1)
Android's delivery model (self-held stream) now differs from web's (Web Push); Android loses the test-send button while web keeps it. Recorded in the ADR.
Follow-up
The ntfy k8s deployment loses its only consumer but is outside this issue's Scope list; tearing it down (manifest,
deploy-ntfy, CoreDNS rewrite) moves on its own.Verification
make checkgreen;:app:assembleDebug, app+data unit tests, andcompileDebugKotlinall pass. Live rendering while the app is open needs on-device confirmation against the live deploy (the one thing CI/make checkcan't cover).Stacking
Stacked for fast-forward on
feat/phone-nav-collapse(#86). Merge after the chain: #82 → #79 → #80 → #81 → #84 → #85 → #86 → this.🤖 Generated with Claude Code
Test report
Coverage: 27.6%
Updated by the check workflow · commit
6a7e3f1770Android test report
Updated by the android workflow · commit
6a7e3f17706a7e3f17704435e6575b