You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-10 05:30:49 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
85120374d6 | ||
![]() |
8b4819faa1 | ||
![]() |
d219276298 | ||
![]() |
79f91e0e5a | ||
![]() |
fadf62f594 | ||
![]() |
ad3d332e27 | ||
![]() |
8f66df7666 | ||
![]() |
80c2e80925 | ||
![]() |
c3db23d3c7 | ||
![]() |
c28584736e | ||
![]() |
6b909c1ee6 | ||
![]() |
0e8446516e |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,3 +1,36 @@
|
||||
## [4.1.2](https://github.com/revanced/revanced-patcher/compare/v4.1.1...v4.1.2) (2022-09-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* invalid types for example options ([79f91e0](https://github.com/revanced/revanced-patcher/commit/79f91e0e5a6d99828f30aae55339ce0d897394c7))
|
||||
|
||||
## [4.1.1](https://github.com/revanced/revanced-patcher/compare/v4.1.0...v4.1.1) (2022-09-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle private companion objects ([ad3d332](https://github.com/revanced/revanced-patcher/commit/ad3d332e27d07e9d074bbaaf51af7eb2f9bfc7d5))
|
||||
|
||||
# [4.1.0](https://github.com/revanced/revanced-patcher/compare/v4.0.0...v4.1.0) (2022-09-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* deprecation for patches ([80c2e80](https://github.com/revanced/revanced-patcher/commit/80c2e809251cdb04d2dd3b3bfdbb8844bdfa31fa))
|
||||
|
||||
# [4.0.0](https://github.com/revanced/revanced-patcher/compare/v3.5.1...v4.0.0) (2022-09-07)
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* Improve Patch Options ([6b909c1](https://github.com/revanced/revanced-patcher/commit/6b909c1ee6b8c2ea08bbca059df755e2e5f31656))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* 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.
|
||||
|
||||
## [3.5.1](https://github.com/revanced/revanced-patcher/compare/v3.5.0...v3.5.1) (2022-09-06)
|
||||
|
||||
|
||||
|
@@ -26,6 +26,7 @@ dependencies {
|
||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||
implementation("org.apktool:apktool-lib:2.7.0-SNAPSHOT")
|
||||
|
||||
implementation(kotlin("reflect"))
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
|
@@ -1,2 +1,2 @@
|
||||
kotlin.code.style = official
|
||||
version = 3.5.1
|
||||
version = 4.1.2
|
||||
|
@@ -4,6 +4,7 @@ import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.data.PackageMetadata
|
||||
import app.revanced.patcher.data.impl.findIndexed
|
||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||
import app.revanced.patcher.extensions.PatchExtensions.deprecated
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.extensions.nullOutputStream
|
||||
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
|
||||
@@ -289,6 +290,10 @@ class Patcher(private val options: PatcherOptions) {
|
||||
return PatchResultError("'$patchName' is a resource patch, but resource patching is disabled")
|
||||
}
|
||||
|
||||
patch.deprecated?.let { (reason, replacement) ->
|
||||
logger.warn("'$patchName' is deprecated: '$reason'" + if (replacement != null) ". Use '$replacement' instead." else "")
|
||||
}
|
||||
|
||||
// TODO: find a solution for this
|
||||
val data = if (isResourcePatch) {
|
||||
data.resourceData
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package app.revanced.patcher.annotation
|
||||
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Declares a [Patch] deprecated for removal.
|
||||
* @param reason The reason why the patch is deprecated.
|
||||
* @param replacement The replacement for the deprecated patch, if any.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MustBeDocumented
|
||||
@Repeatable
|
||||
annotation class PatchDeprecated(
|
||||
val reason: String,
|
||||
val replacement: KClass<out Patch<Data>> = Patch::class
|
||||
// Values cannot be nullable in annotations, so this will have to do.
|
||||
)
|
@@ -1,13 +1,15 @@
|
||||
package app.revanced.patcher.extensions
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.annotation.*
|
||||
import app.revanced.patcher.data.Data
|
||||
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.PatchOptions
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KVisibility
|
||||
import kotlin.reflect.full.companionObject
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
|
||||
/**
|
||||
* Recursively find a given annotation on a class.
|
||||
@@ -43,6 +45,19 @@ object PatchExtensions {
|
||||
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>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
val Class<out Patch<Data>>.options: PatchOptions?
|
||||
get() = kotlin.companionObject?.let { cl ->
|
||||
if (cl.visibility != KVisibility.PUBLIC) return null
|
||||
kotlin.companionObjectInstance?.let {
|
||||
(it as? OptionsContainer)?.options
|
||||
}
|
||||
}
|
||||
val Class<out Patch<Data>>.deprecated: Pair<String, KClass<out Patch<Data>>?>?
|
||||
get() = recursiveAnnotation(PatchDeprecated::class)?.let {
|
||||
it.reason to it.replacement.let { cl ->
|
||||
if (cl == Patch::class) null else cl
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
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.
|
||||
*/
|
||||
abstract fun execute(data: @UnsafeVariance T): PatchResult
|
||||
}
|
||||
|
||||
abstract class OptionsContainer {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class PatchOptions(vararg val options: PatchOption<*>) : Iterable<PatchOption<*>> {
|
||||
private val register = buildMap {
|
||||
for (option in options) {
|
||||
if (containsKey(option.key)) {
|
||||
throw IllegalStateException("Multiple options found with the same key")
|
||||
}
|
||||
put(option.key, option)
|
||||
private val register = mutableMapOf<String, PatchOption<*>>()
|
||||
|
||||
init {
|
||||
options.forEach { register(it) }
|
||||
}
|
||||
|
||||
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.
|
||||
* 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.
|
||||
|
@@ -7,7 +7,7 @@ import java.io.File
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
internal class PatchOptionsTest {
|
||||
private val options = ExampleBytecodePatch().options
|
||||
private val options = ExampleBytecodePatch.options
|
||||
|
||||
@Test
|
||||
fun `should not throw an exception`() {
|
||||
@@ -16,21 +16,25 @@ internal class PatchOptionsTest {
|
||||
is PatchOption.StringOption -> {
|
||||
option.value = "Hello World"
|
||||
}
|
||||
|
||||
is PatchOption.BooleanOption -> {
|
||||
option.value = false
|
||||
}
|
||||
|
||||
is PatchOption.StringListOption -> {
|
||||
option.value = option.options.first()
|
||||
for (choice in option.options) {
|
||||
println(choice)
|
||||
}
|
||||
}
|
||||
|
||||
is PatchOption.IntListOption -> {
|
||||
option.value = option.options.first()
|
||||
for (choice in option.options) {
|
||||
println(choice)
|
||||
}
|
||||
}
|
||||
|
||||
is PatchOption.PathOption -> {
|
||||
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.or
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.OptionsContainer
|
||||
import app.revanced.patcher.patch.PatchOption
|
||||
import app.revanced.patcher.patch.PatchOptions
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
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.util.Preconditions
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
|
||||
@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
|
||||
val result = ExampleFingerprint.result!!
|
||||
|
||||
// Patch options
|
||||
println(key1)
|
||||
key2 = false
|
||||
|
||||
// Get the implementation for the resolved method
|
||||
val method = result.mutableMethod
|
||||
val implementation = method.implementation!!
|
||||
@@ -165,21 +170,31 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
||||
)
|
||||
}
|
||||
|
||||
override val options = PatchOptions(
|
||||
PatchOption.StringOption(
|
||||
"key1", "default", "title", "description", true
|
||||
),
|
||||
PatchOption.BooleanOption(
|
||||
"key2", true, "title", "description" // required defaults to false
|
||||
),
|
||||
PatchOption.StringListOption(
|
||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
||||
),
|
||||
PatchOption.IntListOption(
|
||||
"key4", 1, listOf(1, 2, 3), "title", "description"
|
||||
),
|
||||
PatchOption.PathOption(
|
||||
"key5", File("test.txt").toPath(), "title", "description"
|
||||
),
|
||||
)
|
||||
companion object : OptionsContainer() {
|
||||
private var key1: String by option(
|
||||
PatchOption.StringOption(
|
||||
"key1", "default", "title", "description", true
|
||||
)
|
||||
)
|
||||
private var key2: Boolean by option(
|
||||
PatchOption.BooleanOption(
|
||||
"key2", true, "title", "description" // required defaults to false
|
||||
)
|
||||
)
|
||||
private var key3: String by option(
|
||||
PatchOption.StringListOption(
|
||||
"key3", "TEST", listOf("TEST", "TEST1", "TEST2"), "title", "description"
|
||||
)
|
||||
)
|
||||
private var key4: 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"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user