diff --git a/src/main/kotlin/app/revanced/extensions/Extensions.kt b/src/main/kotlin/app/revanced/extensions/Extensions.kt index e4bb1678..61120799 100644 --- a/src/main/kotlin/app/revanced/extensions/Extensions.kt +++ b/src/main/kotlin/app/revanced/extensions/Extensions.kt @@ -1,8 +1,10 @@ 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.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.toInstruction 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 * diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/SwipeControlsHostActivityFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/SwipeControlsHostActivityFingerprint.kt new file mode 100644 index 00000000..3d59e0f4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/SwipeControlsHostActivityFingerprint.kt @@ -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;", "" +) +@DirectPatternScanMethod +@SwipeControlsCompatibility +@Version("0.0.1") +object SwipeControlsHostActivityFingerprint : MethodFingerprint( + null, null, null, null, null, { methodDef -> + methodDef.definingClass == "Lapp/revanced/integrations/swipecontrols/SwipeControlsHostActivity;" && methodDef.name == "" + } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileOnStartFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileActivityFingerprint.kt similarity index 80% rename from src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileOnStartFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileActivityFingerprint.kt index 6a72387c..af849918 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileOnStartFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/fingerprints/WatchWhileActivityFingerprint.kt @@ -7,15 +7,15 @@ 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") +@Name("watch-while-activity-fingerprint") @MatchingMethod( - "LWatchWhileActivity;", "onCreate" + "LWatchWhileActivity;", "" ) @DirectPatternScanMethod @SwipeControlsCompatibility @Version("0.0.1") -object WatchWhileOnStartFingerprint : MethodFingerprint( +object WatchWhileActivityFingerprint : MethodFingerprint( null, null, null, null, null, { methodDef -> - methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "onStart" + methodDef.definingClass.endsWith("WatchWhileActivity;") && methodDef.name == "" } -) \ No newline at end of file +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/patch/bytecode/SwipeControlsBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/patch/bytecode/SwipeControlsBytecodePatch.kt index a26105ba..53908cd0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/patch/bytecode/SwipeControlsBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/swipecontrols/patch/bytecode/SwipeControlsBytecodePatch.kt @@ -1,45 +1,69 @@ 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.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.DependsOn import app.revanced.patcher.patch.annotations.Patch 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.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.misc.integrations.patch.IntegrationsPatch -import app.revanced.patches.youtube.misc.playeroverlay.patch.PlayerOverlaysHookPatch import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.immutable.ImmutableMethod @Patch @Name("swipe-controls") @Description("Adds volume and brightness swipe controls.") @SwipeControlsCompatibility -@Version("0.0.2") +@Version("0.0.3") @DependsOn( [ IntegrationsPatch::class, PlayerTypeHookPatch::class, - PlayerOverlaysHookPatch::class, SwipeControlsResourcePatch::class ] ) class SwipeControlsBytecodePatch : BytecodePatch( listOf( - WatchWhileOnStartFingerprint + WatchWhileActivityFingerprint, + SwipeControlsHostActivityFingerprint ) ) { 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" - ) + val wrapperClass = SwipeControlsHostActivityFingerprint.result!!.mutableClass + val targetClass = WatchWhileActivityFingerprint.result!!.mutableClass + + // 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() } -} \ No newline at end of file +} +