feat: custom smali bundle selection
This commit is contained in:
parent
562826c2d7
commit
7dd1a5c914
3 changed files with 172 additions and 3 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue