Skip to content

Commit

Permalink
feat(reddit): add hide-promoted patch (ReVanced#2350)
Browse files Browse the repository at this point in the history
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
  • Loading branch information
Blatzar and oSumAtrIX committed Jun 11, 2023
1 parent c522301 commit c3fd36c
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 52 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@ 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.Patch
import app.revanced.patches.reddit.ad.banner.annotations.HideBannerCompatibility

@Patch
@Name("hide-subreddit-banner")
@Description("Hides banner ads from comments on subreddits.")
@HideBannerCompatibility
@Version("0.0.1")
class HideBannerPatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {

context.xmlEditor[RESOURCE_FILE_PATH].use {
it.file.getElementsByTagName("merge").item(0).childNodes.apply {
val attributes = arrayOf("height", "width")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.revanced.patches.reddit.ad.comments.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object HideCommentAdsFingerprint : MethodFingerprint(
strings = listOf(
"link",
// CommentPageRepository is not returning a link object
"is not returning a link object"
),
customFingerprint = { _, classDef ->
classDef.sourceFile == "PostDetailPresenter.kt"
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package app.revanced.patches.reddit.ad.comments.patch

import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.RequiresIntegrations
import app.revanced.patches.reddit.ad.comments.fingerprints.HideCommentAdsFingerprint

@Name("hide-comment-ads")
@Description("Removes all comment ads.")
@RequiresIntegrations
@Version("0.0.1")
class HideCommentAdsPatch : BytecodePatch(
listOf(HideCommentAdsFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = HideCommentAdsFingerprint.result!!.mutableMethod
// Returns a blank object instead of the comment ad.
method.addInstructions(
0,
"""
new-instance v0, Ljava/lang/Object;
invoke-direct {v0}, Ljava/lang/Object;-><init>()V
return-object v0
"""
)
return PatchResultSuccess()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ package app.revanced.patches.reddit.ad.general.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package

@Compatibility(
[Package(
"com.reddit.frontpage"
)]
)
@Compatibility([Package("com.reddit.frontpage")])
@Target(AnnotationTarget.CLASS)
internal annotation class HideAdsCompatibility
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.ad.general.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object AdPostFingerprint : MethodFingerprint(
"V",
// "children" are present throughout multiple versions
strings = listOf("children"),
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("Listing;") },
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package app.revanced.patches.reddit.ad.general.fingerprints

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode

object NewAdPostFingerprint : MethodFingerprint(
opcodes = listOf(Opcode.INVOKE_VIRTUAL),
strings = listOf("chain", "feedElement"),
customFingerprint = { _, classDef -> classDef.sourceFile == "AdElementConverter.kt" },
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,89 @@ import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
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.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
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.annotations.RequiresIntegrations
import app.revanced.patches.reddit.ad.banner.patch.HideBannerPatch
import app.revanced.patches.reddit.ad.comments.patch.HideCommentAdsPatch
import app.revanced.patches.reddit.ad.general.annotations.HideAdsCompatibility
import app.revanced.patches.reddit.ad.general.fingerprints.AdPostFingerprint
import app.revanced.patches.reddit.ad.general.fingerprints.NewAdPostFingerprint
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
import org.jf.dexlib2.iface.reference.FieldReference
import org.jf.dexlib2.iface.reference.MethodReference

@Patch
@Name("hide-ads")
@Description("Removes general ads from the Reddit frontpage and subreddits.")
@Description("Removes ads from the Reddit.")
@DependsOn([HideBannerPatch::class, HideCommentAdsPatch::class])
@HideAdsCompatibility
@Version("0.0.1")
class HideAdsPatch : BytecodePatch() {
@RequiresIntegrations
@Version("0.0.2")
class HideAdsPatch : BytecodePatch(
listOf(AdPostFingerprint, NewAdPostFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
classDef.methods.forEach methodLoop@{ method ->
val implementation = method.implementation ?: return@methodLoop

implementation.instructions.forEachIndexed { i, instruction ->
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed

val proxiedClass = context.proxy(classDef).mutableClass

val proxiedImplementation = proxiedClass.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
}.implementation!!

var newString = "AdPost1"
if (proxiedImplementation.instructions[i - 1].opcode == Opcode.CONST_STRING) {
newString = "SubredditPost"
}
proxiedImplementation.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, (proxiedImplementation.instructions[i] as BuilderInstruction21c).registerA, ImmutableStringReference(newString)
)
)
}
// region Filter promoted ads (does not work in popular or latest feed)

AdPostFingerprint.result?.mutableMethod?.apply {
val setPostsListChildren = implementation!!.instructions.first { instruction ->
if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false

val reference = (instruction as ReferenceInstruction).reference as FieldReference
reference.name == "children"
}

val castedInstruction = setPostsListChildren as Instruction22c
val itemsRegister = castedInstruction.registerA
val listInstanceRegister = castedInstruction.registerB

// postsList.children = filterChildren(postListItems)
removeInstruction(setPostsListChildren.location.index)
addInstructions(
setPostsListChildren.location.index,
"""
invoke-static {v$itemsRegister}, $FILTER_METHOD_DESCRIPTOR
move-result-object v0
iput-object v0, v$listInstanceRegister, ${castedInstruction.reference}
"""
)
}

// endregion

// region Remove ads from popular and latest feed

NewAdPostFingerprint.result?.let { result ->
// The new feeds work by inserting posts into lists.
// AdElementConverter is conveniently responsible for inserting all feed ads.
// By removing the appending instruction no ad posts gets appended to the feed.
val index = result.method.implementation!!.instructions.indexOfFirst {
if (it.opcode != Opcode.INVOKE_VIRTUAL) return@indexOfFirst false

val reference = (it as ReferenceInstruction).reference as MethodReference

reference.name == "add" && reference.definingClass == "Lava/util/ArrayList;"
}

result.mutableMethod.removeInstruction(index)
}

// endregion

return PatchResultSuccess()
}

private companion object {
private const val FILTER_METHOD_DESCRIPTOR =
"Lapp/revanced/reddit/patches/FilterPromotedLinksPatch;" +
"->filterChildren(Ljava/lang/Iterable;)Ljava/util/List;"
}
}

0 comments on commit c3fd36c

Please sign in to comment.