From cb7bb308d08b91b75c22fbe0569fc36df8af5637 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Sat, 3 Aug 2024 23:26:16 -0700 Subject: [PATCH] fix(PatchingScreenModel): handle patch runner errors & cancellations properly --- .../screens/patching/PatchingScreenModel.kt | 93 +++++++++---------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/patching/PatchingScreenModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/patching/PatchingScreenModel.kt index 7ba211a..c8bb182 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/patching/PatchingScreenModel.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/patching/PatchingScreenModel.kt @@ -119,64 +119,57 @@ class PatchingScreenModel( startTime = Date() mutableState.value = PatchingScreenState.Working - val newJob = screenModelScope.launch { - val runner = KotlinPatchRunner(options) - .also { stepRunner = it } - - // Bind InstallStep's GPP Warning state to this - runner.getStep<InstallStep>(completed = false) - .gppWarningLock - .take(1) // Take only the initially trigger value - .onEach { showGppWarning = true } - .launchIn(this@launch) - - steps = runner.steps.groupBy { it.group } - .mapValues { it.value.toUnsafeImmutable() } - .toUnsafeImmutable() - - // Intentionally delay to show the state change of the first step in UI when it runs - // without it, on a fast internet it just immediately shows as "Success" - delay(600) - - // Execute all the steps and catch any errors - when (val error = runner.executeAll()) { - null -> { - // If install step is marked skipped then the installation was manually aborted - // and if so, immediately close install screen - if (runner.getStep<InstallStep>().state == StepState.Skipped) { - mutableState.value = PatchingScreenState.CloseScreen - } - // At this point, the installation has successfully completed - else { - mutableState.value = PatchingScreenState.Success - } - } - - else -> { - Log.e(BuildConfig.TAG, "Failed to perform installation process", error) - - mutableState.value = PatchingScreenState.Failed(failureLog = getFailureInfo(error)) - } + runnerJob = screenModelScope.launch { + try { + startPatchRunner() + } catch (_: CancellationException) { + Log.w(BuildConfig.TAG, "Installation was cancelled before completion") + mutableState.value = PatchingScreenState.CloseScreen + } catch (error: Throwable) { + Log.e(BuildConfig.TAG, "Failed to orchestrate patch runner", error) + mutableState.value = PatchingScreenState.Failed(failureLog = getFailureInfo(error)) } } + } - newJob.invokeOnCompletion { error -> - when (error) { - // Successfully executed, already handled above - null -> {} - - // Job was cancelled before being able to finish setting state - is CancellationException -> { - Log.w(BuildConfig.TAG, "Installation was cancelled before completing", error) + private suspend fun startPatchRunner() { + val runner = KotlinPatchRunner(options) + .also { stepRunner = it } + + // Bind InstallStep's GPP Warning state to this + runner.getStep<InstallStep>(completed = false) + .gppWarningLock + .take(1) // Take only the initially trigger value + .onEach { showGppWarning = true } + .launchIn(CoroutineScope(currentCoroutineContext())) + + steps = runner.steps.groupBy { it.group } + .mapValues { it.value.toUnsafeImmutable() } + .toUnsafeImmutable() + + // Intentionally delay to show the state change of the first step in UI when it runs + // without it, on a fast internet it just immediately shows as "Success" + delay(600) + + // Execute all the steps and catch any errors + when (val error = runner.executeAll()) { + null -> { + // If install step is marked skipped then the installation was manually aborted + // and if so, immediately close install screen + if (runner.getStep<InstallStep>().state == StepState.Skipped) { mutableState.value = PatchingScreenState.CloseScreen } + // At this point, the installation has successfully completed + else { + mutableState.value = PatchingScreenState.Success + } + } - // This should never happen, all install errors are caught - else -> throw error + else -> { + Log.e(BuildConfig.TAG, "Failed to perform installation process", error) + mutableState.value = PatchingScreenState.Failed(failureLog = getFailureInfo(error)) } } - - runnerJob = newJob } private suspend fun getFailureInfo(stacktrace: Throwable): String {