Skip to content

Commit

Permalink
feat: custom smali bundle selection
Browse files Browse the repository at this point in the history
  • Loading branch information
rushiiMachine committed Dec 23, 2024
1 parent 562826c commit 7dd1a5c
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 3 deletions.
24 changes: 22 additions & 2 deletions app/src/main/kotlin/com/aliucord/manager/manager/PathManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
}
}
}
},
)
}

0 comments on commit 7dd1a5c

Please sign in to comment.