Skip to content

Commit 308e1e6

Browse files
committed
feat(Twitter): Added 'Native reader mode' patch
1 parent 15b2760 commit 308e1e6

3 files changed

Lines changed: 137 additions & 3 deletions

File tree

src/main/kotlin/crimera/patches/twitter/misc/settings/SettingsPatch.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,18 @@ object SettingsPatch : BytecodePatch(
3434
) {
3535
private const val INTEGRATIONS_PACKAGE = "Lapp/revanced/integrations/twitter"
3636
const val UTILS_DESCRIPTOR = "$INTEGRATIONS_PACKAGE/Utils"
37-
private const val ACTIVITY_HOOK_CLASS = "Lapp/revanced/integrations/twitter/settings/ActivityHook;"
38-
private const val DEEPLINK_HOOK_CLASS = "Lapp/revanced/integrations/twitter/settings/DeepLink;"
37+
private const val ACTIVITY_SETTINGS_CLASS = "$INTEGRATIONS_PACKAGE/settings"
38+
private const val ACTIVITY_HOOK_CLASS = "$ACTIVITY_SETTINGS_CLASS/ActivityHook;"
39+
private const val DEEPLINK_HOOK_CLASS = "$ACTIVITY_SETTINGS_CLASS/DeepLink;"
3940
private const val ADD_PREF_DESCRIPTOR =
4041
"$UTILS_DESCRIPTOR;->addPref([Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
4142

4243
const val PREF_DESCRIPTOR = "$INTEGRATIONS_PACKAGE/Pref"
4344
const val PATCHES_DESCRIPTOR = "$INTEGRATIONS_PACKAGE/patches"
4445
const val CUSTOMISE_DESCRIPTOR = "$PATCHES_DESCRIPTOR/customise/Customise"
45-
const val SSTS_DESCRIPTOR = "invoke-static {}, $INTEGRATIONS_PACKAGE/settings/SettingsStatus;"
46+
const val READER_MODE_UTILS_DESCRIPTOR = "$ACTIVITY_SETTINGS_CLASS/fragments/readerMode/ReaderModeUtils"
47+
48+
const val SSTS_DESCRIPTOR = "invoke-static {}, $ACTIVITY_SETTINGS_CLASS/SettingsStatus;"
4649
const val FSTS_DESCRIPTOR = "invoke-static {}, $INTEGRATIONS_PACKAGE/patches/FeatureSwitchPatch;"
4750

4851
private const val START_ACTIVITY_DESCRIPTOR =
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package crimera.patches.twitter.misc.shareMenu.nativeReaderMode
2+
3+
import app.revanced.patcher.data.BytecodeContext
4+
import app.revanced.patcher.patch.BytecodePatch
5+
import app.revanced.patcher.patch.PatchException
6+
import app.revanced.patcher.patch.annotation.CompatiblePackage
7+
import app.revanced.patcher.patch.annotation.Patch
8+
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
9+
import com.android.tools.smali.dexlib2.Opcode
10+
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
11+
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
12+
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
13+
import crimera.patches.twitter.misc.settings.SettingsPatch
14+
import crimera.patches.twitter.misc.settings.fingerprints.SettingsStatusLoadFingerprint
15+
import crimera.patches.twitter.misc.shareMenu.fingerprints.ActionEnumsFingerprint
16+
import crimera.patches.twitter.misc.shareMenu.fingerprints.ShareMenuButtonFuncCallFingerprint
17+
import crimera.patches.twitter.misc.shareMenu.hooks.ShareMenuButtonAddHook
18+
import crimera.patches.twitter.misc.shareMenu.hooks.ShareMenuButtonInitHook
19+
import crimera.patches.twitter.misc.shareMenu.nativeDownloader.extractDescriptors
20+
21+
@Patch(
22+
name = "Native reader mode",
23+
description = "Requires X 11.0.0-release.0 or higher.",
24+
dependencies = [SettingsPatch::class, ResourceMappingPatch::class],
25+
compatiblePackages = [CompatiblePackage("com.twitter.android")],
26+
use = true,
27+
)
28+
@Suppress("unused")
29+
object NativeReaderModePatch : BytecodePatch(
30+
setOf(
31+
ShareMenuButtonFuncCallFingerprint,
32+
ShareMenuButtonInitHook,
33+
SettingsStatusLoadFingerprint,
34+
ShareMenuButtonAddHook,
35+
ActionEnumsFingerprint,
36+
),
37+
) {
38+
override fun execute(context: BytecodeContext) {
39+
val actionName = "ReaderMode"
40+
41+
// Add action
42+
val downloadActionReference = ActionEnumsFingerprint.addAction(actionName, ActionEnumsFingerprint.result!!)
43+
44+
// Register button
45+
ShareMenuButtonAddHook.registerButton(actionName, "enableNativeReaderMode")
46+
val viewDebugDialogReference =
47+
(
48+
ShareMenuButtonAddHook.result
49+
?.method
50+
?.implementation
51+
?.instructions
52+
?.first { it.opcode == Opcode.SGET_OBJECT } as Instruction21c
53+
).reference
54+
55+
// Set Button Text
56+
ShareMenuButtonInitHook.setButtonText(actionName, "piko_title_native_reader_mode")
57+
ShareMenuButtonInitHook.setButtonIcon(actionName, "ic_vector_book_stroke_on")
58+
59+
// TODO: handle possible nulls
60+
val buttonFunc = ShareMenuButtonFuncCallFingerprint.result
61+
val buttonFuncMethod =
62+
ShareMenuButtonFuncCallFingerprint.result
63+
?.method
64+
?.implementation
65+
?.instructions
66+
?.toList()
67+
68+
val deleteStatusLoc =
69+
buttonFunc
70+
?.scanResult
71+
?.stringsScanResult
72+
?.matches
73+
?.first { it.string == "Delete Status" }
74+
?.index
75+
?: throw PatchException("Delete status not found")
76+
77+
@Suppress("ktlint:standard:property-naming")
78+
val OkLoc =
79+
buttonFunc
80+
?.scanResult
81+
?.stringsScanResult
82+
?.matches
83+
?.first { it.string == "OK" }
84+
?.index
85+
?: throw PatchException("OK not found")
86+
87+
val conversationalRepliesLoc =
88+
buttonFunc.scanResult.stringsScanResult
89+
?.matches
90+
?.first {
91+
it.string ==
92+
"conversational_replies_android_pinned_replies_creation_enabled"
93+
}?.index
94+
?: throw PatchException("conversational_replies_android_pinned_replies_creation_enabled not found")
95+
96+
val timelineRef =
97+
(
98+
buttonFuncMethod
99+
?.filterIndexed { i, ins ->
100+
i > conversationalRepliesLoc && ins.opcode == Opcode.IGET_OBJECT
101+
}?.first() as Instruction22c?
102+
) ?: throw PatchException("Failed to find timelineRef")
103+
val timelineRefReg = (buttonFuncMethod?.get(deleteStatusLoc - 1) as Instruction35c).registerD
104+
105+
val activityRefReg = (buttonFuncMethod[OkLoc - 3] as Instruction35c).registerD
106+
107+
// Add Button function
108+
ShareMenuButtonFuncCallFingerprint.addButtonInstructions(
109+
downloadActionReference,
110+
"""
111+
check-cast v$timelineRefReg, ${timelineRef.reference.extractDescriptors()[0]}
112+
iget-object v1, v$timelineRefReg, ${timelineRef.reference}
113+
114+
invoke-static {v$activityRefReg, v1}, ${SettingsPatch.READER_MODE_UTILS_DESCRIPTOR};->launchReaderMode(Landroid/content/Context;Ljava/lang/Object;)V
115+
""".trimIndent(),
116+
viewDebugDialogReference,
117+
)
118+
119+
SettingsStatusLoadFingerprint.enableSettings("nativeReaderMode")
120+
}
121+
}

src/main/resources/twitter/settings/values/strings.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@
4444
<string name="piko_native_translator_google">Google translator</string>
4545
<string name="piko_native_translator_google_v2">Google translator V2</string>
4646

47+
<string name="piko_title_native_reader_mode">Reader mode</string>
48+
<string name="piko_native_reader_mode_grok_thread">Analyse this thread</string>
49+
<string name="piko_native_reader_mode_grok_author">More about the author</string>
50+
<string name="piko_native_reader_mode_source">Source</string>
51+
<string name="piko_native_reader_mode_published">Published</string>
52+
<string name="piko_native_reader_mode_total_post">Total Posts</string>
53+
<string name="piko_native_reader_mode_pref_text_only_mode">Text only mode</string>
54+
<string name="piko_native_reader_mode_pref_hide_quoted_posts">Hide quoted posts</string>
55+
<string name="piko_native_reader_mode_pref_no_grok">No Grok analyses</string>
56+
4757
<!-- Ads Settings -->
4858
<string name="piko_title_ads">Ads</string>
4959
<string name="piko_pref_hide_promoted_posts">Promoted posts</string>

0 commit comments

Comments
 (0)