fix: do all patching in internal storage

Up until this point, everything was downloaded to either internal app cache, or external app cache. Unfortunately when the storage is low on Android, cache directories may be wiped automatically, which immediately breaks patching due to missing files.

This means that making all patching files accessible to the user is now impossible without root.

Fixes #91
This commit is contained in:
rushiiMachine 2025-06-23 12:28:28 -07:00
parent 3633e1efe5
commit 172b53c5f6
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
4 changed files with 31 additions and 37 deletions

View file

@ -3,9 +3,11 @@ package com.aliucord.manager.manager
import android.annotation.SuppressLint
import android.app.Application
import android.os.Build
import android.os.storage.StorageManager
import android.text.format.Formatter
import android.util.Log
import androidx.compose.runtime.Immutable
import androidx.core.content.getSystemService
import com.aliucord.manager.BuildConfig
import com.aliucord.manager.ui.screens.patchopts.PatchOptions
import com.aliucord.manager.util.IS_PROBABLY_EMULATOR
@ -94,9 +96,11 @@ class InstallLogManager(
/**
* Creates a list of details about the current installation environment.
*/
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
@SuppressLint("UsableSpace")
suspend fun getEnvironmentInfo(): String {
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
val storageManager = application.getSystemService<StorageManager>()!!
val gitChanges = if (BuildConfig.GIT_LOCAL_CHANGES || BuildConfig.GIT_LOCAL_COMMITS) "(Changes present)" else ""
val soc = if (Build.VERSION.SDK_INT >= 31) (Build.SOC_MANUFACTURER + ' ' + Build.SOC_MODEL) else "Unavailable"
val playProtect = when (application.isPlayProtectEnabled()) {
@ -104,8 +108,13 @@ class InstallLogManager(
true -> "Enabled"
false -> "Disabled"
}
val freeInternalStorage = application.cacheDir.usableSpace
val freeExternalStorage = application.externalCacheDir?.usableSpace ?: 0
val diskFreeSize = application.filesDir.usableSpace
val cacheQuotaSize = if (Build.VERSION.SDK_INT >= 26) {
storageManager.getCacheQuotaBytes(storageManager.getUuidForPath(application.cacheDir))
} else {
0L
}
return """
Aliucord Manager v${BuildConfig.VERSION_NAME}
@ -113,8 +122,8 @@ class InstallLogManager(
Developer mode: ${if (prefs.devMode) "On" else "Off"}
External storage: ${if (prefs.devMode || prefs.keepPatchedApks) "Yes" else "No"}
Disk Free (Internal): ${Formatter.formatShortFileSize(application, freeInternalStorage)}
Disk Free (External): ${Formatter.formatShortFileSize(application, freeExternalStorage)}
Disk Free: ${Formatter.formatShortFileSize(application, diskFreeSize)}
Cache Quota: ${Formatter.formatShortFileSize(application, cacheQuotaSize)}
Android API: ${Build.VERSION.SDK_INT}
Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}

View file

@ -10,7 +10,6 @@ import java.io.File
*/
class PathManager(
private val context: Context,
private val prefs: PreferencesManager,
) {
/**
* The Aliucord folder in which plugins/settings/themes are stored.
@ -34,28 +33,26 @@ class PathManager(
val keystoreFile = aliucordDir.resolve("ks.keystore")
/**
* Uses the external cache directory (`/storage/emulated/0/Android/data/com.aliucord.manager/cache`)
* when dev mode or preserving APKs is enabled. Otherwise, default to internal app cache.
* The internal directory used for downloading components related to patching, and
* running the patching process itself.
*
* This should not be a cache dir provided by Android, since it will be wiped when
* the device is low on storage, and result in a failed patching process.
*/
val cacheDir: File
get() = when (prefs.devMode || prefs.keepPatchedApks) {
false -> context.cacheDir
true -> context.externalCacheDir ?: context.cacheDir
}
val patchingDir = context.filesDir.resolve("patching").apply { mkdirs() }
/**
* Delete the entire cache dir and recreate it.
* Delete all the cache dirs and recreate them.
*/
fun clearCache() {
context.cacheDir.deleteRecursively()
context.externalCacheDir?.deleteRecursively()
cacheDir.mkdirs()
context.cacheDir.apply { deleteRecursively() && mkdirs() }
patchingDir.apply { deleteRecursively() && mkdirs() }
}
/**
* Create a new subfolder in the Discord APK cache for a specific version.
*/
fun discordApkVersionCache(version: Int): File = cacheDir
fun discordApkVersionCache(version: Int): File = patchingDir
.resolve("discord")
.resolve(version.toString())
.apply { mkdirs() }
@ -63,45 +60,45 @@ class PathManager(
/**
* Resolve a specific path for a cached injector.
*/
fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = cacheDir
fun cachedInjectorDex(version: SemVer, custom: Boolean = false) = patchingDir
.resolve("injector").apply { mkdirs() }
.resolve("$version${if (custom) ".custom" else ""}.dex")
/**
* Get all the versions of custom injector builds.
*/
fun customInjectorDexs() = listCustomFiles(cacheDir.resolve("injector"))
fun customInjectorDexs() = listCustomFiles(patchingDir.resolve("injector"))
/**
* Resolve a specific path for a versioned cached Aliuhook build
*/
fun cachedAliuhookAAR(version: SemVer) = cacheDir
fun cachedAliuhookAAR(version: SemVer) = patchingDir
.resolve("aliuhook").apply { mkdirs() }
.resolve("$version.aar")
/**
* Resolve a specific path for a versioned smali patches archive.
*/
fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = cacheDir
fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = patchingDir
.resolve("patches").apply { mkdirs() }
.resolve("$version${if (custom) ".custom" else ""}.zip")
/**
* Get all the versions of custom smali bundles.
*/
fun customSmaliPatches() = listCustomFiles(cacheDir.resolve("patches"))
fun customSmaliPatches() = listCustomFiles(patchingDir.resolve("patches"))
/**
* Singular Kotlin file of the most up-to-date version
* since the stdlib is backwards compatible.
*/
fun cachedKotlinDex() = cacheDir
fun cachedKotlinDex() = patchingDir
.resolve("kotlin.dex")
/**
* The temporary working directory of a currently executing patching process.
*/
fun patchingWorkingDir() = cacheDir
fun patchingWorkingDir() = patchingDir
.resolve("patched")
private companion object {

View file

@ -10,8 +10,6 @@ import com.aliucord.manager.manager.PathManager
import com.aliucord.manager.manager.PreferencesManager
import com.aliucord.manager.ui.theme.Theme
import com.aliucord.manager.util.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class SettingsModel(
private val application: Application,
@ -36,15 +34,6 @@ class SettingsModel(
}
fun setKeepPatchedApks(value: Boolean) {
// Disallow setting keep APKs if externalCacheDir doesn't exist (some ROMs)
if (value && application.externalCacheDir == null) {
screenModelScope.launch {
delay(300)
preferences.keepPatchedApks = false
application.showToast(R.string.setting_keep_patched_apks_error)
}
}
preferences.keepPatchedApks = value
}

View file

@ -54,7 +54,6 @@
<string name="setting_developer_options_desc">Don\'t enable this unless you know what it does! (yes, really)</string>
<string name="setting_keep_patched_apks">Keep patched APKs</string>
<string name="setting_keep_patched_apks_desc">Keep all patched files and APKs after installing for debugging. This setting may drastically slow down (20x) patching on certain devices!</string>
<string name="setting_keep_patched_apks_error">This device does not support external cache directories!</string>
<string name="settings_clear_cache">Clear cache</string>
<string name="settings_see_patched_apks">See patched APKs</string>