Skip to content

Commit e310506

Browse files
committed
Persist home screen state across process death
- Add SavedStateHandle to HomeViewModel to persist current row, platform index, and game index - Fix race condition in loadPlatforms() that could load wrong platform - Save state before launching game to ensure recovery on return
1 parent 927d6ae commit e310506

2 files changed

Lines changed: 56 additions & 5 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ android {
2222
applicationId = "com.nendo.argosy"
2323
minSdk = 26
2424
targetSdk = 34
25-
versionCode = 13
26-
versionName = "0.5.6"
25+
versionCode = 14
26+
versionName = "0.5.7"
2727

2828
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2929
vectorDrawables {

app/src/main/kotlin/com/nendo/argosy/ui/screens/home/HomeViewModel.kt

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.nendo.argosy.ui.screens.home
22

3+
import androidx.lifecycle.SavedStateHandle
34
import androidx.lifecycle.ViewModel
45
import androidx.lifecycle.viewModelScope
56
import com.nendo.argosy.data.download.DownloadManager
@@ -50,6 +51,14 @@ private const val AUTO_SYNC_DAYS = 7L
5051
private const val MENU_INDEX_MAX_DOWNLOADED = 4
5152
private const val MENU_INDEX_MAX_REMOTE = 3
5253

54+
private const val KEY_ROW_TYPE = "home_row_type"
55+
private const val KEY_PLATFORM_INDEX = "home_platform_index"
56+
private const val KEY_GAME_INDEX = "home_game_index"
57+
58+
private const val ROW_TYPE_FAVORITES = "favorites"
59+
private const val ROW_TYPE_PLATFORM = "platform"
60+
private const val ROW_TYPE_CONTINUE = "continue"
61+
5362
data class GameDownloadIndicator(
5463
val isDownloading: Boolean = false,
5564
val isPaused: Boolean = false,
@@ -155,6 +164,7 @@ sealed class HomeEvent {
155164

156165
@HiltViewModel
157166
class HomeViewModel @Inject constructor(
167+
private val savedStateHandle: SavedStateHandle,
158168
private val platformDao: PlatformDao,
159169
private val gameDao: GameDao,
160170
private val romMRepository: RomMRepository,
@@ -168,7 +178,7 @@ class HomeViewModel @Inject constructor(
168178
private val gameActions: GameActionsDelegate
169179
) : ViewModel() {
170180

171-
private val _uiState = MutableStateFlow(HomeUiState())
181+
private val _uiState = MutableStateFlow(restoreInitialState())
172182
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
173183

174184
private val _events = MutableSharedFlow<HomeEvent>()
@@ -182,6 +192,33 @@ class HomeViewModel @Inject constructor(
182192
initializeRomM()
183193
}
184194

195+
private fun restoreInitialState(): HomeUiState {
196+
val rowType = savedStateHandle.get<String>(KEY_ROW_TYPE)
197+
val platformIndex = savedStateHandle.get<Int>(KEY_PLATFORM_INDEX) ?: 0
198+
val gameIndex = savedStateHandle.get<Int>(KEY_GAME_INDEX) ?: 0
199+
200+
val currentRow = when (rowType) {
201+
ROW_TYPE_FAVORITES -> HomeRow.Favorites
202+
ROW_TYPE_PLATFORM -> HomeRow.Platform(platformIndex)
203+
ROW_TYPE_CONTINUE -> HomeRow.Continue
204+
else -> HomeRow.Continue
205+
}
206+
207+
return HomeUiState(currentRow = currentRow, focusedGameIndex = gameIndex)
208+
}
209+
210+
private fun saveCurrentState() {
211+
val state = _uiState.value
212+
val (rowType, platformIndex) = when (val row = state.currentRow) {
213+
HomeRow.Favorites -> ROW_TYPE_FAVORITES to 0
214+
is HomeRow.Platform -> ROW_TYPE_PLATFORM to row.index
215+
HomeRow.Continue -> ROW_TYPE_CONTINUE to 0
216+
}
217+
savedStateHandle[KEY_ROW_TYPE] = rowType
218+
savedStateHandle[KEY_PLATFORM_INDEX] = platformIndex
219+
savedStateHandle[KEY_GAME_INDEX] = state.focusedGameIndex
220+
}
221+
185222
private fun initializeRomM() {
186223
viewModelScope.launch {
187224
romMRepository.initialize()
@@ -285,8 +322,17 @@ class HomeViewModel @Inject constructor(
285322
currentRow = if (shouldSwitchRow) HomeRow.Platform(0) else state.currentRow
286323
)
287324
}
288-
if (platforms.isNotEmpty() && _uiState.value.platformItems.isEmpty()) {
289-
loadGamesForPlatform(platforms.first().id, platformIndex = 0, setLoadingFalse = true)
325+
val state = _uiState.value
326+
if (platforms.isNotEmpty() && state.platformItems.isEmpty()) {
327+
val currentRow = state.currentRow
328+
if (currentRow is HomeRow.Platform) {
329+
val platform = platforms.getOrNull(currentRow.index)
330+
if (platform != null) {
331+
loadGamesForPlatform(platform.id, currentRow.index, setLoadingFalse = true)
332+
}
333+
} else {
334+
loadGamesForPlatform(platforms.first().id, platformIndex = 0, setLoadingFalse = true)
335+
}
290336
}
291337
}
292338
}
@@ -365,6 +411,7 @@ class HomeViewModel @Inject constructor(
365411
} else {
366412
_uiState.update { it.copy(currentRow = nextRow, focusedGameIndex = savedIndex) }
367413
}
414+
saveCurrentState()
368415
}
369416

370417
fun previousRow() {
@@ -388,13 +435,15 @@ class HomeViewModel @Inject constructor(
388435
} else {
389436
_uiState.update { it.copy(currentRow = prevRow, focusedGameIndex = savedIndex) }
390437
}
438+
saveCurrentState()
391439
}
392440

393441
fun nextGame(): Boolean {
394442
val state = _uiState.value
395443
if (state.currentItems.isEmpty()) return false
396444
if (state.focusedGameIndex >= state.currentItems.size - 1) return false
397445
_uiState.update { it.copy(focusedGameIndex = state.focusedGameIndex + 1) }
446+
saveCurrentState()
398447
return true
399448
}
400449

@@ -403,6 +452,7 @@ class HomeViewModel @Inject constructor(
403452
if (state.currentItems.isEmpty()) return false
404453
if (state.focusedGameIndex <= 0) return false
405454
_uiState.update { it.copy(focusedGameIndex = state.focusedGameIndex - 1) }
455+
saveCurrentState()
406456
return true
407457
}
408458

@@ -513,6 +563,7 @@ class HomeViewModel @Inject constructor(
513563
}
514564

515565
fun launchGame(gameId: Long) {
566+
saveCurrentState()
516567
viewModelScope.launch {
517568
when (val result = launchGameUseCase(gameId)) {
518569
is LaunchResult.Success -> {

0 commit comments

Comments
 (0)