Fix crash on certain grid pages when the app is sent to the background (#1605)

## Description
Fixes a crash when backgrounding/restoring the app on certain grid pages

### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/1598
Replaces #1599

### Testing
Emulator

It's easy to reproduce by going to a library's recommended tab,
scrolling to any row's view more, then pressing home to background the
app

## Screenshots
N/A

## AI or LLM usage
None, but includes a commit from #1599 which did, see the note there

---------

Co-authored-by: Christian Tismer <tismer@stackless.com>
This commit is contained in:
Damontecres 2026-06-20 20:06:34 -04:00 • committed by GitHub
parent 8e0798ee16
commit ae49e977ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 251 additions and 217 deletions

View file

@ -61,6 +61,7 @@ import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
import com.github.damontecres.wholphin.util.DebugLogTree
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.WholphinDispatchers
import com.github.damontecres.wholphin.util.requestSerializersModule
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@ -141,6 +142,7 @@ class MainActivity : AppCompatActivity() {
private val json =
Json {
classDiscriminator = "_type"
serializersModule = requestSerializersModule
}
@OptIn(ExperimentalTvMaterial3Api::class)
@ -151,10 +153,22 @@ class MainActivity : AppCompatActivity() {
lifecycle.addObserver(playbackLifecycleObserver)
val backStackStr = savedInstanceState?.getString(KEY_BACK_STACK)
if (backStackStr != null) {
val restoredBackStack =
if (backStackStr != null) {
try {
json.decodeFromString<List<Destination>>(backStackStr)
} catch (ex: Exception) {
// Best-effort: a previously persisted back stack we can no longer decode
// must not crash startup; fall back to a fresh start destination.
Timber.w(ex, "Could not restore back stack; starting fresh")
null
}
} else {
null
}
if (restoredBackStack != null) {
Timber.d("Restoring back stack")
var backStack = json.decodeFromString<List<Destination>>(backStackStr)
var backStack = restoredBackStack
if (!playExternalViewModel.launched.value) {
val lastDest = backStack.lastOrNull()
if (lastDest.isPlayback) {
@ -320,8 +334,14 @@ class MainActivity : AppCompatActivity() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.d("onSaveInstanceState")
val str = json.encodeToString(navigationManager.backStack.toList())
outState.putString(KEY_BACK_STACK, str)
try {
val str = json.encodeToString(navigationManager.backStack.toList())
outState.putString(KEY_BACK_STACK, str)
} catch (ex: Exception) {
// Best-effort: some destinations carry types we can't serialize; skip persisting
// the back stack rather than crashing in onSaveInstanceState.
Timber.w(ex, "Could not persist back stack; skipping")
}
val playerBackend =
runBlocking { userPreferencesDataStore.data.firstOrNull() }?.playbackPreferences?.playerBackend
outState.putBoolean(KEY_EXTERNAL_PLAYER, playerBackend == PlayerBackend.EXTERNAL_PLAYER)