feat(YouTube): Support version 18.32.39
This commit is contained in:
parent
5afb63e052
commit
7b503e2336
|
@ -15,7 +15,7 @@ import java.util.*
|
|||
@Name("Spoof wifi connection")
|
||||
@Description("Spoofs an existing Wi-Fi connection.")
|
||||
@RequiresIntegrations
|
||||
internal class SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
class SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
|
||||
private companion object {
|
||||
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/all/connectivity/wifi/spoof/SpoofWifiPatch"
|
||||
|
|
|
@ -7,7 +7,10 @@ import app.revanced.patcher.patch.annotations.Patch
|
|||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.all.screencapture.removerestriction.resource.patch.RemoveCaptureRestrictionResourcePatch
|
||||
import app.revanced.util.patch.*
|
||||
import app.revanced.util.patch.AbstractTransformInstructionsPatch
|
||||
import app.revanced.util.patch.IMethodCall
|
||||
import app.revanced.util.patch.Instruction35cInfo
|
||||
import app.revanced.util.patch.filterMapInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
@ -17,7 +20,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|||
@Description("Removes the restriction of capturing audio from apps that normally wouldn't allow it.")
|
||||
@DependsOn([RemoveCaptureRestrictionResourcePatch::class])
|
||||
@RequiresIntegrations
|
||||
internal class RemoveCaptureRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
class RemoveCaptureRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
// Information about method calls we want to replace
|
||||
enum class MethodCall(
|
||||
override val definedClassName: String,
|
||||
|
|
|
@ -5,17 +5,19 @@ import app.revanced.patcher.annotation.Name
|
|||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.util.patch.*
|
||||
import app.revanced.util.patch.AbstractTransformInstructionsPatch
|
||||
import app.revanced.util.patch.IMethodCall
|
||||
import app.revanced.util.patch.Instruction35cInfo
|
||||
import app.revanced.util.patch.filterMapInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import java.util.*
|
||||
|
||||
@Patch(false)
|
||||
@Name("Remove screenshot restriction")
|
||||
@Description("Removes the restriction of taking screenshots in apps that normally wouldn't allow it.")
|
||||
@RequiresIntegrations
|
||||
internal class RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
class RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||
|
||||
private companion object {
|
||||
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX =
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package app.revanced.patches.youtube.layout.hide.general.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object ConvertElementToFlatBufferFingerprint : MethodFingerprint(
|
||||
strings = listOf("Failed to convert Element to Flatbuffers: %s"),
|
||||
opcodes = listOf(Opcode.IGET_OBJECT) // Patched at this opcodes index
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
package app.revanced.patches.youtube.layout.hide.general.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object ParseElementFromBufferFingerprint : MethodFingerprint(
|
||||
parameters = listOf("L","L","[B", "L","L"),
|
||||
opcodes = listOf(Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT),
|
||||
strings = listOf("Failed to parse Element")
|
||||
)
|
|
@ -4,9 +4,9 @@ import app.revanced.extensions.exception
|
|||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
|
@ -15,10 +15,12 @@ import app.revanced.patches.shared.settings.preference.impl.StringResource
|
|||
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
|
||||
import app.revanced.patches.shared.settings.preference.impl.TextPreference
|
||||
import app.revanced.patches.youtube.layout.hide.general.annotations.HideLayoutComponentsCompatibility
|
||||
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ConvertElementToFlatBufferFingerprint
|
||||
import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElementFromBufferFingerprint
|
||||
import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.PreferenceScreen
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
|
||||
@Patch
|
||||
@Name("Hide layout components")
|
||||
|
@ -26,7 +28,7 @@ import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.P
|
|||
@DependsOn([LithoFilterPatch::class, SettingsPatch::class])
|
||||
@HideLayoutComponentsCompatibility
|
||||
class HideLayoutComponentsPatch : BytecodePatch(
|
||||
listOf(ConvertElementToFlatBufferFingerprint)
|
||||
listOf(ParseElementFromBufferFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
PreferenceScreen.LAYOUT.addPreferences(
|
||||
|
@ -249,30 +251,26 @@ class HideLayoutComponentsPatch : BytecodePatch(
|
|||
|
||||
// region Mix playlists
|
||||
|
||||
ConvertElementToFlatBufferFingerprint.result?.let {
|
||||
val returnEmptyComponentIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2
|
||||
ParseElementFromBufferFingerprint.result?.let { result ->
|
||||
val returnEmptyComponentInstruction = result.mutableMethod.getInstructions()
|
||||
.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||
|
||||
it.mutableMethod.apply {
|
||||
// The last virtual register (not parameter). Used to store the byte array
|
||||
// that may contain information about a mix playlist.
|
||||
val freeRegister = (implementation!!.registerCount - 1) - parameterTypes.size - 1
|
||||
result.mutableMethod.apply {
|
||||
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
|
||||
val byteBufferRegister = getInstruction<FiveRegisterInstruction>(consumeByteBufferIndex).registerD
|
||||
|
||||
// Check if the byte array contains anything about a mix playlist.
|
||||
addInstructionsWithLabels(
|
||||
it.scanResult.patternScanResult!!.startIndex,
|
||||
result.scanResult.patternScanResult!!.startIndex,
|
||||
"""
|
||||
invoke-static {v$freeRegister}, $FILTER_CLASS_DESCRIPTOR->filterMixPlaylists([B)Z
|
||||
move-result v$freeRegister
|
||||
if-nez v$freeRegister, :return_empty_component
|
||||
invoke-static {v$byteBufferRegister}, $FILTER_CLASS_DESCRIPTOR->filterMixPlaylists([B)Z
|
||||
move-result v0 # Conveniently same register happens to be free.
|
||||
if-nez v0, :return_empty_component
|
||||
""",
|
||||
ExternalLabel("return_empty_component", getInstruction(returnEmptyComponentIndex))
|
||||
ExternalLabel("return_empty_component", returnEmptyComponentInstruction)
|
||||
)
|
||||
|
||||
// Move the byte array to a free register.
|
||||
addInstruction(0, "move-object/from16 v$freeRegister, p3")
|
||||
}
|
||||
|
||||
} ?: throw ConvertElementToFlatBufferFingerprint.exception
|
||||
} ?: throw ParseElementFromBufferFingerprint.exception
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import app.revanced.patches.youtube.layout.hide.shorts.resource.patch.HideShortsComponentsResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object CreateShortsButtonsFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("Z", "Z", "L"),
|
||||
literal = HideShortsComponentsResourcePatch.reelPlayerRightLargeIconSize
|
||||
literal = HideShortsComponentsResourcePatch.reelPlayerRightCellButtonHeight
|
||||
)
|
|
@ -91,11 +91,11 @@ class HideShortsComponentsResourcePatch : ResourcePatch {
|
|||
fun String.getId() = ResourceMappingPatch.resourceMappings.single { it.name == this }.id
|
||||
|
||||
reelMultipleItemShelfId = "reel_multiple_items_shelf".getId()
|
||||
reelPlayerRightLargeIconSize = "reel_player_right_large_icon_size".getId()
|
||||
reelPlayerRightCellButtonHeight = "reel_player_right_cell_button_height".getId()
|
||||
}
|
||||
|
||||
companion object {
|
||||
var reelMultipleItemShelfId: Long = -1
|
||||
var reelPlayerRightLargeIconSize = -1L
|
||||
var reelMultipleItemShelfId = -1L
|
||||
var reelPlayerRightCellButtonHeight = -1L
|
||||
}
|
||||
}
|
|
@ -13,19 +13,17 @@ object TextComponentAtomicReferenceFingerprint : MethodFingerprint(
|
|||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_OBJECT_FROM16, // available unused register
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
null, // move-object/from16 or move/from16
|
||||
Opcode.MOVE_OBJECT_FROM16,
|
||||
Opcode.INVOKE_VIRTUAL, // CharSequence atomic reference
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.MOVE_OBJECT, // Register A and B is context, use B as context, reuse A as free register
|
||||
Opcode.INVOKE_VIRTUAL, // Register C is atomic reference
|
||||
Opcode.MOVE_RESULT_OBJECT, // Register A is char sequence
|
||||
Opcode.MOVE_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.MOVE_OBJECT, // CharSequence reference, and control flow label. Insert code here.
|
||||
null, // invoke-interface or invoke-virtual
|
||||
Opcode.MOVE_OBJECT,
|
||||
Opcode.INVOKE_INTERFACE, // Insert hook here
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
null, // invoke-interface or invoke-virtual
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.GOTO,
|
||||
Opcode.GOTO
|
||||
)
|
||||
)
|
|
@ -83,10 +83,8 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
|
|||
// since the underlying (likes only) text did not change.
|
||||
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
||||
TextComponentContextFingerprint.also {
|
||||
it.resolve(
|
||||
context,
|
||||
TextComponentConstructorFingerprint.result!!.classDef
|
||||
)
|
||||
if (!it.resolve(context, TextComponentConstructorFingerprint.result!!.classDef))
|
||||
throw it.exception
|
||||
}.result?.also { result ->
|
||||
if (!TextComponentAtomicReferenceFingerprint.resolve(context, result.method, result.classDef))
|
||||
throw TextComponentAtomicReferenceFingerprint.exception
|
||||
|
@ -96,33 +94,46 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
|
|||
val atomicReferenceStartIndex = TextComponentAtomicReferenceFingerprint.result!!
|
||||
.scanResult.patternScanResult!!.startIndex
|
||||
|
||||
val insertIndex = atomicReferenceStartIndex + 7
|
||||
val insertIndex = atomicReferenceStartIndex + 6
|
||||
|
||||
textComponentContextFingerprintResult.mutableMethod.apply {
|
||||
// Get the conversion context obfuscated field name, and the registers for the AtomicReference and CharSequence
|
||||
val conversionContextFieldReference =
|
||||
getInstruction<ReferenceInstruction>(conversionContextIndex).reference
|
||||
|
||||
// any free register
|
||||
val contextRegister =
|
||||
// Reuse the free register to make room for the atomic reference register.
|
||||
val freeRegister =
|
||||
getInstruction<TwoRegisterInstruction>(atomicReferenceStartIndex).registerB
|
||||
|
||||
val atomicReferenceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 4).registerC
|
||||
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 1).registerC
|
||||
|
||||
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
|
||||
val charSequenceRegister = moveCharSequenceInstruction.registerB
|
||||
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex - 1)
|
||||
val charSequenceSourceRegister = moveCharSequenceInstruction.registerB
|
||||
val charSequenceTargetRegister = moveCharSequenceInstruction.registerA
|
||||
|
||||
// Insert as first instructions at the control flow label.
|
||||
// Must replace the existing instruction to preserve the label, and then insert the remaining instructions.
|
||||
replaceInstruction(insertIndex, "move-object/from16 v$contextRegister, p0")
|
||||
// In order to preserve the atomic reference register, because it is overwritten,
|
||||
// use another free register to store it.
|
||||
replaceInstruction(
|
||||
atomicReferenceStartIndex + 2,
|
||||
"move-result-object v$freeRegister"
|
||||
)
|
||||
replaceInstruction(
|
||||
atomicReferenceStartIndex + 3,
|
||||
"move-object v$charSequenceSourceRegister, v$freeRegister"
|
||||
)
|
||||
|
||||
// Move the current instance to the free register, and get the conversion context from it.
|
||||
replaceInstruction(insertIndex - 1, "move-object/from16 v$freeRegister, p0")
|
||||
addInstructions(
|
||||
insertIndex + 1,
|
||||
insertIndex,
|
||||
"""
|
||||
iget-object v$contextRegister, v$contextRegister, $conversionContextFieldReference # copy obfuscated context field into free register
|
||||
invoke-static {v$contextRegister, v$atomicReferenceRegister, v$charSequenceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$charSequenceRegister
|
||||
move-object v${moveCharSequenceInstruction.registerA}, v${charSequenceRegister} # original instruction at the insertion point
|
||||
# Move context to free register
|
||||
iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference
|
||||
invoke-static {v$freeRegister, v$atomicReferenceRegister, v$charSequenceSourceRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
|
||||
move-result-object v$freeRegister
|
||||
# Replace the original char sequence with the modified one.
|
||||
move-object v${charSequenceTargetRegister}, v${freeRegister}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.youtube.misc.links.open.annotations
|
||||
package app.revanced.patches.youtube.misc.links.annotations
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
|
@ -1,17 +0,0 @@
|
|||
package app.revanced.patches.youtube.misc.links.open.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object BindSessionServiceFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
opcodes = listOf(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
|
@ -1,18 +0,0 @@
|
|||
package app.revanced.patches.youtube.misc.links.open.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object GetCustomTabPackageNameFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
|
@ -1,18 +0,0 @@
|
|||
package app.revanced.patches.youtube.misc.links.open.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
object InitializeCustomTabSupportFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.NEW_INSTANCE,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
strings = listOf("android.support.customtabs.action.CustomTabsService")
|
||||
)
|
|
@ -1,59 +0,0 @@
|
|||
package app.revanced.patches.youtube.misc.links.open.patch
|
||||
|
||||
import app.revanced.extensions.exception
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
||||
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.links.open.annotations.OpenLinksExternallyCompatibility
|
||||
import app.revanced.patches.youtube.misc.links.open.fingerprints.BindSessionServiceFingerprint
|
||||
import app.revanced.patches.youtube.misc.links.open.fingerprints.GetCustomTabPackageNameFingerprint
|
||||
import app.revanced.patches.youtube.misc.links.open.fingerprints.InitializeCustomTabSupportFingerprint
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
|
||||
@Patch
|
||||
@Name("Open links externally")
|
||||
@Description("Open links outside of the app directly in your browser.")
|
||||
@OpenLinksExternallyCompatibility
|
||||
class OpenLinksExternallyPatch : BytecodePatch(
|
||||
listOf(
|
||||
GetCustomTabPackageNameFingerprint,
|
||||
BindSessionServiceFingerprint,
|
||||
InitializeCustomTabSupportFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_external_browser",
|
||||
StringResource("revanced_external_browser_title", "Open links in browser"),
|
||||
StringResource("revanced_external_browser_summary_on", "Opening links externally"),
|
||||
StringResource("revanced_external_browser_summary_off", "Opening links in app")
|
||||
)
|
||||
)
|
||||
|
||||
arrayOf(
|
||||
GetCustomTabPackageNameFingerprint,
|
||||
BindSessionServiceFingerprint,
|
||||
InitializeCustomTabSupportFingerprint
|
||||
).forEach {
|
||||
val result = it.result ?: throw it.exception
|
||||
val insertIndex = result.scanResult.patternScanResult!!.endIndex + 1
|
||||
with(result.mutableMethod) {
|
||||
val register = (implementation!!.instructions[insertIndex - 1] as Instruction21c).registerA
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static {v$register}, Lapp/revanced/integrations/patches/OpenLinksExternallyPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package app.revanced.patches.youtube.misc.links.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
||||
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.links.annotations.OpenLinksExternallyCompatibility
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import app.revanced.util.patch.AbstractTransformInstructionsPatch
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
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.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
@Patch
|
||||
@Name("Open links externally")
|
||||
@Description("Open links outside of the app directly in your browser.")
|
||||
@OpenLinksExternallyCompatibility
|
||||
class OpenLinksExternallyPatch : AbstractTransformInstructionsPatch<Pair<Int, Int>>(
|
||||
) {
|
||||
override fun filterMap(
|
||||
classDef: ClassDef, method: Method, instruction: Instruction, instructionIndex: Int
|
||||
): Pair<Int, Int>? {
|
||||
if (instruction !is ReferenceInstruction) return null
|
||||
val reference = instruction.reference as? StringReference ?: return null
|
||||
|
||||
if (reference.string != "android.support.customtabs.action.CustomTabsService") return null
|
||||
|
||||
return instructionIndex to (instruction as OneRegisterInstruction).registerA
|
||||
}
|
||||
|
||||
override fun transform(mutableMethod: MutableMethod, entry: Pair<Int, Int>) {
|
||||
val (intentStringIndex, register) = entry
|
||||
|
||||
// Hook the intent string.
|
||||
mutableMethod.addInstructions(
|
||||
intentStringIndex + 1,
|
||||
"""
|
||||
invoke-static {v$register}, Lapp/revanced/integrations/patches/OpenLinksExternallyPatch;->getIntent(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$register
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_external_browser",
|
||||
StringResource("revanced_external_browser_title", "Open links in browser"),
|
||||
StringResource("revanced_external_browser_summary_on", "Opening links externally"),
|
||||
StringResource("revanced_external_browser_summary_off", "Opening links in app")
|
||||
)
|
||||
)
|
||||
|
||||
super.execute(context)
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
|
|||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
internal abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||
|
||||
abstract fun filterMap(
|
||||
classDef: ClassDef,
|
||||
|
|
|
@ -8,9 +8,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal typealias Instruction35cInfo = Triple<IMethodCall, Instruction35c, Int>
|
||||
typealias Instruction35cInfo = Triple<IMethodCall, Instruction35c, Int>
|
||||
|
||||
internal interface IMethodCall {
|
||||
interface IMethodCall {
|
||||
val definedClassName: String
|
||||
val methodName: String
|
||||
val methodParams: Array<String>
|
||||
|
@ -62,14 +62,14 @@ internal interface IMethodCall {
|
|||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified E> fromMethodReference(methodReference: MethodReference)
|
||||
inline fun <reified E> fromMethodReference(methodReference: MethodReference)
|
||||
where E : Enum<E>, E : IMethodCall = enumValues<E>().firstOrNull { search ->
|
||||
search.definedClassName == methodReference.definingClass
|
||||
&& search.methodName == methodReference.name
|
||||
&& methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
|
||||
}
|
||||
|
||||
internal inline fun <reified E> filterMapInstruction35c(
|
||||
inline fun <reified E> filterMapInstruction35c(
|
||||
integrationsClassDescriptorPrefix: String,
|
||||
classDef: ClassDef,
|
||||
instruction: Instruction,
|
||||
|
|
Loading…
Reference in a new issue