docs/long-term-plans/android-background-sync-improvements.md
Status: Planned
The current implementation (branch claude/fix-android-reminder-sync-TdwQY) uses Android WorkManager to poll the SuperSync server every ~15 minutes. When it detects that a task was completed, deleted, or had its reminder cleared on another device, it cancels the stale Android notification. This works but has limitations.
This document outlines two improvements:
When the user opens the app on Android, the sync layer starts from scratch — it doesn't know what the background worker has already seen. The worker has been tracking lastServerSeq in SharedPreferences, but that knowledge is wasted on app start.
Expose the worker's cached state to the TypeScript layer so the app can skip already-processed operations or use the seq as a sync hint.
getLastSyncSeq(): number to AndroidInterfaceSyncService calls androidInterface.getLastSyncSeq() to get the worker's last-processed sequence numberBridge addition:
// android-interface.ts
getLastSyncSeq?(): number;
// JavaScriptInterface.kt
@JavascriptInterface
fun getLastSyncSeq(): Long {
return credentialStore.getLastServerSeq()
}
Sync layer integration point: wherever the initial sinceSeq is determined in SyncService or OperationApplierService, check for the Android hint first.
Trade-offs:
Recommendation: Start with Option A. It's simple, low-risk, and already covers the common case (user opens app after a short break). Option B adds latency savings of one network round-trip, which matters less on modern connections.
getLastSyncSeq() to AndroidInterface and JavaScriptInterfaceIS_ANDROID_WEB_VIEW and call getLastSyncSeq()sinceSeqlastServerSeq is keyed by baseUrl.hashCode(), so switching accounts resets to 0 automaticallyWorkManager's minimum periodic interval is 15 minutes. A user could complete a task on their desktop and still receive the reminder on their phone if it fires within that window.
Use Firebase Cloud Messaging (FCM) to push a lightweight signal from the SuperSync server when reminder-relevant operations occur. The Android app receives the push and immediately cancels the stale notification.
{ "type": "reminder_change", "seq": 12345 }FirebaseMessagingService receives the data messagelastServerSeq from SharedPreferenceslastServerSeq to the new seq using the existing SuperSyncBackgroundProviderSyncReminderWorkerlastServerSeqKeep the 15-minute WorkManager poll as a fallback. FCM delivery is best-effort — messages can be delayed or dropped by the OS (Doze mode, battery optimization). The worker ensures eventual consistency even if FCM fails.
FCM push (immediate, best-effort)
↓
Cancel notification
↓
WorkManager poll (15-min, guaranteed)
↓
Cancel any remaining stale notifications
SyncFirebaseMessagingService extending FirebaseMessagingServiceThe BackgroundSyncProvider interface already supports this. A Dropbox implementation would:
sync-data.json (~100KB+) via the Dropbox APIThis is heavier than SuperSync's operation-based API but workable for the ~15-minute poll interval. WebDAV would be similar.
Key difference: Dropbox/WebDAV providers would need to cache the previous state locally to compute diffs, adding storage overhead. SuperSync's seq-based pagination avoids this entirely.
DropboxBackgroundProvider implementing BackgroundSyncProviderWebDavBackgroundProvider implementing BackgroundSyncProviderSyncReminderWorker based on stored provider ID| Phase | Effort | Impact | Recommendation |
|---|---|---|---|
| Phase 1 (seq hint) | Small (~1 day) | Medium — faster app start | Do first |
| Phase 2 (FCM push) | Large (~1 week, needs server changes) | High — instant cancellation | Do when server supports it |
| Phase 3 (other providers) | Medium per provider | Medium — broader coverage | Do on demand |