From f8e99bad89461b422bc60f497b9baaddb253fd24 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:53:31 -0700 Subject: [PATCH] feat: move downloads to cache when unused This allows Android to clear these files if the space is needed by the OS. --- .../aliucord/manager/manager/PathManager.kt | 48 +++++++++++++------ .../manager/patcher/KotlinPatchRunner.kt | 4 +- .../patcher/steps/install/CleanupStep.kt | 7 +-- .../steps/prepare/RestoreDownloadsStep.kt | 35 ++++++++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/RestoreDownloadsStep.kt diff --git a/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt b/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt index 74640f6..dd590c5 100644 --- a/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt +++ b/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt @@ -41,18 +41,39 @@ class PathManager( */ val patchingDir = context.filesDir.resolve("patching").apply { mkdirs() } + /** + * The internal app directory uses for downloads that should not be wiped, + * to be used during the patching process. When the process completes, then this + * is to be moved to the cache dir, to allow Android to wipe the downloads when + * low on storage. + */ + val patchingDownloadDir = patchingDir.resolve("downloads") + + /** + * Used as a secondary location for downloads when not currently patching. + * This allows Android to clear the download cache when low on storage. + */ + val cacheDownloadDir = context.cacheDir.resolve("downloads") + + /** + * A permanent location used for storing custom patching components. + * This does not get moved to an Android-managed cache dir when + * not currently patching. + */ + val customComponentsDir = patchingDir.resolve("custom") + /** * Delete all the cache dirs and recreate them. */ fun clearCache() { - context.cacheDir.apply { deleteRecursively() && mkdirs() } - patchingDir.apply { deleteRecursively() && mkdirs() } + for (dir in arrayOf(patchingDir, cacheDownloadDir, context.cacheDir)) + dir.deleteRecursively() && dir.mkdirs() } /** * Create a new subfolder in the Discord APK cache for a specific version. */ - fun discordApkVersionCache(version: Int): File = patchingDir + fun discordApkVersionCache(version: Int): File = patchingDownloadDir .resolve("discord") .resolve(version.toString()) .apply { mkdirs() } @@ -60,39 +81,39 @@ class PathManager( /** * Resolve a specific path for a cached injector. */ - fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = patchingDir + fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = patchingDownloadDir .resolve("injector").apply { mkdirs() } .resolve("$version${if (custom) ".custom" else ""}.dex") /** * Get all the versions of custom injector builds. */ - fun customInjectorDexs() = listCustomFiles(patchingDir.resolve("injector")) + fun customInjectorDexs() = listCustomFiles(customComponentsDir.resolve("injector")) /** * Resolve a specific path for a versioned cached Aliuhook build */ - fun cachedAliuhookAAR(version: SemVer) = patchingDir + fun cachedAliuhookAAR(version: SemVer) = patchingDownloadDir .resolve("aliuhook").apply { mkdirs() } .resolve("$version.aar") /** * Resolve a specific path for a versioned smali patches archive. */ - fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = patchingDir + fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = patchingDownloadDir .resolve("patches").apply { mkdirs() } .resolve("$version${if (custom) ".custom" else ""}.zip") /** * Get all the versions of custom smali bundles. */ - fun customSmaliPatches() = listCustomFiles(patchingDir.resolve("patches")) + fun customSmaliPatches() = listCustomFiles(customComponentsDir.resolve("patches")) /** * Singular Kotlin file of the most up-to-date version * since the stdlib is backwards compatible. */ - fun cachedKotlinDex() = patchingDir + fun cachedKotlinDex() = patchingDownloadDir .resolve("kotlin.dex") /** @@ -108,7 +129,7 @@ class PathManager( private companion object { /** - * List all the files that follow the ```[SemVer].custom.*``` naming scheme. + * List all the files in a directory that follow SemVer naming. */ private fun listCustomFiles(dir: File): List? { if (!dir.exists()) return null @@ -116,12 +137,9 @@ class PathManager( val files = dir.listFiles() ?: throw Error("Failed to list directory") - val customVersions = files + return files .map { it.nameWithoutExtension } - .filter { it.endsWith(".custom") } - .map { it.removeSuffix(".custom") } - - return customVersions.mapNotNull(SemVer::parseOrNull) + .mapNotNull(SemVer::parseOrNull) } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/KotlinPatchRunner.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/KotlinPatchRunner.kt index e035706..c536da5 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/KotlinPatchRunner.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/KotlinPatchRunner.kt @@ -3,8 +3,7 @@ package com.aliucord.manager.patcher import com.aliucord.manager.patcher.steps.download.* import com.aliucord.manager.patcher.steps.install.* import com.aliucord.manager.patcher.steps.patch.* -import com.aliucord.manager.patcher.steps.prepare.DowngradeCheckStep -import com.aliucord.manager.patcher.steps.prepare.FetchInfoStep +import com.aliucord.manager.patcher.steps.prepare.* import com.aliucord.manager.ui.screens.patchopts.PatchOptions import kotlinx.collections.immutable.persistentListOf @@ -16,6 +15,7 @@ class KotlinPatchRunner(options: PatchOptions) : StepRunner() { // Prepare FetchInfoStep(), DowngradeCheckStep(options), + RestoreDownloadsStep(), // Download DownloadDiscordStep(), diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/CleanupStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/CleanupStep.kt index 5d623d2..0acff2a 100644 --- a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/CleanupStep.kt +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/install/CleanupStep.kt @@ -6,7 +6,6 @@ import com.aliucord.manager.manager.PreferencesManager import com.aliucord.manager.patcher.StepRunner import com.aliucord.manager.patcher.steps.StepGroup import com.aliucord.manager.patcher.steps.base.Step -import com.aliucord.manager.patcher.steps.base.StepState import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -21,9 +20,11 @@ class CleanupStep : Step(), KoinComponent { override val localizedName = R.string.patch_step_cleanup override suspend fun execute(container: StepRunner) { + container.log("Moving downloads back to cache") + paths.patchingDownloadDir.renameTo(paths.cacheDownloadDir) + if (prefs.keepPatchedApks) { - container.log("keepPatchedApks enabled, skipping...") - state = StepState.Skipped + container.log("keepPatchedApks enabled, keeping working dir") } else { container.log("Deleting patching working dir") if (!paths.patchingWorkingDir().deleteRecursively()) diff --git a/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/RestoreDownloadsStep.kt b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/RestoreDownloadsStep.kt new file mode 100644 index 0000000..6d16137 --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/patcher/steps/prepare/RestoreDownloadsStep.kt @@ -0,0 +1,35 @@ +package com.aliucord.manager.patcher.steps.prepare + +import androidx.compose.runtime.Stable +import com.aliucord.manager.R +import com.aliucord.manager.manager.PathManager +import com.aliucord.manager.patcher.StepRunner +import com.aliucord.manager.patcher.steps.StepGroup +import com.aliucord.manager.patcher.steps.base.Step +import com.aliucord.manager.patcher.steps.base.StepState +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +/** + * Restores downloaded files necessary for patching from the cache dir. + * Refer to [PathManager.patchingDownloadDir] and [PathManager.cacheDownloadDir] + * for more information. + */ +@Stable +class RestoreDownloadsStep : Step(), KoinComponent { + private val paths: PathManager by inject() + + override val group = StepGroup.Prepare + override val localizedName = R.string.patch_step_restore_cache + + override suspend fun execute(container: StepRunner) { + if (paths.cacheDownloadDir.exists()) { + container.log("Moving downloads from cache to permanent storage") + paths.patchingDownloadDir.deleteRecursively() + paths.cacheDownloadDir.renameTo(paths.patchingDownloadDir) + } else { + container.log("No download cache present") + state = StepState.Skipped + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 788bdcb..b5d8a44 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -119,6 +119,7 @@ Fetching target Discord version Checking for newer installations + Restoring from cache Downloading Discord APK Downloading Kotlin library Downloading Aliucord Injector