Skip to content

[TV] Add the Listening History screen - #5792

Merged
sztomek merged 6 commits into
mainfrom
feat/tv-listening-history
Aug 28, 2026
Merged

sztomek merged 6 commits into
mainfrom
feat/tv-listening-history

Conversation

@sztomek

@sztomek sztomek commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds the Listening History screen to the Android TV app, reaching parity with tvOS (UI/History/ListeningHistoryView.swift). The profile modal already rendered a "Listening History" button wired to {} with a TODO; this PR builds the screen and wires the action to open it.

  • New TvListeningHistoryScreen + TvListeningHistoryViewModel under tv/.../history/.
  • Data comes from the shared repositories layer via EpisodeManager.findPlaybackHistoryEpisodesFlow(), whose Room query matches tvOS exactly: last_playback_interaction_date > 0 ORDER BY last_playback_interaction_date DESC LIMIT 1000. It is observed reactively, so the list live-updates as playback interactions change (equivalent to tvOS listeningHistoryChanged/playbackTrackChanged/playbackEnded). No server-history fetch and no clear-history, matching tvOS.
  • Reuses the existing TV building blocks: TvEpisodeListItem, TvEpisodeActionsModal/TvEpisodeInfoModal, TvEmptyState, LoadingView, and the TvDetailOverlay + tvFocusInactiveWhen presentation pattern used by the other full-screen destinations.
  • New TvEpisodeActionContext.ListeningHistory (source listening_history). Episode actions omit Go to podcast, mirroring tvOS .other(showGoToPodcast: false).
  • Empty state uses new strings tv_history_empty_title / tv_history_empty_subtitle ("No listening history" / "Episodes you play will show up here"), matching the tvOS copy.

Analytics

  • Fires ListeningHistoryShownEvent (no properties) when the screen appears, matching tvOS listening_history_shown, using the same EventHorizon.track pattern as the other TV screens.
  • Episode-action events continue to flow from the shared managers, now attributed to the listening_history source via the new action context.

Fixes PCDROID-736 https://linear.app/a8c/issue/PCDROID-736/listening-history-screen

Testing Instructions

  1. Sign in on the TV app.
  2. Play (or partially play) a few episodes so they enter your listening history.
  3. Open the profile modal (top bar) and choose Listening History.
  4. The screen shows your played episodes, newest first; SELECT plays an episode and opens Now Playing; the ellipsis opens the actions modal (note there is no "Go to podcast" option).
  5. With no history, the empty state ("No listening history") is shown.
  6. Press Back to return to the tabs.

Screenshots or Screencast

Screen Analytics
Screenshot_20260827_140310 SCR-20260827-mnnn

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics. (listening_history_shown already exists in the schema for tvOS; this reuses it — Android already lists ListeningHistoryShownEvent.)

@dangermattic

dangermattic commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@claude

claude Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 4m 13s —— View job


Review

The screen itself is a faithful copy of the Up Next pattern and the view model/tests mirror TvUpNextViewModel closely, so the data path looks right. The problem is where the overlay is mounted: unlike every other TvDetailOverlay, this one is a sibling of TvScaffoldContent rather than tab content, which breaks two invariants the component relies on — tab switches no longer dispose it, and no one restores focus when it closes. Both are reachable from the flows described in the testing instructions.

Blocking

  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt:128 — playing an episode (SELECT or Play in the actions modal) calls openNowPlaying(), which only changes the selected tab, so the history overlay stays on top of Now Playing until the user presses Back; clear isListeningHistoryVisible in the scaffold's openNowPlaying lambda. (thread)
  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt:130 — no onHide, so nothing hands D-pad focus back after Back; pass onHide = { focusTopBar() } as the other call sites do. (thread)

Non-blocking

  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/history/TvListeningHistoryScreen.kt:83 — the empty state has no focusable element while the layer behind is canFocus = false, leaving the D-pad dead until Back; confirm that's intended. (thread)
    • branch feat/tv-listening-history

