show update errors when launch cancelled

This commit is contained in:
Chloe committed 2025-12-19 05:08:20 -07:00
1 parent 3887c3fe60
commit f763534a72
3 files changed
+149 -54

No files matched your search

@@ -29,6 +29,11 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@@ -44,6 +49,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@@ -55,11 +61,15 @@ import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.LocalTheme import com.geode.launcher.ui.theme.LocalTheme
import com.geode.launcher.ui.theme.Theme import com.geode.launcher.ui.theme.Theme
import com.geode.launcher.ui.theme.launcherTitleStyle import com.geode.launcher.ui.theme.launcherTitleStyle
import com.geode.launcher.updater.ReleaseManager
import com.geode.launcher.utils.Constants import com.geode.launcher.utils.Constants
import com.geode.launcher.utils.GamePackageUtils import com.geode.launcher.utils.GamePackageUtils
import com.geode.launcher.utils.LaunchUtils import com.geode.launcher.utils.LaunchUtils
import com.geode.launcher.utils.PreferenceUtils import com.geode.launcher.utils.PreferenceUtils
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.ConnectException
import java.net.UnknownHostException
class AltMainActivity : ComponentActivity() { class AltMainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -157,9 +167,11 @@ fun mapCancelReasonToInfo(cancelReason: LaunchViewModel.LaunchCancelReason): Lau
LaunchViewModel.LaunchCancelReason.MANUAL-> LaunchStatusInfo( LaunchViewModel.LaunchCancelReason.MANUAL-> LaunchStatusInfo(
title = null title = null
) )
/*
else -> LaunchStatusInfo( else -> LaunchStatusInfo(
title = stringResource(R.string.launcher_cancelled_manual) title = stringResource(R.string.launcher_cancelled_manual)
) )
*/
} }
} }
@@ -421,6 +433,53 @@ fun GeodeLogo(modifier: Modifier = Modifier) {
} }
} }
@Composable
fun GeodeUpdateIndicator(snackbarHostState: SnackbarHostState, onRetry: () -> Unit) {
val context = LocalContext.current
val resources = LocalResources.current
val releaseState = ReleaseManager.get(context).uiState.value
if (releaseState !is ReleaseManager.ReleaseManagerState.Failure) {
return
}
val message = when (releaseState.exception) {
is UnknownHostException, is ConnectException ->
stringResource(R.string.release_fetch_no_internet_short)
is ReleaseManager.UpdateException -> {
when (releaseState.exception.reason) {
ReleaseManager.UpdateException.Reason.EXTERNAL_FILE_IN_USE -> stringResource(
R.string.release_fetch_external_in_use_short
)
else -> stringResource(R.string.release_fetch_generic_short)
}
}
is CancellationException -> null
else -> stringResource(R.string.release_fetch_generic_short)
}
if (message == null) {
return
}
LaunchedEffect(message) {
val result = snackbarHostState.showSnackbar(
message = message,
actionLabel = resources.getString(R.string.release_fetch_button_retry),
duration = SnackbarDuration.Indefinite,
withDismissAction = true
)
when (result) {
SnackbarResult.ActionPerformed -> {
onRetry()
}
else -> {}
}
}
}
@Composable @Composable
fun AltMainScreen( fun AltMainScreen(
launchViewModel: LaunchViewModel = viewModel(factory = LaunchViewModel.Factory) launchViewModel: LaunchViewModel = viewModel(factory = LaunchViewModel.Factory)
@@ -437,70 +496,81 @@ fun AltMainScreen(
var showErrorInfo by remember { mutableStateOf(false) } var showErrorInfo by remember { mutableStateOf(false) }
Box( val snackbarHostState = remember { SnackbarHostState() }
modifier = Modifier
.fillMaxSize() Scaffold(
.safeDrawingPadding(), snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
contentAlignment = Alignment.Center ) { innerPadding ->
) { Box(
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(8.dp) .fillMaxSize()
.verticalScroll(rememberScrollState()) .safeDrawingPadding()
.padding(innerPadding),
contentAlignment = Alignment.Center
) { ) {
GeodeLogo()
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(8.dp)
.verticalScroll(rememberScrollState())
) { ) {
LaunchProgressCard( GeodeLogo()
launchUIState,
crashInfo = launchViewModel.currentCrashInfo(),
onCancel = {
launchInSafeMode = false
coroutineScope.launch {
launchViewModel.cancelLaunch()
}
},
onResume = { safeMode ->
launchInSafeMode = safeMode
launchViewModel.clearCrashInfo() Column(
coroutineScope.launch { verticalArrangement = Arrangement.spacedBy(8.dp),
launchViewModel.beginLaunchFlow(true) horizontalAlignment = Alignment.CenterHorizontally,
} ) {
}, LaunchProgressCard(
onMore = { launchUIState,
showErrorInfo = true crashInfo = launchViewModel.currentCrashInfo(),
}, onCancel = {
extraOptions = { launchInSafeMode = false
ExtraOptions( coroutineScope.launch {
onSettings = { launchViewModel.cancelLaunch()
coroutineScope.launch {
launchViewModel.cancelLaunch(true)
}
onSettings(context)
} }
) },
}, onResume = { safeMode ->
safeModeEnabled = launchInSafeMode launchInSafeMode = safeMode
)
// only show launcher update in a case where a user won't see it ingame launchViewModel.clearCrashInfo()
if (launchUIState is LaunchViewModel.LaunchUIState.Cancelled) { coroutineScope.launch {
val gameVersion = remember { GamePackageUtils.getGameVersionCodeOrNull(context.packageManager) } launchViewModel.beginLaunchFlow(true)
}
},
onMore = {
showErrorInfo = true
},
extraOptions = {
ExtraOptions(
onSettings = {
coroutineScope.launch {
launchViewModel.cancelLaunch(true)
}
if (gameVersion != null && gameVersion < Constants.SUPPORTED_VERSION_CODE_MIN_WARNING) { onSettings(context)
UnsupportedVersionWarning() }
} )
},
safeModeEnabled = launchInSafeMode
)
val nextLauncherUpdate by launchViewModel.nextLauncherUpdate.collectAsState() // only show launcher update in a case where a user won't see it ingame
if (nextLauncherUpdate != null) { if (launchUIState is LaunchViewModel.LaunchUIState.Cancelled) {
LauncherUpdateIndicator() val gameVersion = remember { GamePackageUtils.getGameVersionCodeOrNull(context.packageManager) }
if (gameVersion != null && gameVersion < Constants.SUPPORTED_VERSION_CODE_MIN_WARNING) {
UnsupportedVersionWarning()
}
val nextLauncherUpdate by launchViewModel.nextLauncherUpdate.collectAsState()
if (nextLauncherUpdate != null) {
LauncherUpdateIndicator()
}
GeodeUpdateIndicator(snackbarHostState) {
launchViewModel.retryUpdate()
}
} }
} }
} }
@@ -105,6 +105,8 @@ class LaunchViewModel(private val application: Application): ViewModel() {
private var isCancelling = false private var isCancelling = false
private var hasManuallyStarted = false private var hasManuallyStarted = false
private var restoreUpdateState: LaunchUIState? = null
private suspend fun determineUpdateStatus() { private suspend fun determineUpdateStatus() {
ReleaseManager.get(application).uiState.takeWhile { ReleaseManager.get(application).uiState.takeWhile {
it is ReleaseManager.ReleaseManagerState.InUpdateCheck || it is ReleaseManager.ReleaseManagerState.InDownload it is ReleaseManager.ReleaseManagerState.InUpdateCheck || it is ReleaseManager.ReleaseManagerState.InDownload
@@ -119,6 +121,13 @@ class LaunchViewModel(private val application: Application): ViewModel() {
// flow terminates once update check is finished // flow terminates once update check is finished
hasCheckedForUpdates = true hasCheckedForUpdates = true
val currentState = restoreUpdateState
if (currentState != null) {
_uiState.emit(currentState)
return
}
preReadyCheck() preReadyCheck()
} }
@@ -214,6 +223,17 @@ class LaunchViewModel(private val application: Application): ViewModel() {
preReadyCheck() preReadyCheck()
} }
fun retryUpdate() {
ReleaseManager.get(application).checkForUpdates(true)
if (ReleaseManager.get(application).isInUpdate) {
restoreUpdateState = _uiState.value
viewModelScope.launch {
determineUpdateStatus()
}
return
}
}
suspend fun cancelLaunch(isAutomatic: Boolean = false) { suspend fun cancelLaunch(isAutomatic: Boolean = false) {
// no need to double cancel // no need to double cancel
if (_uiState.value is LaunchUIState.Cancelled) { if (_uiState.value is LaunchUIState.Cancelled) {
+5
View File
@@ -93,6 +93,11 @@
<string name="release_fetch_try_later">Please try again later.</string> <string name="release_fetch_try_later">Please try again later.</string>
<string name="release_fetch_manual_check_required">Externally managed library in use, please manually check for updates if you want to overwrite it.</string> <string name="release_fetch_manual_check_required">Externally managed library in use, please manually check for updates if you want to overwrite it.</string>
<string name="release_fetch_external_in_use_short">Update failed. External library in use.</string>
<string name="release_fetch_no_internet_short">Update failed. Internet not connected.</string>
<string name="release_fetch_generic_short">Update failed. Please try again later.</string>
<!-- GeometryDashActivity strings --> <!-- GeometryDashActivity strings -->
<string name="load_failed_link_error">Failed to link libraries</string> <string name="load_failed_link_error">Failed to link libraries</string>
<string name="load_failed_link_error_description">Game libraries could not be found. Try to:</string> <string name="load_failed_link_error_description">Game libraries could not be found. Try to:</string>