Offline Mode Implementation#4
Merged
Merged
Conversation
This commit introduces a manual offline mode, allowing users to disconnect from the server while retaining access to cached and downloaded content. It also enhances the authentication process to work seamlessly when offline.
When network connectivity is lost or when manual offline mode is enabled, the app now restricts navigation to "Home" and "Downloads," skips network-dependent data loading, and restores authentication from the local cache without trying to validate the token with the server.
**Key Changes:**
* **Offline Mode Toggle**:
* A new "Offline Mode" switch has been added to the **Settings -> General** section.
* This switch is automatically activated when network connectivity is lost and can be manually toggled by the user.
* The `PreferencesRepository` now stores and exposes the manual offline mode state.
* **Authentication & Data Loading**:
* `JellyfinAuthRepository` now checks for network connectivity before restoring authentication. If offline, it restores the user session from the local cache without making a network request, preventing login failures.
* `MainNavigationViewModel` and `AppDataRepository` now detect offline mode and skip initial server data loads (`loadInitialData`, `refreshServerInfo`) to provide a faster startup experience.
* **UI & Navigation**:
* A new "Downloads" destination has been added to the main navigation bar, providing access to downloaded media.
* When offline, the navigation bar is restricted to only show "Home" and "Downloads".
* The app will automatically navigate to the Home screen if it's on an unsupported screen when offline mode is activated.
* `SettingsViewModel` now observes network status and combines it with the manual toggle to determine the `effectiveOfflineMode`.
* **Code Refactoring**:
* Removed a significant number of KDoc comments from various files (`FieldSets.kt`, `PlaylistManager.kt`, `ServerDatabaseDao.kt`, etc.) to streamline the codebase.
This commit introduces a comprehensive media download system, allowing users to save movies and episodes for offline playback. The system manages the entire download lifecycle, including queueing, pausing, resuming, and cancelling. It also handles downloading associated metadata like images, subtitles, and trickplay files.
A new "Downloads" screen provides a central place to view and manage active and completed downloads.
### Key Changes:
* **Download Management (`JellyfinDownloadRepository`)**:
* A new `DownloadRepository` and its `JellyfinDownloadRepository` implementation manage download operations.
* Uses `WorkManager` to handle background downloading of media, images, subtitles, and trickplay files.
* Downloads are constrained to run on Wi-Fi by default (configurable) and require sufficient storage.
* **Database & Data Models**:
* A new `downloads` table is added to the database (version bumped to 10) to persist download state (`DownloadDto`).
* Data models for `DownloadInfo` and `DownloadStatus` are introduced.
* The `movies` and `episodes` tables are updated to include an `images` column to store local paths for downloaded artwork.
* **UI & UX**:
* **Downloads Screen**: A new screen with "Active" and "Completed" tabs to list and manage downloads. Users can pause, resume, cancel, or delete downloads from this screen.
* **Item Detail Screen**: A `DownloadProgressIndicator` component replaces the static download button, showing the current status (queued, downloading, paused, failed, completed) and progress. A quality selection dialog (`QualitySelectionDialog`) allows users to choose which version of a file to download.
* **Home Screen**: The Home screen now displays a "Downloaded" section for quick access to offline media.
* **Offline Playback**:
* The player now supports `file://` URIs, enabling playback of locally stored media.
* The app loads local subtitles for downloaded content.
* The item detail and player view models now attempt to load data from the local database first, falling back to the network, which improves offline usability.
* **Networking & System**:
* A dedicated `DownloadClient` for `OkHttpClient` is configured with longer timeouts suitable for large file downloads.
* `WorkManagerInitializer` is disabled to allow Hilt to manage worker creation.
This commit refactors the download UI and logic to provide a simpler, more compact download button and enables downloads directly from the episode detail overlay.
The previous expanded download progress indicator has been replaced with a single, unified `DownloadProgressIndicator` IconButton. This new component changes its icon and action based on the current download state (e.g., Download, In Progress, Queued, Delete), creating a more streamlined user experience.
**Key Changes:**
* **`DownloadProgressIndicator.kt`**:
* Replaced the complex `DownloadProgressExpanded` and `DownloadProgressCompact` composables with a single, simplified `IconButton`.
* The button's appearance now reflects the download status:
* **Default/Failed/Cancelled**: Shows a `Download` icon.
* **Downloading**: Displays a `CircularProgressIndicator` with a `Close` icon to cancel.
* **Queued**: Shows an `HourglassEmpty` icon.
* **Paused**: Shows a `CircularProgressIndicator` with a `Close` icon to resume (visual bug, should be resume icon).
* **Completed**: Presents a `Delete` icon to remove the download.
* The `onClick` action is now contextual, triggering download, cancel, or resume based on the current `DownloadStatus`.
* **`EpisodeDetailOverlay.kt`**:
* The new `DownloadProgressIndicator` has been integrated into the episode details bottom sheet.
* It's now possible to initiate, pause, resume, and cancel downloads for an individual episode directly from this overlay.
* **`ItemDetailViewModel.kt`**:
* `onDownloadClick()` and `onQualitySelected()` now prioritize the selected episode (`_selectedEpisode`) over the main series item (`_uiState.value.item`) when determining what to download. This ensures the correct item is downloaded when viewing an episode within a series.
* **`HomeScreen.kt` & `ItemDetailScreen.kt`**:
* Updated calls to `EpisodeDetailOverlay` to pass the necessary download-related callbacks and state.
This commit significantly improves the offline experience by enabling detailed views for cached media and ensuring that all relevant metadata and images are saved locally when an item is downloaded.
When a user navigates to the detail screen for a downloaded show or season while offline, the app now loads all associated seasons and episodes directly from the local database. This includes calculating the "next up" episode based on local watch history.
Additionally, the download process has been extended to save not just the media file, but also related metadata and images. This includes images for shows, seasons, and cast/crew (people), which are now stored locally and displayed in offline mode.
### Key Changes:
* **Offline Item Details (`ItemDetailViewModel.kt`):**
* When offline, the detail view now loads a show's seasons and a season's episodes directly from the local database.
* Introduced offline logic (`getNextEpisodeOffline`, `getNextEpisodeForSeasonOffline`) to determine the next episode to watch based on locally stored playback data.
* When selecting an episode from a list, the app now attempts to load its full details from the database as a fallback if the network request fails.
* The download status for a selected episode is now fetched and displayed.
* **Enhanced Media Downloading (`MediaDownloadWorker.kt`):**
* When an episode is downloaded, the worker now also saves the parent show and season metadata to the database if they don't already exist.
* New functions (`downloadShowImages`, `downloadSeasonImages`, `downloadPersonImages`) have been added to download and save local copies of all associated artwork (primary, backdrop, logo, and person images).
* **Database Schema & Migrations (`AfinityDatabase.kt`, `DatabaseMigrations.kt`):**
* Database version is bumped to 14.
* The `movies` and `shows` tables have been updated to store `genres`, `people`, and `tagline` information.
* The `shows` and `seasons` tables now include an `images` column to store paths to local and remote artwork.
* New `TypeConverter` for `AfinityPerson` list has been added.
* **Data Hydration (`DatabaseRepositoryImpl.kt`):**
* When loading items from the database, the repository now fully hydrates `AfinityShow` and `AfinitySeason` objects with their child episodes/seasons and rich metadata like genres and people.
* **UI (`HomeScreen.kt`, `ItemDetailScreen.kt`):**
* The Home screen now displays downloaded shows and movies in separate, clearly labeled sections ("Downloaded Shows", "Downloaded Movies").
* The quality selection dialog now correctly uses the media sources of the selected episode, not just the main item.
…available This commit enhances the download functionality by automatically starting the download process if a media item has only one available video quality. Previously, tapping the download button would always display a quality selection dialog. Now, the app first checks the number of available media sources. If only a single source exists, the download begins immediately, bypassing the dialog and streamlining the user experience. The quality selection dialog will only be shown if multiple quality options are available.
…ffline UI
This commit introduces a robust system for tracking and syncing offline playback progress. When network connectivity is unavailable, playback position is now saved locally. A new background worker, `UserDataSyncWorker`, is responsible for syncing this data back to the server once connectivity is restored.
The Home screen has been updated to display a "Continue Watching" section in offline mode, populated from locally stored progress data. Additionally, a dedicated "Downloads" management screen has been added to Settings, providing a centralized location to view and manage active and completed downloads.
### Key Changes:
* **Offline Playback Progress Sync:**
* **`JellyfinPlaybackRepository.kt`**: If reporting playback progress to the server fails (e.g., due to being offline), the progress is now saved locally to the database.
* **`UserDataSyncWorker.kt` & `UserDataSyncScheduler.kt`**: A new `WorkManager` worker and scheduler have been created. This worker periodically checks for unsynced local user data (playback position, favorites) and pushes it to the server when the network is available.
* **`PlaybackStateManager.kt`**: Now triggers the `UserDataSyncScheduler` to schedule a sync if reporting playback progress fails.
* **UI & UX Enhancements:**
* **`HomeScreen.kt` & `HomeViewModel.kt`**: The Home screen now displays a "Continue Watching" section in offline mode, showing locally downloaded movies and episodes with saved playback positions.
* **`DownloadSettingsScreen.kt` (New)**: A new comprehensive screen under **Settings -> General -> Downloads** has been added to manage all downloads. It shows network status, storage usage, and lists of active and completed downloads with controls to pause, resume, cancel, or delete.
* **`SettingsScreen.kt`**: The "General" section has been reorganized to include a navigation item for the new "Downloads" screen. The "Account" section has been removed, and the logout button is now located within the "General" section.
* **`AfinityTopAppBar.kt`**: Now displays a "Cloud Off" icon badge on the user profile picture when the app is in offline mode. The TopAppBar has also been refactored to accept a composable title, improving flexibility.
* **Offline Data Handling:**
* **`JellyfinMediaRepository.kt`**: Trickplay data is now loaded from local storage first for downloaded items, falling back to the API if not found locally.
* **`JellyfinSegmentsRepository.kt`**: Media segments (like intros/credits) are now cached in the database when fetched and can be loaded from the cache for offline use.
* **`MediaDownloadWorker.kt`**: The download process now also fetches and caches media segments for offline use.
* **`ItemDetailViewModel.kt`**: Now correctly handles cases where a downloaded season has no episodes cached, preventing crashes and showing an empty list.
* **Bug Fixes & Refinements:**
* **`TrickplayDownloadWorker.kt`**: Corrected the logic for downloading trickplay tiles. It now calculates the number of tiled images correctly and downloads them by `tileIndex` instead of `thumbnailCount`.
* **`ImageDownloadWorker.kt`**: The worker now intelligently skips downloading images if a local `file://` URI is already present, preventing redundant downloads.
This commit updates several key dependencies to their latest versions and increments the app version to `0.1.0-beta`. ### Key Updates: * **Android Gradle Plugin:** `8.13.0` -> `8.13.2` * **Kotlin:** `2.2.20` -> `2.2.21` * **Jetpack Compose BOM:** `2025.10.00` -> `2025.12.00` * **Jetpack Lifecycle:** `2.9.4` -> `2.10.0` * **Jetpack Navigation:** `2.9.5` -> `2.9.6` * **Jetpack Room:** `2.8.2` -> `2.8.4` * **Jetpack WorkManager:** `2.10.5` -> `2.11.0` * **Jetpack DataStore:** `1.1.7` -> `1.2.0` * **Jetpack Splashscreen:** `1.0.1` -> `1.2.0` * **OkHttp:** `5.2.1` -> `5.3.2` * **KSP:** `2.2.20-2.0.4` -> `2.3.3` The app's `versionName` has been changed from `0.0.9-alpha` to `0.1.0-beta`, and the `versionCode` has been updated from `8` to `9`.
This commit introduces a dedicated landscape layout for the item detail screen, providing a more immersive and optimized viewing experience on horizontally oriented devices.
When the device is in landscape mode, the screen now displays a blurred backdrop image with a gradient overlay. Key information, such as the item's logo or title, metadata, and action buttons, are presented on the left side, while the detailed content (overview, seasons, cast, etc.) scrolls on the right.
### Key Changes:
* **`ItemDetailScreen.kt`**:
* Detects the device orientation and switches between the existing `PortraitItemDetailContent` and the new `LandscapeItemDetailContent`.
* The landscape layout uses a full-screen blurred backdrop with a gradient mask for better legibility.
* Metadata, action buttons, and primary details are displayed in a fixed column, making them always accessible.
* A new `VideoQualitySelection` dropdown has been added to the landscape view, allowing users to select the media source for playback or download.
* Layout padding now accounts for status bars and display cutouts (notches) in landscape mode.
* **`HomeScreen.kt` & `HomeViewModel.kt`**:
* The episode detail overlay on the Home screen now supports download functionality.
* Users can now initiate, pause, resume, or cancel downloads for an episode directly from the overlay.
* The view model now fetches and observes the download status for the selected episode.
* If an episode has multiple remote sources, a quality selection dialog is presented to the user.
* **`FieldSets.kt`**:
* The `CONTINUE_WATCHING` and `NEXT_UP` field sets have been updated to include `MEDIA_SOURCES` and `MEDIA_STREAMS`. This ensures that download and playback options are available for items in these sections without requiring an additional network request.
* **Resources**:
* A new `mask.png` drawable has been added to create the gradient overlay effect in the landscape layout.
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.
No description provided.