Comment thread tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt
Comment thread tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt Outdated
@sztomek
sztomek force-pushed the feat/tv-listening-history branch from 58c9dfd to b46fa11 Compare August 27, 2026 11:45
@sztomek
sztomek changed the base branch from main to feat/tv-starred-episodes August 27, 2026 11:45
@sztomek sztomek added [Type] Feature Adding a new feature. [Area] TV labels Aug 27, 2026
@sztomek
sztomek marked this pull request as ready for review August 27, 2026 12:40
@sztomek
sztomek requested a review from a team as a code owner August 27, 2026 12:40
@sztomek
sztomek requested review from geekygecko and removed request for a team August 27, 2026 12:40
@claude

claude Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 1m 3s —— View job


Review

No new commits since my last review (b46fa11 is still HEAD), and both blocking findings are still open. One correction to my earlier framing: the starred overlay at TvScaffold.kt:130 has the same shape — sibling of TvScaffoldContent, no onHide, not cleared by openNowPlaying — so this isn't unique to the history overlay, it comes from the base branch. The fix is still needed here; worth applying to both while you're in the file.

Blocking

  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt:65 — still open: openNowPlaying doesn't clear isListeningHistoryVisible, so playing an episode leaves the history overlay on top of Now Playing. (thread) Fix this →
  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/home/TvScaffold.kt:137 — still open: no onHide, so nothing restores D-pad focus after Back; pass onHide = { focusTopBar() }. (thread) Fix this →

Non-blocking

  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/history/TvListeningHistoryScreen.kt:84 — still open: empty state has no focusable element while the layer behind is canFocus = false, so the D-pad is dead until Back. (thread)

--- • branch feat/tv-listening-history

@sztomek
sztomek force-pushed the feat/tv-listening-history branch from b46fa11 to bee11b6 Compare August 27, 2026 13:18
@claude

claude Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 58s —— View job


Review

bee11b6 closes both blocking findings: openNowPlaying now clears isListeningHistoryVisible (TvScaffold.kt:70) and the overlay passes onHide = focusTopBar (TvScaffold.kt:143), matching the starred overlay that the base branch has since fixed the same way. Nothing new in the diff since my last review.

Non-blocking

  • tv/src/main/java/au/com/shiftyjelly/pocketcasts/history/TvListeningHistoryScreen.kt:83 — still open: the empty state has no focusable element while the layer behind is canFocus = false, so the D-pad is dead until Back. Same shape as TvStarredScreen.kt:190, so it's a consistency question rather than a regression. (thread)
    • branch feat/tv-listening-history

@sztomek
sztomek force-pushed the feat/tv-listening-history branch from bee11b6 to f4d55dc Compare August 27, 2026 19:33
@claude

claude Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 45s —— View job


Review

This push is a rebase onto the updated feat/tv-starred-episodes — the diff content is identical to what I reviewed at bee11b6 (now f4d55dc), with both blocking fixes still in place (TvScaffold.kt:70, TvScaffold.kt:141). Nothing new to review and nothing open.
• branch feat/tv-listening-history

@sztomek
sztomek force-pushed the feat/tv-listening-history branch from f4d55dc to edf5b84 Compare August 28, 2026 06:45
Base automatically changed from feat/tv-starred-episodes to main August 28, 2026 09:43
@sztomek
sztomek force-pushed the feat/tv-listening-history branch from edf5b84 to f48f27c Compare August 28, 2026 09:47
@claude

claude Bot commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @sztomek's task in 52s —— View job


Review

The only change since my last review is f48f27c, which moves the focus hand-back from onHide into onBack on the listening-history overlay (TvScaffold.kt:144). That's correct: TvDetailOverlay fires onHide on any hide, including the openNowPlaying path that clears isListeningHistoryVisible, so the old wiring would have requested top-bar focus while Now Playing was taking it; onBack only fires on Back. It also matches the starred overlay now in main. Nothing new open.
• branch feat/tv-listening-history •

@sztomek
sztomek merged commit 837e958 into main Aug 28, 2026
21 checks passed
@sztomek
sztomek deleted the feat/tv-listening-history branch August 28, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants