feat: swipe-controls override volume button behaviour (#285)

This commit is contained in:
Chris 2022-08-14 22:19:54 +02:00 committed by GitHub
parent 6f14542c18
commit 69465f3a99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 16 deletions

View file

@ -1,8 +1,10 @@
package app.revanced.extensions package app.revanced.extensions
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.ResourceData import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.toInstruction import app.revanced.patcher.util.smali.toInstruction
import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.AccessFlags
@ -27,6 +29,30 @@ internal fun MutableMethodImplementation.injectHideCall(
) )
} }
/**
* traverse the class hierarchy starting from the given root class
*
* @param targetClass the class to start traversing the class hierarchy from
* @param callback function that is called for every class in the hierarchy
*/
fun BytecodeData.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
callback(targetClass)
this.findClass(targetClass.superclass ?: return)?.resolve()?.let {
traverseClassHierarchy(it, callback)
}
}
/**
* apply a transform to all methods of the class
*
* @param transform the transformation function. original method goes in, transformed method goes out
*/
fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) {
val transformedMethods = methods.map { it.transform() }
methods.clear()
methods.addAll(transformedMethods)
}
/** /**
* Insert an event hook at the top of the method. If the hook returns true, the event is consumed and the method will return with true * Insert an event hook at the top of the method. If the hook returns true, the event is consumed and the method will return with true
* *

View file

@ -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("swipe-controls-host-activity-fingerprint")
@MatchingMethod(
"Lapp/revanced/integrations/swipecontrols/SwipeControlsHostActivity;", "<init>"
)
@DirectPatternScanMethod
@SwipeControlsCompatibility
@Version("0.0.1")
object SwipeControlsHostActivityFingerprint : MethodFingerprint(
null, null, null, null, null, { methodDef ->
methodDef.definingClass == "Lapp/revanced/integrations/swipecontrols/SwipeControlsHostActivity;" && methodDef.name == "<init>"
}
)

View file

@ -7,15 +7,15 @@ import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
@Name("watch-while-onStart-fingerprint") @Name("watch-while-activity-fingerprint")
@MatchingMethod( @MatchingMethod(
"LWatchWhileActivity;", "onCreate" "LWatchWhileActivity;", "<init>"
) )
@DirectPatternScanMethod @DirectPatternScanMethod
@SwipeControlsCompatibility @SwipeControlsCompatibility
@Version("0.0.1") @Version("0.0.1")
object WatchWhileOnStartFingerprint : MethodFingerprint( object WatchWhileActivityFingerprint : MethodFingerprint(
null, null, null, null, null, { methodDef -> null, null, null, null, null, { methodDef ->
methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "onStart" methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "<init>"
} }
) )

View file

@ -1,45 +1,69 @@
package app.revanced.patches.youtube.interaction.swipecontrols.patch.bytecode package app.revanced.patches.youtube.interaction.swipecontrols.patch.bytecode
import app.revanced.extensions.transformMethods
import app.revanced.extensions.traverseClassHierarchy
import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility import app.revanced.patches.youtube.interaction.swipecontrols.annotation.SwipeControlsCompatibility
import app.revanced.patches.youtube.interaction.swipecontrols.fingerprints.WatchWhileOnStartFingerprint import app.revanced.patches.youtube.interaction.swipecontrols.fingerprints.SwipeControlsHostActivityFingerprint
import app.revanced.patches.youtube.interaction.swipecontrols.fingerprints.WatchWhileActivityFingerprint
import app.revanced.patches.youtube.interaction.swipecontrols.patch.resource.SwipeControlsResourcePatch import app.revanced.patches.youtube.interaction.swipecontrols.patch.resource.SwipeControlsResourcePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.playeroverlay.patch.PlayerOverlaysHookPatch
import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.immutable.ImmutableMethod
@Patch @Patch
@Name("swipe-controls") @Name("swipe-controls")
@Description("Adds volume and brightness swipe controls.") @Description("Adds volume and brightness swipe controls.")
@SwipeControlsCompatibility @SwipeControlsCompatibility
@Version("0.0.2") @Version("0.0.3")
@DependsOn( @DependsOn(
[ [
IntegrationsPatch::class, IntegrationsPatch::class,
PlayerTypeHookPatch::class, PlayerTypeHookPatch::class,
PlayerOverlaysHookPatch::class,
SwipeControlsResourcePatch::class SwipeControlsResourcePatch::class
] ]
) )
class SwipeControlsBytecodePatch : BytecodePatch( class SwipeControlsBytecodePatch : BytecodePatch(
listOf( listOf(
WatchWhileOnStartFingerprint WatchWhileActivityFingerprint,
SwipeControlsHostActivityFingerprint
) )
) { ) {
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData): PatchResult {
WatchWhileOnStartFingerprint.result!!.mutableMethod.addInstruction( val wrapperClass = SwipeControlsHostActivityFingerprint.result!!.mutableClass
0, val targetClass = WatchWhileActivityFingerprint.result!!.mutableClass
"invoke-static { p0 }, Lapp/revanced/integrations/patches/SwipeControlsPatch;->WatchWhileActivity_onStartHookEX(Ljava/lang/Object;)V"
) // inject the wrapper class from integrations into the class hierarchy of WatchWhileActivity
wrapperClass.setSuperClass(targetClass.superclass)
targetClass.setSuperClass(wrapperClass.type)
// ensure all classes and methods in the hierarchy are non-final, so we can override them in integrations
data.traverseClassHierarchy(targetClass) {
accessFlags = accessFlags and AccessFlags.FINAL.value.inv()
transformMethods {
ImmutableMethod(
definingClass,
name,
parameters,
returnType,
accessFlags and AccessFlags.FINAL.value.inv(),
annotations,
hiddenApiRestrictions,
implementation
).toMutable()
}
}
return PatchResultSuccess() return PatchResultSuccess()
} }
} }