Wire up the existing download button on the Series screen to download
all episodes, and add a per-season download button next to the season
tabs. Episode metadata is fetched in parallel for faster queuing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of just popping the EpisodeRoute (which could land on Home if
opened from ContinueWatching), pop and push a SeriesRoute so playback
exit consistently returns to the SeriesScreen.
Pop the EpisodeRoute from the navigation stack when playback starts,
so finishing PlayerActivity returns to the SeriesScreen instead of
the EpisodeScreen.
Wrap HomeContent with PullToRefreshBox to allow refreshing library,
continue watching, and next up sections by pulling down. Also fix
refreshHomeData to suspend until loading completes so the refresh
indicator dismisses properly.
Adds a MediaResumeButton to the Series screen action bar that directly
launches playback for the next unwatched episode. Shows "Resume" with
progress fill if partially watched, or "Play" for unwatched episodes.
Store the movie/episode title as UTF-8 bytes in DownloadRequest.data
when building each download request. PurefinDownloadService reads it
back in getForegroundNotification() so the notification shows the
actual title (e.g. "Inception") instead of the generic "Downloading".
For multiple simultaneous downloads the existing "Downloading N files"
text is kept. The title is persisted by Media3 alongside the request,
so it survives app restarts.
EpisodeScreen called viewModel.onDownloadClick() directly without first
requesting the POST_NOTIFICATIONS runtime permission. On Android 13+
(API 33+), foreground service notifications are suppressed without this
permission, so downloads ran silently with no notification shown.
Mirrors the existing permission pattern from MovieScreen: on Android 13+
the system dialog is shown before the first download; on denial the
download still proceeds (notification is nice-to-have).
- Add ActiveDownloadItem data class to represent a download in progress
- Add observeActiveDownloads() to MediaDownloadManager, polling the Media3
download index every 500ms on Dispatchers.IO for reliable real-time progress
(listener callbacks alone do not fire on every progress update)
- DownloadsViewModel exposes activeDownloads (StateFlow) and cancelDownload();
the completed downloads flow filters out items currently in progress
- DownloadsContent shows a "Downloading" section with thumbnail, title,
progress bar + percentage, and a cancel button above the completed grid
Capture the player position when the horizontal drag starts and compute
an absolute seek target instead of using relative seekBy, which could
drift or snap back due to playback advancing during the gesture.
D-pad left/right now seeks -10s/+10s both when controls are hidden
and when the seek bar is focused. Controls auto-focus the seek bar
when shown, removing the need to press OK first to interact.
- Add animated focus feedback (scale, border, background) to all interactive
TV UI components: PosterCard, GhostIconButton, MediaActionButton,
MediaResumeButton, PurefinIconButton, TvHomeDrawer nav items,
TvHomeSections cards, TvPlayerScreen track/queue panels
- Refactor EpisodeScreen, MovieScreen, and SeriesScreen from Scaffold +
verticalScroll to LazyColumn layout with Box-overlaid top bar
- Switch MovieComponents and MovieScreen from MovieUiModel to core Movie model
- Conditionally render cast sections only when cast is non-empty
The detectTapGestures and drag awaitEachGesture handlers competed for
pointer events, causing drag() to sometimes end prematurely and the
seek to not fire. A shared dragActive flag now gates tap/double-tap
callbacks so they are suppressed during active drags.
- Create InMemoryMediaRepository to hold in-memory media state (movies,
series, episodes), lazy series content loading, and watch progress updates
- InMemoryAppContentRepository delegates MediaRepository via Kotlin's by
delegation, injecting InMemoryMediaRepository as a composite component
- CompositeMediaRepository now uses InMemoryMediaRepository directly as the
online repository instead of AppContentRepository
Extract AppContentRepository interface from MediaRepository for home/library-specific features.
CompositeMediaRepository delegates to OfflineMediaRepository or AppContentRepository based on
UserSessionRepository.isOfflineMode, allowing content ViewModels to use a single MediaRepository
interface that automatically switches data source.
- Remove RoomMediaLocalDataSource dependency from InMemoryMediaRepository;
libraries, movies, series, and episodes are now held in MutableStateFlows
- observeSeriesWithContent derives from _series flow via map {}
- updateWatchProgress mutates _movies / _episodes in-memory
- ensureSeriesContentLoaded checks and updates _series in-memory
- Reorganize Room package: move entities/DAOs from local/room to room/entity and room/dao
- Add PlayerRoute to the Route sealed interface
- Refactor PlayerViewModel to expose loadMedia() for external callers
- Add onPlay() to EpisodeScreenViewModel and MovieScreenViewModel
- Wire play/resume buttons in TV episode and movie screens
- Create TvPlayerScreen with TV-optimized controls: seek bar, playback
buttons, track selection panels, queue panel, and state cards
- Register PlayerRoute in TvRouteEntryBuilder and TvNavigationModule
- Add media3-ui dependency to app-tv module
- Created MovieScreen, MovieComponents for movie detail view
- Created SeriesScreen, SeriesComponents for series detail with season selection
- Created EpisodeScreen, EpisodeComponents for episode detail view
- Copied all required common UI components from mobile app
- Updated TvNavigationModule with route entry builders
- Updated TvRouteEntryBuilder with new screen entries
- Added missing launcher resources for TV app build
- All screens reuse ViewModels from :feature:shared module
- TV app now supports browsing movies, series, and episodes
Creates a new :app-tv application module that reuses shared business
logic from :core:model, :core:data, :core:player, and :feature:shared,
with its own TV-specific UI layer.
- TvApplication + TvActivity with full Hilt + Navigation3 wiring
- TvHomePage reusing HomePageViewModel from :feature:shared
- Copied common UI components (PosterCard, WaitingScreen, etc.)
- Copied login screen and form components
- TV navigation module with Home + Login route entry builders
- Material3 theme copied from mobile (ready for TV customization)
- AndroidManifest with LAUNCHER + LEANBACK_LAUNCHER intent filters
- Excludes :feature:download (downloads are mobile-only)
Vertical drag gestures (brightness/volume) were not consuming pointer
events, causing detectTapGestures to fire onTap and toggle the player
overlay when the gesture ended.