build: Bump dependencies
This commit also migrates away from deprecated to new APIs
This commit is contained in:
parent
2c438e414d
commit
bdc54ef318
|
@ -1736,6 +1736,7 @@ public final class app/revanced/util/ResourceUtilsKt {
|
||||||
public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence;
|
public static final fun asSequence (Lorg/w3c/dom/NodeList;)Lkotlin/sequences/Sequence;
|
||||||
public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence;
|
public static final fun childElementsSequence (Lorg/w3c/dom/Node;)Lkotlin/sequences/Sequence;
|
||||||
public static final fun copyResources (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V
|
public static final fun copyResources (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;[Lapp/revanced/util/ResourceGroup;)V
|
||||||
|
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/Document;Lapp/revanced/patcher/util/Document;)Ljava/lang/AutoCloseable;
|
||||||
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
|
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
|
||||||
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||||
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||||
|
|
|
@ -7,31 +7,34 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Export all activities",
|
name = "Export all activities",
|
||||||
description = "Makes all app activities exportable.",
|
description = "Makes all app activities exportable.",
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object ExportAllActivitiesPatch : ResourcePatch() {
|
object ExportAllActivitiesPatch : ResourcePatch() {
|
||||||
private const val EXPORTED_FLAG = "android:exported"
|
private const val EXPORTED_FLAG = "android:exported"
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val document = editor.file
|
|
||||||
val activities = document.getElementsByTagName("activity")
|
val activities = document.getElementsByTagName("activity")
|
||||||
|
|
||||||
for(i in 0..activities.length) {
|
for (i in 0..activities.length) {
|
||||||
activities.item(i)?.apply {
|
activities.item(i)?.apply {
|
||||||
val exportedAttribute = attributes.getNamedItem(EXPORTED_FLAG)
|
val exportedAttribute = attributes.getNamedItem(EXPORTED_FLAG)
|
||||||
|
|
||||||
if (exportedAttribute != null) {
|
if (exportedAttribute != null) {
|
||||||
if (exportedAttribute.nodeValue != "true")
|
if (exportedAttribute.nodeValue != "true") {
|
||||||
exportedAttribute.nodeValue = "true"
|
exportedAttribute.nodeValue = "true"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Reason why the attribute is added in the case it does not exist:
|
// Reason why the attribute is added in the case it does not exist:
|
||||||
// https://github.com/revanced/revanced-patches/pull/1751/files#r1141481604
|
// https://github.com/revanced/revanced-patches/pull/1751/files#r1141481604
|
||||||
else document.createAttribute(EXPORTED_FLAG)
|
else {
|
||||||
|
document.createAttribute(EXPORTED_FLAG)
|
||||||
.apply { value = "true" }
|
.apply { value = "true" }
|
||||||
.let(attributes::setNamedItem)
|
.let(attributes::setNamedItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,14 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Predictive back gesture",
|
name = "Predictive back gesture",
|
||||||
description = "Enables the predictive back gesture introduced on Android 13.",
|
description = "Enables the predictive back gesture introduced on Android 13.",
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object PredictiveBackGesturePatch : ResourcePatch() {
|
object PredictiveBackGesturePatch : ResourcePatch() {
|
||||||
private const val FLAG = "android:enableOnBackInvokedCallback"
|
private const val FLAG = "android:enableOnBackInvokedCallback"
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val document = editor.file
|
|
||||||
|
|
||||||
with(document.getElementsByTagName("application").item(0)) {
|
with(document.getElementsByTagName("application").item(0)) {
|
||||||
if (attributes.getNamedItem(FLAG) != null) return@with
|
if (attributes.getNamedItem(FLAG) != null) return@with
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,14 @@ import org.w3c.dom.Element
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Enable Android debugging",
|
name = "Enable Android debugging",
|
||||||
description = "Enables Android debugging capabilities. This can slow down the app.",
|
description = "Enables Android debugging capabilities. This can slow down the app.",
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object EnableAndroidDebuggingPatch : ResourcePatch() {
|
object EnableAndroidDebuggingPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { dom ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val applicationNode = dom
|
val applicationNode =
|
||||||
.file
|
document
|
||||||
.getElementsByTagName("application")
|
.getElementsByTagName("application")
|
||||||
.item(0) as Element
|
.item(0) as Element
|
||||||
|
|
||||||
|
|
|
@ -11,16 +11,15 @@ import java.io.File
|
||||||
name = "Override certificate pinning",
|
name = "Override certificate pinning",
|
||||||
description = "Overrides certificate pinning, allowing to inspect traffic via a proxy.",
|
description = "Overrides certificate pinning, allowing to inspect traffic via a proxy.",
|
||||||
dependencies = [EnableAndroidDebuggingPatch::class],
|
dependencies = [EnableAndroidDebuggingPatch::class],
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object OverrideCertificatePinningPatch : ResourcePatch() {
|
object OverrideCertificatePinningPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
val resXmlDirectory = context["res/xml"]
|
val resXmlDirectory = context.get("res/xml", false)
|
||||||
|
|
||||||
// Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist.
|
// Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist.
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val document = editor.file
|
|
||||||
val applicationNode = document.getElementsByTagName("application").item(0) as Element
|
val applicationNode = document.getElementsByTagName("application").item(0) as Element
|
||||||
|
|
||||||
if (!applicationNode.hasAttribute("networkSecurityConfig")) {
|
if (!applicationNode.hasAttribute("networkSecurityConfig")) {
|
||||||
|
@ -54,7 +53,7 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
|
||||||
</trust-anchors>
|
</trust-anchors>
|
||||||
</debug-overrides>
|
</debug-overrides>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// If the file already exists.
|
// If the file already exists.
|
||||||
|
@ -63,12 +62,11 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
|
||||||
writeText(
|
writeText(
|
||||||
text.replace(
|
text.replace(
|
||||||
"<trust-anchors>",
|
"<trust-anchors>",
|
||||||
"<trust-anchors>\n<certificates src=\"user\" overridePins=\"true\" />\n<certificates src=\"system\" />"
|
"<trust-anchors>\n<certificates src=\"user\" overridePins=\"true\" />\n<certificates src=\"system\" />",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,18 @@ import java.io.Closeable
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Change package name",
|
name = "Change package name",
|
||||||
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
|
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object ChangePackageNamePatch : ResourcePatch(), Closeable {
|
object ChangePackageNamePatch : ResourcePatch(), Closeable {
|
||||||
private val packageNameOption = stringPatchOption(
|
private val packageNameOption =
|
||||||
|
stringPatchOption(
|
||||||
key = "packageName",
|
key = "packageName",
|
||||||
default = "Default",
|
default = "Default",
|
||||||
values = mapOf("Default" to "Default"),
|
values = mapOf("Default" to "Default"),
|
||||||
title = "Package name",
|
title = "Package name",
|
||||||
description = "The name of the package to rename the app to.",
|
description = "The name of the package to rename the app to.",
|
||||||
required = true
|
required = true,
|
||||||
) {
|
) {
|
||||||
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
|
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
|
||||||
}
|
}
|
||||||
|
@ -43,20 +44,25 @@ object ChangePackageNamePatch : ResourcePatch(), Closeable {
|
||||||
fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
|
fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
|
||||||
val packageName = packageNameOption.value!!
|
val packageName = packageNameOption.value!!
|
||||||
|
|
||||||
return if (packageName == packageNameOption.default)
|
return if (packageName == packageNameOption.default) {
|
||||||
fallbackPackageName.also { packageNameOption.value = it }
|
fallbackPackageName.also { packageNameOption.value = it }
|
||||||
else
|
} else {
|
||||||
packageName
|
packageName
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun close() = context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
override fun close() =
|
||||||
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val replacementPackageName = packageNameOption.value
|
val replacementPackageName = packageNameOption.value
|
||||||
|
|
||||||
val manifest = editor.file.getElementsByTagName("manifest").item(0) as Element
|
val manifest = document.getElementsByTagName("manifest").item(0) as Element
|
||||||
manifest.setAttribute(
|
manifest.setAttribute(
|
||||||
"package",
|
"package",
|
||||||
if (replacementPackageName != packageNameOption.default) replacementPackageName
|
if (replacementPackageName != packageNameOption.default) {
|
||||||
else "${manifest.getAttribute("package")}.revanced"
|
replacementPackageName
|
||||||
|
} else {
|
||||||
|
"${manifest.getAttribute("package")}.revanced"
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.DomFileEditor
|
import app.revanced.patcher.util.Document
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch.resources
|
import app.revanced.patches.all.misc.resources.AddResourcesPatch.resources
|
||||||
import app.revanced.util.*
|
import app.revanced.util.*
|
||||||
import app.revanced.util.resource.ArrayResource
|
import app.revanced.util.resource.ArrayResource
|
||||||
|
@ -19,6 +19,7 @@ import java.util.*
|
||||||
* An identifier of an app. For example, `youtube`.
|
* An identifier of an app. For example, `youtube`.
|
||||||
*/
|
*/
|
||||||
private typealias AppId = String
|
private typealias AppId = String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An identifier of a patch. For example, `ad.general.HideAdsPatch`.
|
* An identifier of a patch. For example, `ad.general.HideAdsPatch`.
|
||||||
*/
|
*/
|
||||||
|
@ -28,10 +29,12 @@ private typealias PatchId = String
|
||||||
* A set of resources of a patch.
|
* A set of resources of a patch.
|
||||||
*/
|
*/
|
||||||
private typealias PatchResources = MutableSet<BaseResource>
|
private typealias PatchResources = MutableSet<BaseResource>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of resources belonging to a patch.
|
* A map of resources belonging to a patch.
|
||||||
*/
|
*/
|
||||||
private typealias AppResources = MutableMap<PatchId, PatchResources>
|
private typealias AppResources = MutableMap<PatchId, PatchResources>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of resources belonging to an app.
|
* A map of resources belonging to an app.
|
||||||
*/
|
*/
|
||||||
|
@ -67,7 +70,8 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
this.context = context
|
this.context = context
|
||||||
|
|
||||||
resources = buildMap {
|
resources =
|
||||||
|
buildMap {
|
||||||
/**
|
/**
|
||||||
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
|
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
|
||||||
*
|
*
|
||||||
|
@ -82,14 +86,14 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
) {
|
) {
|
||||||
inputStreamFromBundledResource(
|
inputStreamFromBundledResource(
|
||||||
"addresources",
|
"addresources",
|
||||||
"$value/$resourceKind.xml"
|
"$value/$resourceKind.xml",
|
||||||
)?.let { stream ->
|
)?.let { stream ->
|
||||||
// Add the resources associated with the given value to the map,
|
// Add the resources associated with the given value to the map,
|
||||||
// instead of overwriting it.
|
// instead of overwriting it.
|
||||||
// This covers the example case such as adding strings and arrays of the same value.
|
// This covers the example case such as adding strings and arrays of the same value.
|
||||||
getOrPut(value, ::mutableMapOf).apply {
|
getOrPut(value, ::mutableMapOf).apply {
|
||||||
context.xmlEditor[stream].use {
|
context.document[stream].use {
|
||||||
it.file.getElementsByTagName("app").asSequence().forEach { app ->
|
it.getElementsByTagName("app").asSequence().forEach { app ->
|
||||||
val appId = app.attributes.getNamedItem("id").textContent
|
val appId = app.attributes.getNamedItem("id").textContent
|
||||||
|
|
||||||
getOrPut(appId, ::mutableMapOf).apply {
|
getOrPut(appId, ::mutableMapOf).apply {
|
||||||
|
@ -136,8 +140,10 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
*
|
*
|
||||||
* @return True if the resource was added, false if it already existed.
|
* @return True if the resource was added, false if it already existed.
|
||||||
*/
|
*/
|
||||||
operator fun invoke(value: Value, resource: BaseResource) =
|
operator fun invoke(
|
||||||
getOrPut(value, ::mutableSetOf).add(resource)
|
value: Value,
|
||||||
|
resource: BaseResource,
|
||||||
|
) = getOrPut(value, ::mutableSetOf).add(resource)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut].
|
* Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut].
|
||||||
|
@ -147,8 +153,10 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
*
|
*
|
||||||
* @return True if the resources were added, false if they already existed.
|
* @return True if the resources were added, false if they already existed.
|
||||||
*/
|
*/
|
||||||
operator fun invoke(value: Value, resources: Iterable<BaseResource>) =
|
operator fun invoke(
|
||||||
getOrPut(value, ::mutableSetOf).addAll(resources)
|
value: Value,
|
||||||
|
resources: Iterable<BaseResource>,
|
||||||
|
) = getOrPut(value, ::mutableSetOf).addAll(resources)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a [StringResource].
|
* Adds a [StringResource].
|
||||||
|
@ -177,10 +185,9 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
*/
|
*/
|
||||||
operator fun invoke(
|
operator fun invoke(
|
||||||
name: String,
|
name: String,
|
||||||
items: List<String>
|
items: List<String>,
|
||||||
) = invoke("values", ArrayResource(name, items))
|
) = invoke("values", ArrayResource(name, items))
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts all resources of any [Value] staged in [resources] for the given [PatchClass] to [AddResourcesPatch].
|
* Puts all resources of any [Value] staged in [resources] for the given [PatchClass] to [AddResourcesPatch].
|
||||||
*
|
*
|
||||||
|
@ -209,7 +216,7 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
|
|
||||||
appId to patchId
|
appId to patchId
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val (appId, patchId) = patch.parseIds()
|
val (appId, patchId) = patch.parseIds()
|
||||||
|
|
||||||
|
@ -230,30 +237,32 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
* This is called after all patches that depend on [AddResourcesPatch] have been executed.
|
* This is called after all patches that depend on [AddResourcesPatch] have been executed.
|
||||||
*/
|
*/
|
||||||
override fun close() {
|
override fun close() {
|
||||||
operator fun MutableMap<String, Pair<DomFileEditor, Node>>.invoke(
|
operator fun MutableMap<String, Pair<Document, Node>>.invoke(
|
||||||
value: Value,
|
value: Value,
|
||||||
resource: BaseResource
|
resource: BaseResource,
|
||||||
) {
|
) {
|
||||||
// TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts
|
// TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts
|
||||||
// a Value and the map of editors. It will then get or put the editor suitable for its resource type
|
// a Value and the map of documents. It will then get or put the document suitable for its resource type
|
||||||
// to serialize itself to it.
|
// to serialize itself to it.
|
||||||
val resourceFileName = when (resource) {
|
val resourceFileName =
|
||||||
|
when (resource) {
|
||||||
is StringResource -> "strings"
|
is StringResource -> "strings"
|
||||||
is ArrayResource -> "arrays"
|
is ArrayResource -> "arrays"
|
||||||
else -> throw NotImplementedError("Unsupported resource type")
|
else -> throw NotImplementedError("Unsupported resource type")
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrPut(resourceFileName) {
|
getOrPut(resourceFileName) {
|
||||||
val targetFile = context["res/$value/$resourceFileName.xml"].also {
|
val targetFile =
|
||||||
|
context.get("res/$value/$resourceFileName.xml", false).also {
|
||||||
it.parentFile?.mkdirs()
|
it.parentFile?.mkdirs()
|
||||||
it.createNewFile()
|
it.createNewFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
context.xmlEditor[targetFile.path].let { editor ->
|
context.document[targetFile.path].let { document ->
|
||||||
// Save the target node here as well
|
// Save the target node here as well
|
||||||
// in order to avoid having to call editor.getNode("resources")
|
// in order to avoid having to call document.getNode("resources")
|
||||||
// every time addUsingEditors is called but also save the editor so that it can be closed later.
|
// but also save the document so that it can be closed later.
|
||||||
editor to editor.getNode("resources")
|
document to document.getNode("resources")
|
||||||
}
|
}
|
||||||
}.let { (_, targetNode) ->
|
}.let { (_, targetNode) ->
|
||||||
targetNode.addResource(resource) { invoke(value, it) }
|
targetNode.addResource(resource) { invoke(value, it) }
|
||||||
|
@ -261,17 +270,17 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
|
||||||
}
|
}
|
||||||
|
|
||||||
forEach { (value, resources) ->
|
forEach { (value, resources) ->
|
||||||
// A map of editors associated by their kind (e.g. strings, arrays).
|
// A map of document associated by their kind (e.g. strings, arrays).
|
||||||
// Each editor is accompanied by the target node to which resources are added.
|
// Each document is accompanied by the target node to which resources are added.
|
||||||
// A map is used because Map#getOrPut allows opening a new editor for the duration of a resource value.
|
// A map is used because Map#getOrPut allows opening a new document for the duration of a resource value.
|
||||||
// This is done to prevent having to open the files for every resource that is added.
|
// This is done to prevent having to open the files for every resource that is added.
|
||||||
// Instead, it is cached once and reused for resources of the same value.
|
// Instead, it is cached once and reused for resources of the same value.
|
||||||
// This map is later accessed to close all editors for the current resource value.
|
// This map is later accessed to close all documents for the current resource value.
|
||||||
val resourceFileEditors = mutableMapOf<String, Pair<DomFileEditor, Node>>()
|
val documents = mutableMapOf<String, Pair<Document, Node>>()
|
||||||
|
|
||||||
resources.forEach { resource -> resourceFileEditors(value, resource) }
|
resources.forEach { resource -> documents(value, resource) }
|
||||||
|
|
||||||
resourceFileEditors.values.forEach { (editor, _) -> editor.close() }
|
documents.values.forEach { (document, _) -> document.close() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@ import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
abstract class BaseTransformInstructionsPatch<T> : BytecodePatch() {
|
abstract class BaseTransformInstructionsPatch<T> : BytecodePatch(emptySet()) {
|
||||||
abstract fun filterMap(
|
abstract fun filterMap(
|
||||||
classDef: ClassDef,
|
classDef: ClassDef,
|
||||||
method: Method,
|
method: Method,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
instructionIndex: Int
|
instructionIndex: Int,
|
||||||
): T?
|
): T?
|
||||||
|
|
||||||
abstract fun transform(mutableMethod: MutableMethod, entry: T)
|
abstract fun transform(mutableMethod: MutableMethod, entry: T)
|
||||||
|
|
|
@ -8,11 +8,10 @@ import org.w3c.dom.Element
|
||||||
@Patch(description = "Sets allowAudioPlaybackCapture in manifest to true.")
|
@Patch(description = "Sets allowAudioPlaybackCapture in manifest to true.")
|
||||||
internal object RemoveCaptureRestrictionResourcePatch : ResourcePatch() {
|
internal object RemoveCaptureRestrictionResourcePatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
// create an xml editor instance
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { dom ->
|
|
||||||
// get the application node
|
// get the application node
|
||||||
val applicationNode = dom
|
val applicationNode =
|
||||||
.file
|
document
|
||||||
.getElementsByTagName("application")
|
.getElementsByTagName("application")
|
||||||
.item(0) as Element
|
.item(0) as Element
|
||||||
|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
|
||||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
|
|
||||||
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
|
|
||||||
internal object ExclusiveAudioFingerprint : MethodFingerprint(
|
|
||||||
"V",
|
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
|
||||||
listOf("L", "Z"),
|
|
||||||
listOf(
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQ,
|
|
||||||
Opcode.CONST_4,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.NOP,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.IF_EQZ,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.INVOKE_INTERFACE,
|
|
||||||
Opcode.GOTO,
|
|
||||||
Opcode.RETURN_VOID
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -1,14 +1,12 @@
|
||||||
package app.revanced.patches.music.misc.gms.fingerprints
|
package app.revanced.patches.music.misc.gms.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
|
||||||
internal object ServiceCheckFingerprint : MethodFingerprint(
|
internal object ServiceCheckFingerprint : MethodFingerprint(
|
||||||
"V",
|
"V",
|
||||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
listOf("L", "I"),
|
listOf("L", "I"),
|
||||||
strings = listOf("Google Play Services not available")
|
strings = listOf("Google Play Services not available"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,8 +6,5 @@ import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
|
||||||
|
|
||||||
@Patch(requiresIntegrations = true)
|
@Patch(requiresIntegrations = true)
|
||||||
object IntegrationsPatch : BaseIntegrationsPatch(
|
object IntegrationsPatch : BaseIntegrationsPatch(
|
||||||
"Lapp/revanced/integrations/utils/ReVancedUtils;",
|
setOf(ApplicationInitFingerprint),
|
||||||
setOf(
|
|
||||||
ApplicationInitFingerprint,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,19 +6,18 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Remove broadcasts restriction",
|
name = "Remove broadcasts restriction",
|
||||||
description = "Enables starting/stopping NetGuard via broadcasts.",
|
description = "Enables starting/stopping NetGuard via broadcasts.",
|
||||||
compatiblePackages = [CompatiblePackage("eu.faircode.netguard")],
|
compatiblePackages = [CompatiblePackage("eu.faircode.netguard")],
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object RemoveBroadcastsRestrictionPatch : ResourcePatch() {
|
object RemoveBroadcastsRestrictionPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { dom ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
val applicationNode = dom
|
val applicationNode =
|
||||||
.file
|
document
|
||||||
.getElementsByTagName("application")
|
.getElementsByTagName("application")
|
||||||
.item(0) as Element
|
.item(0) as Element
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ object HideBannerPatch : ResourcePatch() {
|
||||||
private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml"
|
private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml"
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor[RESOURCE_FILE_PATH].use {
|
context.document[RESOURCE_FILE_PATH].use {
|
||||||
it.file.getElementsByTagName("merge").item(0).childNodes.apply {
|
it.getElementsByTagName("merge").item(0).childNodes.apply {
|
||||||
val attributes = arrayOf("height", "width")
|
val attributes = arrayOf("height", "width")
|
||||||
|
|
||||||
for (i in 1 until length) {
|
for (i in 1 until length) {
|
||||||
|
@ -30,4 +30,3 @@ object HideBannerPatch : ResourcePatch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,18 +22,20 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||||
private val fromPackageName: String,
|
private val fromPackageName: String,
|
||||||
private val toPackageName: String,
|
private val toPackageName: String,
|
||||||
private val spoofedPackageSignature: String,
|
private val spoofedPackageSignature: String,
|
||||||
dependencies: Set<PatchClass> = setOf()
|
dependencies: Set<PatchClass> = setOf(),
|
||||||
) : ResourcePatch(dependencies = setOf(ChangePackageNamePatch::class, AddResourcesPatch::class) + dependencies) {
|
) : ResourcePatch(dependencies = setOf(ChangePackageNamePatch::class, AddResourcesPatch::class) + dependencies) {
|
||||||
internal val gmsCoreVendorOption = stringPatchOption(
|
internal val gmsCoreVendorOption =
|
||||||
|
stringPatchOption(
|
||||||
key = "gmsCoreVendor",
|
key = "gmsCoreVendor",
|
||||||
default = "com.mgoogle",
|
default = "com.mgoogle",
|
||||||
values = mapOf(
|
values =
|
||||||
|
mapOf(
|
||||||
"Vanced" to "com.mgoogle",
|
"Vanced" to "com.mgoogle",
|
||||||
"ReVanced" to "app.revanced"
|
"ReVanced" to "app.revanced",
|
||||||
),
|
),
|
||||||
title = "GmsCore Vendor",
|
title = "GmsCore Vendor",
|
||||||
description = "The group id of the GmsCore vendor.",
|
description = "The group id of the GmsCore vendor.",
|
||||||
required = true
|
required = true,
|
||||||
) { it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) }
|
) { it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) }
|
||||||
|
|
||||||
protected val gmsCoreVendor by gmsCoreVendorOption
|
protected val gmsCoreVendor by gmsCoreVendorOption
|
||||||
|
@ -49,15 +51,18 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
||||||
*/
|
*/
|
||||||
private fun ResourceContext.addSpoofingMetadata() {
|
private fun ResourceContext.addSpoofingMetadata() {
|
||||||
fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
|
fun Node.adoptChild(
|
||||||
|
tagName: String,
|
||||||
|
block: Element.() -> Unit,
|
||||||
|
) {
|
||||||
val child = ownerDocument.createElement(tagName)
|
val child = ownerDocument.createElement(tagName)
|
||||||
child.block()
|
child.block()
|
||||||
appendChild(child)
|
appendChild(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlEditor["AndroidManifest.xml"].use {
|
document["AndroidManifest.xml"].use { document ->
|
||||||
val applicationNode = it
|
val applicationNode =
|
||||||
.file
|
document
|
||||||
.getElementsByTagName("application")
|
.getElementsByTagName("application")
|
||||||
.item(0)
|
.item(0)
|
||||||
|
|
||||||
|
@ -87,27 +92,27 @@ abstract class BaseGmsCoreSupportResourcePatch(
|
||||||
private fun ResourceContext.patchManifest() {
|
private fun ResourceContext.patchManifest() {
|
||||||
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
|
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
|
||||||
|
|
||||||
val manifest = this["AndroidManifest.xml"].readText()
|
val manifest = this.get("AndroidManifest.xml", false).readText()
|
||||||
this["AndroidManifest.xml"].writeText(
|
this.get("AndroidManifest.xml", false).writeText(
|
||||||
manifest.replace(
|
manifest.replace(
|
||||||
"package=\"$fromPackageName",
|
"package=\"$fromPackageName",
|
||||||
"package=\"$packageName"
|
"package=\"$packageName",
|
||||||
).replace(
|
).replace(
|
||||||
"android:authorities=\"$fromPackageName",
|
"android:authorities=\"$fromPackageName",
|
||||||
"android:authorities=\"$packageName"
|
"android:authorities=\"$packageName",
|
||||||
).replace(
|
).replace(
|
||||||
"$fromPackageName.permission.C2D_MESSAGE",
|
"$fromPackageName.permission.C2D_MESSAGE",
|
||||||
"$packageName.permission.C2D_MESSAGE"
|
"$packageName.permission.C2D_MESSAGE",
|
||||||
).replace(
|
).replace(
|
||||||
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||||
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||||
).replace(
|
).replace(
|
||||||
"com.google.android.c2dm",
|
"com.google.android.c2dm",
|
||||||
"$gmsCoreVendor.android.c2dm"
|
"$gmsCoreVendor.android.c2dm",
|
||||||
).replace(
|
).replace(
|
||||||
"</queries>",
|
"</queries>",
|
||||||
"<package android:name=\"$gmsCoreVendor.android.gms\"/></queries>"
|
"<package android:name=\"$gmsCoreVendor.android.gms\"/></queries>",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,6 @@ import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
object ResourceMappingPatch : ResourcePatch() {
|
object ResourceMappingPatch : ResourcePatch() {
|
||||||
internal lateinit var resourceMappings: List<ResourceElement>
|
internal lateinit var resourceMappings: List<ResourceElement>
|
||||||
private set
|
private set
|
||||||
|
@ -17,15 +16,15 @@ object ResourceMappingPatch : ResourcePatch() {
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
// save the file in memory to concurrently read from
|
// save the file in memory to concurrently read from
|
||||||
val resourceXmlFile = context["res/values/public.xml"].readBytes()
|
val resourceXmlFile = context.get("res/values/public.xml", false).readBytes()
|
||||||
|
|
||||||
// create a synchronized list to store the resource mappings
|
// create a synchronized list to store the resource mappings
|
||||||
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
|
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
|
||||||
|
|
||||||
for (threadIndex in 0 until THREAD_COUNT) {
|
for (threadIndex in 0 until THREAD_COUNT) {
|
||||||
threadPoolExecutor.execute thread@{
|
threadPoolExecutor.execute thread@{
|
||||||
context.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
|
context.document[resourceXmlFile.inputStream()].use { document ->
|
||||||
val resources = editor.file.documentElement.childNodes
|
val resources = document.documentElement.childNodes
|
||||||
val resourcesLength = resources.length
|
val resourcesLength = resources.length
|
||||||
val jobSize = resourcesLength / THREAD_COUNT
|
val jobSize = resourcesLength / THREAD_COUNT
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,18 @@ import java.io.Closeable
|
||||||
*/
|
*/
|
||||||
abstract class BaseSettingsResourcePatch(
|
abstract class BaseSettingsResourcePatch(
|
||||||
private val rootPreference: Pair<IntentPreference, String>? = null,
|
private val rootPreference: Pair<IntentPreference, String>? = null,
|
||||||
dependencies: Set<PatchClass> = emptySet()
|
dependencies: Set<PatchClass> = emptySet(),
|
||||||
) : ResourcePatch(
|
) : ResourcePatch(
|
||||||
dependencies = setOf(AddResourcesPatch::class) + dependencies
|
dependencies = setOf(AddResourcesPatch::class) + dependencies,
|
||||||
), MutableSet<BasePreference> by mutableSetOf(), Closeable {
|
),
|
||||||
|
MutableSet<BasePreference> by mutableSetOf(),
|
||||||
|
Closeable {
|
||||||
private lateinit var context: ResourceContext
|
private lateinit var context: ResourceContext
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.copyResources(
|
context.copyResources(
|
||||||
"settings",
|
"settings",
|
||||||
ResourceGroup("xml", "revanced_prefs.xml")
|
ResourceGroup("xml", "revanced_prefs.xml"),
|
||||||
)
|
)
|
||||||
|
|
||||||
this.context = context
|
this.context = context
|
||||||
|
@ -49,14 +51,14 @@ abstract class BaseSettingsResourcePatch(
|
||||||
|
|
||||||
// Add the root preference to an existing fragment if needed.
|
// Add the root preference to an existing fragment if needed.
|
||||||
rootPreference?.let { (intentPreference, fragment) ->
|
rootPreference?.let { (intentPreference, fragment) ->
|
||||||
context.xmlEditor["res/xml/$fragment.xml"].use {
|
context.document["res/xml/$fragment.xml"].use {
|
||||||
it.getNode("PreferenceScreen").addPreference(intentPreference)
|
it.getNode("PreferenceScreen").addPreference(intentPreference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all preferences to the ReVanced fragment.
|
// Add all preferences to the ReVanced fragment.
|
||||||
context.xmlEditor["res/xml/revanced_prefs.xml"].use { editor ->
|
context.document["res/xml/revanced_prefs.xml"].use { document ->
|
||||||
val revancedPreferenceScreenNode = editor.getNode("PreferenceScreen")
|
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
|
||||||
forEach { revancedPreferenceScreenNode.addPreference(it) }
|
forEach { revancedPreferenceScreenNode.addPreference(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.w3c.dom.Element
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Custom theme",
|
name = "Custom theme",
|
||||||
description = "Applies a custom theme.",
|
description = "Applies a custom theme.",
|
||||||
compatiblePackages = [CompatiblePackage("com.spotify.music")]
|
compatiblePackages = [CompatiblePackage("com.spotify.music")],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object CustomThemePatch : ResourcePatch() {
|
object CustomThemePatch : ResourcePatch() {
|
||||||
|
@ -19,7 +19,7 @@ object CustomThemePatch : ResourcePatch() {
|
||||||
default = "@android:color/black",
|
default = "@android:color/black",
|
||||||
title = "Primary background color",
|
title = "Primary background color",
|
||||||
description = "The background color. Can be a hex color or a resource reference.",
|
description = "The background color. Can be a hex color or a resource reference.",
|
||||||
required = true
|
required = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var backgroundColorSecondary by stringPatchOption(
|
private var backgroundColorSecondary by stringPatchOption(
|
||||||
|
@ -27,7 +27,7 @@ object CustomThemePatch : ResourcePatch() {
|
||||||
default = "#ff282828",
|
default = "#ff282828",
|
||||||
title = "Secondary background color",
|
title = "Secondary background color",
|
||||||
description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.",
|
description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.",
|
||||||
required = true
|
required = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var accentColor by stringPatchOption(
|
private var accentColor by stringPatchOption(
|
||||||
|
@ -35,16 +35,17 @@ object CustomThemePatch : ResourcePatch() {
|
||||||
default = "#ff1ed760",
|
default = "#ff1ed760",
|
||||||
title = "Accent color",
|
title = "Accent color",
|
||||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||||
required = true
|
required = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var accentColorPressed by stringPatchOption(
|
private var accentColorPressed by stringPatchOption(
|
||||||
key = "accentColorPressed",
|
key = "accentColorPressed",
|
||||||
default = "#ff169c46",
|
default = "#ff169c46",
|
||||||
title = "Pressed dark theme accent color",
|
title = "Pressed dark theme accent color",
|
||||||
description = "The color when accented buttons are pressed, by default slightly darker than accent. "
|
description =
|
||||||
+ "Can be a hex color or a resource reference.",
|
"The color when accented buttons are pressed, by default slightly darker than accent. " +
|
||||||
required = true
|
"Can be a hex color or a resource reference.",
|
||||||
|
required = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
|
@ -53,16 +54,18 @@ object CustomThemePatch : ResourcePatch() {
|
||||||
val accentColor = accentColor!!
|
val accentColor = accentColor!!
|
||||||
val accentColorPressed = accentColorPressed!!
|
val accentColorPressed = accentColorPressed!!
|
||||||
|
|
||||||
context.xmlEditor["res/values/colors.xml"].use { editor ->
|
context.document["res/values/colors.xml"].use { document ->
|
||||||
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
for (i in 0 until resourcesNode.childNodes.length) {
|
for (i in 0 until resourcesNode.childNodes.length) {
|
||||||
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||||
|
|
||||||
node.textContent = when (node.getAttribute("name")) {
|
node.textContent =
|
||||||
|
when (node.getAttribute("name")) {
|
||||||
"dark_base_background_elevated_base", "design_dark_default_color_background",
|
"dark_base_background_elevated_base", "design_dark_default_color_background",
|
||||||
"design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer",
|
"design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer",
|
||||||
"sthlm_blk" -> backgroundColor
|
"sthlm_blk",
|
||||||
|
-> backgroundColor
|
||||||
|
|
||||||
"gray_15" -> backgroundColorSecondary
|
"gray_15" -> backgroundColorSecondary
|
||||||
|
|
||||||
|
|
|
@ -23,19 +23,19 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.ss.android.ugc.trill"),
|
CompatiblePackage("com.ss.android.ugc.trill"),
|
||||||
CompatiblePackage("com.zhiliaoapp.musically")
|
CompatiblePackage("com.zhiliaoapp.musically"),
|
||||||
],
|
],
|
||||||
use = false
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object SpoofSimPatch : BytecodePatch() {
|
object SpoofSimPatch : BytecodePatch(emptySet()) {
|
||||||
private val replacements = hashMapOf(
|
private val replacements = hashMapOf(
|
||||||
"getSimCountryIso" to "getCountryIso",
|
"getSimCountryIso" to "getCountryIso",
|
||||||
"getNetworkCountryIso" to "getCountryIso",
|
"getNetworkCountryIso" to "getCountryIso",
|
||||||
"getSimOperator" to "getOperator",
|
"getSimOperator" to "getOperator",
|
||||||
"getNetworkOperator" to "getOperator",
|
"getNetworkOperator" to "getOperator",
|
||||||
"getSimOperatorName" to "getOperatorName",
|
"getSimOperatorName" to "getOperatorName",
|
||||||
"getNetworkOperatorName" to "getOperatorName"
|
"getNetworkOperatorName" to "getOperatorName",
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
@ -85,7 +85,7 @@ object SpoofSimPatch : BytecodePatch() {
|
||||||
with(SettingsStatusLoadFingerprint.result!!.mutableMethod) {
|
with(SettingsStatusLoadFingerprint.result!!.mutableMethod) {
|
||||||
addInstruction(
|
addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V"
|
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ object SpoofSimPatch : BytecodePatch() {
|
||||||
"""
|
"""
|
||||||
invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String;
|
invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object v$resultReg
|
move-result-object v$resultReg
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,10 +10,10 @@ import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
|
||||||
name = "Disable dashboard ads",
|
name = "Disable dashboard ads",
|
||||||
description = "Disables ads in the dashboard.",
|
description = "Disables ads in the dashboard.",
|
||||||
compatiblePackages = [CompatiblePackage("com.tumblr")],
|
compatiblePackages = [CompatiblePackage("com.tumblr")],
|
||||||
dependencies = [TimelineFilterPatch::class]
|
dependencies = [TimelineFilterPatch::class],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object DisableDashboardAds : BytecodePatch() {
|
object DisableDashboardAds : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// The timeline object types are filtered by their name in the TimelineObjectType enum.
|
// The timeline object types are filtered by their name in the TimelineObjectType enum.
|
||||||
// This is often different from the "object_type" returned in the api (noted in comments here)
|
// This is often different from the "object_type" returned in the api (noted in comments here)
|
||||||
|
@ -29,7 +29,7 @@ object DisableDashboardAds : BytecodePatch() {
|
||||||
"DISPLAY_IO_INTERSCROLLER_AD", // "display_io_interscroller"
|
"DISPLAY_IO_INTERSCROLLER_AD", // "display_io_interscroller"
|
||||||
"DISPLAY_IO_HEADLINE_VIDEO_AD", // "display_io_headline_video"
|
"DISPLAY_IO_HEADLINE_VIDEO_AD", // "display_io_headline_video"
|
||||||
"FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad"
|
"FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad"
|
||||||
"GOOGLE_NATIVE" // "google_native_ad"
|
"GOOGLE_NATIVE", // "google_native_ad"
|
||||||
).forEach {
|
).forEach {
|
||||||
TimelineFilterPatch.addObjectTypeFilter(it)
|
TimelineFilterPatch.addObjectTypeFilter(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ import app.revanced.patches.tumblr.featureflags.OverrideFeatureFlagsPatch
|
||||||
name = "Disable in-app update",
|
name = "Disable in-app update",
|
||||||
description = "Disables the in-app update check and update prompt.",
|
description = "Disables the in-app update check and update prompt.",
|
||||||
dependencies = [OverrideFeatureFlagsPatch::class],
|
dependencies = [OverrideFeatureFlagsPatch::class],
|
||||||
compatiblePackages = [CompatiblePackage("com.tumblr")]
|
compatiblePackages = [CompatiblePackage("com.tumblr")],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object DisableInAppUpdatePatch : BytecodePatch() {
|
object DisableInAppUpdatePatch : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked.
|
// Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked.
|
||||||
// If this flag is false or the last update check was today and no update check is performed.
|
// If this flag is false or the last update check was today and no update check is performed.
|
||||||
|
|
|
@ -11,10 +11,10 @@ import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
|
||||||
name = "Disable Tumblr Live",
|
name = "Disable Tumblr Live",
|
||||||
description = "Disable the Tumblr Live tab button and dashboard carousel.",
|
description = "Disable the Tumblr Live tab button and dashboard carousel.",
|
||||||
dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class],
|
dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class],
|
||||||
compatiblePackages = [CompatiblePackage("com.tumblr")]
|
compatiblePackages = [CompatiblePackage("com.tumblr")],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object DisableTumblrLivePatch : BytecodePatch() {
|
object DisableTumblrLivePatch : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// Hide the LIVE_MARQUEE timeline element that appears in the feed
|
// Hide the LIVE_MARQUEE timeline element that appears in the feed
|
||||||
// Called "live_marquee" in api response
|
// Called "live_marquee" in api response
|
||||||
|
|
|
@ -11,12 +11,12 @@ import java.nio.file.Files
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Dynamic color",
|
name = "Dynamic color",
|
||||||
description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.",
|
description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.",
|
||||||
compatiblePackages = [CompatiblePackage("com.twitter.android")]
|
compatiblePackages = [CompatiblePackage("com.twitter.android")],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object DynamicColorPatch : ResourcePatch() {
|
object DynamicColorPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
val resDirectory = context["res"]
|
val resDirectory = context.get("res", false)
|
||||||
if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.")
|
if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.")
|
||||||
|
|
||||||
val valuesV31Directory = resDirectory.resolve("values-v31")
|
val valuesV31Directory = resDirectory.resolve("values-v31")
|
||||||
|
@ -28,16 +28,14 @@ object DynamicColorPatch : ResourcePatch() {
|
||||||
listOf(valuesV31Directory, valuesNightV31Directory).forEach { it ->
|
listOf(valuesV31Directory, valuesNightV31Directory).forEach { it ->
|
||||||
val colorsXml = it.resolve("colors.xml")
|
val colorsXml = it.resolve("colors.xml")
|
||||||
|
|
||||||
if(!colorsXml.exists()) {
|
if (!colorsXml.exists()) {
|
||||||
FileWriter(colorsXml).use {
|
FileWriter(colorsXml).use {
|
||||||
it.write("<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>")
|
it.write("<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.xmlEditor["res/values-v31/colors.xml"].use { editor ->
|
context.document["res/values-v31/colors.xml"].use { document ->
|
||||||
val document = editor.file
|
|
||||||
|
|
||||||
mapOf(
|
mapOf(
|
||||||
"ps__twitter_blue" to "@color/twitter_blue",
|
"ps__twitter_blue" to "@color/twitter_blue",
|
||||||
"ps__twitter_blue_pressed" to "@color/twitter_blue_fill_pressed",
|
"ps__twitter_blue_pressed" to "@color/twitter_blue_fill_pressed",
|
||||||
|
@ -46,7 +44,7 @@ object DynamicColorPatch : ResourcePatch() {
|
||||||
"twitter_blue_opacity_30" to "@android:color/system_accent1_100",
|
"twitter_blue_opacity_30" to "@android:color/system_accent1_100",
|
||||||
"twitter_blue_opacity_50" to "@android:color/system_accent1_200",
|
"twitter_blue_opacity_50" to "@android:color/system_accent1_200",
|
||||||
"twitter_blue_opacity_58" to "@android:color/system_accent1_300",
|
"twitter_blue_opacity_58" to "@android:color/system_accent1_300",
|
||||||
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200"
|
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
|
||||||
).forEach { (k, v) ->
|
).forEach { (k, v) ->
|
||||||
val colorElement = document.createElement("color")
|
val colorElement = document.createElement("color")
|
||||||
|
|
||||||
|
@ -57,16 +55,14 @@ object DynamicColorPatch : ResourcePatch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.xmlEditor["res/values-night-v31/colors.xml"].use { editor ->
|
context.document["res/values-night-v31/colors.xml"].use { document ->
|
||||||
val document = editor.file
|
|
||||||
|
|
||||||
mapOf(
|
mapOf(
|
||||||
"twitter_blue" to "@android:color/system_accent1_200",
|
"twitter_blue" to "@android:color/system_accent1_200",
|
||||||
"twitter_blue_fill_pressed" to "@android:color/system_accent1_300",
|
"twitter_blue_fill_pressed" to "@android:color/system_accent1_300",
|
||||||
"twitter_blue_opacity_30" to "@android:color/system_accent1_50",
|
"twitter_blue_opacity_30" to "@android:color/system_accent1_50",
|
||||||
"twitter_blue_opacity_50" to "@android:color/system_accent1_100",
|
"twitter_blue_opacity_50" to "@android:color/system_accent1_100",
|
||||||
"twitter_blue_opacity_58" to "@android:color/system_accent1_200",
|
"twitter_blue_opacity_58" to "@android:color/system_accent1_200",
|
||||||
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200"
|
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
|
||||||
).forEach { (k, v) ->
|
).forEach { (k, v) ->
|
||||||
val colorElement = document.createElement("color")
|
val colorElement = document.createElement("color")
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patches.twitter.misc.hook.json.JsonHookPatch
|
import app.revanced.patches.twitter.misc.hook.json.JsonHookPatch
|
||||||
|
|
||||||
abstract class BaseHookPatch(private val hookClassDescriptor: String) : BytecodePatch() {
|
abstract class BaseHookPatch(private val hookClassDescriptor: String) : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) =
|
override fun execute(context: BytecodeContext) =
|
||||||
JsonHookPatch.hooks.addHook(JsonHookPatch.Hook(context, hookClassDescriptor))
|
JsonHookPatch.hooks.addHook(JsonHookPatch.Hook(context, hookClassDescriptor))
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package app.revanced.patches.youtube.ad.general
|
package app.revanced.patches.youtube.ad.general
|
||||||
|
|
||||||
import app.revanced.util.findMutableMethodOf
|
|
||||||
import app.revanced.util.injectHideViewCall
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
@ -9,6 +7,8 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.shared.misc.fix.verticalscroll.VerticalScrollPatch
|
import app.revanced.patches.shared.misc.fix.verticalscroll.VerticalScrollPatch
|
||||||
import app.revanced.patches.youtube.ad.getpremium.HideGetPremiumPatch
|
import app.revanced.patches.youtube.ad.getpremium.HideGetPremiumPatch
|
||||||
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.FixBackToExitGesturePatch
|
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.FixBackToExitGesturePatch
|
||||||
|
import app.revanced.util.findMutableMethodOf
|
||||||
|
import app.revanced.util.injectHideViewCall
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
@ -20,11 +20,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
HideGetPremiumPatch::class,
|
HideGetPremiumPatch::class,
|
||||||
HideAdsResourcePatch::class,
|
HideAdsResourcePatch::class,
|
||||||
VerticalScrollPatch::class,
|
VerticalScrollPatch::class,
|
||||||
FixBackToExitGesturePatch::class
|
FixBackToExitGesturePatch::class,
|
||||||
],
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
"18.32.39",
|
"18.32.39",
|
||||||
"18.37.36",
|
"18.37.36",
|
||||||
"18.38.44",
|
"18.38.44",
|
||||||
|
@ -37,30 +38,33 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
"19.02.39",
|
"19.02.39",
|
||||||
"19.03.35",
|
"19.03.35",
|
||||||
"19.03.36",
|
"19.03.36",
|
||||||
"19.04.37"
|
"19.04.37",
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object HideAdsPatch : BytecodePatch() {
|
object HideAdsPatch : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
context.classes.forEach { classDef ->
|
context.classes.forEach { classDef ->
|
||||||
classDef.methods.forEach { method ->
|
classDef.methods.forEach { method ->
|
||||||
with(method.implementation) {
|
with(method.implementation) {
|
||||||
this?.instructions?.forEachIndexed { index, instruction ->
|
this?.instructions?.forEachIndexed { index, instruction ->
|
||||||
if (instruction.opcode != Opcode.CONST)
|
if (instruction.opcode != Opcode.CONST) {
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
|
}
|
||||||
// Instruction to store the id adAttribution into a register
|
// Instruction to store the id adAttribution into a register
|
||||||
if ((instruction as Instruction31i).wideLiteral != HideAdsResourcePatch.adAttributionId)
|
if ((instruction as Instruction31i).wideLiteral != HideAdsResourcePatch.adAttributionId) {
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
|
||||||
val insertIndex = index + 1
|
val insertIndex = index + 1
|
||||||
|
|
||||||
// Call to get the view with the id adAttribution
|
// Call to get the view with the id adAttribution
|
||||||
with(instructions.elementAt(insertIndex)) {
|
with(instructions.elementAt(insertIndex)) {
|
||||||
if (opcode != Opcode.INVOKE_VIRTUAL)
|
if (opcode != Opcode.INVOKE_VIRTUAL) {
|
||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
|
||||||
// Hide the view
|
// Hide the view
|
||||||
val viewRegister = (this as Instruction35c).registerC
|
val viewRegister = (this as Instruction35c).registerC
|
||||||
|
@ -71,7 +75,7 @@ object HideAdsPatch : BytecodePatch() {
|
||||||
insertIndex,
|
insertIndex,
|
||||||
viewRegister,
|
viewRegister,
|
||||||
"Lapp/revanced/integrations/youtube/patches/components/AdsFilter;",
|
"Lapp/revanced/integrations/youtube/patches/components/AdsFilter;",
|
||||||
"hideAdAttributionView"
|
"hideAdAttributionView",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,28 +13,29 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||||
dependencies = [
|
dependencies = [
|
||||||
CopyVideoUrlResourcePatch::class,
|
CopyVideoUrlResourcePatch::class,
|
||||||
PlayerControlsBytecodePatch::class,
|
PlayerControlsBytecodePatch::class,
|
||||||
VideoInformationPatch::class
|
VideoInformationPatch::class,
|
||||||
],
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
"18.48.39",
|
"18.48.39",
|
||||||
"18.49.37",
|
"18.49.37",
|
||||||
"19.01.34",
|
"19.01.34",
|
||||||
"19.02.39",
|
"19.02.39",
|
||||||
"19.03.35",
|
"19.03.35",
|
||||||
"19.03.36",
|
"19.03.36",
|
||||||
"19.04.37"
|
"19.04.37",
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object CopyVideoUrlBytecodePatch : BytecodePatch() {
|
object CopyVideoUrlBytecodePatch : BytecodePatch(emptySet()) {
|
||||||
private const val INTEGRATIONS_PLAYER_PACKAGE = "Lapp/revanced/integrations/youtube/videoplayer"
|
private const val INTEGRATIONS_PLAYER_PACKAGE = "Lapp/revanced/integrations/youtube/videoplayer"
|
||||||
private val BUTTONS_DESCRIPTORS = listOf(
|
private val BUTTONS_DESCRIPTORS = listOf(
|
||||||
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlButton;",
|
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlButton;",
|
||||||
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlTimestampButton;"
|
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlTimestampButton;",
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
|
|
@ -13,24 +13,25 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||||
dependencies = [
|
dependencies = [
|
||||||
ExternalDownloadsResourcePatch::class,
|
ExternalDownloadsResourcePatch::class,
|
||||||
PlayerControlsBytecodePatch::class,
|
PlayerControlsBytecodePatch::class,
|
||||||
VideoInformationPatch::class
|
VideoInformationPatch::class,
|
||||||
],
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
"18.48.39",
|
"18.48.39",
|
||||||
"18.49.37",
|
"18.49.37",
|
||||||
"19.01.34",
|
"19.01.34",
|
||||||
"19.02.39",
|
"19.02.39",
|
||||||
"19.03.35",
|
"19.03.35",
|
||||||
"19.03.36",
|
"19.03.36",
|
||||||
"19.04.37"
|
"19.04.37",
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object ExternalDownloadsBytecodePatch : BytecodePatch() {
|
object ExternalDownloadsBytecodePatch : BytecodePatch(emptySet()) {
|
||||||
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
|
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
@ -39,13 +40,15 @@ object ExternalDownloadsBytecodePatch : BytecodePatch() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PlayerControlsBytecodePatch.initializeControl(
|
PlayerControlsBytecodePatch.initializeControl(
|
||||||
"$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
"$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V",
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
add code to change the visibility of the control
|
add code to change the visibility of the control
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(
|
PlayerControlsBytecodePatch.injectVisibilityCheckCall(
|
||||||
"$BUTTON_DESCRIPTOR->changeVisibility(Z)V")
|
"$BUTTON_DESCRIPTOR->changeVisibility(Z)V",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -81,7 +81,7 @@ object CustomBrandingPatch : ResourcePatch() {
|
||||||
}.let { resourceGroups ->
|
}.let { resourceGroups ->
|
||||||
if (icon != REVANCED_ICON) {
|
if (icon != REVANCED_ICON) {
|
||||||
val path = File(icon)
|
val path = File(icon)
|
||||||
val resourceDirectory = context["res"]
|
val resourceDirectory = context.get("res", false)
|
||||||
|
|
||||||
resourceGroups.forEach { group ->
|
resourceGroups.forEach { group ->
|
||||||
val fromDirectory = path.resolve(group.resourceDirectoryName)
|
val fromDirectory = path.resolve(group.resourceDirectoryName)
|
||||||
|
@ -102,7 +102,7 @@ object CustomBrandingPatch : ResourcePatch() {
|
||||||
|
|
||||||
appName?.let { name ->
|
appName?.let { name ->
|
||||||
// Change the app name.
|
// Change the app name.
|
||||||
val manifest = context["AndroidManifest.xml"]
|
val manifest = context.get("AndroidManifest.xml", false)
|
||||||
manifest.writeText(
|
manifest.writeText(
|
||||||
manifest.readText()
|
manifest.readText()
|
||||||
.replace(
|
.replace(
|
||||||
|
|
|
@ -71,7 +71,7 @@ object ChangeHeaderPatch : ResourcePatch() {
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
// The directories to copy the header to.
|
// The directories to copy the header to.
|
||||||
val targetResourceDirectories = targetResourceDirectoryNames.keys.mapNotNull {
|
val targetResourceDirectories = targetResourceDirectoryNames.keys.mapNotNull {
|
||||||
context["res"].resolve(it).takeIf(File::exists)
|
context.get("res", false).resolve(it).takeIf(File::exists)
|
||||||
}
|
}
|
||||||
// The files to replace in the target directories.
|
// The files to replace in the target directories.
|
||||||
val targetResourceFiles = targetResourceDirectoryNames.keys.map { directoryName ->
|
val targetResourceFiles = targetResourceDirectoryNames.keys.map { directoryName ->
|
||||||
|
@ -120,7 +120,7 @@ object ChangeHeaderPatch : ResourcePatch() {
|
||||||
|
|
||||||
// For each source folder, copy the files to the target resource directories.
|
// For each source folder, copy the files to the target resource directories.
|
||||||
sourceFolders.forEach { dpiSourceFolder ->
|
sourceFolders.forEach { dpiSourceFolder ->
|
||||||
val targetDpiFolder = context["res"].resolve(dpiSourceFolder.name)
|
val targetDpiFolder = context.get("res", false).resolve(dpiSourceFolder.name)
|
||||||
if (!targetDpiFolder.exists()) return@forEach
|
if (!targetDpiFolder.exists()) return@forEach
|
||||||
|
|
||||||
val imgSourceFiles = dpiSourceFolder.listFiles { file -> file.isFile }!!
|
val imgSourceFiles = dpiSourceFolder.listFiles { file -> file.isFile }!!
|
||||||
|
|
|
@ -17,13 +17,13 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
dependencies = [
|
dependencies = [
|
||||||
IntegrationsPatch::class,
|
IntegrationsPatch::class,
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
AddResourcesPatch::class
|
AddResourcesPatch::class,
|
||||||
],
|
],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage("com.google.android.youtube")
|
CompatiblePackage("com.google.android.youtube"),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
object HideCastButtonPatch : BytecodePatch() {
|
object HideCastButtonPatch : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ object HideCastButtonPatch : BytecodePatch() {
|
||||||
"""
|
"""
|
||||||
invoke-static {p1}, Lapp/revanced/integrations/youtube/patches/HideCastButtonPatch;->getCastButtonOverrideV2(I)I
|
invoke-static {p1}, Lapp/revanced/integrations/youtube/patches/HideCastButtonPatch;->getCastButtonOverrideV2(I)I
|
||||||
move-result p1
|
move-result p1
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
} ?: throw PatchException("setVisibility method not found.")
|
} ?: throw PatchException("setVisibility method not found.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ import org.w3c.dom.Element
|
||||||
description = "Removes the dark background surrounding the video player controls.",
|
description = "Removes the dark background surrounding the video player controls.",
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
"18.32.39",
|
"18.32.39",
|
||||||
"18.37.36",
|
"18.37.36",
|
||||||
"18.38.44",
|
"18.38.44",
|
||||||
|
@ -25,19 +26,19 @@ import org.w3c.dom.Element
|
||||||
"19.02.39",
|
"19.02.39",
|
||||||
"19.03.35",
|
"19.03.35",
|
||||||
"19.03.36",
|
"19.03.36",
|
||||||
"19.04.37"
|
"19.04.37",
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
use = false
|
),
|
||||||
|
],
|
||||||
|
use = false,
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object PlayerControlsBackgroundPatch : ResourcePatch() {
|
object PlayerControlsBackgroundPatch : ResourcePatch() {
|
||||||
private const val RESOURCE_FILE_PATH = "res/drawable/player_button_circle_background.xml"
|
private const val RESOURCE_FILE_PATH = "res/drawable/player_button_circle_background.xml"
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
context.xmlEditor[RESOURCE_FILE_PATH].use { editor ->
|
context.document[RESOURCE_FILE_PATH].use { document ->
|
||||||
editor.file.doRecursively node@{ node ->
|
document.doRecursively node@{ node ->
|
||||||
if (node !is Element) return@node
|
if (node !is Element) return@node
|
||||||
|
|
||||||
node.getAttributeNode("android:color")?.let { attribute ->
|
node.getAttributeNode("android:color")?.let { attribute ->
|
||||||
|
|
|
@ -9,7 +9,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class])
|
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class])
|
||||||
internal object SeekbarColorResourcePatch : ResourcePatch(){
|
internal object SeekbarColorResourcePatch : ResourcePatch() {
|
||||||
internal var reelTimeBarPlayedColorId = -1L
|
internal var reelTimeBarPlayedColorId = -1L
|
||||||
internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
|
internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
|
||||||
internal var inlineTimeBarPlayedNotHighlightedColorId = -1L
|
internal var inlineTimeBarPlayedNotHighlightedColorId = -1L
|
||||||
|
@ -29,16 +29,18 @@ internal object SeekbarColorResourcePatch : ResourcePatch(){
|
||||||
findColorResource("inline_time_bar_played_not_highlighted_color")
|
findColorResource("inline_time_bar_played_not_highlighted_color")
|
||||||
|
|
||||||
// Edit the resume playback drawable and replace the progress bar with a custom drawable
|
// Edit the resume playback drawable and replace the progress bar with a custom drawable
|
||||||
context.xmlEditor["res/drawable/resume_playback_progressbar_drawable.xml"].use { editor ->
|
context.document["res/drawable/resume_playback_progressbar_drawable.xml"].use { document ->
|
||||||
val layerList = editor.file.getElementsByTagName("layer-list").item(0) as Element
|
val layerList = document.getElementsByTagName("layer-list").item(0) as Element
|
||||||
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
|
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
|
||||||
if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) {
|
if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) {
|
||||||
throw PatchException("Could not find progress bar")
|
throw PatchException("Could not find progress bar")
|
||||||
}
|
}
|
||||||
val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element
|
val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element
|
||||||
val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element
|
val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element
|
||||||
val replacementNode = editor.file.createElement(
|
val replacementNode =
|
||||||
"app.revanced.integrations.youtube.patches.theme.ProgressBarDrawable")
|
document.createElement(
|
||||||
|
"app.revanced.integrations.youtube.patches.theme.ProgressBarDrawable",
|
||||||
|
)
|
||||||
scaleNode.replaceChild(replacementNode, shapeNode)
|
scaleNode.replaceChild(replacementNode, shapeNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,26 +17,25 @@ import app.revanced.util.inputStreamFromBundledResource
|
||||||
dependencies = [
|
dependencies = [
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
ResourceMappingPatch::class,
|
ResourceMappingPatch::class,
|
||||||
AddResourcesPatch::class
|
AddResourcesPatch::class,
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
internal object SponsorBlockResourcePatch : ResourcePatch() {
|
internal object SponsorBlockResourcePatch : ResourcePatch() {
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
|
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
|
||||||
IntentPreference(
|
IntentPreference(
|
||||||
"revanced_sb_settings",
|
"revanced_sb_settings",
|
||||||
intent = SettingsPatch.newIntent("revanced_sb_settings_intent")
|
intent = SettingsPatch.newIntent("revanced_sb_settings_intent"),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
arrayOf(
|
arrayOf(
|
||||||
ResourceGroup(
|
ResourceGroup(
|
||||||
"layout",
|
"layout",
|
||||||
"revanced_sb_inline_sponsor_overlay.xml",
|
"revanced_sb_inline_sponsor_overlay.xml",
|
||||||
"revanced_sb_new_segment.xml",
|
"revanced_sb_new_segment.xml",
|
||||||
"revanced_sb_skip_sponsor_button.xml"
|
"revanced_sb_skip_sponsor_button.xml",
|
||||||
),
|
),
|
||||||
ResourceGroup(
|
ResourceGroup(
|
||||||
// required resource for back button, because when the base APK is used, this resource will not exist
|
// required resource for back button, because when the base APK is used, this resource will not exist
|
||||||
|
@ -46,37 +45,47 @@ internal object SponsorBlockResourcePatch : ResourcePatch() {
|
||||||
"revanced_sb_edit.xml",
|
"revanced_sb_edit.xml",
|
||||||
"revanced_sb_logo.xml",
|
"revanced_sb_logo.xml",
|
||||||
"revanced_sb_publish.xml",
|
"revanced_sb_publish.xml",
|
||||||
"revanced_sb_voting.xml"
|
"revanced_sb_voting.xml",
|
||||||
),
|
),
|
||||||
ResourceGroup(
|
ResourceGroup(
|
||||||
// required resource for back button, because when the base APK is used, this resource will not exist
|
// required resource for back button, because when the base APK is used, this resource will not exist
|
||||||
"drawable-xxxhdpi", "quantum_ic_skip_next_white_24.png"
|
"drawable-xxxhdpi",
|
||||||
)
|
"quantum_ic_skip_next_white_24.png",
|
||||||
|
),
|
||||||
).forEach { resourceGroup ->
|
).forEach { resourceGroup ->
|
||||||
context.copyResources("sponsorblock", resourceGroup)
|
context.copyResources("sponsorblock", resourceGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy nodes from host resources to their real xml files
|
// copy nodes from host resources to their real xml files
|
||||||
|
|
||||||
val hostingResourceStream = inputStreamFromBundledResource(
|
val hostingResourceStream =
|
||||||
|
inputStreamFromBundledResource(
|
||||||
"sponsorblock",
|
"sponsorblock",
|
||||||
"host/layout/youtube_controls_layout.xml"
|
"host/layout/youtube_controls_layout.xml",
|
||||||
)!!
|
)!!
|
||||||
|
|
||||||
var modifiedControlsLayout = false
|
var modifiedControlsLayout = false
|
||||||
val targetXmlEditor = context.xmlEditor["res/layout/youtube_controls_layout.xml"]
|
val targetDocument = context.document["res/layout/youtube_controls_layout.xml"]
|
||||||
"RelativeLayout".copyXmlNode(
|
"RelativeLayout".copyXmlNode(
|
||||||
context.xmlEditor[hostingResourceStream],
|
context.document[hostingResourceStream],
|
||||||
targetXmlEditor
|
targetDocument,
|
||||||
).also {
|
).also {
|
||||||
val children = targetXmlEditor.file.getElementsByTagName("RelativeLayout").item(0).childNodes
|
val children = targetDocument.getElementsByTagName("RelativeLayout").item(0).childNodes
|
||||||
|
|
||||||
// Replace the startOf with the voting button view so that the button does not overlap
|
// Replace the startOf with the voting button view so that the button does not overlap
|
||||||
for (i in 1 until children.length) {
|
for (i in 1 until children.length) {
|
||||||
val view = children.item(i)
|
val view = children.item(i)
|
||||||
|
|
||||||
// Replace the attribute for a specific node only
|
// Replace the attribute for a specific node only
|
||||||
if (!(view.hasAttributes() && view.attributes.getNamedItem("android:id").nodeValue.endsWith("live_chat_overlay_button"))) continue
|
if (!(
|
||||||
|
view.hasAttributes() &&
|
||||||
|
view.attributes.getNamedItem(
|
||||||
|
"android:id",
|
||||||
|
).nodeValue.endsWith("live_chat_overlay_button")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
||||||
val votingButtonId = "@+id/revanced_sb_voting_button"
|
val votingButtonId = "@+id/revanced_sb_voting_button"
|
||||||
|
|
|
@ -20,8 +20,8 @@ import org.w3c.dom.Element
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
ResourceMappingPatch::class,
|
ResourceMappingPatch::class,
|
||||||
SeekbarPreferencesPatch::class,
|
SeekbarPreferencesPatch::class,
|
||||||
AddResourcesPatch::class
|
AddResourcesPatch::class,
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
internal object ThemeResourcePatch : ResourcePatch() {
|
internal object ThemeResourcePatch : ResourcePatch() {
|
||||||
private const val SPLASH_BACKGROUND_COLOR = "revanced_splash_background_color"
|
private const val SPLASH_BACKGROUND_COLOR = "revanced_splash_background_color"
|
||||||
|
@ -31,20 +31,21 @@ internal object ThemeResourcePatch : ResourcePatch() {
|
||||||
|
|
||||||
SeekbarPreferencesPatch.addPreferences(
|
SeekbarPreferencesPatch.addPreferences(
|
||||||
SwitchPreference("revanced_seekbar_custom_color"),
|
SwitchPreference("revanced_seekbar_custom_color"),
|
||||||
TextPreference("revanced_seekbar_custom_color_value", inputType = InputType.TEXT_CAP_CHARACTERS)
|
TextPreference("revanced_seekbar_custom_color_value", inputType = InputType.TEXT_CAP_CHARACTERS),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Edit theme colors via resources.
|
// Edit theme colors via resources.
|
||||||
context.xmlEditor["res/values/colors.xml"].use { editor ->
|
context.document["res/values/colors.xml"].use { document ->
|
||||||
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
val children = resourcesNode.childNodes
|
val children = resourcesNode.childNodes
|
||||||
for (i in 0 until children.length) {
|
for (i in 0 until children.length) {
|
||||||
val node = children.item(i) as? Element ?: continue
|
val node = children.item(i) as? Element ?: continue
|
||||||
|
|
||||||
node.textContent = when (node.getAttribute("name")) {
|
node.textContent =
|
||||||
|
when (node.getAttribute("name")) {
|
||||||
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3",
|
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3",
|
||||||
"yt_black4", "yt_status_bar_background_dark", "material_grey_850"
|
"yt_black4", "yt_status_bar_background_dark", "material_grey_850",
|
||||||
-> darkThemeBackgroundColor ?: continue
|
-> darkThemeBackgroundColor ?: continue
|
||||||
|
|
||||||
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
|
"yt_white1", "yt_white1_opacity95", "yt_white1_opacity98",
|
||||||
|
@ -68,14 +69,15 @@ internal object ThemeResourcePatch : ResourcePatch() {
|
||||||
// Edit splash screen files and change the background color,
|
// Edit splash screen files and change the background color,
|
||||||
// if the background colors are set.
|
// if the background colors are set.
|
||||||
if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) {
|
if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) {
|
||||||
val splashScreenResourceFiles = listOf(
|
val splashScreenResourceFiles =
|
||||||
|
listOf(
|
||||||
"res/drawable/quantum_launchscreen_youtube.xml",
|
"res/drawable/quantum_launchscreen_youtube.xml",
|
||||||
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml"
|
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
|
||||||
)
|
)
|
||||||
|
|
||||||
splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile ->
|
splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile ->
|
||||||
context.xmlEditor[resourceFile].use {
|
context.document[resourceFile].use {
|
||||||
val layerList = it.file.getElementsByTagName("layer-list").item(0) as Element
|
val layerList = it.getElementsByTagName("layer-list").item(0) as Element
|
||||||
|
|
||||||
val childNodes = layerList.childNodes
|
val childNodes = layerList.childNodes
|
||||||
for (i in 0 until childNodes.length) {
|
for (i in 0 until childNodes.length) {
|
||||||
|
@ -89,24 +91,24 @@ internal object ThemeResourcePatch : ResourcePatch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addColorResource(
|
private fun addColorResource(
|
||||||
context: ResourceContext,
|
context: ResourceContext,
|
||||||
resourceFile: String,
|
resourceFile: String,
|
||||||
colorName: String,
|
colorName: String,
|
||||||
colorValue: String
|
colorValue: String,
|
||||||
) {
|
) {
|
||||||
context.xmlEditor[resourceFile].use {
|
context.document[resourceFile].use {
|
||||||
val resourcesNode = it.file.getElementsByTagName("resources").item(0) as Element
|
val resourcesNode = it.getElementsByTagName("resources").item(0) as Element
|
||||||
|
|
||||||
resourcesNode.appendChild(
|
resourcesNode.appendChild(
|
||||||
it.file.createElement("color").apply {
|
it.createElement("color").apply {
|
||||||
setAttribute("name", colorName)
|
setAttribute("name", colorName)
|
||||||
setAttribute("category", "color")
|
setAttribute("category", "color")
|
||||||
textContent = colorValue
|
textContent = colorValue
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ package app.revanced.patches.youtube.misc.playercontrols
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.DomFileEditor
|
import app.revanced.patcher.util.Document
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
|
@ -18,13 +18,14 @@ object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
private var lastLeftOf = "fullscreen_button"
|
private var lastLeftOf = "fullscreen_button"
|
||||||
|
|
||||||
private lateinit var resourceContext: ResourceContext
|
private lateinit var resourceContext: ResourceContext
|
||||||
private lateinit var targetXmlEditor: DomFileEditor
|
private lateinit var targetDocument: Document
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
override fun execute(context: ResourceContext) {
|
||||||
resourceContext = context
|
resourceContext = context
|
||||||
targetXmlEditor = context.xmlEditor[TARGET_RESOURCE]
|
targetDocument = context.document[TARGET_RESOURCE]
|
||||||
|
|
||||||
bottomUiContainerResourceId = ResourceMappingPatch.resourceMappings
|
bottomUiContainerResourceId =
|
||||||
|
ResourceMappingPatch.resourceMappings
|
||||||
.single { it.type == "id" && it.name == "bottom_ui_container_stub" }.id
|
.single { it.type == "id" && it.name == "bottom_ui_container_stub" }.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,18 +35,18 @@ object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
||||||
*/
|
*/
|
||||||
fun addControls(resourceDirectoryName: String) {
|
fun addControls(resourceDirectoryName: String) {
|
||||||
val sourceXmlEditor = resourceContext.xmlEditor[
|
val sourceDocument =
|
||||||
|
resourceContext.document[
|
||||||
this::class.java.classLoader.getResourceAsStream(
|
this::class.java.classLoader.getResourceAsStream(
|
||||||
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME"
|
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
|
||||||
)!!
|
)!!,
|
||||||
]
|
]
|
||||||
|
|
||||||
val targetElement = "android.support.constraint.ConstraintLayout"
|
val targetElement = "android.support.constraint.ConstraintLayout"
|
||||||
|
|
||||||
val hostElements = sourceXmlEditor.file.getElementsByTagName(targetElement).item(0).childNodes
|
val hostElements = sourceDocument.getElementsByTagName(targetElement).item(0).childNodes
|
||||||
|
|
||||||
val destinationResourceFile = targetXmlEditor.file
|
val destinationElement = targetDocument.getElementsByTagName(targetElement).item(0)
|
||||||
val destinationElement = destinationResourceFile.getElementsByTagName(targetElement).item(0)
|
|
||||||
|
|
||||||
for (index in 1 until hostElements.length) {
|
for (index in 1 until hostElements.length) {
|
||||||
val element = hostElements.item(index).cloneNode(true)
|
val element = hostElements.item(index).cloneNode(true)
|
||||||
|
@ -63,11 +64,11 @@ object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||||
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)
|
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)
|
||||||
|
|
||||||
// Add the element.
|
// Add the element.
|
||||||
destinationResourceFile.adoptNode(element)
|
targetDocument.adoptNode(element)
|
||||||
destinationElement.appendChild(element)
|
destinationElement.appendChild(element)
|
||||||
}
|
}
|
||||||
sourceXmlEditor.close()
|
sourceDocument.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() = targetXmlEditor.close()
|
override fun close() = targetDocument.close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,13 @@ import org.w3c.dom.Element
|
||||||
object SettingsResourcePatch : BaseSettingsResourcePatch(
|
object SettingsResourcePatch : BaseSettingsResourcePatch(
|
||||||
IntentPreference(
|
IntentPreference(
|
||||||
"revanced_settings",
|
"revanced_settings",
|
||||||
intent = SettingsPatch.newIntent("revanced_settings_intent")
|
intent = SettingsPatch.newIntent("revanced_settings_intent"),
|
||||||
) to "settings_fragment",
|
) to "settings_fragment",
|
||||||
dependencies = setOf(
|
dependencies =
|
||||||
|
setOf(
|
||||||
ResourceMappingPatch::class,
|
ResourceMappingPatch::class,
|
||||||
AddResourcesPatch::class,
|
AddResourcesPatch::class,
|
||||||
)
|
),
|
||||||
) {
|
) {
|
||||||
// Used for a fingerprint from SettingsPatch.
|
// Used for a fingerprint from SettingsPatch.
|
||||||
internal var appearanceStringId = -1L
|
internal var appearanceStringId = -1L
|
||||||
|
@ -28,12 +29,13 @@ object SettingsResourcePatch : BaseSettingsResourcePatch(
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
// Used for a fingerprint from SettingsPatch.
|
// Used for a fingerprint from SettingsPatch.
|
||||||
appearanceStringId = ResourceMappingPatch.resourceMappings.find {
|
appearanceStringId =
|
||||||
|
ResourceMappingPatch.resourceMappings.find {
|
||||||
it.type == "string" && it.name == "app_theme_appearance_dark"
|
it.type == "string" && it.name == "app_theme_appearance_dark"
|
||||||
}!!.id
|
}!!.id
|
||||||
|
|
||||||
arrayOf(
|
arrayOf(
|
||||||
ResourceGroup("layout", "revanced_settings_with_toolbar.xml")
|
ResourceGroup("layout", "revanced_settings_with_toolbar.xml"),
|
||||||
).forEach { resourceGroup ->
|
).forEach { resourceGroup ->
|
||||||
context.copyResources("settings", resourceGroup)
|
context.copyResources("settings", resourceGroup)
|
||||||
}
|
}
|
||||||
|
@ -41,20 +43,20 @@ object SettingsResourcePatch : BaseSettingsResourcePatch(
|
||||||
// Modify the manifest and add a data intent filter to the LicenseActivity.
|
// Modify the manifest and add a data intent filter to the LicenseActivity.
|
||||||
// Some devices freak out if undeclared data is passed to an intent,
|
// Some devices freak out if undeclared data is passed to an intent,
|
||||||
// and this change appears to fix the issue.
|
// and this change appears to fix the issue.
|
||||||
context.xmlEditor["AndroidManifest.xml"].use { editor ->
|
context.document["AndroidManifest.xml"].use { document ->
|
||||||
// A xml regular-expression would probably work better than this manual searching.
|
// A xml regular-expression would probably work better than this manual searching.
|
||||||
val manifestNodes = editor.file.getElementsByTagName("manifest").item(0).childNodes
|
val manifestNodes = document.getElementsByTagName("manifest").item(0).childNodes
|
||||||
for (i in 0..manifestNodes.length) {
|
for (i in 0..manifestNodes.length) {
|
||||||
val node = manifestNodes.item(i)
|
val node = manifestNodes.item(i)
|
||||||
if (node != null && node.nodeName == "application") {
|
if (node != null && node.nodeName == "application") {
|
||||||
val applicationNodes = node.childNodes
|
val applicationNodes = node.childNodes
|
||||||
for (j in 0..applicationNodes.length) {
|
for (j in 0..applicationNodes.length) {
|
||||||
val applicationChild = applicationNodes.item(j)
|
val applicationChild = applicationNodes.item(j)
|
||||||
if (applicationChild is Element && applicationChild.nodeName == "activity"
|
if (applicationChild is Element && applicationChild.nodeName == "activity" &&
|
||||||
&& applicationChild.getAttribute("android:name") == "com.google.android.libraries.social.licenses.LicenseActivity"
|
applicationChild.getAttribute("android:name") == "com.google.android.libraries.social.licenses.LicenseActivity"
|
||||||
) {
|
) {
|
||||||
val intentFilter = editor.file.createElement("intent-filter")
|
val intentFilter = document.createElement("intent-filter")
|
||||||
val mimeType = editor.file.createElement("data")
|
val mimeType = document.createElement("data")
|
||||||
mimeType.setAttribute("android:mimeType", "text/plain")
|
mimeType.setAttribute("android:mimeType", "text/plain")
|
||||||
intentFilter.appendChild(mimeType)
|
intentFilter.appendChild(mimeType)
|
||||||
applicationChild.appendChild(intentFilter)
|
applicationChild.appendChild(intentFilter)
|
||||||
|
|
|
@ -13,20 +13,21 @@ import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPa
|
||||||
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
|
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
|
||||||
compatiblePackages = [
|
compatiblePackages = [
|
||||||
CompatiblePackage(
|
CompatiblePackage(
|
||||||
"com.google.android.youtube", [
|
"com.google.android.youtube",
|
||||||
|
[
|
||||||
"18.48.39",
|
"18.48.39",
|
||||||
"18.49.37",
|
"18.49.37",
|
||||||
"19.01.34",
|
"19.01.34",
|
||||||
"19.02.39",
|
"19.02.39",
|
||||||
"19.03.35",
|
"19.03.35",
|
||||||
"19.03.36",
|
"19.03.36",
|
||||||
"19.04.37"
|
"19.04.37",
|
||||||
]
|
],
|
||||||
)
|
),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
object PlaybackSpeedPatch : BytecodePatch() {
|
object PlaybackSpeedPatch : BytecodePatch(emptySet()) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
// All patches this patch depends on succeed.
|
// All patches this patch depends on succeed.
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package app.revanced.util
|
package app.revanced.util
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.util.Document
|
||||||
import app.revanced.patcher.util.DomFileEditor
|
import app.revanced.patcher.util.DomFileEditor
|
||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
|
||||||
import app.revanced.util.resource.BaseResource
|
import app.revanced.util.resource.BaseResource
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import org.w3c.dom.NodeList
|
import org.w3c.dom.NodeList
|
||||||
|
@ -25,9 +25,10 @@ fun Node.childElementsSequence() = this.childNodes.asSequence().filter { it.node
|
||||||
/**
|
/**
|
||||||
* Performs the given [action] on each child element.
|
* Performs the given [action] on each child element.
|
||||||
*/
|
*/
|
||||||
fun Node.forEachChildElement(action: (Node) -> Unit) = childElementsSequence().forEach {
|
fun Node.forEachChildElement(action: (Node) -> Unit) =
|
||||||
|
childElementsSequence().forEach {
|
||||||
action(it)
|
action(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively traverse the DOM tree starting from the given root node.
|
* Recursively traverse the DOM tree starting from the given root node.
|
||||||
|
@ -45,15 +46,19 @@ fun Node.doRecursively(action: (Node) -> Unit) {
|
||||||
* @param sourceResourceDirectory The source resource directory name.
|
* @param sourceResourceDirectory The source resource directory name.
|
||||||
* @param resources The resources to copy.
|
* @param resources The resources to copy.
|
||||||
*/
|
*/
|
||||||
fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resources: ResourceGroup) {
|
fun ResourceContext.copyResources(
|
||||||
val targetResourceDirectory = this["res"]
|
sourceResourceDirectory: String,
|
||||||
|
vararg resources: ResourceGroup,
|
||||||
|
) {
|
||||||
|
val targetResourceDirectory = this.get("res", false)
|
||||||
|
|
||||||
for (resourceGroup in resources) {
|
for (resourceGroup in resources) {
|
||||||
resourceGroup.resources.forEach { resource ->
|
resourceGroup.resources.forEach { resource ->
|
||||||
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
||||||
Files.copy(
|
Files.copy(
|
||||||
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
|
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
|
||||||
targetResourceDirectory.resolve(resourceFile).toPath(), StandardCopyOption.REPLACE_EXISTING
|
targetResourceDirectory.resolve(resourceFile).toPath(),
|
||||||
|
StandardCopyOption.REPLACE_EXISTING,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +66,7 @@ fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resour
|
||||||
|
|
||||||
internal fun inputStreamFromBundledResource(
|
internal fun inputStreamFromBundledResource(
|
||||||
sourceResourceDirectory: String,
|
sourceResourceDirectory: String,
|
||||||
resourceFile: String
|
resourceFile: String,
|
||||||
): InputStream? = classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")
|
): InputStream? = classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,29 +85,29 @@ class ResourceGroup(val resourceDirectoryName: String, vararg val resources: Str
|
||||||
fun ResourceContext.iterateXmlNodeChildren(
|
fun ResourceContext.iterateXmlNodeChildren(
|
||||||
resource: String,
|
resource: String,
|
||||||
targetTag: String,
|
targetTag: String,
|
||||||
callback: (node: Node) -> Unit
|
callback: (node: Node) -> Unit,
|
||||||
) =
|
) = document[classLoader.getResourceAsStream(resource)!!].use {
|
||||||
xmlEditor[classLoader.getResourceAsStream(resource)!!].use {
|
val stringsNode = it.getElementsByTagName(targetTag).item(0).childNodes
|
||||||
val stringsNode = it.file.getElementsByTagName(targetTag).item(0).childNodes
|
|
||||||
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the specified node of the source [DomFileEditor] to the target [DomFileEditor].
|
* Copies the specified node of the source [Document] to the target [Document].
|
||||||
* @param source the source [DomFileEditor].
|
* @param source the source [Document].
|
||||||
* @param target the target [DomFileEditor]-
|
* @param target the target [Document]-
|
||||||
* @return AutoCloseable that closes the target [DomFileEditor]s.
|
* @return AutoCloseable that closes the [Document]s.
|
||||||
*/
|
*/
|
||||||
fun String.copyXmlNode(source: DomFileEditor, target: DomFileEditor): AutoCloseable {
|
fun String.copyXmlNode(
|
||||||
val hostNodes = source.file.getElementsByTagName(this).item(0).childNodes
|
source: Document,
|
||||||
|
target: Document,
|
||||||
|
): AutoCloseable {
|
||||||
|
val hostNodes = source.getElementsByTagName(this).item(0).childNodes
|
||||||
|
|
||||||
val destinationResourceFile = target.file
|
val destinationNode = target.getElementsByTagName(this).item(0)
|
||||||
val destinationNode = destinationResourceFile.getElementsByTagName(this).item(0)
|
|
||||||
|
|
||||||
for (index in 0 until hostNodes.length) {
|
for (index in 0 until hostNodes.length) {
|
||||||
val node = hostNodes.item(index).cloneNode(true)
|
val node = hostNodes.item(index).cloneNode(true)
|
||||||
destinationResourceFile.adoptNode(node)
|
target.adoptNode(node)
|
||||||
destinationNode.appendChild(node)
|
destinationNode.appendChild(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,14 +117,30 @@ fun String.copyXmlNode(source: DomFileEditor, target: DomFileEditor): AutoClosea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
"Use copyXmlNode(Document, Document) instead.",
|
||||||
|
ReplaceWith(
|
||||||
|
"this.copyXmlNode(source.file as Document, target.file as Document)",
|
||||||
|
"app.revanced.patcher.util.Document",
|
||||||
|
"app.revanced.patcher.util.Document",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
fun String.copyXmlNode(
|
||||||
|
source: DomFileEditor,
|
||||||
|
target: DomFileEditor,
|
||||||
|
) = this.copyXmlNode(source.file as Document, target.file as Document)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a resource node child.
|
* Add a resource node child.
|
||||||
*
|
*
|
||||||
* @param resource The resource to add.
|
* @param resource The resource to add.
|
||||||
* @param resourceCallback Called when a resource has been processed.
|
* @param resourceCallback Called when a resource has been processed.
|
||||||
*/
|
*/
|
||||||
internal fun Node.addResource(resource: BaseResource, resourceCallback: (BaseResource) -> Unit = { }) {
|
internal fun Node.addResource(
|
||||||
|
resource: BaseResource,
|
||||||
|
resourceCallback: (BaseResource) -> Unit = { },
|
||||||
|
) {
|
||||||
appendChild(resource.serialize(ownerDocument, resourceCallback))
|
appendChild(resource.serialize(ownerDocument, resourceCallback))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun DomFileEditor?.getNode(tagName: String) = this!!.file.getElementsByTagName(tagName).item(0)
|
internal fun Document?.getNode(tagName: String) = this!!.getElementsByTagName(tagName).item(0)
|
||||||
|
|
Loading…
Reference in a new issue