mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-31 23:01:03 +00:00
Compare commits
1 Commits
v1.26.0-de
...
feat/patch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51b6b449bf |
@@ -23,6 +23,10 @@ kotlin {
|
|||||||
jvmToolchain(17)
|
jvmToolchain(17)
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
jvmTarget = JvmTarget.JVM_17
|
jvmTarget = JvmTarget.JVM_17
|
||||||
|
freeCompilerArgs.addAll(
|
||||||
|
"-Xexplicit-backing-fields",
|
||||||
|
"-Xcontext-parameters",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Save FAB freaking out in select patches screen ([4c0b6b0](https://github.com/ReVanced/revanced-manager/commit/4c0b6b02e95a8d6f655bcf5c25493b1f9a4a4dcd))
|
|
||||||
|
|
||||||
# app [1.26.0-dev.19](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.18...v1.26.0-dev.19) (2026-01-08)
|
# app [1.26.0-dev.19](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.18...v1.26.0-dev.19) (2026-01-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -260,6 +260,10 @@ kotlin {
|
|||||||
jvmToolchain(17)
|
jvmToolchain(17)
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
jvmTarget = JvmTarget.JVM_17
|
jvmTarget = JvmTarget.JVM_17
|
||||||
|
freeCompilerArgs.addAll(
|
||||||
|
"-Xexplicit-backing-fields",
|
||||||
|
"-Xcontext-parameters",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
version = 1.26.0-dev.20
|
version = 1.26.0-dev.19
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import androidx.room.Entity
|
|||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import app.revanced.manager.patcher.patch.Option
|
import app.revanced.manager.patcher.patch.Option
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.SerializationException
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import kotlinx.serialization.json.JsonNull
|
import kotlinx.serialization.json.JsonNull
|
||||||
@@ -36,7 +34,7 @@ import kotlin.reflect.typeOf
|
|||||||
data class Option(
|
data class Option(
|
||||||
@ColumnInfo(name = "group") val group: Int,
|
@ColumnInfo(name = "group") val group: Int,
|
||||||
@ColumnInfo(name = "patch_name") val patchName: String,
|
@ColumnInfo(name = "patch_name") val patchName: String,
|
||||||
@ColumnInfo(name = "key") val key: String,
|
@ColumnInfo(name = "key") val name: String,
|
||||||
// Encoded as Json.
|
// Encoded as Json.
|
||||||
@ColumnInfo(name = "value") val value: SerializedValue,
|
@ColumnInfo(name = "value") val value: SerializedValue,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -35,15 +35,15 @@ class PatchOptionsRepository(db: AppDatabase) {
|
|||||||
bundlePatchOptions.getOrPut(dbOption.patchName, ::mutableMapOf)
|
bundlePatchOptions.getOrPut(dbOption.patchName, ::mutableMapOf)
|
||||||
|
|
||||||
val option =
|
val option =
|
||||||
bundlePatches[sourceUid]?.get(dbOption.patchName)?.options?.find { it.key == dbOption.key }
|
bundlePatches[sourceUid]?.get(dbOption.patchName)?.options?.find { it.name == dbOption.name }
|
||||||
if (option != null) {
|
if (option != null) {
|
||||||
try {
|
try {
|
||||||
deserializedPatchOptions[option.key] =
|
deserializedPatchOptions[option.name] =
|
||||||
dbOption.value.deserializeFor(option)
|
dbOption.value.deserializeFor(option)
|
||||||
} catch (e: Option.SerializationException) {
|
} catch (e: Option.SerializationException) {
|
||||||
Log.w(
|
Log.w(
|
||||||
tag,
|
tag,
|
||||||
"Option ${dbOption.patchName}:${option.key} could not be deserialized",
|
"Option ${dbOption.patchName}:${option.name} could not be deserialized",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,66 +4,68 @@ import app.revanced.library.ApkUtils.applyTo
|
|||||||
import app.revanced.manager.patcher.Session.Companion.component1
|
import app.revanced.manager.patcher.Session.Companion.component1
|
||||||
import app.revanced.manager.patcher.Session.Companion.component2
|
import app.revanced.manager.patcher.Session.Companion.component2
|
||||||
import app.revanced.manager.patcher.logger.Logger
|
import app.revanced.manager.patcher.logger.Logger
|
||||||
import app.revanced.patcher.Patcher
|
import app.revanced.patcher.PatchesResult
|
||||||
import app.revanced.patcher.PatcherConfig
|
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
internal typealias PatchList = List<Patch<*>>
|
internal typealias PatchList = List<Patch>
|
||||||
|
private typealias Patcher = (emit: (PatchResult) -> Unit) -> PatchesResult
|
||||||
|
|
||||||
class Session(
|
class Session(
|
||||||
cacheDir: String,
|
cacheDir: String,
|
||||||
frameworkDir: String,
|
private val frameworkDir: String,
|
||||||
aaptPath: String,
|
private val aaptPath: String,
|
||||||
private val logger: Logger,
|
private val logger: Logger,
|
||||||
private val input: File,
|
private val input: File,
|
||||||
private val onEvent: (ProgressEvent) -> Unit,
|
private val onEvent: (ProgressEvent) -> Unit,
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
private val tempDir = File(cacheDir).resolve("patcher").also { it.mkdirs() }
|
private val tempDir = File(cacheDir).resolve("patcher").also { it.mkdirs() }
|
||||||
private val patcher = Patcher(
|
|
||||||
PatcherConfig(
|
|
||||||
apkFile = input,
|
|
||||||
temporaryFilesPath = tempDir,
|
|
||||||
frameworkFileDirectory = frameworkDir,
|
|
||||||
aaptBinaryPath = aaptPath
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private suspend fun Patcher.applyPatchesVerbose(selectedPatches: PatchList) {
|
private suspend fun applyPatchesVerbose(patcher: Patcher, selectedPatches: PatchList) =
|
||||||
this().collect { (patch, exception) ->
|
withContext(
|
||||||
val index = selectedPatches.indexOf(patch)
|
Dispatchers.Default
|
||||||
if (index == -1) return@collect
|
) {
|
||||||
|
val context = currentCoroutineContext()
|
||||||
|
patcher { (patch, exception) ->
|
||||||
|
// Make the patching process cancelable.
|
||||||
|
context.ensureActive()
|
||||||
|
|
||||||
|
val index = selectedPatches.indexOf(patch)
|
||||||
|
if (index == -1) return@patcher
|
||||||
|
|
||||||
|
if (exception != null) {
|
||||||
|
onEvent(
|
||||||
|
ProgressEvent.Failed(
|
||||||
|
StepId.ExecutePatch(index),
|
||||||
|
exception.toRemoteError(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.error("${patch.name} failed:")
|
||||||
|
logger.error(exception.stackTraceToString())
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
|
||||||
if (exception != null) {
|
|
||||||
onEvent(
|
onEvent(
|
||||||
ProgressEvent.Failed(
|
ProgressEvent.Completed(
|
||||||
StepId.ExecutePatch(index),
|
StepId.ExecutePatch(index),
|
||||||
exception.toRemoteError(),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
logger.error("${patch.name} failed:")
|
|
||||||
logger.error(exception.stackTraceToString())
|
logger.info("${patch.name} succeeded")
|
||||||
throw exception
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onEvent(
|
|
||||||
ProgressEvent.Completed(
|
|
||||||
StepId.ExecutePatch(index),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("${patch.name} succeeded")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun run(output: File, selectedPatches: PatchList) {
|
suspend fun run(output: File, selectedPatches: PatchList) {
|
||||||
runStep(StepId.ExecutePatches, onEvent) {
|
val result = runStep(StepId.ExecutePatches, onEvent) {
|
||||||
java.util.logging.Logger.getLogger("").apply {
|
java.util.logging.Logger.getLogger("").apply {
|
||||||
handlers.forEach {
|
handlers.forEach {
|
||||||
it.close()
|
it.close()
|
||||||
@@ -73,18 +75,21 @@ class Session(
|
|||||||
addHandler(logger.handler)
|
addHandler(logger.handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(patcher) {
|
val patcher = patcher(
|
||||||
logger.info("Merging integrations")
|
apkFile = input,
|
||||||
this += selectedPatches.toSet()
|
temporaryFilesPath = tempDir,
|
||||||
|
frameworkFileDirectory = frameworkDir,
|
||||||
logger.info("Applying patches...")
|
aaptBinaryPath = File(aaptPath)
|
||||||
applyPatchesVerbose(selectedPatches.sortedBy { it.name })
|
) { _packageName, _version ->
|
||||||
|
selectedPatches.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("Applying patches...")
|
||||||
|
applyPatchesVerbose(patcher, selectedPatches.sortedBy { it.name })
|
||||||
}
|
}
|
||||||
|
|
||||||
runStep(StepId.WriteAPK, onEvent) {
|
runStep(StepId.WriteAPK, onEvent) {
|
||||||
logger.info("Writing patched files...")
|
logger.info("Writing patched files...")
|
||||||
val result = patcher.get()
|
|
||||||
|
|
||||||
val patched = tempDir.resolve("result.apk")
|
val patched = tempDir.resolve("result.apk")
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
@@ -102,7 +107,7 @@ class Session(
|
|||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
tempDir.deleteRecursively()
|
tempDir.deleteRecursively()
|
||||||
patcher.close()
|
//patcher.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package app.revanced.manager.patcher.patch
|
|||||||
|
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import app.revanced.patcher.patch.loadPatchesFromDex
|
import app.revanced.patcher.patch.loadPatches
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -55,9 +55,12 @@ data class PatchBundle(val patchesJar: String) : Parcelable {
|
|||||||
|
|
||||||
object Loader {
|
object Loader {
|
||||||
private fun patches(bundles: Iterable<PatchBundle>) =
|
private fun patches(bundles: Iterable<PatchBundle>) =
|
||||||
loadPatchesFromDex(
|
loadPatches(
|
||||||
bundles.map { File(it.patchesJar) }.toSet()
|
*bundles.map { File(it.patchesJar) }.toTypedArray(),
|
||||||
).byPatchesFile.mapKeys { (file, _) ->
|
onFailedToLoad = { file, throwable ->
|
||||||
|
// TODO: handle error
|
||||||
|
}
|
||||||
|
).patchesByFile.mapKeys { (file, _) ->
|
||||||
val absPath = file.absolutePath
|
val absPath = file.absolutePath
|
||||||
bundles.single { absPath == it.patchesJar }
|
bundles.single { absPath == it.patchesJar }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ sealed class PatchBundleInfo {
|
|||||||
it.options.all option@{ option ->
|
it.options.all option@{ option ->
|
||||||
if (!option.required || option.default != null) return@option true
|
if (!option.required || option.default != null) return@option true
|
||||||
|
|
||||||
option.key in opts
|
option.name in opts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ data class PatchInfo(
|
|||||||
val compatiblePackages: ImmutableList<CompatiblePackage>?,
|
val compatiblePackages: ImmutableList<CompatiblePackage>?,
|
||||||
val options: ImmutableList<Option<*>>?
|
val options: ImmutableList<Option<*>>?
|
||||||
) {
|
) {
|
||||||
constructor(patch: Patch<*>) : this(
|
constructor(patch: Patch) : this(
|
||||||
patch.name.orEmpty(),
|
patch.name.orEmpty(),
|
||||||
patch.description,
|
patch.description,
|
||||||
patch.use,
|
patch.use,
|
||||||
@@ -49,7 +49,7 @@ data class PatchInfo(
|
|||||||
* The resulting patch cannot be executed.
|
* The resulting patch cannot be executed.
|
||||||
* This is necessary because some functions in ReVanced Library only accept full [Patch] objects.
|
* This is necessary because some functions in ReVanced Library only accept full [Patch] objects.
|
||||||
*/
|
*/
|
||||||
fun toPatcherPatch(): Patch<*> =
|
fun toPatcherPatch(): Patch =
|
||||||
resourcePatch(name = name, description = description, use = include) {
|
resourcePatch(name = name, description = description, use = include) {
|
||||||
compatiblePackages?.let { pkgs ->
|
compatiblePackages?.let { pkgs ->
|
||||||
compatibleWith(*pkgs.map { it.packageName to it.versions }.toTypedArray())
|
compatibleWith(*pkgs.map { it.packageName to it.versions }.toTypedArray())
|
||||||
@@ -65,8 +65,7 @@ data class CompatiblePackage(
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class Option<T>(
|
data class Option<T>(
|
||||||
val title: String,
|
val name: String,
|
||||||
val key: String,
|
|
||||||
val description: String,
|
val description: String,
|
||||||
val required: Boolean,
|
val required: Boolean,
|
||||||
val type: KType,
|
val type: KType,
|
||||||
@@ -75,8 +74,7 @@ data class Option<T>(
|
|||||||
val validator: (T?) -> Boolean,
|
val validator: (T?) -> Boolean,
|
||||||
) {
|
) {
|
||||||
constructor(option: PatchOption<T>) : this(
|
constructor(option: PatchOption<T>) : this(
|
||||||
option.title ?: option.key,
|
option.name,
|
||||||
option.key,
|
|
||||||
option.description.orEmpty(),
|
option.description.orEmpty(),
|
||||||
option.required,
|
option.required,
|
||||||
option.type,
|
option.type,
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ fun PatchItem(
|
|||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = option.title,
|
text = option.name,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ fun <T : Any> OptionItem(
|
|||||||
WithOptionEditor(editor, option, value, setValue, selectionWarningEnabled) {
|
WithOptionEditor(editor, option, value, setValue, selectionWarningEnabled) {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable(onClick = ::clickAction),
|
modifier = Modifier.clickable(onClick = ::clickAction),
|
||||||
headlineContent = { Text(option.title) },
|
headlineContent = { Text(option.name) },
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Column {
|
Column {
|
||||||
Text(option.description)
|
Text(option.description)
|
||||||
@@ -250,7 +250,7 @@ private object StringOptionEditor : OptionEditor<String> {
|
|||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = scope.dismissDialog,
|
onDismissRequest = scope.dismissDialog,
|
||||||
title = { Text(scope.option.title) },
|
title = { Text(scope.option.name) },
|
||||||
text = {
|
text = {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = fieldValue,
|
value = fieldValue,
|
||||||
@@ -330,7 +330,7 @@ private abstract class NumberOptionEditor<T : Number> : OptionEditor<T> {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Dialog(scope: OptionEditorScope<T>) {
|
override fun Dialog(scope: OptionEditorScope<T>) {
|
||||||
NumberDialog(scope.option.title, scope.value, scope.option.validator) {
|
NumberDialog(scope.option.name, scope.value, scope.option.validator) {
|
||||||
if (it == null) return@NumberDialog scope.dismissDialog()
|
if (it == null) return@NumberDialog scope.dismissDialog()
|
||||||
|
|
||||||
scope.submitDialog(it)
|
scope.submitDialog(it)
|
||||||
@@ -455,7 +455,7 @@ private class PresetOptionEditor<T : Any>(private val innerEditor: OptionEditor<
|
|||||||
Text(stringResource(R.string.cancel))
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title = { Text(scope.option.title) },
|
title = { Text(scope.option.name) },
|
||||||
textHorizontalPadding = PaddingValues(horizontal = 0.dp),
|
textHorizontalPadding = PaddingValues(horizontal = 0.dp),
|
||||||
text = {
|
text = {
|
||||||
val presets = remember(scope.option.presets) {
|
val presets = remember(scope.option.presets) {
|
||||||
@@ -496,8 +496,7 @@ private class PresetOptionEditor<T : Any>(private val innerEditor: OptionEditor<
|
|||||||
private class ListOptionEditor<T : Serializable>(private val elementEditor: OptionEditor<T>) :
|
private class ListOptionEditor<T : Serializable>(private val elementEditor: OptionEditor<T>) :
|
||||||
OptionEditor<List<T>> {
|
OptionEditor<List<T>> {
|
||||||
private fun createElementOption(option: Option<List<T>>) = Option<T>(
|
private fun createElementOption(option: Option<List<T>>) = Option<T>(
|
||||||
option.title,
|
option.name,
|
||||||
option.key,
|
|
||||||
option.description,
|
option.description,
|
||||||
option.required,
|
option.required,
|
||||||
option.type.arguments.first().type!!,
|
option.type.arguments.first().type!!,
|
||||||
@@ -566,7 +565,7 @@ private class ListOptionEditor<T : Serializable>(private val elementEditor: Opti
|
|||||||
R.plurals.selected_count,
|
R.plurals.selected_count,
|
||||||
deletionTargets.size,
|
deletionTargets.size,
|
||||||
deletionTargets.size
|
deletionTargets.size
|
||||||
) else scope.option.title,
|
) else scope.option.name,
|
||||||
onBackClick = back,
|
onBackClick = back,
|
||||||
backIcon = {
|
backIcon = {
|
||||||
if (deleteMode) {
|
if (deleteMode) {
|
||||||
|
|||||||
@@ -640,17 +640,17 @@ private fun OptionsDialog(
|
|||||||
) {
|
) {
|
||||||
if (patch.options == null) return@LazyColumnWithScrollbar
|
if (patch.options == null) return@LazyColumnWithScrollbar
|
||||||
|
|
||||||
items(patch.options, key = { it.key }) { option ->
|
items(patch.options, key = { it.name }) { option ->
|
||||||
val key = option.key
|
val name = option.name
|
||||||
val value =
|
val value =
|
||||||
if (values == null || !values.contains(key)) option.default else values[key]
|
if (values == null || !values.contains(name)) option.default else values[name]
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
OptionItem(
|
OptionItem(
|
||||||
option = option as Option<Any>,
|
option = option as Option<Any>,
|
||||||
value = value,
|
value = value,
|
||||||
setValue = {
|
setValue = {
|
||||||
set(key, it)
|
set(name, it)
|
||||||
},
|
},
|
||||||
selectionWarningEnabled = selectionWarningEnabled
|
selectionWarningEnabled = selectionWarningEnabled
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -144,16 +144,16 @@ fun RequiredOptionsScreen(
|
|||||||
|
|
||||||
val values = vm.getOptions(bundle.uid, it)
|
val values = vm.getOptions(bundle.uid, it)
|
||||||
it.options?.forEach { option ->
|
it.options?.forEach { option ->
|
||||||
val key = option.key
|
val name = option.name
|
||||||
val value =
|
val value =
|
||||||
if (values == null || key !in values) option.default else values[key]
|
if (values == null || name !in values) option.default else values[name]
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
OptionItem(
|
OptionItem(
|
||||||
option = option as Option<Any>,
|
option = option as Option<Any>,
|
||||||
value = value,
|
value = value,
|
||||||
setValue = { new ->
|
setValue = { new ->
|
||||||
vm.setOption(bundle.uid, it, key, new)
|
vm.setOption(bundle.uid, it, name, new)
|
||||||
},
|
},
|
||||||
selectionWarningEnabled = vm.selectionWarningEnabled
|
selectionWarningEnabled = vm.selectionWarningEnabled
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
|
|||||||
isSelected(
|
isSelected(
|
||||||
bundle.uid,
|
bundle.uid,
|
||||||
patch
|
patch
|
||||||
) && patch.options?.any { it.required && it.default == null && it.key !in opts } ?: false
|
) && patch.options?.any { it.required && it.default == null && it.name !in opts } ?: false
|
||||||
}.toList()
|
}.toList()
|
||||||
}.filter { (_, patches) -> patches.isNotEmpty() }
|
}.filter { (_, patches) -> patches.isNotEmpty() }
|
||||||
}
|
}
|
||||||
@@ -180,13 +180,13 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
|
|||||||
|
|
||||||
fun getOptions(bundle: Int, patch: PatchInfo) = patchOptions[bundle]?.get(patch.name)
|
fun getOptions(bundle: Int, patch: PatchInfo) = patchOptions[bundle]?.get(patch.name)
|
||||||
|
|
||||||
fun setOption(bundle: Int, patch: PatchInfo, key: String, value: Any?) {
|
fun setOption(bundle: Int, patch: PatchInfo, name: String, value: Any?) {
|
||||||
// All patches
|
// All patches
|
||||||
val patchesToOpts = patchOptions.getOrElse(bundle, ::persistentMapOf)
|
val patchesToOpts = patchOptions.getOrElse(bundle, ::persistentMapOf)
|
||||||
// The key-value options of an individual patch
|
// The key-value options of an individual patch
|
||||||
val patchToOpts = patchesToOpts
|
val patchToOpts = patchesToOpts
|
||||||
.getOrElse(patch.name, ::persistentMapOf)
|
.getOrElse(patch.name, ::persistentMapOf)
|
||||||
.put(key, value)
|
.put(name, value)
|
||||||
|
|
||||||
patchOptions[bundle] = patchesToOpts.put(patch.name, patchToOpts)
|
patchOptions[bundle] = patchesToOpts.put(patch.name, patchToOpts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ class SelectedAppInfoViewModel(
|
|||||||
bundleOptions.forEach patch@{ (patchName, values) ->
|
bundleOptions.forEach patch@{ (patchName, values) ->
|
||||||
// Get all valid option keys for the patch.
|
// Get all valid option keys for the patch.
|
||||||
val validOptionKeys =
|
val validOptionKeys =
|
||||||
patches[patchName]?.options?.map { it.key }?.toSet() ?: return@patch
|
patches[patchName]?.options?.map { it.name }?.toSet() ?: return@patch
|
||||||
|
|
||||||
this@bundleOptions[patchName] = values.filterKeys { key ->
|
this@bundleOptions[patchName] = values.filterKeys { key ->
|
||||||
key in validOptionKeys
|
key in validOptionKeys
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ serialization = "1.9.0"
|
|||||||
collection = "0.4.0"
|
collection = "0.4.0"
|
||||||
datetime = "0.7.1"
|
datetime = "0.7.1"
|
||||||
room-version = "2.8.4"
|
room-version = "2.8.4"
|
||||||
revanced-patcher = "21.0.0"
|
revanced-patcher = "22.0.0-local"
|
||||||
revanced-library = "3.0.2"
|
revanced-library = "3.2.0-dev.2-local"
|
||||||
koin = "4.1.1"
|
koin = "4.1.1"
|
||||||
ktor = "3.3.3"
|
ktor = "3.3.3"
|
||||||
markdown-renderer = "0.39.0"
|
markdown-renderer = "0.39.0"
|
||||||
@@ -81,8 +81,8 @@ room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room-ver
|
|||||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room-version" }
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room-version" }
|
||||||
|
|
||||||
# Patcher
|
# Patcher
|
||||||
revanced-patcher = { group = "app.revanced", name = "revanced-patcher", version.ref = "revanced-patcher" }
|
revanced-patcher = { group = "app.revanced", name = "patcher-android", version.ref = "revanced-patcher" }
|
||||||
revanced-library = { group = "app.revanced", name = "revanced-library", version.ref = "revanced-library" }
|
revanced-library = { group = "app.revanced", name = "revanced-library-android", version.ref = "revanced-library" }
|
||||||
|
|
||||||
# Koin
|
# Koin
|
||||||
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
|
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
@@ -8,6 +9,7 @@ pluginManagement {
|
|||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
maven("https://jitpack.io")
|
maven("https://jitpack.io")
|
||||||
|
|||||||
Reference in New Issue
Block a user