Wire up the phone/watch sync bridge (issue #276) - #281
Merged
Merged
Conversation
Step 4 of Wear OS phase 1: the phone now pushes its Next Up queue and playback position to a paired watch, and applies position reports back from it, both directions durable to either device being briefly unreachable. - QueueSyncPublisher (:app) debounces QueueRepository.observeQueue() and pushes snapshots via WearSyncClient; started from MainActivity.onCreate (not Application.onCreate, which also runs under every Robolectric-hosted unit test -- same reasoning as the existing feedRefreshScheduler wiring there). - PlaybackService's existing 5s position-save loop now also pushes to WearSyncClient alongside its local FeedRepository write. - PositionSyncApplier (:core) applies an incoming position update last-write-wins by timestamp; shared by both apps' manifest- registered WearableListenerService (WearSyncListenerService on :wear, PhoneSyncListenerService on :app), which wake the process even when it isn't already running. - WearQueueSyncApplier (:wear) applies an incoming queue snapshot into the watch's own local Room database (upserting the referenced Feed rows first, for the feed_items FK). - QueueRepository's download trigger is a no-op on the watch in phase 1 (NoOpQueueDownloadTrigger) -- streaming only, downloads are phase 2 (issue #277). Moved PlayServicesWearSyncClient (added in #280 under :wear) into :core along with its Hilt bindings, since :app now needs the same DataClient-backed WearSyncClient -- Hilt's per-app module discovery means defining it once in :core wires it into both apps' components for free, avoiding a duplicate copy in each. Verified: full assembleDebug/testDebugUnitTest/lintDebug across all three modules, installed and launched both debug builds with no crash. Did not verify a live phone<->watch sync round trip -- that needs the two emulators (or a real watch) actually paired, which is a separate setup from anything this PR touches.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 4 of #276 (see #278-280 for steps 1-3). The phone now pushes its Next Up queue and playback position to a paired watch, and applies position reports back from it, both directions durable to either device being briefly unreachable.
QueueSyncPublisher(:app) debouncesQueueRepository.observeQueue()and pushes snapshots viaWearSyncClient; started fromMainActivity.onCreate(notApplication.onCreate, which also runs under every Robolectric-hosted unit test — same reasoning as the existingfeedRefreshSchedulerwiring already there).PlaybackService's existing 5s position-save loop now also pushes toWearSyncClientalongside its localFeedRepositorywrite.PositionSyncApplier(:core) applies an incoming position update last-write-wins by timestamp; shared by both apps' manifest-registeredWearableListenerService(WearSyncListenerServiceon:wear,PhoneSyncListenerServiceon:app), which wake the process even when it isn't already running.WearQueueSyncApplier(:wear) applies an incoming queue snapshot into the watch's own local Room database (upserting the referencedFeedrows first, for thefeed_itemsFK).QueueRepository's download trigger is a no-op on the watch in phase 1 (NoOpQueueDownloadTrigger) — streaming only, downloads are phase 2 (Send downloaded episodes to Wear OS for offline playback (phase 2) #277).Moved
PlayServicesWearSyncClient(added in #280 under:wear) into:corealong with its Hilt bindings, since:appnow needs the sameDataClient-backedWearSyncClient— Hilt's per-app module discovery means defining it once in:corewires it into both apps' components for free, avoiding a duplicate copy in each.Hit and fixed a real bug along the way: an early version started the queue publisher from
Application.onCreate(), which caused every Robolectric-hosted:appunit test to spin up a background coroutine hitting a real database file and real Play Services. Moved the start call toMainActivity.onCreate()instead, matching the codebase's existing pattern for exactly this kind of startup work.Test plan
./gradlew assembleDebug testDebugUnitTest lintDebugpasses across:app/:core/:wearQueueSyncPublisherTest(debounce/coalescing, failure resilience — against a virtual-time-controlledFlowrather than Room's real-dispatcher-backed one, which proved unreliable to assert exact timing against),PositionSyncApplierTest(last-write-wins),WearQueueSyncApplierTest(Room upsert/replace behavior)mycasts_wear_testWear OS emulator) — confirmed no crash on either