Skip to content

Commit a1e7594

Browse files
committed
feat: always use Ktor downloader
Android's DownloadManager has too many issues at this point, it's become unbearable. SecurityException here, random failure there, horrible logging, etc. It's much more reliable to directly use an HTTP client to download our stuff. Maybe I'll reimplement the download progress notification separately. Also turns out you can't use Android's DownloadManager to download to the app's internal data directory. very funny.
1 parent b2e5353 commit a1e7594

File tree

11 files changed

+29
-180
lines changed

11 files changed

+29
-180
lines changed

app/src/main/kotlin/com/aliucord/manager/ManagerApplication.kt

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class ManagerApplication : Application() {
7070
singleOf(::InstallerManager)
7171
singleOf(::OverlayManager)
7272

73-
singleOf(::DownloadManagerProvider)
7473
singleOf(::AndroidDownloadManager)
7574
singleOf(::KtorDownloadManager)
7675
})

app/src/main/kotlin/com/aliucord/manager/di/DownloadManagerProvider.kt

-40
This file was deleted.

app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt

+11-11
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class PathManager(
3737
* Use the external cache directory (`/storage/emulated/0/Android/data/com.aliucord.manager/cache`)
3838
* when dev mode or preserving APKs is enabled. Otherwise, default to internal app cache.
3939
*/
40-
private val externalCacheDir
40+
private val cacheDir
4141
get() = when (prefs.devMode || prefs.keepPatchedApks) {
4242
false -> context.cacheDir
4343
true -> context.externalCacheDir
@@ -48,62 +48,62 @@ class PathManager(
4848
* Delete the entire cache dir and recreate it.
4949
*/
5050
fun clearCache() {
51-
if (!externalCacheDir.deleteRecursively())
51+
if (!cacheDir.deleteRecursively())
5252
throw IllegalStateException("Failed to delete cache")
5353

54-
externalCacheDir.mkdirs()
54+
cacheDir.mkdirs()
5555
}
5656

5757
/**
5858
* Create a new subfolder in the Discord APK cache for a specific version.
5959
*/
60-
fun discordApkVersionCache(version: Int): File = externalCacheDir
60+
fun discordApkVersionCache(version: Int): File = cacheDir
6161
.resolve("discord")
6262
.resolve(version.toString())
6363
.apply { mkdirs() }
6464

6565
/**
6666
* Resolve a specific path for a cached injector.
6767
*/
68-
fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = externalCacheDir
68+
fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = cacheDir
6969
.resolve("injector").apply { mkdirs() }
7070
.resolve("$version${if (custom) ".custom" else ""}.dex")
7171

7272
/**
7373
* Get all the versions of custom injector builds.
7474
*/
75-
fun customInjectorDexs() = listCustomFiles(externalCacheDir.resolve("injector"))
75+
fun customInjectorDexs() = listCustomFiles(cacheDir.resolve("injector"))
7676

7777
/**
7878
* Resolve a specific path for a versioned cached Aliuhook build
7979
*/
80-
fun cachedAliuhookAAR(version: SemVer) = externalCacheDir
80+
fun cachedAliuhookAAR(version: SemVer) = cacheDir
8181
.resolve("aliuhook").apply { mkdirs() }
8282
.resolve("$version.aar")
8383

8484
/**
8585
* Resolve a specific path for a versioned smali patches archive.
8686
*/
87-
fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = externalCacheDir
87+
fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = cacheDir
8888
.resolve("patches").apply { mkdirs() }
8989
.resolve("$version${if (custom) ".custom" else ""}.zip")
9090

9191
/**
9292
* Get all the versions of custom smali bundles.
9393
*/
94-
fun customSmaliPatches() = listCustomFiles(externalCacheDir.resolve("patches"))
94+
fun customSmaliPatches() = listCustomFiles(cacheDir.resolve("patches"))
9595

9696
/**
9797
* Singular Kotlin file of the most up-to-date version
9898
* since the stdlib is backwards compatible.
9999
*/
100-
fun cachedKotlinDex() = externalCacheDir
100+
fun cachedKotlinDex() = cacheDir
101101
.resolve("kotlin.dex")
102102

103103
/**
104104
* The temporary working directory of a currently executing patching process.
105105
*/
106-
fun patchingWorkingDir() = externalCacheDir
106+
fun patchingWorkingDir() = cacheDir
107107
.resolve("patched")
108108

109109
private companion object {
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package com.aliucord.manager.manager
22

33
import android.content.SharedPreferences
4-
import com.aliucord.manager.di.DownloadManagerProvider
5-
import com.aliucord.manager.di.DownloaderSetting
64
import com.aliucord.manager.manager.base.BasePreferenceManager
75
import com.aliucord.manager.ui.components.Theme
86

97
class PreferencesManager(preferences: SharedPreferences) : BasePreferenceManager(preferences) {
108
var theme by enumPreference("theme", Theme.DARK)
119
var dynamicColor by booleanPreference("dynamic_color", false)
1210
var devMode by booleanPreference("dev_mode", false)
13-
var downloader by enumPreference<DownloaderSetting>("downloader", DownloadManagerProvider.getDefaultDownloader())
1411
var installer by enumPreference<InstallerSetting>("installer", InstallerSetting.PM)
1512
var keepPatchedApks by booleanPreference("keep_patched_apks", false)
1613
}

app/src/main/kotlin/com/aliucord/manager/manager/download/AndroidDownloadManager.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.content.Context
66
import android.database.Cursor
77
import android.net.Uri
88
import androidx.core.content.getSystemService
9+
import androidx.core.net.toUri
910
import com.aliucord.manager.BuildConfig
1011
import com.aliucord.manager.R
1112
import com.aliucord.manager.manager.download.IDownloadManager.ProgressListener
@@ -34,7 +35,7 @@ class AndroidDownloadManager(application: Application) : IDownloadManager {
3435
out.parentFile?.mkdirs()
3536

3637
// Create and start a download in the system DownloadManager
37-
val downloadId = DownloadManager.Request(Uri.parse(url))
38+
val downloadId = DownloadManager.Request(url.toUri())
3839
.setTitle("Aliucord Manager")
3940
.setDescription("Downloading ${out.name}...")
4041
.setDestinationUri(Uri.fromFile(out))

app/src/main/kotlin/com/aliucord/manager/manager/download/KtorDownloadManager.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.aliucord.manager.manager.download
22

33
import com.aliucord.manager.manager.download.IDownloadManager.Result
4+
import com.aliucord.manager.util.IS_PROBABLY_EMULATOR
45
import io.ktor.client.HttpClient
56
import io.ktor.client.request.header
67
import io.ktor.client.request.prepareGet
@@ -28,7 +29,9 @@ class KtorDownloadManager(private val http: HttpClient) : IDownloadManager {
2829
val httpStmt = http.prepareGet(url) {
2930
// Disable compression due to bug on emulators
3031
// This header cannot be set with Android's DownloadManager
31-
header(HttpHeaders.AcceptEncoding, null)
32+
if (IS_PROBABLY_EMULATOR) {
33+
header(HttpHeaders.AcceptEncoding, null)
34+
}
3235
}
3336

3437
httpStmt.execute { resp ->

app/src/main/kotlin/com/aliucord/manager/patcher/steps/base/DownloadStep.kt

+3-36
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
package com.aliucord.manager.patcher.steps.base
22

3-
import android.app.DownloadManager
43
import android.content.Context
5-
import android.util.Log
64
import android.widget.Toast
75
import androidx.annotation.CallSuper
86
import androidx.compose.runtime.Stable
9-
import com.aliucord.manager.BuildConfig
107
import com.aliucord.manager.R
11-
import com.aliucord.manager.di.DownloadManagerProvider
12-
import com.aliucord.manager.di.DownloaderSetting
13-
import com.aliucord.manager.manager.OverlayManager
14-
import com.aliucord.manager.manager.PreferencesManager
15-
import com.aliucord.manager.manager.download.AndroidDownloadManager
168
import com.aliucord.manager.manager.download.IDownloadManager
9+
import com.aliucord.manager.manager.download.KtorDownloadManager
1710
import com.aliucord.manager.patcher.StepRunner
1811
import com.aliucord.manager.patcher.steps.StepGroup
19-
import com.aliucord.manager.ui.components.dialogs.AlternativeDownloaderDialog
2012
import com.aliucord.manager.util.showToast
2113
import kotlinx.coroutines.Dispatchers
2214
import kotlinx.coroutines.withContext
@@ -27,9 +19,7 @@ import java.io.File
2719
@Stable
2820
abstract class DownloadStep : Step(), KoinComponent {
2921
private val context: Context by inject()
30-
private val overlays: OverlayManager by inject()
31-
private val prefs: PreferencesManager by inject()
32-
private val downloaders: DownloadManagerProvider by inject()
22+
private val downloader: KtorDownloadManager by inject()
3323

3424
/**
3525
* The remote url to download
@@ -67,7 +57,7 @@ abstract class DownloadStep : Step(), KoinComponent {
6757
targetFile.delete()
6858
}
6959

70-
val result = downloaders.getActiveDownloader().download(targetUrl, targetFile) { newProgress ->
60+
val result = downloader.download(targetUrl, targetFile) { newProgress ->
7161
progress = newProgress ?: -1f
7262
}
7363

@@ -96,29 +86,6 @@ abstract class DownloadStep : Step(), KoinComponent {
9686
Toast.makeText(context, toastText, Toast.LENGTH_LONG).show()
9787
}
9888

99-
// If this is a specific Android DownloadManager error, then prompt to use a different downloader
100-
if (result is AndroidDownloadManager.Error
101-
&& result.reason in arrayOf(DownloadManager.ERROR_HTTP_DATA_ERROR, DownloadManager.ERROR_CANNOT_RESUME)
102-
&& prefs.downloader != DownloaderSetting.Ktor
103-
) {
104-
state = StepState.Error
105-
106-
val confirmed = overlays.startComposableForResult { callback ->
107-
AlternativeDownloaderDialog(
108-
onConfirm = { callback(true) },
109-
onDismiss = { callback(false) },
110-
)
111-
}
112-
113-
if (confirmed) {
114-
Log.i(BuildConfig.TAG, "Changing to alternative downloader after failure")
115-
prefs.downloader = DownloaderSetting.Ktor
116-
state = StepState.Running
117-
118-
return execute(container)
119-
}
120-
}
121-
12289
throw Error("Failed to download: $result")
12390
}
12491
}

app/src/main/kotlin/com/aliucord/manager/ui/components/dialogs/AlternativeDownloaderDialog.kt

-56
This file was deleted.

app/src/main/kotlin/com/aliucord/manager/ui/screens/settings/SettingsScreen.kt

-15
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import androidx.compose.ui.unit.dp
2020
import cafe.adriel.voyager.core.screen.Screen
2121
import cafe.adriel.voyager.koin.getScreenModel
2222
import com.aliucord.manager.R
23-
import com.aliucord.manager.di.DownloaderSetting
2423
import com.aliucord.manager.ui.components.BackButton
2524
import com.aliucord.manager.ui.components.settings.*
2625
import com.aliucord.manager.ui.screens.settings.components.ThemeDialog
@@ -102,20 +101,6 @@ class SettingsScreen : Screen, Parcelable {
102101
onPrefChange = { preferences.keepPatchedApks = it },
103102
)
104103

105-
SettingsSwitch(
106-
label = stringResource(R.string.setting_alt_downloader),
107-
secondaryLabel = stringResource(R.string.setting_alt_downloader_desc),
108-
icon = { Icon(painterResource(R.drawable.ic_download), null) },
109-
pref = preferences.downloader == DownloaderSetting.Ktor,
110-
onPrefChange = {
111-
preferences.downloader = if (it) {
112-
DownloaderSetting.Ktor
113-
} else {
114-
DownloaderSetting.Android
115-
}
116-
}
117-
)
118-
119104
var clearedCache by rememberSaveable { mutableStateOf(false) }
120105
Button(
121106
shape = ShapeDefaults.Large,

app/src/main/kotlin/com/aliucord/manager/ui/widgets/updater/UpdaterViewModel.kt

+9-11
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import androidx.compose.runtime.*
55
import androidx.lifecycle.ViewModel
66
import androidx.lifecycle.viewModelScope
77
import com.aliucord.manager.BuildConfig
8-
import com.aliucord.manager.di.DownloadManagerProvider
98
import com.aliucord.manager.domain.repository.GithubRepository
109
import com.aliucord.manager.manager.InstallerManager
1110
import com.aliucord.manager.manager.InstallerSetting
11+
import com.aliucord.manager.manager.download.KtorDownloadManager
1212
import com.aliucord.manager.network.utils.SemVer
1313
import com.aliucord.manager.network.utils.getOrNull
1414
import kotlinx.coroutines.Dispatchers
1515
import kotlinx.coroutines.launch
1616

1717
class UpdaterViewModel(
1818
private val github: GithubRepository,
19-
private val downloaders: DownloadManagerProvider,
19+
private val downloader: KtorDownloadManager,
2020
private val installers: InstallerManager,
2121
private val application: Application,
2222
) : ViewModel() {
@@ -40,17 +40,15 @@ class UpdaterViewModel(
4040
viewModelScope.launch {
4141
isWorking = true
4242

43-
application.externalCacheDir!!.resolve("manager.apk").also {
44-
it.delete()
43+
val apkFile = application.cacheDir.resolve("manager.apk").apply { delete() }
4544

46-
downloaders.getActiveDownloader().download(url, it)
47-
installers.getInstaller(InstallerSetting.PM).install(
48-
apks = listOf(it),
49-
silent = true,
50-
)
45+
downloader.download(url, apkFile)
46+
installers.getInstaller(InstallerSetting.PM).install(
47+
apks = listOf(apkFile),
48+
silent = true,
49+
)
5150

52-
it.delete()
53-
}
51+
apkFile.delete()
5452
}
5553
}
5654

0 commit comments

Comments
 (0)