mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-04 14:34:25 +02:00
refactor: Improve Patch Options
It's so much better now. Really happy with the current system. BREAKING CHANGE: Options has been moved from Patch to a new interface called OptionsContainer and are now handled entirely different. Make sure to check the examples to understand how it works.
This commit is contained in:
parent
0e8446516e
commit
6b909c1ee6
@ -6,8 +6,10 @@ import app.revanced.patcher.annotation.Name
|
|||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.Data
|
import app.revanced.patcher.data.Data
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
import app.revanced.patcher.patch.Patch
|
import app.revanced.patcher.patch.Patch
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively find a given annotation on a class.
|
* Recursively find a given annotation on a class.
|
||||||
@ -43,6 +45,9 @@ object PatchExtensions {
|
|||||||
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
val Class<out Patch<Data>>.description get() = recursiveAnnotation(Description::class)?.description
|
||||||
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
|
val Class<out Patch<Data>>.dependencies get() = recursiveAnnotation(app.revanced.patcher.patch.annotations.DependsOn::class)?.dependencies
|
||||||
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||||
|
val Class<out Patch<Data>>.options get() = kotlin.companionObjectInstance?.let {
|
||||||
|
(it as? OptionsContainer)?.options
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {
|
fun Class<out Patch<Data>>.dependsOn(patch: Class<out Patch<Data>>): Boolean {
|
||||||
|
@ -17,9 +17,18 @@ abstract class Patch<out T : Data> {
|
|||||||
* The main function of the [Patch] which the patcher will call.
|
* The main function of the [Patch] which the patcher will call.
|
||||||
*/
|
*/
|
||||||
abstract fun execute(data: @UnsafeVariance T): PatchResult
|
abstract fun execute(data: @UnsafeVariance T): PatchResult
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class OptionsContainer {
|
||||||
/**
|
/**
|
||||||
* A list of [PatchOption]s.
|
* A list of [PatchOption]s.
|
||||||
|
* @see PatchOptions
|
||||||
*/
|
*/
|
||||||
open val options = PatchOptions()
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
val options = PatchOptions()
|
||||||
|
|
||||||
|
protected fun option(opt: PatchOption<*>): PatchOption<*> {
|
||||||
|
options.register(opt)
|
||||||
|
return opt
|
||||||
|
}
|
||||||
}
|
}
|
@ -18,13 +18,17 @@ object RequirementNotMetException : Exception("null was passed into an option th
|
|||||||
* @param options An array of [PatchOption]s.
|
* @param options An array of [PatchOption]s.
|
||||||
*/
|
*/
|
||||||
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
||||||
private val register = buildMap {
|
private val register = mutableMapOf<String, PatchOption<*>>()
|
||||||
for (option in options) {
|
|
||||||
if (containsKey(option.key)) {
|
init {
|
||||||
throw IllegalStateException("Multiple options found with the same key")
|
options.forEach { register(it) }
|
||||||
}
|
}
|
||||||
put(option.key, option)
|
|
||||||
|
internal fun register(option: PatchOption<*>) {
|
||||||
|
if (register.containsKey(option.key)) {
|
||||||
|
throw IllegalStateException("Multiple options found with the same key")
|
||||||
}
|
}
|
||||||
|
register[option.key] = option
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +95,7 @@ sealed class PatchOption<T>(
|
|||||||
* Gets the value of the option.
|
* Gets the value of the option.
|
||||||
* Please note that using the wrong value type results in a runtime error.
|
* Please note that using the wrong value type results in a runtime error.
|
||||||
*/
|
*/
|
||||||
operator fun <T> getValue(thisRef: Nothing?, property: KProperty<*>) = value as T
|
operator fun <T> getValue(thisRef: Any?, property: KProperty<*>) = value as T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the option.
|
* Gets the value of the option.
|
||||||
|
@ -7,7 +7,7 @@ import java.io.File
|
|||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
internal class PatchOptionsTest {
|
internal class PatchOptionsTest {
|
||||||
private val options = ExampleBytecodePatch().options
|
private val options = ExampleBytecodePatch.options
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should not throw an exception`() {
|
fun `should not throw an exception`() {
|
||||||
@ -16,21 +16,25 @@ internal class PatchOptionsTest {
|
|||||||
is PatchOption.StringOption -> {
|
is PatchOption.StringOption -> {
|
||||||
option.value = "Hello World"
|
option.value = "Hello World"
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.BooleanOption -> {
|
is PatchOption.BooleanOption -> {
|
||||||
option.value = false
|
option.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.StringListOption -> {
|
is PatchOption.StringListOption -> {
|
||||||
option.value = option.options.first()
|
option.value = option.options.first()
|
||||||
for (choice in option.options) {
|
for (choice in option.options) {
|
||||||
println(choice)
|
println(choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.IntListOption -> {
|
is PatchOption.IntListOption -> {
|
||||||
option.value = option.options.first()
|
option.value = option.options.first()
|
||||||
for (choice in option.options) {
|
for (choice in option.options) {
|
||||||
println(choice)
|
println(choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is PatchOption.PathOption -> {
|
is PatchOption.PathOption -> {
|
||||||
option.value = File("test.txt").toPath()
|
option.value = File("test.txt").toPath()
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import app.revanced.patcher.data.impl.BytecodeData
|
|||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
import app.revanced.patcher.patch.PatchOption
|
import app.revanced.patcher.patch.PatchOption
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
@ -33,6 +33,7 @@ import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
|||||||
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
import org.jf.dexlib2.immutable.value.ImmutableFieldEncodedValue
|
||||||
import org.jf.dexlib2.util.Preconditions
|
import org.jf.dexlib2.util.Preconditions
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@Name("example-bytecode-patch")
|
@Name("example-bytecode-patch")
|
||||||
@ -47,6 +48,10 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|||||||
// Get the resolved method by its fingerprint from the resolver cache
|
// Get the resolved method by its fingerprint from the resolver cache
|
||||||
val result = ExampleFingerprint.result!!
|
val result = ExampleFingerprint.result!!
|
||||||
|
|
||||||
|
// Patch options
|
||||||
|
println(key1)
|
||||||
|
key2 = false
|
||||||
|
|
||||||
// Get the implementation for the resolved method
|
// Get the implementation for the resolved method
|
||||||
val method = result.mutableMethod
|
val method = result.mutableMethod
|
||||||
val implementation = method.implementation!!
|
val implementation = method.implementation!!
|
||||||
@ -165,21 +170,31 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val options = PatchOptions(
|
companion object : OptionsContainer() {
|
||||||
PatchOption.StringOption(
|
private var key1: String by option(
|
||||||
"key1", "default", "title", "description", true
|
PatchOption.StringOption(
|
||||||
),
|
"key1", "default", "title", "description", true
|
||||||
PatchOption.BooleanOption(
|
)
|
||||||
"key2", true, "title", "description" // required defaults to false
|
)
|
||||||
),
|
private var key2: Boolean by option(
|
||||||
PatchOption.StringListOption(
|
PatchOption.BooleanOption(
|
||||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
"key2", true, "title", "description" // required defaults to false
|
||||||
),
|
)
|
||||||
PatchOption.IntListOption(
|
)
|
||||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
private var key3: List<String> by option(
|
||||||
),
|
PatchOption.StringListOption(
|
||||||
PatchOption.PathOption(
|
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
||||||
"key5", File("test.txt").toPath(), "title", "description"
|
)
|
||||||
),
|
)
|
||||||
)
|
private var key4: List<Int> by option(
|
||||||
|
PatchOption.IntListOption(
|
||||||
|
"key4", 1, listOf(1, 2, 3), "title", "description"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private var key5: Path by option(
|
||||||
|
PatchOption.PathOption(
|
||||||
|
"key5", File("test.txt").toPath(), "title", "description"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user