From 8d295979bee60f4531ba60b65c3dcd8b70b8317a Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:39:04 -0700 Subject: [PATCH] refactor: clean up usages of ~/Aliucord --- .../com/aliucord/manager/MainActivity.kt | 4 --- .../aliucord/manager/installer/util/Signer.kt | 31 ++++++++++--------- .../aliucord/manager/manager/PathManager.kt | 22 +++++++++++++ .../ui/screens/plugins/PluginsModel.kt | 22 ++++++------- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt index e3a4660..184752b 100644 --- a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt +++ b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt @@ -6,7 +6,6 @@ package com.aliucord.manager import android.os.Bundle -import android.os.Environment import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent @@ -27,9 +26,6 @@ import com.aliucord.manager.ui.widgets.updater.UpdaterDialog import com.aliucord.manager.util.IS_CUSTOM_BUILD import org.koin.android.ext.android.inject -// TODO: move to a path provider in DI -val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord") - class MainActivity : ComponentActivity() { private val preferences: PreferencesManager by inject() diff --git a/app/src/main/kotlin/com/aliucord/manager/installer/util/Signer.kt b/app/src/main/kotlin/com/aliucord/manager/installer/util/Signer.kt index 070d71e..dd6986c 100644 --- a/app/src/main/kotlin/com/aliucord/manager/installer/util/Signer.kt +++ b/app/src/main/kotlin/com/aliucord/manager/installer/util/Signer.kt @@ -5,13 +5,15 @@ package com.aliucord.manager.installer.util -import com.aliucord.manager.aliucordDir +import com.aliucord.manager.manager.PathManager import com.android.apksig.ApkSigner import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import java.io.File import java.math.BigInteger import java.security.* @@ -20,21 +22,22 @@ import java.security.cert.X509Certificate import java.util.Date import java.util.Locale -object Signer { +object Signer : KoinComponent { + private val paths = get() private val password = "password".toCharArray() private val signerConfig: ApkSigner.SignerConfig by lazy { val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) - aliucordDir.resolve("ks.keystore").also { - if (!it.exists()) { - aliucordDir.mkdir() - newKeystore(it) - } - }.inputStream().use { stream -> - keyStore.load(stream, null) + // Create new keystore if it doesn't exist + if (!paths.keystoreFile.exists()) { + paths.aliucordDir.mkdirs() + newKeystore(paths.keystoreFile) } + paths.keystoreFile.inputStream() + .use { keyStore.load(it, /* password = */ null) } + val alias = keyStore.aliases().nextElement() val certificate = keyStore.getCertificate(alias) as X509Certificate @@ -45,13 +48,13 @@ object Signer { ).build() } - private fun newKeystore(out: File?) { + private fun newKeystore(out: File) { val key = createKey() with(KeyStore.getInstance(KeyStore.getDefaultType())) { load(null, password) setKeyEntry("alias", key.privateKey, password, arrayOf(key.publicKey)) - store(out?.outputStream(), password) + store(out.outputStream(), password) } } @@ -81,18 +84,18 @@ object Signer { } fun signApk(apkFile: File) { - val outputApk = aliucordDir.resolve(apkFile.name) + val tmpApk = apkFile.resolveSibling(apkFile.name + ".tmp") ApkSigner.Builder(listOf(signerConfig)) .setV1SigningEnabled(false) // TODO: enable so api <24 devices can work, however zip-alignment breaks .setV2SigningEnabled(true) .setV3SigningEnabled(true) .setInputApk(apkFile) - .setOutputApk(outputApk) + .setOutputApk(tmpApk) .build() .sign() - outputApk.renameTo(apkFile) + tmpApk.renameTo(apkFile) } private class KeySet(val publicKey: X509Certificate, val privateKey: PrivateKey) 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 6070aa8..8de3741 100644 --- a/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt +++ b/app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt @@ -1,12 +1,34 @@ package com.aliucord.manager.manager import android.content.Context +import android.os.Environment import java.io.File /** * A central place to provide all system paths that are used. */ class PathManager(context: Context) { + /** + * The Aliucord folder in which plugins/settings/themes are stored. + * Standard path: `~/Aliucord` + */ + val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord") + + /** + * The directory in external storage in which plugins are stored by Aliucord. + */ + val pluginsDir = aliucordDir.resolve("plugins") + + /** + * The settings file in which Aliucord's core uses. + */ + val coreSettingsFile = aliucordDir.resolve("settings/Aliucord.json") + + /** + * Global keystore used for signing APKs. + */ + val keystoreFile = aliucordDir.resolve("ks.keystore") + private val externalCacheDir = context.externalCacheDir ?: throw Error("External cache directory isn't supported") diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt index 336910a..b1e84a8 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/screens/plugins/PluginsModel.kt @@ -5,13 +5,15 @@ import androidx.compose.runtime.* import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.screenModelScope import com.aliucord.manager.BuildConfig -import com.aliucord.manager.aliucordDir import com.aliucord.manager.domain.model.Plugin +import com.aliucord.manager.manager.PathManager import kotlinx.coroutines.* import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.* -class PluginsModel : ScreenModel { +class PluginsModel( + private val paths: PathManager, +) : ScreenModel { var showChangelogDialog by mutableStateOf(false) private set @@ -57,8 +59,6 @@ class PluginsModel : ScreenModel { plugins.remove(plugin) } - private val settingsFile = aliucordDir.resolve("settings/Aliucord.json") - fun setPluginEnabled(plugin: String, enable: Boolean) { screenModelScope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { @@ -69,13 +69,13 @@ class PluginsModel : ScreenModel { @OptIn(ExperimentalSerializationApi::class) try { - val settings = settingsFile.inputStream() + val settings = paths.coreSettingsFile.inputStream() .use { Json.decodeFromStream>(it) } .toMutableMap() settings["AC_PM_$plugin"] = JsonPrimitive(enable) - settingsFile.outputStream() + paths.coreSettingsFile.outputStream() .use { Json.encodeToStream(settings, it) } } catch (t: Throwable) { Log.e(BuildConfig.TAG, "Failed to write Aliucord.json: ${Log.getStackTraceString(t)}") @@ -85,11 +85,11 @@ class PluginsModel : ScreenModel { } private suspend fun loadEnabled(): Map = withContext(Dispatchers.IO) { - if (!settingsFile.exists()) return@withContext emptyMap() + if (!paths.coreSettingsFile.exists()) return@withContext emptyMap() @OptIn(ExperimentalSerializationApi::class) try { - settingsFile.inputStream() + paths.coreSettingsFile.inputStream() .use { Json.decodeFromStream>(it) } .filterKeys { it.startsWith("AC_PM_") } .mapKeys { it.key.substring("AC_PM_".length) } @@ -107,14 +107,12 @@ class PluginsModel : ScreenModel { } screenModelScope.launch(Dispatchers.IO) { - val pluginsDir = aliucordDir.resolve("plugins") - - if (!pluginsDir.exists() && !pluginsDir.mkdirs()) { + if (!paths.pluginsDir.exists() && !paths.pluginsDir.mkdirs()) { Log.e(BuildConfig.TAG, "Failed to create plugins dir. Missing Permissions?") return@launch } - val files = pluginsDir.listFiles { file -> file.extension == "zip" } ?: return@launch + val files = paths.pluginsDir.listFiles { file -> file.extension == "zip" } ?: return@launch val loadedPlugins = files.mapNotNull { file -> try { Plugin(file)