mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-23 10:41:03 +00:00
Compare commits
6 Commits
v4.17.0-de
...
v4.17.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
651e34b997 | ||
|
|
fc7644d3b7 | ||
|
|
061ebcb7c6 | ||
|
|
38e7884d17 | ||
|
|
64680c718b | ||
|
|
007702825b |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
|||||||
|
# [4.17.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.9...v4.17.0-dev.10) (2024-10-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Facebook:** Add `Hide sponsored stories` patch ([#3627](https://github.com/ReVanced/revanced-patches/issues/3627)) ([214c72b](https://github.com/ReVanced/revanced-patches/commit/214c72baeb7f87f21cd2ca34301ab11fa0ff1a4f))
|
||||||
|
|
||||||
|
# [4.17.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.8...v4.17.0-dev.9) (2024-10-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Sync for Reddit:** Add `Fix video downloads` patch ([#3739](https://github.com/ReVanced/revanced-patches/issues/3739)) ([a47ee38](https://github.com/ReVanced/revanced-patches/commit/a47ee38b1cdd974a959008006ecaf58917addc60))
|
||||||
|
|
||||||
|
# [4.17.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.7...v4.17.0-dev.8) (2024-10-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Twitter:** Add `Change link sharing domain` patch ([#3753](https://github.com/ReVanced/revanced-patches/issues/3753)) ([9269a07](https://github.com/ReVanced/revanced-patches/commit/9269a076b674ecdcf478bca842238f6e30869f44))
|
||||||
|
|
||||||
# [4.17.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.6...v4.17.0-dev.7) (2024-10-17)
|
# [4.17.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.17.0-dev.6...v4.17.0-dev.7) (2024-10-17)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -263,6 +263,12 @@ public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatch : ap
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatch;
|
||||||
|
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/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
public final class app/revanced/patches/facebook/ads/story/HideStoryAdsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
|
public static final field INSTANCE Lapp/revanced/patches/facebook/ads/story/HideStoryAdsPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
@@ -840,6 +846,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/u
|
|||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/video/FixVideoDownloadsPatch;
|
||||||
|
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/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
|
public final class app/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
|
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch;
|
||||||
}
|
}
|
||||||
@@ -1490,6 +1502,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 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 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 static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 4.17.0-dev.7
|
version = 4.17.0-dev.10
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package app.revanced.patches.facebook.ads.mainfeed
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.BaseModelMapperFingerprint
|
||||||
|
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetSponsoredDataModelTemplateFingerprint
|
||||||
|
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetStoryVisibilityFingerprint
|
||||||
|
import app.revanced.util.exception
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Hide 'Sponsored Stories'",
|
||||||
|
compatiblePackages = [CompatiblePackage("com.facebook.katana")],
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object HideSponsoredStoriesPatch : BytecodePatch(
|
||||||
|
setOf(GetStoryVisibilityFingerprint, GetSponsoredDataModelTemplateFingerprint, BaseModelMapperFingerprint),
|
||||||
|
) {
|
||||||
|
private const val GRAPHQL_STORY_TYPE = "Lcom/facebook/graphql/model/GraphQLStory;"
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
GetStoryVisibilityFingerprint.result?.apply {
|
||||||
|
val sponsoredDataModelTemplateMethod = GetSponsoredDataModelTemplateFingerprint.resultOrThrow().method
|
||||||
|
val baseModelMapperMethod = BaseModelMapperFingerprint.resultOrThrow().method
|
||||||
|
val baseModelWithTreeType = baseModelMapperMethod.returnType
|
||||||
|
|
||||||
|
// The "SponsoredDataModelTemplate" methods has the ids in its body to extract sponsored data
|
||||||
|
// from GraphQL models, but targets the wrong derived type of "BaseModelWithTree". Since those ids
|
||||||
|
// could change in future version, we need to extract them and call the base implementation directly.
|
||||||
|
val getSponsoredDataHelperMethod = ImmutableMethod(
|
||||||
|
classDef.type,
|
||||||
|
"getSponsoredData",
|
||||||
|
listOf(ImmutableMethodParameter(GRAPHQL_STORY_TYPE, null, null)),
|
||||||
|
baseModelWithTreeType,
|
||||||
|
AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(4),
|
||||||
|
).toMutable().apply {
|
||||||
|
// Extract the ids of the original method. These ids seem to correspond to model types for
|
||||||
|
// GraphQL data structure. They are then fed to a method of BaseModelWithTree that populate
|
||||||
|
// and cast the requested GraphQL subtype. The Ids are found in the two first "CONST" instructions.
|
||||||
|
val constInstructions = sponsoredDataModelTemplateMethod.implementation!!.instructions
|
||||||
|
.asSequence()
|
||||||
|
.filterIsInstance<Instruction31i>()
|
||||||
|
.take(2)
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val storyTypeId = constInstructions[0].narrowLiteral
|
||||||
|
val sponsoredDataTypeId = constInstructions[1].narrowLiteral
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
"""
|
||||||
|
const-class v2, $baseModelWithTreeType
|
||||||
|
const v1, $storyTypeId
|
||||||
|
const v0, $sponsoredDataTypeId
|
||||||
|
invoke-virtual {p0, v2, v1, v0}, $baseModelMapperMethod
|
||||||
|
move-result-object v0
|
||||||
|
check-cast v0, $baseModelWithTreeType
|
||||||
|
return-object v0
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutableClass.methods.add(getSponsoredDataHelperMethod)
|
||||||
|
|
||||||
|
// Check if the parameter type is GraphQLStory and if sponsoredDataModelGetter returns a non-null value.
|
||||||
|
// If so, hide the story by setting the visibility to StoryVisibility.GONE.
|
||||||
|
mutableMethod.addInstructionsWithLabels(
|
||||||
|
scanResult.patternScanResult!!.startIndex,
|
||||||
|
"""
|
||||||
|
instance-of v0, p0, $GRAPHQL_STORY_TYPE
|
||||||
|
if-eqz v0, :resume_normal
|
||||||
|
invoke-static {p0}, $getSponsoredDataHelperMethod
|
||||||
|
move-result-object v0
|
||||||
|
if-eqz v0, :resume_normal
|
||||||
|
const-string v0, "GONE"
|
||||||
|
return-object v0
|
||||||
|
:resume_normal
|
||||||
|
nop
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
} ?: throw GetStoryVisibilityFingerprint.exception
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal object BaseModelMapperFingerprint : MethodFingerprint(
|
||||||
|
|
||||||
|
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
|
||||||
|
parameters = listOf("Ljava/lang/Class","I","I"),
|
||||||
|
returnType = "Lcom/facebook/graphql/modelutil/BaseModelWithTree;",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.SGET_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.IF_EQ
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal object GetSponsoredDataModelTemplateFingerprint : MethodFingerprint(
|
||||||
|
|
||||||
|
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
|
||||||
|
parameters = listOf(),
|
||||||
|
returnType = "L",
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.CONST,
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.RETURN_OBJECT
|
||||||
|
),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Annotation
|
||||||
|
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
|
||||||
|
|
||||||
|
internal object GetStoryVisibilityFingerprint : MethodFingerprint(
|
||||||
|
returnType = "Ljava/lang/String;",
|
||||||
|
accessFlags = (AccessFlags.PUBLIC or AccessFlags.STATIC),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.INSTANCE_OF,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.INSTANCE_OF,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.INSTANCE_OF,
|
||||||
|
Opcode.IF_NEZ,
|
||||||
|
Opcode.CONST
|
||||||
|
),
|
||||||
|
strings = listOf("This should not be called for base class object"),
|
||||||
|
)
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package app.revanced.patches.reddit.customclients.syncforreddit.fix.video
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patches.reddit.customclients.syncforreddit.fix.video.fingerprints.ParseRedditVideoNetworkResponseFingerprint
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Fix video downloads",
|
||||||
|
description = "Fixes a bug in Sync's MPD parser resulting in only the audio-track being saved.",
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage("com.laurencedawson.reddit_sync"),
|
||||||
|
CompatiblePackage("com.laurencedawson.reddit_sync.pro"),
|
||||||
|
CompatiblePackage("com.laurencedawson.reddit_sync.dev"),
|
||||||
|
],
|
||||||
|
requiresIntegrations = true,
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object FixVideoDownloadsPatch : BytecodePatch(
|
||||||
|
fingerprints = setOf(ParseRedditVideoNetworkResponseFingerprint),
|
||||||
|
) {
|
||||||
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/integrations/syncforreddit/FixRedditVideoDownloadPatch;"
|
||||||
|
private const val GET_LINKS_METHOD = "getLinks([B)[Ljava/lang/String;"
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
ParseRedditVideoNetworkResponseFingerprint.resultOrThrow().let {
|
||||||
|
val scanResult = it.scanResult.patternScanResult!!
|
||||||
|
val newInstanceIndex = scanResult.startIndex
|
||||||
|
val invokeDirectIndex = scanResult.endIndex - 1
|
||||||
|
|
||||||
|
val buildResponseInstruction = it.mutableMethod.getInstruction<Instruction35c>(invokeDirectIndex)
|
||||||
|
|
||||||
|
it.mutableMethod.addInstructions(
|
||||||
|
newInstanceIndex + 1,
|
||||||
|
"""
|
||||||
|
# Get byte array from response.
|
||||||
|
iget-object v2, p1, Lcom/android/volley/NetworkResponse;->data:[B
|
||||||
|
|
||||||
|
# Parse the videoUrl and audioUrl from the byte array.
|
||||||
|
invoke-static { v2 }, $INTEGRATIONS_CLASS_DESCRIPTOR->$GET_LINKS_METHOD
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
# Get videoUrl (Index 0).
|
||||||
|
const/4 v5, 0x0
|
||||||
|
aget-object v${buildResponseInstruction.registerE}, v2, v5
|
||||||
|
|
||||||
|
# Get audioUrl (Index 1).
|
||||||
|
const/4 v6, 0x1
|
||||||
|
aget-object v${buildResponseInstruction.registerF}, v2, v6
|
||||||
|
|
||||||
|
# Register E and F are used to build the response.
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.reddit.customclients.syncforreddit.fix.video.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal object ParseRedditVideoNetworkResponseFingerprint : MethodFingerprint(
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.NEW_INSTANCE,
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.CONST_WIDE_32
|
||||||
|
),
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
classDef.sourceFile == "RedditVideoRequest.java" && methodDef.name == "parseNetworkResponse"
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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<OneRegisterInstruction>(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<StringReference>()?.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<Instruction35c>(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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"),
|
||||||
|
)
|
||||||
@@ -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())"),
|
||||||
|
)
|
||||||
@@ -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"),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user