fix: Halt playback for the data warning and respect it on stream retry - #5533
fix: Halt playback for the data warning and respect it on stream retry#5533joashrajin wants to merge 10 commits into
Conversation
Restores the return removed in #5017 so the warn before using data notification pauses playback until the user confirms, stops the missing-download stream retry from forcing past the warning, and skips the warning while Android Auto is connected since it is only visible on the phone. Fixes PCDROID-647, addresses PCDROID-397
|
Claude finished @joashrajin's task in 3m 44s —— View job Review: PR #5533 — Halt playback for the data warning and respect it on stream retry
Nice, well-scoped fix with a clear write-up. The core logic is correct: restoring the 🟠 Condition ordering makes the Android Auto flow check run on the hot path (
|
|
Claude finished @joashrajin's task in 2m 1s —— View job Reviewing PR #5533
SummaryThe fix is correct and well-scoped. Restoring the I do have one performance / ordering concern worth addressing before merge, plus a couple of minor notes. 🟠 Reorder the Android Auto check to the end of the
|
Moves the CarConnection query to the end of the warning condition so it only runs when a warning would actually fire, and wraps it in withTimeoutOrNull so a slow or missing car connection state falls back to showing the warning instead of stalling playback.
|
Claude finished @joashrajin's task in 1m 6s —— View job Re-review of PR #5533 (after latest push)
The latest commit ( ✅ Ordering fixed (
|
| CarConnection state | withTimeoutOrNull { … } |
!= true |
Result |
|---|---|---|---|
Connected (PROJECTION) |
true |
false |
warning skipped — car keeps playing ✅ |
| Not connected | false |
true |
warning shown ✅ |
| Slow / never emits | null (timeout) |
true |
warning shown ✅ (no hang) |
This removes the "playback silently never starts if the LiveData is slow" risk from .first() and picks the correct fail-open default (show the warning) rather than silently streaming.
🟢 Minor / non-blocking (unchanged from before)
withTimeoutOrNullguards against a hang, but not against the flow throwing —.first()on an empty flow throwsNoSuchElementException, which propagates past the timeout wrapper. In practice androidxCarConnectionpostsCONNECTION_TYPE_NOT_CONNECTEDon query failure rather than completing empty, so this stays theoretical. Not worth changing.- The early
returnat:2022still (intentionally) skips thelastPlayedEpisodeUuidupdate below — matches the pre-Replace downloads code #5017 halt behavior. Correct. - Still no unit test on this branch. Understandable given the player/network/notification coupling, but since this is the second time the
returnregressed (first was Replace downloads code #5017), a small pureshouldWarnBeforeStreaming(...)helper + test would be a cheap regression guard for a follow-up. Optional.
The CHANGELOG.md entry and the missing-file retry change (:1339, dropping forceStream = true) are both correct. The manual Android Auto / missing-file on-device verification you flagged as TODO is the only thing left to confirm before merge.
Nice iteration. 👍
• Branch: pcdroid-647-android-autoplay-streams-undownloaded-phantom-deleted-up
There was a problem hiding this comment.
Pull request overview
This PR fixes playback continuing to stream over mobile data without honoring the “Warn before using data” setting, particularly for Up Next auto-advance and “missing downloaded file” fallback streaming. It updates the central playback flow in PlaybackManager and documents the user-facing fix in the changelog.
Changes:
- Restore the early
returnin the metered-data warning path so playback halts until the user confirms streaming. - Ensure “missing downloaded file” retry no longer forces streaming, so it re-enters the same warning logic.
- Bypass the warning halt when Android Auto is connected (since the confirmation notification is on the phone).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackManager.kt | Re-introduces the halt/return in the metered-data warning path, removes forced-stream on missing-file retry, and skips the halt when Android Auto is connected. |
| CHANGELOG.md | Adds an 8.17 bug-fix entry describing the restored “Warn before using data” behavior for auto-advance and missing-file retry. |
Comments suppressed due to low confidence (1)
modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackManager.kt:1999
Util.isAndroidAutoConnectedFlow(application).first()is currently evaluated before cheap checks likewarnOnMeteredNetwork,isUnmeteredConnection, and evenplay. Becausefirst()suspends and allocates a newCarConnection, this can add unnecessary latency/overhead on every not-downloaded episode load (including Wi‑Fi / warning-off cases where the branch will never run). Reorder the conditions so the Android Auto Flow is only queried when all other warning prerequisites are already satisfied.
episodeSubscription?.dispose()
if (!episode.isDownloaded) {
if (!Util.isCarUiMode(application) &&
!Util.isWearOs(application) &&
// The watch handles these warnings before this is called
settings.warnOnMeteredNetwork.value &&
…n is needed checkForNotificationPermission never invoked onPermissionGranted on Android 12 and below, and dropped it when the Android 13+ permission dialog was granted. Both cases left the data warning notification unposted while playback halted waiting for confirmation.
|
Claude finished @joashrajin's task in 1m 42s —— View job Re-review of PR #5533 (after latest push)
The latest push adds a ✅ Why the
|
|
Claude finished @joashrajin's task in 1m 8s —— View job Re-review of PR #5533 (after latest push)
The two new commits close the notification-permission gaps flagged in the previous review. Both the pre-Android-13 path and the "callback intentionally dropped" documentation are now handled. This looks good to merge from my side. ✅ Pre-Android-13 path now fires the callback (
|
…utoplay-streams-undownloaded-phantom-deleted-up # Conflicts: # CHANGELOG.md
|
Claude finished @joashrajin's task in 51s —— View job Re-review of PR #5533 (after latest push)
The latest push is the merge of ✅
|
…utoplay-streams-undownloaded-phantom-deleted-up # Conflicts: # CHANGELOG.md # modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackManager.kt
|
Claude finished @joashrajin's task in 47s —— View job Re-review of PR #5533 (after latest push)
I re-checked the full diff ( ✅
|
|
Claude finished @geekygecko's task in 1m 27s —— View job Re-review of PR #5533 (after latest push)
This push adds the "Fix Android Auto check crash" commit ( ✅
|
| CarConnection state | inner result | withTimeoutOrNull |
!= true |
outcome |
|---|---|---|---|---|
Connected (PROJECTION) |
true |
true |
false |
warning skipped — car keeps playing ✅ |
| Not connected | false |
false |
true |
warning shown ✅ |
| Slow / never emits | (suspends) | null (timeout) |
true |
warning shown, no hang ✅ |
| Empty completion | null |
null |
true |
warning shown ✅ (no more throw) |
The check is still the last operand of the && chain, so the CarConnection/ContentProvider round-trip only runs in the narrow metered + warning-on + not-forced + play case — WiFi, warning-off, forceStream, and play = false preloads all short-circuit before it.
✅ Other callers unaffected
MediaSessionManager.kt:1269(onAddBookmark) collects onDispatchers.Mainalready — the extrawithContext(Dispatchers.Main)is a harmless no-op there.Media3SessionCallback.kt:330uses.first()inside its owntry/catchonDispatchers.Main— still fine, and now the construction is guaranteed on main regardless of caller.
🟢 Minor / non-blocking (carried over)
- The restored
return(:2075) intentionally skips thelastPlayedEpisodeUuidupdate — matches pre-Replace downloads code #5017 halt behavior. Correct. MainActivity.ktnotification-permission handling (pre-Android-13else, stored-then-reset callback, documented rationale-branch drop) is unchanged and still correct.- Still no regression test. Since the
returnhas now regressed twice (Replace downloads code #5017 and this bug), extracting a pureshouldWarnBeforeStreaming(...)helper for a cheap unit test remains a worthwhile follow-up — the crash fix reinforces this, since the warning path is now exercised more broadly.
The CHANGELOG.md entry is correct. The Android Auto / missing-file on-device verification flagged as TODO in the description remains the only outstanding item before merge. Nice iteration. 👍
• Branch: pcdroid-647-android-autoplay-streams-undownloaded-phantom-deleted-up
geekygecko
left a comment
There was a problem hiding this comment.
Thanks for making this fix.
| play | ||
| play && | ||
| // Don't block playback while driving as the warning is only shown on the phone | ||
| withTimeoutOrNull(CAR_CONNECTION_TIMEOUT) { Util.isAndroidAutoConnectedFlow(application).first() } != true |
There was a problem hiding this comment.
The app crashed when auto playing an episode.
java.lang.RuntimeException: Can't create handler inside thread Thread[DefaultDispatcher-worker-14,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:253)
at android.os.Handler.<init>(Handler.java:155)
at android.content.AsyncQueryHandler.<init>(AsyncQueryHandler.java:125)
at androidx.car.app.connection.CarConnectionTypeLiveData$CarConnectionQueryHandler.<init>(CarConnectionTypeLiveData.java:92)
at androidx.car.app.connection.CarConnectionTypeLiveData.<init>(CarConnectionTypeLiveData.java:59)
at androidx.car.app.connection.CarConnection.<init>(CarConnection.java:94)
at au.com.shiftyjelly.pocketcasts.utils.Util.isAndroidAutoConnectedFlow(Util.kt:21)
at au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager$loadCurrentEpisode$2.invokeSuspend(PlaybackManager.kt:2058)
I have merged some minor changes to fix this.
There's also a warning "Legacy Long overload can be converted to Duration" so I have changed CAR_CONNECTION_TIMEOUT to a Duration.
There was a problem hiding this comment.
Thank you so much Phil 🙇
…oaded-phantom-deleted-up
|
Claude finished @geekygecko's task in 1m 26s —— View job Re-review of PR #5533 (after latest push)
This push is the "Fix Android Auto check crash" commit ( ✅
|
| CarConnection state | inner result | withTimeoutOrNull |
!= true |
outcome |
|---|---|---|---|---|
Connected (PROJECTION) |
true |
true |
false |
warning skipped — car keeps playing ✅ |
| Not connected | false |
false |
true |
warning shown ✅ |
| Slow / never emits | (suspends) | null (timeout) |
true |
warning shown, no hang ✅ |
| Empty completion | null |
null |
true |
warning shown, no throw ✅ |
CAR_CONNECTION_TIMEOUT is now 1000.milliseconds (Duration) — resolves the "legacy Long overload can be converted to Duration" warning while keeping the same 1s budget. The check is still the last operand of the && chain, so the CarConnection/ContentProvider round-trip only runs in the narrow metered + warning-on + not-forced + play case; WiFi, warning-off, forceStream, and play = false preloads all short-circuit before it.
✅ Other callers unaffected
MediaSessionManager.kt:1269(onAddBookmark) already collects onDispatchers.Main; the addedwithContext(Dispatchers.Main)is a harmless no-op there.Media3SessionCallback.kt:330still uses.first()inside its owntry/catchonDispatchers.Main— construction is now guaranteed on main regardless of caller, and thetry/catchstill guards the empty-flow throw. Unchanged and fine.
🟢 Minor / non-blocking (carried over, no change needed)
- The restored
return(:2075) intentionally skips thelastPlayedEpisodeUuidupdate — matches pre-Replace downloads code #5017 halt behavior. MainActivity.ktnotification-permission handling (pre-Android-13else, stored-then-reset callback, documented rationale-branch drop) is unchanged and still correct.- Still no regression test. Since the
returnhas now regressed twice (Replace downloads code #5017 and this bug), a small pureshouldWarnBeforeStreaming(...)helper for a cheap unit test remains a worthwhile follow-up — the crash fix reinforces this, as the warning path is now exercised more broadly.
CI: Gradle Wrapper validation passed; the Claude Code Review run is still in progress. The CHANGELOG.md entry is correct. The Android Auto / missing-file on-device verification flagged as TODO in the description is the only outstanding item. Nice iteration. 👍
• Branch: pcdroid-647-android-autoplay-streams-undownloaded-phantom-deleted-up
|
Version |
|
Version |
Description
With Warn before using data ON and on a metered connection, two playback paths stream undownloaded episodes with no warning:
forceStream = true, bypassing the warning entirely.Root cause of path 1: #5017 removed the
returnat the end of the data-warning branch inPlaybackManager.loadCurrentEpisode()(the deletion sits inside theepisodeObservableif/else →whenrefactor hunk; the notification, its "Yes, keep playing" resume action, and theEMPTYstate push all survived, so the halt was clearly still intended). The branch still posts the "This episode is not downloaded, do you want to stream it?" notification, but execution now falls through andplay()starts anyway — since 8.10 the warning has been decorative for any non-forced play. This became much more visible after the setting became ON by default (PCDROID-534).Changes (all in
PlaybackManager.kt):returnin the data-warning branch ofloadCurrentEpisode(), so playback halts until the user taps Yes, keep playing on the notification (which resumes via the existingplayNow(forceStream = true)receiver path) or Play next downloaded.onPlayerError()no longer passesforceStream = true, so the retry goes through the same warning check. Behavior on unmetered networks, or with the warning off, is unchanged.Util.isAndroidAutoConnectedFlow), because the confirmation notification is only visible on the phone — restoring thereturnwithout this would bring back the "playback silently stops in the car" behavior reported in PCDROID-397. The setting's description already says the warning doesn't apply to Android Auto.Fixes PCDROID-647
Addresses PCDROID-397 (the halt-in-car symptom; keeping it open pending on-device Android Auto verification)
Testing Instructions
Preconditions: metered/mobile connection, Settings → Storage & data use → Warn before using data = ON, Autoplay ON.
Android/data/au.com.shiftyjelly.pocketcasts/files/PocketCasts/podcasts/(adb or file manager), then tap play on it while on mobile dataScreenshots or Screencast
Checklist
./gradlew spotlessApplyto automatically apply formatting/linting) (ranspotlessCheck— passed)loadCurrentEpisode's warning branch has no existing test harness and depends on player/network/notification state; behavior is covered by the manual steps above)modules/services/localization/src/main/res/values/strings.xml— n/a, no new stringsI have tested any UI changes...
n/a — no UI changes (existing notification/dialog behavior only)