Skip to content

Commit

Permalink
feat: swipe-controls rewrite (#131)
Browse files Browse the repository at this point in the history
* move patch 'fenster' to 'swipe-controls'

* remove 'injectIntoNamedMethod' function

* move updatePlayerType hook into its own patch

* refactor 'swipe-controls' patch

* add resources for new ui to patch

Co-authored-by: TheJeterLP <joey.peter1998@gmail.com>
  • Loading branch information
shadow578 and TheJeterLP committed Jul 11, 2022
1 parent ffe68a1 commit b7dba09
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 153 deletions.
84 changes: 57 additions & 27 deletions src/main/kotlin/app/revanced/extensions/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package app.revanced.extensions

import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
Expand All @@ -14,6 +14,8 @@ import org.jf.dexlib2.builder.instruction.BuilderInstruction21t
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
import org.w3c.dom.Node
import java.io.OutputStream
import java.nio.file.Files

internal fun MutableMethodImplementation.injectHideCall(
index: Int,
Expand Down Expand Up @@ -100,37 +102,65 @@ internal fun MutableMethod.injectConsumableEventHook(hookRef: ImmutableMethodRef
}

/**
* Insert instructions into a named method
* inject resources into the patched app
*
* @param targetClass the name of the class of which the method is a member
* @param targetMethod the name of the method to insert into
* @param index index to insert the instructions at. If the index is negative, it is used as an offset to the last method (so -1 inserts at the end of the method)
* @param instructions the smali instructions to insert (they'll be compiled by MutableMethod.addInstructions)
* @param classLoader classloader to use for loading the resources
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
* @param resourceType the resource type, for example 'drawable'. this has to match both the source and the target
* @param resourceFileNames names of all resources of this type to inject
*/
internal fun BytecodeData.injectIntoNamedMethod(
targetClass: String,
targetMethod: String,
index: Int,
instructions: String
fun ResourceData.injectResources(
classLoader: ClassLoader,
patchDirectoryPath: String,
resourceType: String,
resourceFileNames: List<String>
) {
var injections = 0
this.classes.filter { it.type.endsWith("$targetClass;") }.forEach { classDef ->
this.proxy(classDef).resolve().methods.filter { it.name == targetMethod }.forEach { methodDef ->
// if index is negative, interpret as an offset from the back
var insertIndex = index
if (insertIndex < 0) {
insertIndex += methodDef.implementation!!.instructions.size
}

// insert instructions
methodDef.addInstructions(insertIndex, instructions)
injections++
}
resourceFileNames.forEach { name ->
val relativePath = "$resourceType/$name"
val sourceRes = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
?: throw PatchResultError("could not open resource '$patchDirectoryPath/$relativePath'")

Files.copy(
sourceRes,
this["res"].resolve(relativePath).toPath()
)
}
}

// fail if nothing was injected
if (injections <= 0) {
throw PatchResultError("failed to inject into $targetClass.$targetMethod: no targets were found")
/**
* inject strings into the patched app
*
* @param classLoader classloader to use for loading the resources
* @param patchDirectoryPath path to the files. this will be the directory you created under the 'resources' source folder
* @param languageIdentifier ISO 639-2 two- letter language code identifier (aka the one android uses for values directory)
*/
fun ResourceData.injectStrings(
classLoader: ClassLoader,
patchDirectoryPath: String,
languageIdentifier: String? = null,
) {
val relativePath =
if (languageIdentifier.isNullOrBlank()) "values/strings.xml" else "values/strings-$languageIdentifier.xml"

// open source strings.xml
val sourceInputStream = classLoader.getResourceAsStream("$patchDirectoryPath/$relativePath")
?: throw PatchResultError("failed to open '$patchDirectoryPath/$relativePath'")
xmlEditor[sourceInputStream, OutputStream.nullOutputStream()].use { sourceStringsXml ->
val strings = sourceStringsXml.file.getElementsByTagName("resources").item(0).childNodes

// open target strings.xml
xmlEditor["res/$relativePath"].use { targetStringsXml ->
val targetFile = targetStringsXml.file
val targetRootNode = targetFile.getElementsByTagName("resources").item(0)

// process all children strings in the source
for (i in 0 until strings.length) {
// clone the node from source to target
val node = strings.item(i).cloneNode(true)
targetFile.adoptNode(node)
targetRootNode.appendChild(node)
}
}
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.interaction.swipecontrols.annotation

import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package

@Compatibility(
[Package(
"com.google.android.youtube", arrayOf("17.24.34", "17.25.34")
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SwipeControlsCompatibility
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package app.revanced.patches.youtube.interaction.swipecontrols.fingerprints

import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility

@Name("watch-while-onStart-fingerprint")
@MatchingMethod(
"LWatchWhileActivity;", "onCreate"
)
@DirectPatternScanMethod
@SwipeControlsCompatibility
@Version("0.0.1")
object WatchWhileOnStartFingerprint : MethodFingerprint(
null, null, null, null, null, { methodDef ->
methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "onStart"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.revanced.patches.youtube.interaction.swipecontrols.patch

import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Dependencies
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
import app.revanced.patches.youtube.interaction.swipecontrols.fingerprints.WatchWhileOnStartFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch

@Patch
@Name("swipe-controls")
@Description("Adds volume and brightness swipe controls.")
@SwipeControlsCompatibility
@Version("0.0.2")
@Dependencies(
dependencies = [
IntegrationsPatch::class,
PlayerTypeHookPatch::class,
SwipeControlsResourcesPatch::class
]
)
class SwipeControlsPatch : BytecodePatch(
listOf(
WatchWhileOnStartFingerprint
)
) {
override fun execute(data: BytecodeData): PatchResult {
WatchWhileOnStartFingerprint.result!!.mutableMethod.addInstruction(
0,
"invoke-static { p0 }, Lapp/revanced/integrations/patches/SwipeControlsPatch;->WatchWhileActivity_onStartHookEX(Ljava/lang/Object;)V"
)
return PatchResultSuccess()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package app.revanced.patches.youtube.interaction.swipecontrols.patch

import app.revanced.extensions.injectResources
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility

@Name("swipe-controls-resource-patch")
@SwipeControlsCompatibility
@Version("0.0.1")
class SwipeControlsResourcesPatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
val resourcesDir = "swipecontrols"

data.injectResources(
this.javaClass.classLoader,
resourcesDir,
"drawable",
listOf(
"ic_sc_brightness_auto",
"ic_sc_brightness_manual",
"ic_sc_volume_mute",
"ic_sc_volume_normal"
).map { "$it.xml" }
)
return PatchResultSuccess()
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.interaction.fenster.annotation
package app.revanced.patches.youtube.misc.playertype.annotation

import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
Expand All @@ -10,4 +10,4 @@ import app.revanced.patcher.annotation.Package
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class FensterCompatibility
internal annotation class PlayerTypeHookCompatibility
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package app.revanced.patches.youtube.interaction.fenster.fingerprints
package app.revanced.patches.youtube.misc.playertype.fingerprint

import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.interaction.fenster.annotation.FensterCompatibility
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode

//TODO constrain to only match in YoutubePlayerOverlaysLayout?
@Name("update-player-type-fingerprint")
@MatchingMethod(
"LYoutubePlayerOverlaysLayout;",
"nM"
)
@FuzzyPatternScanMethod(2)
@FensterCompatibility
@SwipeControlsCompatibility
@Version("0.0.1")
object UpdatePlayerTypeFingerprint : MethodFingerprint(
"V",
Expand Down
Loading

0 comments on commit b7dba09

Please sign in to comment.