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