- 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.
ensureReady() had a race condition where concurrent callers from init
block and ViewModels would all run the loading logic simultaneously.
Added Mutex to ensure only the first caller loads; others await.
Also added a cooldown to refreshHomeData() since LifecycleResumeEffect
fires immediately on first composition, triggering a redundant reload
right after the initial load completes.
Switch seek parameters from CLOSEST_SYNC to PREVIOUS_SYNC so seeks always
land at or before the requested position, preventing subtitle cues from
being skipped. On backward seek discontinuity, briefly disable and re-enable
the text track to flush TextRenderer state so the current cue is re-delivered.
Removing the item key disables Compose's scroll anchoring, which was
shifting the scroll offset rightward whenever a refresh prepended new
items, making the list appear to start in the middle.
Implement media downloading with foreground notification showing progress
percentage and speed. Uses Media3's DownloadManager + SimpleCache with
OkHttp datasource for authenticated Jellyfin downloads. Downloaded movies
are saved to the offline database and the player reads from cache
automatically via CacheDataSource.
Replaced competing pointerInput modifiers with a single unified gesture handler that determines drag direction early. This eliminates conflicts between horizontal seeking and vertical brightness/volume gestures, making swipe interactions more reliable and predictable.
Subtitles were not showing the active cue after rewinding/seeking because
they were delivered externally and Media3 only picks up forward-starting
cues. Changed device profile to prefer EMBED delivery so subtitles stay
in the container where Media3's extraction-time parser handles them
correctly. Also added support for attaching external subtitle tracks to
MediaItem when embedding isn't possible.
Implements a toggle switch in the HomeScreen top bar (next to Menu) that allows users to switch between online mode (fetching from Jellyfin server) and offline mode (using local database only). The preference persists across app restarts via Proto DataStore.
Key changes:
- Added ActiveMediaRepository that delegates to online/offline repositories based on user preference
- Extended MediaRepository interface with continueWatching, nextUp, and latestLibraryContent
- Added isOfflineMode state to UserSession with reactive Flow
- Added Cloud/CloudOff icon toggle button to HomeTopBar
- Updated ViewModels to use MediaRepository interface for better abstraction