feat(reddit): add hide-promoted patch (#2350)

Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
LagradOst 2023-06-11 23:19:41 +00:00 committed by oSumAtrIX
parent c522301d29
commit c3fd36cfba
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
8 changed files with 135 additions and 50 deletions

View file

@ -1,12 +0,0 @@
package app.revanced.patches.reddit.ad.banner.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.reddit.frontpage"
)]
)
@Target(AnnotationTarget.CLASS)
internal annotation class HideBannerCompatibility

View file

@ -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")

View file

@ -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"
},
)

View file

@ -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()
}
}

View file

@ -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

View file

@ -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;") },
)

View file

@ -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" },
)

View file

@ -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
// region Filter promoted ads (does not work in popular or latest feed)
implementation.instructions.forEachIndexed { i, instruction ->
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed
AdPostFingerprint.result?.mutableMethod?.apply {
val setPostsListChildren = implementation!!.instructions.first { instruction ->
if (instruction.opcode != Opcode.IPUT_OBJECT) return@first false
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"
val reference = (instruction as ReferenceInstruction).reference as FieldReference
reference.name == "children"
}
proxiedImplementation.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, (proxiedImplementation.instructions[i] as BuilderInstruction21c).registerA, ImmutableStringReference(newString)
)
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;"
}
}