This repository has been archived by the owner on Jul 26, 2024. It is now read-only.
generated from ReVanced/revanced-patches-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(twitter): add video download & viewcount patch (from revanced)
- Loading branch information
1 parent
b0ca0e9
commit 8fe29b2
Showing
17 changed files
with
622 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
src/main/kotlin/indus/org/patches/twitter/interaction/downloads/UnlockDownloadsPatch.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package indus.org.patches.twitter.interaction.downloads | ||
|
||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions | ||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
import app.revanced.patcher.fingerprint.MethodFingerprintResult | ||
import app.revanced.patcher.patch.BytecodePatch | ||
import app.revanced.patcher.patch.annotation.CompatiblePackage | ||
import app.revanced.patcher.patch.annotation.Patch | ||
import indus.org.patches.twitter.interaction.downloads.fingerprints.ConstructMediaOptionsSheetFingerprint | ||
import indus.org.patches.twitter.interaction.downloads.fingerprints.ShowDownloadVideoUpsellBottomSheetFingerprint | ||
import indus.org.patches.util.exception | ||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction | ||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction | ||
|
||
@Patch( | ||
name = "Unlock downloads", | ||
description = "Unlocks the ability to download any video.", | ||
compatiblePackages = [CompatiblePackage("com.twitter.android")] | ||
) | ||
@Suppress("unused") | ||
object UnlockDownloadsPatch : BytecodePatch( | ||
setOf(ConstructMediaOptionsSheetFingerprint, ShowDownloadVideoUpsellBottomSheetFingerprint) | ||
) { | ||
override fun execute(context: BytecodeContext) { | ||
fun MethodFingerprint.patch(getRegisterAndIndex: MethodFingerprintResult.() -> Pair<Int, Int>) = result?.let { | ||
getRegisterAndIndex(it).let { (index, register) -> | ||
it.mutableMethod.addInstruction(index, "const/4 v$register, 0x1") | ||
} | ||
} ?: throw exception | ||
|
||
// Allow downloads for non-premium users. | ||
ShowDownloadVideoUpsellBottomSheetFingerprint.patch { | ||
val checkIndex = scanResult.patternScanResult!!.startIndex | ||
val register = mutableMethod.getInstruction<OneRegisterInstruction>(checkIndex).registerA | ||
|
||
checkIndex to register | ||
} | ||
|
||
// Force show the download menu item. | ||
ConstructMediaOptionsSheetFingerprint.patch { | ||
val showDownloadButtonIndex = mutableMethod.getInstructions().lastIndex - 1 | ||
val register = mutableMethod.getInstruction<TwoRegisterInstruction>(showDownloadButtonIndex).registerA | ||
|
||
showDownloadButtonIndex to register | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...tches/twitter/interaction/downloads/fingerprints/ConstructMediaOptionsSheetFingerprint.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package indus.org.patches.twitter.interaction.downloads.fingerprints | ||
|
||
import app.revanced.patcher.extensions.or | ||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
import com.android.tools.smali.dexlib2.AccessFlags | ||
|
||
|
||
internal object ConstructMediaOptionsSheetFingerprint : MethodFingerprint( | ||
returnType = "V", | ||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, | ||
strings = listOf("captionsState") | ||
) |
10 changes: 10 additions & 0 deletions
10
...itter/interaction/downloads/fingerprints/ShowDownloadVideoUpsellBottomSheetFingerprint.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package indus.org.patches.twitter.interaction.downloads.fingerprints | ||
|
||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
import com.android.tools.smali.dexlib2.Opcode | ||
|
||
internal object ShowDownloadVideoUpsellBottomSheetFingerprint : MethodFingerprint( | ||
returnType = "Z", | ||
strings = listOf("variantToDownload.url"), | ||
opcodes = listOf(Opcode.IF_EQZ) | ||
) |
30 changes: 30 additions & 0 deletions
30
src/main/kotlin/indus/org/patches/twitter/layout/viewcount/HideViewCountPatch.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package indus.org.patches.twitter.layout.viewcount | ||
|
||
|
||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions | ||
import app.revanced.patcher.patch.BytecodePatch | ||
import app.revanced.patcher.patch.annotation.CompatiblePackage | ||
import app.revanced.patcher.patch.annotation.Patch | ||
import app.revanced.patches.twitter.layout.viewcount.fingerprints.ViewCountsEnabledFingerprint | ||
import indus.org.patches.util.exception | ||
|
||
@Patch( | ||
name = "Hide view count", | ||
description = "Hides the view count of Tweets.", | ||
compatiblePackages = [CompatiblePackage("com.twitter.android")], | ||
use = false | ||
) | ||
@Suppress("unused") | ||
object HideViewCountPatch : BytecodePatch( | ||
setOf(ViewCountsEnabledFingerprint) | ||
) { | ||
override fun execute(context: BytecodeContext) = | ||
ViewCountsEnabledFingerprint.result?.mutableMethod?.addInstructions( | ||
0, | ||
""" | ||
const/4 v0, 0x0 | ||
return v0 | ||
""" | ||
) ?: throw ViewCountsEnabledFingerprint.exception | ||
} |
8 changes: 8 additions & 0 deletions
8
...n/indus/org/patches/twitter/layout/viewcount/fingerprints/ViewCountsEnabledFingerprint.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package app.revanced.patches.twitter.layout.viewcount.fingerprints | ||
|
||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
|
||
internal object ViewCountsEnabledFingerprint : MethodFingerprint( | ||
returnType = "Z", | ||
strings = listOf("view_counts_public_visibility_enabled") | ||
) |
153 changes: 153 additions & 0 deletions
153
src/main/kotlin/indus/org/patches/util/BytecodeUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package indus.org.patches.util | ||
|
||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions | ||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
import app.revanced.patcher.patch.PatchException | ||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass | ||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod | ||
import indus.org.patches.util.mapping.ResourceMappingPatch | ||
import com.android.tools.smali.dexlib2.iface.Method | ||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction | ||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction | ||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction | ||
import com.android.tools.smali.dexlib2.iface.reference.Reference | ||
import com.android.tools.smali.dexlib2.util.MethodUtil | ||
|
||
/** | ||
* The [PatchException] of failing to resolve a [MethodFingerprint]. | ||
* | ||
* @return The [PatchException]. | ||
*/ | ||
val MethodFingerprint.exception | ||
get() = PatchException("Failed to resolve ${this.javaClass.simpleName}") | ||
|
||
/** | ||
* Find the [MutableMethod] from a given [Method] in a [MutableClass]. | ||
* | ||
* @param method The [Method] to find. | ||
* @return The [MutableMethod]. | ||
*/ | ||
fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first { | ||
MethodUtil.methodSignaturesMatch(it, method) | ||
} | ||
|
||
/** | ||
* Apply a transform to all methods of the class. | ||
* | ||
* @param transform The transformation function. Accepts a [MutableMethod] and returns a transformed [MutableMethod]. | ||
*/ | ||
fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) { | ||
val transformedMethods = methods.map { it.transform() } | ||
methods.clear() | ||
methods.addAll(transformedMethods) | ||
} | ||
|
||
/** | ||
* Inject a call to a method that hides a view. | ||
* | ||
* @param insertIndex The index to insert the call at. | ||
* @param viewRegister The register of the view to hide. | ||
* @param classDescriptor The descriptor of the class that contains the method. | ||
* @param targetMethod The name of the method to call. | ||
*/ | ||
fun MutableMethod.injectHideViewCall( | ||
insertIndex: Int, | ||
viewRegister: Int, | ||
classDescriptor: String, | ||
targetMethod: String | ||
) = addInstruction( | ||
insertIndex, | ||
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V" | ||
) | ||
|
||
/** | ||
* Find the index of the first instruction with the id of the given resource name. | ||
* | ||
* @param resourceName the name of the resource to find the id for. | ||
* @return the index of the first instruction with the id of the given resource name, or -1 if not found. | ||
*/ | ||
fun Method.findIndexForIdResource(resourceName: String): Int { | ||
fun getIdResourceId(resourceName: String) = ResourceMappingPatch.resourceMappings.single { | ||
it.type == "id" && it.name == resourceName | ||
}.id | ||
|
||
return indexOfFirstWideLiteralInstructionValue(getIdResourceId(resourceName)) | ||
} | ||
|
||
/** | ||
* Find the index of the first wide literal instruction with the given value. | ||
* | ||
* @return the first literal instruction with the value, or -1 if not found. | ||
*/ | ||
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let { | ||
it.instructions.indexOfFirst { instruction -> | ||
(instruction as? WideLiteralInstruction)?.wideLiteral == literal | ||
} | ||
} ?: -1 | ||
|
||
/** | ||
* Check if the method contains a literal with the given value. | ||
* | ||
* @return if the method contains a literal with the given value. | ||
*/ | ||
fun Method.containsWideLiteralInstructionValue(literal: Long) = | ||
indexOfFirstWideLiteralInstructionValue(literal) >= 0 | ||
|
||
/** | ||
* Traverse the class hierarchy starting from the given root class. | ||
* | ||
* @param targetClass the class to start traversing the class hierarchy from. | ||
* @param callback function that is called for every class in the hierarchy. | ||
*/ | ||
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { | ||
callback(targetClass) | ||
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let { | ||
traverseClassHierarchy(it, callback) | ||
} | ||
} | ||
|
||
/** | ||
* Get the [Reference] of an [Instruction] as [T]. | ||
* | ||
* @param T The type of [Reference] to cast to. | ||
* @return The [Reference] as [T] or null | ||
* if the [Instruction] is not a [ReferenceInstruction] or the [Reference] is not of type [T]. | ||
* @see ReferenceInstruction | ||
*/ | ||
inline fun <reified T : Reference> Instruction.getReference() = (this as? ReferenceInstruction)?.reference as? T | ||
|
||
/** | ||
* Get the index of the first [Instruction] that matches the predicate. | ||
* | ||
* @param predicate The predicate to match. | ||
* @return The index of the first [Instruction] that matches the predicate. | ||
*/ | ||
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = | ||
this.implementation!!.instructions.indexOfFirst(predicate) | ||
|
||
/** | ||
* Return the resolved methods of [MethodFingerprint]s early. | ||
*/ | ||
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) { | ||
val const = if (bool) "0x1" else "0x0" | ||
this.forEach { fingerprint -> | ||
fingerprint.result?.let { result -> | ||
val stringInstructions = when (result.method.returnType.first()) { | ||
'L' -> """ | ||
const/4 v0, $const | ||
return-object v0 | ||
""" | ||
'V' -> "return-void" | ||
'I', 'Z' -> """ | ||
const/4 v0, $const | ||
return v0 | ||
""" | ||
else -> throw Exception("This case should never happen.") | ||
} | ||
|
||
result.mutableMethod.addInstructions(0, stringInstructions) | ||
} ?: throw fingerprint.exception | ||
} | ||
} |
Oops, something went wrong.