diff --git a/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt b/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt index 9e29eb9ae9..4045e8ff5f 100644 --- a/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt @@ -3,9 +3,8 @@ package app.revanced.patches.music.ads.general import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction 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.util.smali.ExternalLabel +import app.revanced.patches.music.ads.general.MusicAdsPatch.hookLithoFullscreenAds import app.revanced.patches.music.ads.general.fingerprints.AccountMenuFooterFingerprint import app.revanced.patches.music.ads.general.fingerprints.FloatingLayoutFingerprint import app.revanced.patches.music.ads.general.fingerprints.GetPremiumTextViewFingerprint @@ -25,6 +24,7 @@ import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch.Interst import app.revanced.patches.music.utils.settings.CategoryType import app.revanced.patches.music.utils.settings.SettingsPatch import app.revanced.patches.shared.litho.LithoFilterPatch +import app.revanced.patches.youtube.ads.general.VideoAdsPatch.hookNonLithoFullscreenAds import app.revanced.util.getTargetIndex import app.revanced.util.getTargetIndexWithReference import app.revanced.util.getWalkerMethod @@ -58,11 +58,11 @@ object AdsPatch : BaseBytecodePatch( ShowDialogCommandFingerprint ) ) { - private const val FILTER_CLASS_DESCRIPTOR = + private const val ADS_FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/AdsFilter;" - private const val FULLSCREEN_ADS_CLASS_DESCRIPTOR = - "$ADS_PATH/FullscreenAdsPatch;" + private const val FULLSCREEN_ADS_FILTER_CLASS_DESCRIPTOR = + "${app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH}/FullscreenAdsFilter;" private const val PREMIUM_PROMOTION_POP_UP_CLASS_DESCRIPTOR = "$ADS_PATH/PremiumPromotionPatch;" @@ -71,72 +71,18 @@ object AdsPatch : BaseBytecodePatch( "$ADS_PATH/PremiumRenewalPatch;" override fun execute(context: BytecodeContext) { - LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) // region patch for hide fullscreen ads // non-litho view, used in some old clients - InterstitialsContainerFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val targetIndex = getWideLiteralInstructionIndex(InterstitialsContainer) + 2 - val targetRegister = getInstruction(targetIndex).registerA - - addInstruction( - targetIndex + 1, - "invoke-static {v$targetRegister}, $FULLSCREEN_ADS_CLASS_DESCRIPTOR->hideFullscreenAds(Landroid/view/View;)V" - ) - } - } + InterstitialsContainerFingerprint + .resultOrThrow() + .hookNonLithoFullscreenAds(InterstitialsContainer) // litho view, used in 'ShowDialogCommandOuterClass' in innertube - ShowDialogCommandFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - // In this method, custom dialog is created and shown. - // There were no issues despite adding “return-void” to the first index. - addInstructionsWithLabels( - 0, - """ - invoke-static/range {p2 .. p2}, $FULLSCREEN_ADS_CLASS_DESCRIPTOR->hideFullscreenAds(Ljava/lang/Object;)Z - move-result v0 - if-eqz v0, :show - return-void - """, ExternalLabel("show", getInstruction(0)) - ) - - // If an issue occurs due to patching due to server-side changes in the future, - // Find the instruction whose name is "show" in [MethodReference] and click the 'AlertDialog.BUTTON_POSITIVE' button. - // - // In this case, an instruction for 'getButton' must be added to smali, not in integrations - // (This custom dialog cannot be cast to [AlertDialog] or [Dialog]) - // - // See the comments below. - - // val dialogIndex = getTargetIndexWithMethodReferenceName("show") - // val dialogReference = getInstruction(dialogIndex).reference - // val dialogDefiningClass = (dialogReference as MethodReference).definingClass - // val getButtonMethod = context.findClass(dialogDefiningClass)!! - // .mutableClass.methods.first { method -> - // method.parameters == listOf("I") - // && method.returnType == "Landroid/widget/Button;" - // } - // val getButtonCall = dialogDefiningClass + "->" + getButtonMethod.name + "(I)Landroid/widget/Button;" - // val dialogRegister = getInstruction(dialogIndex).registerC - // val freeIndex = getTargetIndex(dialogIndex, Opcode.IF_EQZ) - // val freeRegister = getInstruction(freeIndex).registerA - - // addInstructions( - // dialogIndex + 1, """ - // # Get the 'AlertDialog.BUTTON_POSITIVE' from custom dialog - // # Since this custom dialog cannot be cast to AlertDialog or Dialog, - // # It should come from smali, not integrations. - // const/4 v$freeRegister, -0x1 - // invoke-virtual {v$dialogRegister, $freeRegister}, $getButtonCall - // move-result-object $freeRegister - // invoke-static {$freeRegister}, $FULLSCREEN_ADS_CLASS_DESCRIPTOR->confirmDialog(Landroid/widget/Button;)V - // """ - // ) - } - } + ShowDialogCommandFingerprint + .resultOrThrow() + .hookLithoFullscreenAds(context) // endregion @@ -227,11 +173,20 @@ object AdsPatch : BaseBytecodePatch( // endregion + LithoFilterPatch.addFilter(ADS_FILTER_CLASS_DESCRIPTOR) + LithoFilterPatch.addFilter(FULLSCREEN_ADS_FILTER_CLASS_DESCRIPTOR) + SettingsPatch.addSwitchPreference( CategoryType.ADS, "revanced_hide_fullscreen_ads", "true" ) + SettingsPatch.addSwitchPreference( + CategoryType.ADS, + "revanced_hide_fullscreen_ads_type", + "true", + "revanced_hide_fullscreen_ads" + ) SettingsPatch.addSwitchPreference( CategoryType.ADS, "revanced_hide_general_ads", diff --git a/src/main/kotlin/app/revanced/patches/music/ads/general/fingerprints/ShowDialogCommandFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/ads/general/fingerprints/ShowDialogCommandFingerprint.kt index ddc87e0b5f..fed10e5e95 100644 --- a/src/main/kotlin/app/revanced/patches/music/ads/general/fingerprints/ShowDialogCommandFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/ads/general/fingerprints/ShowDialogCommandFingerprint.kt @@ -2,9 +2,16 @@ package app.revanced.patches.music.ads.general.fingerprints import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch.SlidingDialogAnimation import app.revanced.util.fingerprint.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.Opcode internal object ShowDialogCommandFingerprint : LiteralValueFingerprint( returnType = "V", parameters = listOf("[B", "L"), + opcodes = listOf( + Opcode.IF_EQ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET, // get dialog code + ), literalSupplier = { SlidingDialogAnimation } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/shared/ads/BaseAdsPatch.kt b/src/main/kotlin/app/revanced/patches/shared/ads/BaseAdsPatch.kt index a6ac1724e3..4b4e3afa20 100644 --- a/src/main/kotlin/app/revanced/patches/shared/ads/BaseAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/ads/BaseAdsPatch.kt @@ -1,18 +1,28 @@ package app.revanced.patches.shared.ads import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction 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.fingerprint.MethodFingerprintResult import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.ads.fingerprints.MusicAdsFingerprint import app.revanced.patches.shared.ads.fingerprints.VideoAdsFingerprint +import app.revanced.patches.shared.integrations.Constants.PATCHES_PATH +import app.revanced.util.getTargetIndex +import app.revanced.util.getTargetIndexWithMethodReferenceName import app.revanced.util.getWalkerMethod +import app.revanced.util.getWideLiteralInstructionIndex import app.revanced.util.indexOfFirstInstruction import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference abstract class BaseAdsPatch( @@ -59,4 +69,77 @@ abstract class BaseAdsPatch( } } } + + internal fun MethodFingerprintResult.hookNonLithoFullscreenAds(literal: Long) { + mutableMethod.apply { + val targetIndex = getWideLiteralInstructionIndex(literal) + 2 + val targetRegister = getInstruction(targetIndex).registerA + + addInstruction( + targetIndex + 1, + "invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideFullscreenAds(Landroid/view/View;)V" + ) + } + } + + internal fun MethodFingerprintResult.hookLithoFullscreenAds(context: BytecodeContext) { + mutableMethod.apply { + val dialogCodeIndex = scanResult.patternScanResult!!.endIndex + val dialogCodeField = getInstruction(dialogCodeIndex).reference as FieldReference + if (dialogCodeField.type != "I") + throw PatchException("Invalid dialogCodeField: $dialogCodeField") + + // Disable fullscreen ads + addInstructionsWithLabels( + 0, + """ + move-object v0, p2 + + # In the latest version of YouTube and YouTube Music, it is used after being cast + + check-cast v0, ${dialogCodeField.definingClass} + iget v0, v0, $dialogCodeField + invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->disableFullscreenAds(I)Z + move-result v0 + if-eqz v0, :show + return-void + """, ExternalLabel("show", getInstruction(0)) + ) + + // Close fullscreen ads + + // Find the instruction whose name is "show" in [MethodReference] and click the 'AlertDialog.BUTTON_POSITIVE' button. + // In this case, an instruction for 'getButton' must be added to smali, not in integrations + // (This custom dialog cannot be cast to [AlertDialog] or [Dialog]) + val dialogIndex = getTargetIndexWithMethodReferenceName("show") + val dialogReference = getInstruction(dialogIndex).reference + val dialogDefiningClass = (dialogReference as MethodReference).definingClass + val getButtonMethod = context.findClass(dialogDefiningClass)!! + .mutableClass.methods.first { method -> + method.parameters == listOf("I") + && method.returnType == "Landroid/widget/Button;" + } + val getButtonCall = dialogDefiningClass + "->" + getButtonMethod.name + "(I)Landroid/widget/Button;" + val dialogRegister = getInstruction(dialogIndex).registerC + val freeIndex = getTargetIndex(dialogIndex, Opcode.IF_EQZ) + val freeRegister = getInstruction(freeIndex).registerA + + addInstructions( + dialogIndex + 1, """ + # Get the 'AlertDialog.BUTTON_POSITIVE' from custom dialog + # Since this custom dialog cannot be cast to AlertDialog or Dialog, + # It should come from smali, not integrations. + const/4 v$freeRegister, -0x1 + invoke-virtual {v$dialogRegister, v$freeRegister}, $getButtonCall + move-result-object v$freeRegister + invoke-static {v$freeRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setCloseButton(Landroid/widget/Button;)V + """ + ) + } + } + + private companion object { + const val INTEGRATIONS_CLASS_DESCRIPTOR = + "$PATCHES_PATH/FullscreenAdsPatch;" + } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/shared/gms/BaseGmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/shared/gms/BaseGmsCoreSupportPatch.kt index 7942eab71a..6bf06a0282 100644 --- a/src/main/kotlin/app/revanced/patches/shared/gms/BaseGmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/gms/BaseGmsCoreSupportPatch.kt @@ -18,7 +18,7 @@ import app.revanced.patches.shared.gms.fingerprints.GmsCoreSupportFingerprint.GE import app.revanced.patches.shared.gms.fingerprints.GooglePlayUtilityFingerprint import app.revanced.patches.shared.gms.fingerprints.PrimeMethodFingerprint import app.revanced.patches.shared.gms.fingerprints.ServiceCheckFingerprint -import app.revanced.patches.shared.integrations.Constants.INTEGRATIONS_PATH +import app.revanced.patches.shared.integrations.Constants.PATCHES_PATH import app.revanced.patches.shared.packagename.PackageNamePatch import app.revanced.util.getReference import app.revanced.util.resultOrThrow @@ -106,7 +106,7 @@ abstract class BaseGmsCoreSupportPatch( // Verify GmsCore is installed and whitelisted for power optimizations and background usage. mainActivityOnCreateFingerprint.resultOrThrow().mutableMethod.addInstructions( 1, // Hack to not disturb other patches (such as the YTMusic integrations patch). - "invoke-static/range { p0 .. p0 }, $INTEGRATIONS_PATH/patches/GmsCoreSupport;->" + + "invoke-static/range { p0 .. p0 }, $PATCHES_PATH/GmsCoreSupport;->" + "checkGmsCore(Landroid/app/Activity;)V", ) diff --git a/src/main/kotlin/app/revanced/patches/shared/integrations/Constants.kt b/src/main/kotlin/app/revanced/patches/shared/integrations/Constants.kt index 602f141aa3..59d7f47aeb 100644 --- a/src/main/kotlin/app/revanced/patches/shared/integrations/Constants.kt +++ b/src/main/kotlin/app/revanced/patches/shared/integrations/Constants.kt @@ -1,9 +1,10 @@ package app.revanced.patches.shared.integrations -@Suppress("MemberVisibilityCanBePrivate", "SpellCheckingInspection") +@Suppress("MemberVisibilityCanBePrivate") object Constants { const val INTEGRATIONS_PATH = "Lapp/revanced/integrations/shared" - const val COMPONENTS_PATH = "$INTEGRATIONS_PATH/patches/components" + const val PATCHES_PATH = "$INTEGRATIONS_PATH/patches" + const val COMPONENTS_PATH = "$PATCHES_PATH/components" const val INTEGRATIONS_SETTING_CLASS_DESCRIPTOR = "$INTEGRATIONS_PATH/settings/Setting;" const val INTEGRATIONS_UTILS_CLASS_DESCRIPTOR = "$INTEGRATIONS_PATH/utils/Utils;" diff --git a/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsBytecodePatch.kt index 51a2632d1d..b4305dc143 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsBytecodePatch.kt @@ -1,12 +1,13 @@ package app.revanced.patches.youtube.ads.general import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.music.ads.general.MusicAdsPatch.hookLithoFullscreenAds +import app.revanced.patches.youtube.ads.general.VideoAdsPatch.hookNonLithoFullscreenAds import app.revanced.patches.youtube.ads.general.fingerprints.CompactYpcOfferModuleViewFingerprint import app.revanced.patches.youtube.ads.general.fingerprints.InterstitialsContainerFingerprint import app.revanced.patches.youtube.ads.general.fingerprints.ShowDialogCommandFingerprint @@ -15,11 +16,9 @@ import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.AdAttribution import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.InterstitialsContainer import app.revanced.util.findMutableMethodOf -import app.revanced.util.getWideLiteralInstructionIndex import app.revanced.util.injectHideViewCall import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c @@ -37,34 +36,14 @@ object AdsBytecodePatch : BytecodePatch( // region patch for hide fullscreen ads // non-litho view, used in some old clients. - InterstitialsContainerFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val targetIndex = getWideLiteralInstructionIndex(InterstitialsContainer) + 2 - val targetRegister = getInstruction(targetIndex).registerA - - addInstruction( - targetIndex + 1, - "invoke-static {v$targetRegister}, $ADS_CLASS_DESCRIPTOR->hideFullscreenAds(Landroid/view/View;)V" - ) - } - } + InterstitialsContainerFingerprint + .resultOrThrow() + .hookNonLithoFullscreenAds(InterstitialsContainer) // litho view, used in 'ShowDialogCommandOuterClass' in innertube - ShowDialogCommandFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - // In this method, custom dialog is created and shown. - // There were no issues despite adding “return-void” to the first index. - addInstructionsWithLabels( - 0, - """ - invoke-static/range {p2 .. p2}, $ADS_CLASS_DESCRIPTOR->hideFullscreenAds(Ljava/lang/Object;)Z - move-result v0 - if-eqz v0, :show - return-void - """, ExternalLabel("show", getInstruction(0)) - ) - } - } + ShowDialogCommandFingerprint + .resultOrThrow() + .hookLithoFullscreenAds(context) // endregion diff --git a/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsPatch.kt index 059c81c5f3..3e7c435ee2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ads/general/AdsPatch.kt @@ -47,11 +47,15 @@ object AdsPatch : BaseResourcePatch( "Top" ) - private const val FILTER_CLASS_DESCRIPTOR = + private const val ADS_FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/AdsFilter;" + private const val FULLSCREEN_ADS_FILTER_CLASS_DESCRIPTOR = + "${app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH}/FullscreenAdsFilter;" + override fun execute(context: ResourceContext) { - LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR) + LithoFilterPatch.addFilter(ADS_FILTER_CLASS_DESCRIPTOR) + LithoFilterPatch.addFilter(FULLSCREEN_ADS_FILTER_CLASS_DESCRIPTOR) context.forEach { diff --git a/src/main/kotlin/app/revanced/patches/youtube/ads/general/fingerprints/ShowDialogCommandFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/ads/general/fingerprints/ShowDialogCommandFingerprint.kt index 2e07fd100a..1edafb0b12 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ads/general/fingerprints/ShowDialogCommandFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ads/general/fingerprints/ShowDialogCommandFingerprint.kt @@ -1,9 +1,28 @@ package app.revanced.patches.youtube.ads.general.fingerprints +import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.SlidingDialogAnimation -import app.revanced.util.fingerprint.LiteralValueFingerprint +import app.revanced.util.containsWideLiteralInstructionIndex +import com.android.tools.smali.dexlib2.Opcode -internal object ShowDialogCommandFingerprint : LiteralValueFingerprint( +internal object ShowDialogCommandFingerprint : MethodFingerprint( returnType = "V", - literalSupplier = { SlidingDialogAnimation } + opcodes = listOf( + Opcode.IF_EQ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET, // get dialog code + ), + // 18.43 and earlier has a different first parameter. + // Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures. + customFingerprint = custom@{ methodDef, _ -> + if (!methodDef.containsWideLiteralInstructionIndex(SlidingDialogAnimation)) { + return@custom false + } + // 18.43 and earlier parameters are: "L", "L" + // 18.44+ parameters are "[B", "L" + val parameterTypes = methodDef.parameterTypes + + parameterTypes.size == 2 && parameterTypes[1].startsWith("L") + }, ) \ No newline at end of file diff --git a/src/main/resources/music/settings/host/values/strings.xml b/src/main/resources/music/settings/host/values/strings.xml index 5b7003ee8e..68f05c48fe 100644 --- a/src/main/resources/music/settings/host/values/strings.xml +++ b/src/main/resources/music/settings/host/values/strings.xml @@ -58,6 +58,11 @@ Please download %2$s from the website." Hide fullscreen ads Hides fullscreen ads. + Close fullscreen ads + "If it is enabled, fullscreen ads are closed through the Close button. +If it is disabled, fullscreen ads are blocked. (there may be side effects)" + Fullscreen ads have been blocked. (DialogType: %s) + Fullscreen ads have been closed. Hide general ads Hides general ads. Hide media ads diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index d8d12f66f8..a56b07f15b 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -21,6 +21,13 @@ Hide fullscreen ads Fullscreen ads are hidden. Fullscreen ads are shown. + Close fullscreen ads + Fullscreen ads are closed through the Close button. + "Fullscreen ads are blocked. + +Limitation: Community post image on the fullscreen may be blocked." + Fullscreen ads have been blocked. (DialogType: %s) + Fullscreen ads have been closed. Hide general ads General ads are hidden. General ads are shown.