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?