Fix issues when the system kills the app during playback (#1116)

## Description
This fixes some issues restoring the app state after it is killed by the
system, such as to free up memory while the screensaver is showing.

### Dev notes

Reproduce:
1. Start playing media from beginning
2. Pause at 10 minute mark
3. Wait for screensaver to start
4. Press a button
5. Media starts from beginning

Note: It may take a few iterations of 2-4 before the OS destroys the
app.

If the app is killed during playback, ideally Wholphin restores state to
_previous_ page, not playback since restoring playback is complex.
Before this PR, the back stack was maintained by Compose's
`rememberSerializable` and would be restored _after_ the
`PlaybackLifecycleObserver` cleans up the playback destination.

This meant that the app would be restored, from scratch, to the playback
page. The `PlaybackViewModel` would be initialized from scratch as it
was originally called, ie play from beginning.

The fix is to save and restore the back stack outside of Compose.

### Related issues
Fixes #767

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Ray 2026-03-18 18:09:13 -04:00 • committed by GitHub
parent d184990b16
commit f9ff04c5b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 39 additions and 30 deletions

View file

@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.saveable.rememberSerializable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
@ -28,12 +27,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.runtime.serialization.NavBackStackSerializer
import androidx.navigation3.runtime.serialization.NavKeySerializer
import androidx.navigation3.ui.NavDisplay
import androidx.tv.material3.DrawerValue
import androidx.tv.material3.MaterialTheme
@ -78,22 +73,12 @@ class ApplicationContentViewModel
fun ApplicationContent(
server: JellyfinServer,
user: JellyfinUser,
startDestination: Destination,
navigationManager: NavigationManager,
preferences: UserPreferences,
modifier: Modifier = Modifier,
enableTopScrim: Boolean = true,
viewModel: ApplicationContentViewModel = hiltViewModel(),
) {
val backStack: MutableList<NavKey> =
rememberSerializable(
server,
user,
serializer = NavBackStackSerializer(elementSerializer = NavKeySerializer()),
) {
NavBackStack(startDestination)
}
navigationManager.backStack = backStack
val backdrop by viewModel.backdropService.backdropFlow.collectAsStateWithLifecycle()
val backdropStyle = preferences.appPreferences.interfacePreferences.backdropStyle
val drawerState = rememberDrawerState(DrawerValue.Closed)