feat: custom smali bundle selection

This commit is contained in:
rushiiMachine 2024-12-22 19:10:37 -08:00
parent 562826c2d7
commit 7dd1a5c914
No known key found for this signature in database
GPG key ID: DCBE5952BB3B6420
3 changed files with 172 additions and 3 deletions

View file

@ -73,9 +73,14 @@ class PathManager(context: Context) {
/**
* Resolve a specific path for a versioned smali patches archive.
*/
fun cachedSmaliPatches(version: SemVer) = externalCacheDir
fun cachedSmaliPatches(version: SemVer, custom: Boolean = false) = externalCacheDir
.resolve("patches").apply { mkdirs() }
.resolve("$version.zip")
.resolve("$version${if (custom) ".custom" else ""}.zip")
/**
* Get all the versions of custom smali bundles.
*/
fun customSmaliPatches() = listCustomFiles(externalCacheDir.resolve("patches"))
/**
* Singular Kotlin file of the most up-to-date version
@ -89,4 +94,19 @@ class PathManager(context: Context) {
*/
fun patchingWorkingDir() = externalCacheDir
.resolve("patched")
private companion object {
/**
* List all the files that follow the ```[SemVer].custom.*``` naming scheme.
*/
private fun listCustomFiles(dir: File): List<SemVer> {
val files = dir.listFiles() ?: return emptyList()
val customVersions = files
.map { it.nameWithoutExtension }
.filter { it.endsWith(".custom") }
.map { it.removeSuffix(".custom") }
return customVersions.mapNotNull(SemVer::parseOrNull)
}
}
}

View file

@ -1,13 +1,17 @@
package com.aliucord.manager.patcher.steps.download
import android.util.Log
import androidx.compose.runtime.Stable
import com.aliucord.manager.BuildConfig
import com.aliucord.manager.R
import com.aliucord.manager.manager.OverlayManager
import com.aliucord.manager.manager.PathManager
import com.aliucord.manager.network.utils.SemVer
import com.aliucord.manager.patcher.StepRunner
import com.aliucord.manager.patcher.steps.base.DownloadStep
import com.aliucord.manager.patcher.steps.base.StepState
import com.aliucord.manager.patcher.steps.prepare.FetchInfoStep
import com.aliucord.manager.ui.components.dialogs.CustomComponentVersionPicker
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -22,14 +26,48 @@ class DownloadPatchesStep : DownloadStep(), KoinComponent {
/**
* This is populated right before the download starts (ref: [execute])
*/
private var isCustomVersion: Boolean = false
lateinit var targetVersion: SemVer
private set
override val localizedName = R.string.patch_step_dl_smali
override val targetUrl get() = URL
override val targetFile get() = paths.cachedSmaliPatches(targetVersion)
override val targetFile get() = paths.cachedSmaliPatches(targetVersion, isCustomVersion)
override suspend fun execute(container: StepRunner) {
var customVersions = mutableListOf<SemVer>()
.apply { addAll(paths.customSmaliPatches()) }
// Prompt to select or manage custom versions instead of downloading
if (customVersions.isNotEmpty()) {
val selectedVersion = overlays.startComposableForResult { callback ->
CustomComponentVersionPicker(
componentTitle = "Smali Patches",
versions = customVersions,
onConfirm = { version -> callback(version) },
onDelete = { version ->
try {
paths.cachedSmaliPatches(version, custom = true).delete()
customVersions.remove(version)
// Dismiss if no custom versions left
if (customVersions.isEmpty())
callback(null)
} catch (t: Throwable) {
Log.e(BuildConfig.TAG, "Failed to delete custom component", t)
}
},
onCancel = { callback(null) },
)
}
if (selectedVersion != null) {
isCustomVersion = true
targetVersion = selectedVersion
state = StepState.Skipped
return
}
}
targetVersion = container.getStep<FetchInfoStep>()
.data.patchesVersion

View file

@ -0,0 +1,111 @@
package com.aliucord.manager.ui.components.dialogs
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.aliucord.manager.R
import com.aliucord.manager.network.utils.SemVer
@Composable
fun CustomComponentVersionPicker(
componentTitle: String,
versions: List<SemVer>,
onConfirm: (SemVer) -> Unit,
onDelete: (SemVer) -> Unit,
onCancel: () -> Unit,
) {
var selectedVersion by remember { mutableStateOf<SemVer?>(null) }
AlertDialog(
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = false,
),
onDismissRequest = onCancel,
confirmButton = {
FilledTonalButton(
onClick = { onConfirm(selectedVersion!!) },
enabled = selectedVersion != null,
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary,
),
) {
Text(stringResource(R.string.action_confirm))
}
},
dismissButton = {
TextButton(
onClick = onCancel,
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error,
),
) {
Text(stringResource(R.string.action_dismiss))
}
},
title = { Text("Custom Component") },
icon = { Icon(painterResource(R.drawable.ic_download), contentDescription = null) },
text = {
Column {
Text(
text = "A custom local version for a patching component ($componentTitle) has been found. Would you like to use it?",
textAlign = TextAlign.Center,
modifier = Modifier.padding(bottom = 6.dp),
)
Column(
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier.verticalScroll(rememberScrollState())
) {
for (version in versions) key(version) {
val clickSource = remember(::MutableInteractionSource)
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable(
interactionSource = clickSource,
indication = LocalIndication.current,
onClick = { selectedVersion = version },
),
) {
RadioButton(
selected = selectedVersion == version,
onClick = { selectedVersion = version },
interactionSource = clickSource,
)
Text(
text = "v$version",
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold),
)
Spacer(Modifier.weight(1f))
IconButton(
onClick = { onDelete(version) },
) {
Icon(
painter = painterResource(R.drawable.ic_delete_forever),
tint = MaterialTheme.colorScheme.error,
contentDescription = null,
)
}
}
}
}
}
},
)
}