From 2f04a06e3b782931870d973fd0937f8731062f12 Mon Sep 17 00:00:00 2001 From: Franck V Date: Thu, 29 Dec 2022 03:10:04 +0100 Subject: [PATCH] feat(twitter): `hide-views-stats` patch (#1371) Co-authored-by: oSumAtrIX --- .../annotations/HideViewsCompatibility.kt | 9 ++ .../InlineActionTypesFingerprint.kt | 15 +++ ...eetStatsContainerConstructorFingerprint.kt | 22 ++++ ...sContainerWrapperConstructorFingerprint.kt | 22 ++++ ...TweetStatsViewDelegateBinderFingerprint.kt | 26 ++++ .../hideviews/patch/HideViewsBytecodePatch.kt | 114 ++++++++++++++++++ .../hideviews/patch/HideViewsResourcePatch.kt | 34 ++++++ 7 files changed, 242 insertions(+) create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/annotations/HideViewsCompatibility.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/InlineActionTypesFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerConstructorFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerWrapperConstructorFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsViewDelegateBinderFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsBytecodePatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsResourcePatch.kt diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/annotations/HideViewsCompatibility.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/annotations/HideViewsCompatibility.kt new file mode 100644 index 000000000..44c21454f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/annotations/HideViewsCompatibility.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.twitter.layout.hideviews.annotations + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility([Package("com.twitter.android")]) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class HideViewsCompatibility \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/InlineActionTypesFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/InlineActionTypesFingerprint.kt new file mode 100644 index 000000000..eb4f86a25 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/InlineActionTypesFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.twitter.layout.hideviews.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags + +object InlineActionTypesFingerprint : MethodFingerprint( + returnType = "Ljava/util/List", + access = AccessFlags.PUBLIC or AccessFlags.STATIC, + strings = listOf( + "getCurrentMemoizing()", + "android_animated_reply_icon_enabled", + "reply_voting_android_position_before_favorite_enabled" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerConstructorFingerprint.kt new file mode 100644 index 000000000..dda7c74cc --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerConstructorFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.twitter.layout.hideviews.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object TweetStatsContainerConstructorFingerprint : MethodFingerprint( + access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + parameters = listOf("L"), + opcodes = listOf( + Opcode.INVOKE_DIRECT, + Opcode.IPUT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.CONST_4, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerWrapperConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerWrapperConstructorFingerprint.kt new file mode 100644 index 000000000..791e15162 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsContainerWrapperConstructorFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.twitter.layout.hideviews.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object TweetStatsContainerWrapperConstructorFingerprint : MethodFingerprint( + access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + parameters = listOf("L"), + opcodes = listOf( + Opcode.INVOKE_DIRECT, + Opcode.IPUT_OBJECT, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsViewDelegateBinderFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsViewDelegateBinderFingerprint.kt new file mode 100644 index 000000000..3d3f845b4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/fingerprints/TweetStatsViewDelegateBinderFingerprint.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.twitter.layout.hideviews.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object TweetStatsViewDelegateBinderFingerprint : MethodFingerprint( + access = AccessFlags.PUBLIC or AccessFlags.FINAL, + opcodes = listOf( + Opcode.NEW_INSTANCE, + Opcode.CONST_16, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IGET_OBJECT, + Opcode.NEW_INSTANCE, + Opcode.CONST_16, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_OBJECT + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsBytecodePatch.kt new file mode 100644 index 000000000..3752d0798 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsBytecodePatch.kt @@ -0,0 +1,114 @@ +package app.revanced.patches.twitter.layout.hideviews.patch + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.removeInstruction +import app.revanced.patcher.extensions.removeInstructions +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.twitter.layout.hideviews.fingerprints.InlineActionTypesFingerprint +import app.revanced.patches.twitter.layout.hideviews.fingerprints.TweetStatsContainerConstructorFingerprint +import app.revanced.patches.twitter.layout.hideviews.fingerprints.TweetStatsContainerWrapperConstructorFingerprint +import app.revanced.patches.twitter.layout.hideviews.fingerprints.TweetStatsViewDelegateBinderFingerprint +import org.jf.dexlib2.Opcode + +class HideViewsBytecodePatch : BytecodePatch( + listOf( + InlineActionTypesFingerprint, + TweetStatsContainerWrapperConstructorFingerprint, + TweetStatsContainerConstructorFingerprint, + TweetStatsViewDelegateBinderFingerprint + ) +) { + override fun execute(context: BytecodeContext): PatchResult { + removeViewsFromTimeline(context) + removeTweetStatViewInitializer(context) + removeTweetStatViewWrapperInitializer(context) + removeViewDelegateBinderSubscription() + return PatchResultSuccess() + } + + private fun removeViewsFromTimeline(context: BytecodeContext) { + val addViewsToActionBarMethodFingerprint = object : MethodFingerprint( + opcodes = listOf( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.IF_EQZ, + ) + ) {} + transformMethodAtPattern( + context, + InlineActionTypesFingerprint, + addViewsToActionBarMethodFingerprint + ) { patternScanResult, method -> + method.removeInstruction(patternScanResult.endIndex - 1) + } + } + + private fun removeTweetStatViewInitializer(context: BytecodeContext) { + val returnFingerprint = object : MethodFingerprint( + opcodes = listOf(Opcode.RETURN_VOID) + ) {} + transformMethodAtPattern( + context, + TweetStatsContainerConstructorFingerprint, + returnFingerprint + ) { patternScanResult, method -> + method.removeInstructions(patternScanResult.endIndex - 3, 2) + } + } + + private fun removeTweetStatViewWrapperInitializer(context: BytecodeContext) { + val wrapperReturnFingerprint = object : MethodFingerprint( + opcodes = listOf( + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.RETURN_VOID, + ) + ) {} + transformMethodAtPattern( + context, + TweetStatsContainerWrapperConstructorFingerprint, + wrapperReturnFingerprint + ) { patternScanResult, method -> + method.removeInstructions(patternScanResult.startIndex - 4, 3) + } + } + + private fun removeViewDelegateBinderSubscription() { + transformMethod(TweetStatsViewDelegateBinderFingerprint) { result, method -> + method.removeInstructions(result.scanResult.patternScanResult!!.startIndex - 4, 9) + } + } + + private fun transformMethodAtPattern( + context: BytecodeContext, methodFingerprint: MethodFingerprint, + patternFingerprint: MethodFingerprint, transformer: TransformerAtPattern + ) { + transformMethod(methodFingerprint) { result, method -> + val patternResult = patternFingerprint.also { + it.resolve(context, method, result.classDef) + }.result!! + transformer(patternResult.scanResult.patternScanResult!!, method) + } + } + + private fun transformMethod(methodFingerprint: MethodFingerprint, transformer: Transformer) { + val result = methodFingerprint.result!! + val method = result.mutableMethod + transformer(result, method) + } +} + +private typealias Transformer = (MethodFingerprintResult, MutableMethod) -> Unit + +private typealias TransformerAtPattern = (MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult, MutableMethod) -> Unit \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsResourcePatch.kt new file mode 100644 index 000000000..734326668 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/layout/hideviews/patch/HideViewsResourcePatch.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.twitter.layout.hideviews.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.* +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patches.twitter.layout.hideviews.annotations.HideViewsCompatibility +import org.w3c.dom.Element + +@Patch +@DependsOn([HideViewsBytecodePatch::class]) +@Name("hide-views-stats") +@Description("Hides the view stats under tweets.") +@HideViewsCompatibility +@Version("0.0.1") +class HideViewsResourcePatch : ResourcePatch { + override fun execute(context: ResourceContext): PatchResult { + arrayOf( + "res/layout/condensed_tweet_stats.xml", + "res/layout/focal_tweet_stats.xml" + ).forEach { file -> + context.xmlEditor[file].use { editor -> + val tags = editor.file.getElementsByTagName("com.twitter.ui.tweet.TweetStatView") + List(tags.length) { tags.item(it) as Element } + .filter { it.getAttribute("android:id").contains("views_stat") } + .forEach { it.parentNode.removeChild(it) } + } + } + return PatchResultSuccess() + } +}