diff --git a/api/revanced-patches.api b/api/revanced-patches.api index d5a47720a..74523ce8c 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -1490,6 +1490,12 @@ public final class app/revanced/patches/twitter/misc/hook/patch/recommendation/H public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch; } +public final class app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt new file mode 100644 index 000000000..06597dd81 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt @@ -0,0 +1,93 @@ +package app.revanced.patches.twitter.misc.links + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patches.twitter.misc.links.fingerprints.LinkBuilderFingerprint +import app.revanced.patches.twitter.misc.links.fingerprints.LinkResourceGetterFingerprint +import app.revanced.patches.twitter.misc.links.fingerprints.LinkSharingDomainFingerprint +import app.revanced.util.exception +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +@Patch( + name = "Change link sharing domain", + description = "Replaces the domain name of Twitter links when sharing them.", + compatiblePackages = [CompatiblePackage("com.twitter.android")], +) +@Suppress("unused") +object ChangeLinkSharingDomainPatch : BytecodePatch( + setOf( + LinkBuilderFingerprint, + LinkResourceGetterFingerprint, + LinkSharingDomainFingerprint, + ), +) { + private var domainName by stringPatchOption( + key = "domainName", + default = "fxtwitter.com", + title = "Domain name", + description = "The domain name to use when sharing links.", + required = true, + ) + + // This method is used to build the link that is shared when the "Share via..." button is pressed. + private const val FORMAT_METHOD_RESOURCE_REFERENCE = + "Lapp/revanced/integrations/twitter/patches/links/ChangeLinkSharingDomainPatch;->" + + "formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;" + + // This method is used to build the link that is shared when the "Copy link" button is pressed. + private const val FORMAT_METHOD_REFERENCE = + "Lapp/revanced/integrations/twitter/patches/links/ChangeLinkSharingDomainPatch;->" + + "formatLink(JLjava/lang/String;)Ljava/lang/String;" + + override fun execute(context: BytecodeContext) { + LinkSharingDomainFingerprint.result?.let { + val replacementIndex = it.scanResult.stringsScanResult!!.matches.first().index + val domainRegister = it.mutableMethod.getInstruction(replacementIndex).registerA + it.mutableMethod.replaceInstruction( + replacementIndex, + "const-string v$domainRegister, \"https://$domainName\"", + ) + } ?: throw LinkSharingDomainFingerprint.exception + + // Replace the domain name when copying a link with "Copy link" button. + LinkBuilderFingerprint.result?.let { + it.mutableMethod.apply { + addInstructions( + 0, + """ + invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE + move-result-object p0 + return-object p0 + """, + ) + } + } ?: throw LinkBuilderFingerprint.exception + + // Used in the Share via... dialog. + LinkResourceGetterFingerprint.result?.mutableMethod?.apply { + val constWithParameterName = indexOfFirstInstructionOrThrow { + getReference()?.string?.contains("id.toString()") == true + } + + // Format the link with the new domain name register (2 instructions above the const-string). + val formatLinkCallIndex = constWithParameterName - 2 + val formatLinkCall = getInstruction(formatLinkCallIndex) + + // Replace the original method call with the new method call. + replaceInstruction( + formatLinkCallIndex, + "invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE", + ) + } ?: throw LinkResourceGetterFingerprint.exception + } +} diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkBuilderFingerprint.kt new file mode 100644 index 000000000..0df584cf2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkBuilderFingerprint.kt @@ -0,0 +1,8 @@ +package app.revanced.patches.twitter.misc.links.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +// Returns a shareable link string based on a tweet ID and a username. +internal object LinkBuilderFingerprint : MethodFingerprint( + strings = listOf("/%1\$s/status/%2\$d"), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt new file mode 100644 index 000000000..a84181f62 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkResourceGetterFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.twitter.misc.links.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +// Gets Resource string for share link view available by pressing "Share via" button. +internal object LinkResourceGetterFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Landroid/content/res/Resources;"), + strings = listOf("res.getString(R.string.t…lUsername, id.toString())"), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkSharingDomainFingerprint.kt b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkSharingDomainFingerprint.kt new file mode 100644 index 000000000..e4b1a7f30 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/twitter/misc/links/fingerprints/LinkSharingDomainFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.twitter.misc.links.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object LinkSharingDomainFingerprint : MethodFingerprint( + strings = listOf("https://fxtwitter.com"), +) \ No newline at end of file