mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-10 21:36:17 +00:00
refactor: Move functions to top level
This commit is contained in:
@@ -88,6 +88,15 @@ public final class app/revanced/library/Options$Patch$Option {
|
|||||||
public final fun getValue ()Ljava/lang/Object;
|
public final fun getValue ()Ljava/lang/Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/OptionsKt {
|
||||||
|
public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/PatchKt {
|
||||||
|
public static final fun mostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
||||||
|
public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/PatchUtils {
|
public final class app/revanced/library/PatchUtils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
||||||
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
||||||
@@ -122,6 +131,11 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa
|
|||||||
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
|
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/SerializationKt {
|
||||||
|
public static final fun serializeTo (Ljava/util/Set;Ljava/io/OutputStream;Z)V
|
||||||
|
public static synthetic fun serializeTo$default (Ljava/util/Set;Ljava/io/OutputStream;ZILjava/lang/Object;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/Utils {
|
public final class app/revanced/library/Utils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/Utils;
|
public static final field INSTANCE Lapp/revanced/library/Utils;
|
||||||
public final fun isAndroidEnvironment ()Z
|
public final fun isAndroidEnvironment ()Z
|
||||||
|
|||||||
@@ -88,6 +88,15 @@ public final class app/revanced/library/Options$Patch$Option {
|
|||||||
public final fun getValue ()Ljava/lang/Object;
|
public final fun getValue ()Ljava/lang/Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/OptionsKt {
|
||||||
|
public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/PatchKt {
|
||||||
|
public static final fun mostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
||||||
|
public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/PatchUtils {
|
public final class app/revanced/library/PatchUtils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
|
||||||
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
|
||||||
@@ -122,6 +131,11 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa
|
|||||||
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
|
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/library/SerializationKt {
|
||||||
|
public static final fun serializeTo (Ljava/util/Set;Ljava/io/OutputStream;Z)V
|
||||||
|
public static synthetic fun serializeTo$default (Ljava/util/Set;Ljava/io/OutputStream;ZILjava/lang/Object;)V
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/library/Utils {
|
public final class app/revanced/library/Utils {
|
||||||
public static final field INSTANCE Lapp/revanced/library/Utils;
|
public static final field INSTANCE Lapp/revanced/library/Utils;
|
||||||
public final fun isAndroidEnvironment ()Z
|
public final fun isAndroidEnvironment ()Z
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.multiplatform)
|
|
||||||
alias(libs.plugins.android.library)
|
alias(libs.plugins.android.library)
|
||||||
alias(libs.plugins.binary.compatibility.validator)
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
|
alias(libs.plugins.kotlin.serialization)
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
signing
|
signing
|
||||||
}
|
}
|
||||||
@@ -47,25 +48,26 @@ kotlin {
|
|||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
|
implementation(libs.core.ktx)
|
||||||
implementation(libs.libsu.nio)
|
implementation(libs.libsu.nio)
|
||||||
implementation(libs.libsu.service)
|
implementation(libs.libsu.service)
|
||||||
implementation(libs.core.ktx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(libs.revanced.patcher)
|
|
||||||
implementation(libs.kotlin.reflect)
|
|
||||||
implementation(libs.jadb) // Fork with Shell v2 support.
|
|
||||||
implementation(libs.bcpkix.jdk15on)
|
|
||||||
implementation(libs.jackson.module.kotlin)
|
|
||||||
implementation(libs.apkzlib)
|
|
||||||
implementation(libs.apksig)
|
implementation(libs.apksig)
|
||||||
|
implementation(libs.apkzlib)
|
||||||
|
implementation(libs.bcpkix.jdk15on)
|
||||||
implementation(libs.guava)
|
implementation(libs.guava)
|
||||||
|
implementation(libs.jadb)
|
||||||
|
implementation(libs.jackson.module.kotlin)
|
||||||
|
implementation(libs.kotlin.reflect)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.revanced.patcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
implementation(libs.revanced.patcher)
|
|
||||||
implementation(libs.kotlin.test.junit)
|
implementation(libs.kotlin.test.junit)
|
||||||
|
implementation(libs.revanced.patcher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
android = "8.5.1"
|
||||||
|
bcpkix-jdk15on = "1.70"
|
||||||
|
binary-compatibility-validator = "0.15.1"
|
||||||
|
core-ktx = "1.13.1"
|
||||||
|
guava = "33.0.0-jre"
|
||||||
jackson-module-kotlin = "2.16.1"
|
jackson-module-kotlin = "2.16.1"
|
||||||
jadb = "1.2.1"
|
jadb = "1.2.1"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
@@ -8,22 +13,25 @@ libsu = "5.2.2"
|
|||||||
revanced-patcher = "20.0.0"
|
revanced-patcher = "20.0.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
|
|
||||||
jadb = { module = "app.revanced:jadb", version.ref = "jadb" }
|
|
||||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
|
||||||
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
|
||||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
|
||||||
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" }
|
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" }
|
||||||
apksig = { module = "com.android.tools.build:apksig", version.ref = "android" }
|
apksig = { module = "com.android.tools.build:apksig", version.ref = "android" }
|
||||||
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
|
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
|
||||||
|
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
|
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
|
||||||
|
jadb = { module = "app.revanced:jadb", version.ref = "jadb" } # Fork with Shell v2 support.
|
||||||
|
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||||
|
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||||
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
|
||||||
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||||
libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
|
libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
|
||||||
libsu-nio = { module = "com.github.topjohnwu.libsu:nio", version.ref = "libsu" }
|
libsu-nio = { module = "com.github.topjohnwu.libsu:nio", version.ref = "libsu" }
|
||||||
libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }
|
libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }
|
||||||
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
|
||||||
android-library = { id = "com.android.library", version.ref = "android" }
|
android-library = { id = "com.android.library", version.ref = "android" }
|
||||||
|
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
|
|||||||
@@ -8,7 +8,32 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
private val logger = Logger.getLogger("Options")
|
||||||
|
|
||||||
|
typealias PatchName = String
|
||||||
|
typealias OptionKey = String
|
||||||
|
typealias OptionValue = Any?
|
||||||
|
typealias PatchesOptions = Map<PatchName, Map<OptionKey, OptionValue>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the options for a set of patches that have a name.
|
||||||
|
*
|
||||||
|
* @param options The options to set. The key is the patch name and the value is a map of option keys to option values.
|
||||||
|
*/
|
||||||
|
fun Set<Patch<*>>.setOptions(options: PatchesOptions) = filter { it.name != null }.forEach { patch ->
|
||||||
|
val patchOptions = options[patch.name] ?: return@forEach
|
||||||
|
|
||||||
|
patch.options.forEach option@{ option ->
|
||||||
|
try {
|
||||||
|
patch.options[option.key] = patchOptions[option.key] ?: return@option
|
||||||
|
} catch (e: OptionException) {
|
||||||
|
logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@Deprecated("Functions have been moved to top level.")
|
||||||
object Options {
|
object Options {
|
||||||
private val logger = Logger.getLogger(Options::class.java.name)
|
private val logger = Logger.getLogger(Options::class.java.name)
|
||||||
|
|
||||||
@@ -21,6 +46,7 @@ object Options {
|
|||||||
* @param prettyPrint Whether to pretty print the JSON.
|
* @param prettyPrint Whether to pretty print the JSON.
|
||||||
* @return The JSON string containing the options.
|
* @return The JSON string containing the options.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Functions have been moved to the Serialization class.")
|
||||||
fun serialize(
|
fun serialize(
|
||||||
patches: Set<app.revanced.patcher.patch.Patch<*>>,
|
patches: Set<app.revanced.patcher.patch.Patch<*>>,
|
||||||
prettyPrint: Boolean = false,
|
prettyPrint: Boolean = false,
|
||||||
@@ -35,7 +61,7 @@ object Options {
|
|||||||
try {
|
try {
|
||||||
option.value
|
option.value
|
||||||
} catch (e: OptionException) {
|
} catch (e: OptionException) {
|
||||||
logger.warning("Using default option value for the ${patch.name} patch: ${e.message}")
|
logger.warning("Using default option value for the \"${patch.name}\" patch: ${e.message}")
|
||||||
option.default
|
option.default
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +86,7 @@ object Options {
|
|||||||
* @return A set of [Patch]s.
|
* @return A set of [Patch]s.
|
||||||
* @see Patch
|
* @see Patch
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Functions have been moved to the Serialization class.")
|
||||||
fun deserialize(json: String): Array<Patch> = mapper.readValue(json, Array<Patch>::class.java)
|
fun deserialize(json: String): Array<Patch> = mapper.readValue(json, Array<Patch>::class.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,26 +94,16 @@ object Options {
|
|||||||
*
|
*
|
||||||
* @param json The JSON string containing the options.
|
* @param json The JSON string containing the options.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Function has been moved to top level.")
|
||||||
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(json: String) {
|
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(json: String) {
|
||||||
filter { it.options.any() }.let { patches ->
|
filter { it.options.any() }.let { patches ->
|
||||||
if (patches.isEmpty()) return
|
if (patches.isEmpty()) return
|
||||||
|
|
||||||
val jsonPatches =
|
val jsonPatches = deserialize(json).associate {
|
||||||
deserialize(json).associate {
|
it.patchName to it.options.associate { option -> option.key to option.value }
|
||||||
it.patchName to it.options.associate { option -> option.key to option.value }
|
|
||||||
}
|
|
||||||
|
|
||||||
patches.forEach { patch ->
|
|
||||||
jsonPatches[patch.name]?.let { jsonPatchOptions ->
|
|
||||||
jsonPatchOptions.forEach { (option, value) ->
|
|
||||||
try {
|
|
||||||
patch.options[option] = value
|
|
||||||
} catch (e: OptionException) {
|
|
||||||
logger.warning("Could not set option value for the ${patch.name} patch: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOptions(jsonPatches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +113,7 @@ object Options {
|
|||||||
* @param file The file containing the JSON string containing the options.
|
* @param file The file containing the JSON string containing the options.
|
||||||
* @see setOptions
|
* @see setOptions
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Function has been moved to top level.")
|
||||||
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(file: File) = setOptions(file.readText())
|
fun Set<app.revanced.patcher.patch.Patch<*>>.setOptions(file: File) = setOptions(file.readText())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal file
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package app.revanced.library
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Package
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
|
||||||
|
typealias PackageName = String
|
||||||
|
typealias Version = String
|
||||||
|
typealias Count = Int
|
||||||
|
|
||||||
|
typealias VersionMap = LinkedHashMap<Version, Count>
|
||||||
|
typealias PackageNameMap = Map<PackageName, VersionMap>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the count of versions for each compatible package from the set of [Patch] ordered by the most common version.
|
||||||
|
*
|
||||||
|
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
|
||||||
|
* @param countUnusedPatches Whether to count patches that are not used.
|
||||||
|
* @return A map of package names to a map of versions to their count.
|
||||||
|
*/
|
||||||
|
fun Set<Patch<*>>.mostCommonCompatibleVersions(
|
||||||
|
packageNames: Set<String>? = null,
|
||||||
|
countUnusedPatches: Boolean = false,
|
||||||
|
): PackageNameMap = buildMap {
|
||||||
|
fun filterWantedPackages(compatiblePackages: List<Package>): List<Package> {
|
||||||
|
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
|
||||||
|
return compatiblePackages.filter { (name, _) -> name in wantedPackages }
|
||||||
|
}
|
||||||
|
|
||||||
|
this@mostCommonCompatibleVersions.filter { it.use || countUnusedPatches }
|
||||||
|
.flatMap { it.compatiblePackages ?: emptyList() }
|
||||||
|
.let(::filterWantedPackages)
|
||||||
|
.forEach { (name, versions) ->
|
||||||
|
if (versions?.isEmpty() == true) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
val versionMap = getOrPut(name) { linkedMapOf() }
|
||||||
|
|
||||||
|
versions?.forEach { version ->
|
||||||
|
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the version maps by the most common version.
|
||||||
|
forEach { (packageName, versionMap) ->
|
||||||
|
this[packageName] =
|
||||||
|
versionMap
|
||||||
|
.asIterable()
|
||||||
|
.sortedWith(compareByDescending { it.value })
|
||||||
|
.associate { it.key to it.value } as VersionMap
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,63 +8,19 @@ import java.io.InputStream
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
typealias PackageName = String
|
@Deprecated("Functions have been moved to top level.")
|
||||||
typealias Version = String
|
|
||||||
typealias Count = Int
|
|
||||||
|
|
||||||
typealias VersionMap = LinkedHashMap<Version, Count>
|
|
||||||
typealias PackageNameMap = Map<PackageName, VersionMap>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility functions for working with patches.
|
|
||||||
*/
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
|
||||||
object PatchUtils {
|
object PatchUtils {
|
||||||
/**
|
@Deprecated(
|
||||||
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
|
"Function has been moved to top level.",
|
||||||
*
|
ReplaceWith("patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches)"),
|
||||||
* @param patches The set of patches to check.
|
)
|
||||||
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
|
|
||||||
* @param countUnusedPatches Whether to count patches that are not used.
|
|
||||||
* @return A map of package names to a map of versions to their count.
|
|
||||||
*/
|
|
||||||
fun getMostCommonCompatibleVersions(
|
fun getMostCommonCompatibleVersions(
|
||||||
patches: Set<Patch<*>>,
|
patches: Set<Patch<*>>,
|
||||||
packageNames: Set<String>? = null,
|
packageNames: Set<String>? = null,
|
||||||
countUnusedPatches: Boolean = false,
|
countUnusedPatches: Boolean = false,
|
||||||
): PackageNameMap =
|
): PackageNameMap = patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches)
|
||||||
buildMap {
|
|
||||||
fun filterWantedPackages(compatiblePackages: Iterable<Package>): Iterable<Package> {
|
|
||||||
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
|
|
||||||
return compatiblePackages.filter { (name, _) -> name in wantedPackages }
|
|
||||||
}
|
|
||||||
|
|
||||||
patches
|
|
||||||
.filter { it.use || countUnusedPatches }
|
|
||||||
.flatMap { it.compatiblePackages ?: emptyList() }
|
|
||||||
.let(::filterWantedPackages)
|
|
||||||
.forEach { (name, versions) ->
|
|
||||||
if (versions?.isEmpty() == true) {
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
|
|
||||||
val versionMap = getOrPut(name) { linkedMapOf() }
|
|
||||||
|
|
||||||
versions?.forEach { version ->
|
|
||||||
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the version maps by the most common version.
|
|
||||||
forEach { (packageName, versionMap) ->
|
|
||||||
this[packageName] =
|
|
||||||
versionMap
|
|
||||||
.asIterable()
|
|
||||||
.sortedWith(compareByDescending { it.value })
|
|
||||||
.associate { it.key to it.value } as VersionMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Deprecated("Functions have been moved to the Serialization class.")
|
||||||
object Json {
|
object Json {
|
||||||
private val mapper = jacksonObjectMapper()
|
private val mapper = jacksonObjectMapper()
|
||||||
|
|
||||||
@@ -76,6 +32,7 @@ object PatchUtils {
|
|||||||
* @param prettyPrint Whether to pretty print the JSON.
|
* @param prettyPrint Whether to pretty print the JSON.
|
||||||
* @param outputStream The output stream to write the JSON to.
|
* @param outputStream The output stream to write the JSON to.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Functions have been moved to the Serialization class.")
|
||||||
fun serialize(
|
fun serialize(
|
||||||
patches: Set<Patch<*>>,
|
patches: Set<Patch<*>>,
|
||||||
transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) },
|
transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) },
|
||||||
@@ -99,6 +56,7 @@ object PatchUtils {
|
|||||||
* @return A set of [JsonPatch]es.
|
* @return A set of [JsonPatch]es.
|
||||||
* @see FullJsonPatch
|
* @see FullJsonPatch
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("This function will be removed in the future.")
|
||||||
fun <T : JsonPatch> deserialize(
|
fun <T : JsonPatch> deserialize(
|
||||||
inputStream: InputStream,
|
inputStream: InputStream,
|
||||||
jsonPatchElementClass: Class<T>,
|
jsonPatchElementClass: Class<T>,
|
||||||
|
|||||||
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package app.revanced.library
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.*
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.builtins.*
|
||||||
|
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.element
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.encoding.encodeStructure
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
private class PatchSerializer : KSerializer<Patch<*>> {
|
||||||
|
override val descriptor = buildClassSerialDescriptor("Patch") {
|
||||||
|
element<String?>("name")
|
||||||
|
element<String?>("description")
|
||||||
|
element<Boolean>("use")
|
||||||
|
element<List<String>>("dependencies")
|
||||||
|
element<Set<Package>?>("compatiblePackages")
|
||||||
|
element("options", OptionSerializer.descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
override fun serialize(encoder: Encoder, value: Patch<*>) {
|
||||||
|
encoder.encodeStructure(descriptor) {
|
||||||
|
encodeNullableSerializableElement(
|
||||||
|
descriptor,
|
||||||
|
0,
|
||||||
|
String.serializer(),
|
||||||
|
value.name,
|
||||||
|
)
|
||||||
|
encodeNullableSerializableElement(
|
||||||
|
descriptor,
|
||||||
|
1,
|
||||||
|
String.serializer(),
|
||||||
|
value.description,
|
||||||
|
)
|
||||||
|
encodeBooleanElement(
|
||||||
|
descriptor,
|
||||||
|
2,
|
||||||
|
value.use,
|
||||||
|
)
|
||||||
|
encodeSerializableElement(
|
||||||
|
descriptor,
|
||||||
|
3,
|
||||||
|
ListSerializer(String.serializer()),
|
||||||
|
value.dependencies.map { it.name ?: it.toString() },
|
||||||
|
)
|
||||||
|
encodeNullableSerializableElement(
|
||||||
|
descriptor,
|
||||||
|
4,
|
||||||
|
SetSerializer(PairSerializer(String.serializer(), SetSerializer(String.serializer()).nullable)),
|
||||||
|
value.compatiblePackages,
|
||||||
|
)
|
||||||
|
encodeSerializableElement(
|
||||||
|
descriptor,
|
||||||
|
5,
|
||||||
|
SetSerializer(OptionSerializer),
|
||||||
|
value.options.values.toSet(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object OptionSerializer : KSerializer<Option<*>> {
|
||||||
|
override val descriptor = buildClassSerialDescriptor("Option") {
|
||||||
|
element<String>("key")
|
||||||
|
element<String?>("title")
|
||||||
|
element<String?>("description")
|
||||||
|
element<Boolean>("required")
|
||||||
|
// Type does not matter for serialization. Using String.
|
||||||
|
element<String>("type")
|
||||||
|
element<String?>("default")
|
||||||
|
// Map value type does not matter for serialization. Using String.
|
||||||
|
element<Map<String, String?>?>("values")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
override fun serialize(encoder: Encoder, value: Option<*>) {
|
||||||
|
encoder.encodeStructure(descriptor) {
|
||||||
|
encodeStringElement(descriptor, 0, value.key)
|
||||||
|
encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.title)
|
||||||
|
encodeNullableSerializableElement(descriptor, 2, String.serializer(), value.description)
|
||||||
|
encodeBooleanElement(descriptor, 3, value.required)
|
||||||
|
encodeSerializableElement(descriptor, 4, String.serializer(), value.type.toString())
|
||||||
|
encodeNullableSerializableElement(descriptor, 5, serializer(value.type), value.default)
|
||||||
|
encodeNullableSerializableElement(descriptor, 6, MapSerializer(String.serializer(), serializer(value.type)), value.values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val patchPrettySerializer by lazy { Json { prettyPrint = true } }
|
||||||
|
private val patchSerializer by lazy { Json }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize this set of [Patch] to JSON and write it to the given [outputStream].
|
||||||
|
*
|
||||||
|
* @param outputStream The output stream to write the JSON to.
|
||||||
|
* @param prettyPrint Whether to pretty print the JSON.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
fun Set<Patch<*>>.serializeTo(
|
||||||
|
outputStream: OutputStream,
|
||||||
|
prettyPrint: Boolean = true,
|
||||||
|
) = if (prettyPrint) {
|
||||||
|
patchPrettySerializer
|
||||||
|
} else {
|
||||||
|
patchSerializer
|
||||||
|
}.encodeToStream(SetSerializer(PatchSerializer()), this, outputStream)
|
||||||
@@ -1,27 +1,19 @@
|
|||||||
package app.revanced.library
|
package app.revanced.library
|
||||||
|
|
||||||
import app.revanced.patcher.PatchSet
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intArrayPatchOption
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
|
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
internal class PatchUtilsTest {
|
internal class MostCommonCompatibleVersionsTest {
|
||||||
private val patches =
|
private val patches =
|
||||||
arrayOf(
|
arrayOf(
|
||||||
newPatch("some.package", setOf("a")) { stringPatchOption("string", "value") },
|
newPatch("some.package", setOf("a")) { stringOption("string", "value") },
|
||||||
newPatch("some.package", setOf("a", "b"), use = false),
|
newPatch("some.package", setOf("a", "b"), use = false),
|
||||||
newPatch("some.package", setOf("a", "b", "c"), use = false),
|
newPatch("some.package", setOf("a", "b", "c"), use = false),
|
||||||
newPatch("some.other.package", setOf("b"), use = false),
|
newPatch("some.other.package", setOf("b"), use = false),
|
||||||
newPatch("some.other.package", setOf("b", "c")) { booleanPatchOption("bool", true) },
|
newPatch("some.other.package", setOf("b", "c")) { booleanOption("bool", true) },
|
||||||
newPatch("some.other.package", setOf("b", "c", "d")),
|
newPatch("some.other.package", setOf("b", "c", "d")),
|
||||||
newPatch("some.other.other.package") { intArrayPatchOption("intArray", arrayOf(1, 2, 3)) },
|
newPatch("some.other.other.package") { intsOption("intArray", listOf(1, 2, 3)) },
|
||||||
newPatch("some.other.other.package", setOf("a")),
|
newPatch("some.other.other.package", setOf("a")),
|
||||||
newPatch("some.other.other.package", setOf("b")),
|
newPatch("some.other.other.package", setOf("b")),
|
||||||
newPatch("some.other.other.other.package", use = false),
|
newPatch("some.other.other.other.package", use = false),
|
||||||
@@ -141,38 +133,24 @@ internal class PatchUtilsTest {
|
|||||||
assertEqualsVersion(null, patches, "other.package")
|
assertEqualsVersion(null, patches, "other.package")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `serializes to and deserializes from JSON string correctly`() {
|
|
||||||
val out = ByteArrayOutputStream()
|
|
||||||
PatchUtils.Json.serialize(patches, outputStream = out)
|
|
||||||
|
|
||||||
val deserialized =
|
|
||||||
PatchUtils.Json.deserialize(
|
|
||||||
ByteArrayInputStream(out.toByteArray()),
|
|
||||||
PatchUtils.Json.FullJsonPatch::class.java,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert(patches.size == deserialized.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assertEqualsVersions(
|
private fun assertEqualsVersions(
|
||||||
expected: PackageNameMap,
|
expected: PackageNameMap,
|
||||||
patches: PatchSet,
|
patches: Set<Patch<*>>,
|
||||||
compatiblePackageNames: Set<String>?,
|
compatiblePackageNames: Set<String>?,
|
||||||
countUnusedPatches: Boolean = false,
|
countUnusedPatches: Boolean = false,
|
||||||
) = assertEquals(
|
) = assertEquals(
|
||||||
expected,
|
expected,
|
||||||
PatchUtils.getMostCommonCompatibleVersions(patches, compatiblePackageNames, countUnusedPatches),
|
patches.mostCommonCompatibleVersions(compatiblePackageNames, countUnusedPatches),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun assertEqualsVersion(
|
private fun assertEqualsVersion(
|
||||||
expected: String?,
|
expected: String?,
|
||||||
patches: PatchSet,
|
patches: Set<Patch<*>>,
|
||||||
compatiblePackageName: String,
|
compatiblePackageName: String,
|
||||||
) {
|
) {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
expected,
|
expected,
|
||||||
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName))
|
patches.mostCommonCompatibleVersions(setOf(compatiblePackageName))
|
||||||
.entries.firstOrNull()?.value?.keys?.firstOrNull(),
|
.entries.firstOrNull()?.value?.keys?.firstOrNull(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -181,19 +159,23 @@ internal class PatchUtilsTest {
|
|||||||
packageName: String,
|
packageName: String,
|
||||||
versions: Set<String>? = null,
|
versions: Set<String>? = null,
|
||||||
use: Boolean = true,
|
use: Boolean = true,
|
||||||
options: Patch<*>.() -> Unit = {},
|
options: PatchBuilder<*>.() -> Unit = {},
|
||||||
) = object : BytecodePatch(
|
) = bytecodePatch(
|
||||||
name = "test",
|
name = "test",
|
||||||
compatiblePackages = setOf(CompatiblePackage(packageName, versions?.toSet())),
|
|
||||||
use = use,
|
use = use,
|
||||||
) {
|
) {
|
||||||
init {
|
if (versions == null) {
|
||||||
options()
|
compatibleWith(packageName)
|
||||||
|
} else {
|
||||||
|
compatibleWith(
|
||||||
|
if (versions.isEmpty()) {
|
||||||
|
packageName()
|
||||||
|
} else {
|
||||||
|
packageName(*versions.toTypedArray())
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {}
|
options()
|
||||||
|
|
||||||
// Needed to make the patches unique.
|
|
||||||
override fun equals(other: Any?) = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
src/commonTest/kotlin/app/revanced/library/OptionsTest.kt
Normal file
36
src/commonTest/kotlin/app/revanced/library/OptionsTest.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.library
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.booleanOption
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.patch.stringOption
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class OptionsTest {
|
||||||
|
@Test
|
||||||
|
fun `serializes and deserializes`() {
|
||||||
|
val options = mapOf(
|
||||||
|
"Test patch" to mapOf("key1" to "test", "key2" to false),
|
||||||
|
)
|
||||||
|
|
||||||
|
val patch = bytecodePatch("Test patch") {
|
||||||
|
stringOption("key1")
|
||||||
|
booleanOption("key2", true)
|
||||||
|
}
|
||||||
|
val duplicatePatch = bytecodePatch("Test patch") {
|
||||||
|
stringOption("key1")
|
||||||
|
}
|
||||||
|
val unnamedPatch = bytecodePatch {
|
||||||
|
booleanOption("key1")
|
||||||
|
}
|
||||||
|
|
||||||
|
setOf(patch, duplicatePatch, unnamedPatch).setOptions(options)
|
||||||
|
|
||||||
|
assert(patch.options["key1"].value == "test")
|
||||||
|
assert(patch.options["key2"].value == false)
|
||||||
|
|
||||||
|
assertEquals(patch.options["key1"].value, duplicatePatch.options["key1"].value)
|
||||||
|
|
||||||
|
assert(unnamedPatch.options["key1"].value == null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package app.revanced.library
|
|
||||||
|
|
||||||
import app.revanced.library.Options.setOptions
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class PatchOptionsTest {
|
|
||||||
private var patches = setOf(PatchOptionsTestPatch)
|
|
||||||
|
|
||||||
private val serializedJson =
|
|
||||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\"," +
|
|
||||||
"\"value\":true}]}]"
|
|
||||||
|
|
||||||
private val changedJson =
|
|
||||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2" +
|
|
||||||
"\",\"value\":false}]}]"
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `serializes and deserializes`() {
|
|
||||||
assert(serializedJson == Options.serialize(patches))
|
|
||||||
|
|
||||||
patches.setOptions(changedJson)
|
|
||||||
|
|
||||||
assert(PatchOptionsTestPatch.option1 == "test")
|
|
||||||
assert(PatchOptionsTestPatch.option2 == false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Patch("PatchOptionsTestPatch")
|
|
||||||
object PatchOptionsTestPatch : BytecodePatch(emptySet()) {
|
|
||||||
var option1 by stringPatchOption("key1", null, null, "title1", "description1")
|
|
||||||
var option2 by booleanPatchOption("key2", true, null, "title2", "description2")
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package app.revanced.library
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.*
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
|
||||||
|
class SerializationTest {
|
||||||
|
private val testPatch = bytecodePatch("Test patch") {
|
||||||
|
compatibleWith("com.example.package"("1.0.0"))
|
||||||
|
compatibleWith("com.example.package2")
|
||||||
|
|
||||||
|
dependsOn(bytecodePatch(), bytecodePatch())
|
||||||
|
|
||||||
|
stringOption("key1", null, null, "title1", "description1")
|
||||||
|
booleanOption("key2", true, null, "title2", "description2")
|
||||||
|
floatsOption("key3", listOf(1.0f), mapOf("list" to listOf(1f)), "title3", "description3")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var patches = setOf(testPatch)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `serializes and deserializes`() {
|
||||||
|
val serializedJson = ByteArrayOutputStream().apply { patches.serializeTo(this) }.toString()
|
||||||
|
val deserializedJson = Json.parseToJsonElement(serializedJson)
|
||||||
|
|
||||||
|
// Test patch serialization.
|
||||||
|
|
||||||
|
assertIs<JsonArray>(deserializedJson)
|
||||||
|
|
||||||
|
val deserializedPatch = deserializedJson[0].jsonObject
|
||||||
|
|
||||||
|
assert(deserializedPatch["name"]!!.jsonPrimitive.content == "Test patch")
|
||||||
|
|
||||||
|
assert(deserializedPatch["compatiblePackages"]!!.jsonArray.size == 2) {
|
||||||
|
"The patch should be compatible with two packages."
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(deserializedPatch["dependencies"]!!.jsonArray.size == 2) {
|
||||||
|
"Even though the dependencies are named the same, they are different objects."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test option serialization.
|
||||||
|
|
||||||
|
val options = deserializedPatch["options"]!!.jsonArray
|
||||||
|
|
||||||
|
assert(options.size == 3) { "The patch should have three options." }
|
||||||
|
|
||||||
|
assert(options[0].jsonObject["title"]!!.jsonPrimitive.content == "title1")
|
||||||
|
assert(options[0].jsonObject["default"]!!.jsonPrimitive.contentOrNull == null)
|
||||||
|
assert(options[1].jsonObject["default"]!!.jsonPrimitive.boolean)
|
||||||
|
assert(options[2].jsonObject["values"]!!.jsonObject["list"]!!.jsonArray[0].jsonPrimitive.float == 1f)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user