From 3662b67ad695f0dd5ec252d6e8ae285c6b9a358d Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:08:24 -0700 Subject: [PATCH] feat: prompt to use Ktor downloader on Android DownloadManager failure --- .../download/AndroidDownloadManager.kt | 2 +- .../aliucord/manager/patcher/StepRunner.kt | 7 +++- .../patcher/steps/base/DownloadStep.kt | 41 +++++++++++++++++-- .../steps/download/DownloadDiscordStep.kt | 2 +- .../dialogs/AlternativeDownloaderDialog.kt | 41 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 ++ 6 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 app/src/main/kotlin/com/aliucord/manager/ui/components/dialogs/AlternativeDownloaderDialog.kt diff --git a/app/src/main/kotlin/com/aliucord/manager/manager/download/AndroidDownloadManager.kt b/app/src/main/kotlin/com/aliucord/manager/manager/download/AndroidDownloadManager.kt index b5abf7c..6ea6726 100644 --- a/app/src/main/kotlin/com/aliucord/manager/manager/download/AndroidDownloadManager.kt +++ b/app/src/main/kotlin/com/aliucord/manager/manager/download/AndroidDownloadManager.kt @@ -110,7 +110,7 @@ class AndroidDownloadManager(application: Application) : IDownloadManager { * Error returned by the system [DownloadManager]. * @param reason The reason code returned by the [DownloadManager.COLUMN_REASON] column. */ - data class Error(private val reason: Int) : Result.Error() { + data class Error(val reason: Int) : Result.Error() { /** * Convert a [DownloadManager.COLUMN_REASON] code into its name. */ diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/StepRunner.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/StepRunner.kt index 5b517c5..ed13d38 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/StepRunner.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/StepRunner.kt @@ -19,9 +19,12 @@ import org.koin.core.component.inject * quickly switching the step groups in the UI. (very disorienting) * Larger delay leads to a perception that it's doing more work than it actually is. */ -const val MINIMUM_STEP_DELAY: Long = 600L +private const val MINIMUM_STEP_DELAY: Long = 600L -const val ERROR_NOTIF_ID = 200002 +/** + * ID used for showing error notifications emanating from this step runner. + */ +private const val ERROR_NOTIF_ID = 200002 abstract class StepRunner : KoinComponent { private val context: Context by inject() diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/base/DownloadStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/base/DownloadStep.kt index 4d66fac..7986eac 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/base/DownloadStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/base/DownloadStep.kt @@ -1,13 +1,22 @@ package com.aliucord.manager.patcher.steps.base +import android.app.DownloadManager import android.content.Context +import android.util.Log import android.widget.Toast +import androidx.annotation.CallSuper import androidx.compose.runtime.Stable +import com.aliucord.manager.BuildConfig import com.aliucord.manager.R import com.aliucord.manager.di.DownloadManagerProvider +import com.aliucord.manager.di.DownloaderSetting +import com.aliucord.manager.manager.OverlayManager +import com.aliucord.manager.manager.PreferencesManager +import com.aliucord.manager.manager.download.AndroidDownloadManager import com.aliucord.manager.manager.download.IDownloadManager import com.aliucord.manager.patcher.StepRunner import com.aliucord.manager.patcher.steps.StepGroup +import com.aliucord.manager.ui.components.dialogs.AlternativeDownloaderDialog import com.aliucord.manager.util.showToast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -18,6 +27,8 @@ import java.io.File @Stable abstract class DownloadStep : Step(), KoinComponent { private val context: Context by inject() + private val overlays: OverlayManager by inject() + private val prefs: PreferencesManager by inject() private val downloaders: DownloadManagerProvider by inject() /** @@ -35,6 +46,7 @@ abstract class DownloadStep : Step(), KoinComponent { * Verify that the download completely successfully without errors. * @throws Throwable If verification fails. */ + @CallSuper open suspend fun verify() { if (!targetFile.exists()) throw Error("Downloaded file is missing!") @@ -60,6 +72,9 @@ abstract class DownloadStep : Step(), KoinComponent { } when (result) { + is IDownloadManager.Result.Cancelled -> + state = StepState.Error + is IDownloadManager.Result.Success -> { try { verify() @@ -80,11 +95,31 @@ abstract class DownloadStep : Step(), KoinComponent { Toast.makeText(context, toastText, Toast.LENGTH_LONG).show() } + // If this is a specific Android DownloadManager error, then prompt to use a different downloader + if (result is AndroidDownloadManager.Error + && result.reason in arrayOf(DownloadManager.ERROR_HTTP_DATA_ERROR, DownloadManager.ERROR_CANNOT_RESUME) + && prefs.downloader != DownloaderSetting.Ktor + ) { + state = StepState.Error + + val confirmed = overlays.startComposableForResult { callback -> + AlternativeDownloaderDialog( + onConfirm = { callback(true) }, + onDismiss = { callback(false) }, + ) + } + + if (confirmed) { + Log.i(BuildConfig.TAG, "Changing to alternative downloader after failure") + prefs.downloader = DownloaderSetting.Ktor + state = StepState.Running + + return execute(container) + } + } + throw Error("Failed to download: $result") } - - is IDownloadManager.Result.Cancelled -> - state = StepState.Error } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/download/DownloadDiscordStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/download/DownloadDiscordStep.kt index 89bed78..3b81ddb 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/download/DownloadDiscordStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/download/DownloadDiscordStep.kt @@ -29,7 +29,7 @@ class DownloadDiscordStep : DownloadStep(), KoinComponent { private companion object { /** - * Last version of Discord before the RN transition. + * Last version of Discord before the RNA rewrite */ const val DISCORD_KT_VERSION = 126021 diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/components/dialogs/AlternativeDownloaderDialog.kt b/app/src/main/kotlin/com/aliucord/manager/ui/components/dialogs/AlternativeDownloaderDialog.kt new file mode 100644 index 0000000..55a80fc --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/ui/components/dialogs/AlternativeDownloaderDialog.kt @@ -0,0 +1,41 @@ +package com.aliucord.manager.ui.components.dialogs + +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.aliucord.manager.R + +@Composable +fun AlternativeDownloaderDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + FilledTonalButton( + onClick = onConfirm, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + ), + ) { + Text(stringResource(R.string.action_retry)) + } + }, + dismissButton = { + TextButton( + onClick = onDismiss, + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.error, + ), + ) { + Text(stringResource(R.string.action_cancel)) + } + }, + title = { Text(stringResource(R.string.alt_downloader_dialog_title)) }, + text = { Text(stringResource(R.string.alt_downloader_dialog_desc)) }, + icon = { Icon(painterResource(R.drawable.ic_download), contentDescription = null) }, + ) +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 11b85f6..8f74a28 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -164,6 +164,9 @@ Continue Open Play Protect + Alternative Downloader + This download failed with a specific error that may indicate issues with the system\'s DownloadManager. Would you like to retry with the the alternative downloader implementation? + Fun Fact: %s Did you know that Google Play Protect is useless? Did you know Aliucord with 150+ plugins somehow runs better than Discord\'s new app?