diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/HideAdsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/HideAdsResourcePatch.kt index 22d812689..1701e02e1 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/HideAdsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/resource/patch/HideAdsResourcePatch.kt @@ -23,7 +23,6 @@ import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.P @HideAdsCompatibility @Version("0.0.1") class HideAdsResourcePatch : ResourcePatch { - override fun execute(context: ResourceContext): PatchResult { PreferenceScreen.LAYOUT.addPreferences( SwitchPreference( @@ -200,6 +199,12 @@ class HideAdsResourcePatch : ResourcePatch { StringResource("revanced_hide_mix_playlists_summary_on", "Mix playlists are hidden"), StringResource("revanced_hide_mix_playlists_summary_off", "Mix playlists are shown") ), + SwitchPreference( + "revanced_hide_artist_cards", + StringResource("revanced_hide_artist_cards_title", "Hide artist cards"), + StringResource("revanced_hide_artist_cards_on", "Artist cards is hidden"), + StringResource("revanced_hide_artist_cards_off", "Artist cards is shown") + ), ) PreferenceScreen.ADS.addPreferences( @@ -259,6 +264,8 @@ class HideAdsResourcePatch : ResourcePatch { ) ) + LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) + adAttributionId = ResourceMappingPatch.resourceMappings.single { it.name == "ad_attribution" }.id return PatchResultSuccess() @@ -266,5 +273,8 @@ class HideAdsResourcePatch : ResourcePatch { internal companion object { var adAttributionId: Long = -1 + + private const val FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/patches/components/AdsFilter;" } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/patch/HideButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/patch/HideButtonsPatch.kt index 073d9b05a..cbc27dd2c 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/patch/HideButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/patch/HideButtonsPatch.kt @@ -66,6 +66,14 @@ class HideButtonsPatch : ResourcePatch { StringResource("revanced_hide_buttons_preference_screen_summary", "Hide or show buttons under videos") ) ) + + LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) + return PatchResultSuccess() } + + private companion object { + private const val FILTER_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/patches/components/ButtonsFilter;" + } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/annotations/HideArtistCardCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/annotations/HideArtistCardCompatibility.kt deleted file mode 100644 index b3245b815..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/annotations/HideArtistCardCompatibility.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.artistcards.annotations - -import app.revanced.patcher.annotation.Compatibility -import app.revanced.patcher.annotation.Package - -@Compatibility([Package("com.google.android.youtube", arrayOf("18.16.37", "18.19.35"))]) -@Target(AnnotationTarget.CLASS) -internal annotation class HideArtistCardCompatibility diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/patch/HideArtistCardsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/patch/HideArtistCardsPatch.kt deleted file mode 100644 index e4921df9e..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/artistcards/patch/HideArtistCardsPatch.kt +++ /dev/null @@ -1,37 +0,0 @@ -package app.revanced.patches.youtube.layout.hide.artistcards.patch - -import app.revanced.patcher.annotation.Description -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.PatchResult -import app.revanced.patcher.patch.PatchResultSuccess -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotations.DependsOn -import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch -import app.revanced.patches.shared.settings.preference.impl.StringResource -import app.revanced.patches.shared.settings.preference.impl.SwitchPreference -import app.revanced.patches.youtube.layout.hide.artistcards.annotations.HideArtistCardCompatibility -import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch -import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch - -@Patch -@DependsOn([ResourceMappingPatch::class, LithoFilterPatch::class]) -@Name("hide-artist-card") -@Description("Hides the artist card below the searchbar.") -@HideArtistCardCompatibility -@Version("0.0.1") -class HideArtistCardsPatch : ResourcePatch { - override fun execute(context: ResourceContext): PatchResult { - SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( - SwitchPreference( - "revanced_hide_artist_cards", - StringResource("revanced_hide_artist_cards_title", "Hide artist cards"), - StringResource("revanced_hide_artist_cards_on", "Artist cards is hidden"), - StringResource("revanced_hide_artist_cards_off", "Artist cards is shown") - ), - ) - return PatchResultSuccess() - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/bytecode/patch/HideShortsComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/bytecode/patch/HideShortsComponentsPatch.kt index 91fe39b84..451512be5 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/bytecode/patch/HideShortsComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/bytecode/patch/HideShortsComponentsPatch.kt @@ -49,6 +49,8 @@ class HideShortsComponentsPatch : BytecodePatch( ) ) { override fun execute(context: BytecodeContext): PatchResult { + LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) + // region Hide the Shorts shelf. ReelConstructorFingerprint.result?.let { @@ -59,7 +61,7 @@ class HideShortsComponentsPatch : BytecodePatch( injectHideViewCall( insertIndex, viewRegister, - CLASS_DESCRIPTOR, + FILTER_CLASS_DESCRIPTOR, "hideShortsShelf" ) } @@ -89,7 +91,7 @@ class HideShortsComponentsPatch : BytecodePatch( val viewRegister = getInstruction(checkCastIndex).registerA addInstruction( checkCastIndex + 1, - "sput-object v$viewRegister, $CLASS_DESCRIPTOR->pivotBar:" + + "sput-object v$viewRegister, $FILTER_CLASS_DESCRIPTOR->pivotBar:" + "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" ) } @@ -102,7 +104,7 @@ class HideShortsComponentsPatch : BytecodePatch( throw RenderBottomNavigationBarFingerprint.toErrorResult() RenderBottomNavigationBarFingerprint.result!!.mutableMethod.apply { - addInstruction(0, "invoke-static { }, $CLASS_DESCRIPTOR->hideNavigationBar()V") + addInstruction(0, "invoke-static { }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar()V") } } ?: return RenderBottomNavigationBarParentFingerprint.toErrorResult() @@ -115,7 +117,7 @@ class HideShortsComponentsPatch : BytecodePatch( addInstruction( insertIndex, - "invoke-static { v$viewRegister }, $CLASS_DESCRIPTOR->" + + "invoke-static { v$viewRegister }, $FILTER_CLASS_DESCRIPTOR->" + "hideNavigationBar(Landroid/view/View;)Landroid/view/View;" ) } @@ -127,7 +129,7 @@ class HideShortsComponentsPatch : BytecodePatch( } private companion object { - private const val CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/components/ShortsFilter;" + private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/components/ShortsFilter;" private enum class ShortsButtons(private val resourceName: String, private val methodName: String) { COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"), @@ -139,7 +141,7 @@ class HideShortsComponentsPatch : BytecodePatch( val setIdIndex = referencedIndex + 1 val viewRegister = method.getInstruction(setIdIndex).registerC - method.injectHideViewCall(setIdIndex, viewRegister, CLASS_DESCRIPTOR, methodName) + method.injectHideViewCall(setIdIndex, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName) } } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/LithoFilterFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/LithoFilterFingerprint.kt new file mode 100644 index 000000000..0d3760725 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/fingerprints/LithoFilterFingerprint.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.youtube.misc.litho.filter.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint + +object LithoFilterFingerprint : MethodFingerprint( + customFingerprint = custom@{ method, classDef -> + if (method.name != "") return@custom false + + classDef.type.endsWith("LithoFilterPatch;") + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt index a93e35b30..93f85db79 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/patch/LithoFilterPatch.kt @@ -4,8 +4,11 @@ import app.revanced.extensions.toErrorResult import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.patch.BytecodePatch @@ -15,29 +18,29 @@ import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.litho.filter.annotation.LithoFilterCompatibility -import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ComponentContextParserFingerprint -import app.revanced.patches.youtube.misc.litho.filter.fingerprints.EmptyComponentBuilderFingerprint -import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ProtobufBufferFingerprint -import app.revanced.patches.youtube.misc.litho.filter.fingerprints.ReadComponentIdentifierFingerprint +import app.revanced.patches.youtube.misc.litho.filter.fingerprints.* +import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction -import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction +import java.io.Closeable @DependsOn([IntegrationsPatch::class]) @Description("Hooks the method which parses the bytes into a ComponentContext to filter components.") @LithoFilterCompatibility @Version("0.0.1") class LithoFilterPatch : BytecodePatch( - listOf(ComponentContextParserFingerprint) -) { + listOf(ComponentContextParserFingerprint, LithoFilterFingerprint) +), Closeable { override fun execute(context: BytecodeContext): PatchResult { ComponentContextParserFingerprint.result?.also { arrayOf( - EmptyComponentBuilderFingerprint, ReadComponentIdentifierFingerprint, ProtobufBufferFingerprint + EmptyComponentBuilderFingerprint, + ReadComponentIdentifierFingerprint, + ProtobufBufferFingerprint ).forEach { fingerprint -> - if (!fingerprint.resolve(context, it.mutableMethod, it.mutableClass)) - return fingerprint.toErrorResult() + if (fingerprint.resolve(context, it.mutableMethod, it.mutableClass)) return@forEach + return fingerprint.toErrorResult() } }?.let { result -> val builderMethodIndex = EmptyComponentBuilderFingerprint.patternScanEndIndex @@ -45,16 +48,28 @@ class LithoFilterPatch : BytecodePatch( result.mutableMethod.apply { val insertHookIndex = result.scanResult.patternScanResult!!.endIndex - val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor - val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor - // Register is overwritten right after it is used in this patch, therefore free to clobber. - val free = getInstruction(insertHookIndex - 1).registerA - val free2 = getInstruction(insertHookIndex).registerA + + // region Get free registers that this patch uses + // Registers are overwritten right after they are used in this patch, therefore free to clobber. + + val freeRegistersInstruction = getInstruction(insertHookIndex - 2) + + // Later used to store the protobuf buffer object. + val free1 = getInstruction(insertHookIndex).registerA + // Later used to store the identifier of the component. + // This register currently holds a reference to the StringBuilder object + // that is required before clobbering. + val free2 = freeRegistersInstruction.registerC @Suppress("UnnecessaryVariable") - // The register, this patch clobbers, is previously used for the StringBuilder, - // later on a new StringBuilder is instantiated on it. - val stringBuilderRegister = free + val stringBuilderRegister = free2 + + // endregion + + // region Get references that this patch needs + + val builderMethodDescriptor = getInstruction(builderMethodIndex).descriptor + val emptyComponentFieldDescriptor = getInstruction(emptyComponentFieldIndex).descriptor val identifierRegister = getInstruction(ReadComponentIdentifierFingerprint.patternScanEndIndex).registerA @@ -69,57 +84,87 @@ class LithoFilterPatch : BytecodePatch( getInstruction(ProtobufBufferFingerprint.patternScanEndIndex - 1).descriptor val protobufBufferFieldDescriptor = "$protobufBufferRefTypeDescriptor->b:Ljava/nio/ByteBuffer;" + // endregion + + // region Patch the method + + // Insert the instructions that are responsible + // to return an EmptyComponent instead of the original component if the filter method returns false. addInstructionsWithLabels( - insertHookIndex, // right after setting the component.pathBuilder field. + insertHookIndex, """ # Get the protobuf buffer object. - move-object/from16 v$free2, p$protobufParameterNumber - iget-object v$free2, v$free2, $protobufBufferRefTypeRefFieldDescriptor - check-cast v$free2, $protobufBufferRefTypeDescriptor + move-object/from16 v$free1, p$protobufParameterNumber + iget-object v$free1, v$free1, $protobufBufferRefTypeRefFieldDescriptor + check-cast v$free1, $protobufBufferRefTypeDescriptor # Register "free" now holds the protobuf buffer object - iget-object v$free2, v$free2, $protobufBufferFieldDescriptor - + iget-object v$free1, v$free1, $protobufBufferFieldDescriptor + # Invoke the filter method. - invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free2 }, $FILTER_METHOD_DESCRIPTOR - move-result v$free + invoke-static { v$stringBuilderRegister, v$identifierRegister, v$free1 }, $FILTER_METHOD_DESCRIPTOR + move-result v$free1 - if-eqz v$free, :not_an_ad - - # If the filter method returned true, then return a replacement empty component. - - move-object/from16 v$free, p1 - invoke-static {v$free}, $builderMethodDescriptor - move-result-object v$free - iget-object v$free, v$free, $emptyComponentFieldDescriptor - return-object v$free + if-eqz v$free1, :unfiltered + + move-object/from16 v$free2, p1 + invoke-static {v$free2}, $builderMethodDescriptor + move-result-object v$free2 + iget-object v$free2, v$free2, $emptyComponentFieldDescriptor + return-object v$free2 """, - ExternalLabel("not_an_ad", getInstruction(insertHookIndex)) + // Used to jump over the instruction which block the component from being created. + ExternalLabel("unfiltered", getInstruction(insertHookIndex)) ) + // endregion } } ?: return ComponentContextParserFingerprint.toErrorResult() + LithoFilterFingerprint.result?.mutableMethod?.apply { + removeInstructions(2, 4) // Remove dummy filter. + + addFilter = { classDescriptor -> + addInstructions( + 2, + """ + new-instance v1, $classDescriptor + invoke-direct {v1}, $classDescriptor->()V + const/4 v2, ${filterCount++} + aput-object v1, v0, v2 + """ + ) + } + } ?: return LithoFilterFingerprint.toErrorResult() + return PatchResultSuccess() } - private companion object { + override fun close() = LithoFilterFingerprint.result!! + .mutableMethod.replaceInstruction(0, "const/4 v0, $filterCount") + + companion object { private val MethodFingerprint.patternScanResult get() = result!!.scanResult.patternScanResult!! - val MethodFingerprint.patternScanEndIndex + private val MethodFingerprint.patternScanEndIndex get() = patternScanResult.endIndex - val MethodFingerprint.patternScanStartIndex + private val MethodFingerprint.patternScanStartIndex get() = patternScanResult.startIndex - val Instruction.descriptor + private val Instruction.descriptor get() = (this as ReferenceInstruction).reference.toString() - const val FILTER_METHOD_DESCRIPTOR = + private const val FILTER_METHOD_DESCRIPTOR = "Lapp/revanced/integrations/patches/components/LithoFilterPatch;" + "->filter(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/nio/ByteBuffer;)Z" + + internal lateinit var addFilter: (String) -> Unit + private set + + private var filterCount = 0 } } \ No newline at end of file