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")
|
@Name("Spoof wifi connection")
|
||||||
@Description("Spoofs an existing Wi-Fi connection.")
|
@Description("Spoofs an existing Wi-Fi connection.")
|
||||||
@RequiresIntegrations
|
@RequiresIntegrations
|
||||||
internal class SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
class SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/all/connectivity/wifi/spoof/SpoofWifiPatch"
|
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.patch.annotations.RequiresIntegrations
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.all.screencapture.removerestriction.resource.patch.RemoveCaptureRestrictionResourcePatch
|
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.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
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.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.")
|
@Description("Removes the restriction of capturing audio from apps that normally wouldn't allow it.")
|
||||||
@DependsOn([RemoveCaptureRestrictionResourcePatch::class])
|
@DependsOn([RemoveCaptureRestrictionResourcePatch::class])
|
||||||
@RequiresIntegrations
|
@RequiresIntegrations
|
||||||
internal class RemoveCaptureRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
class RemoveCaptureRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||||
// Information about method calls we want to replace
|
// Information about method calls we want to replace
|
||||||
enum class MethodCall(
|
enum class MethodCall(
|
||||||
override val definedClassName: String,
|
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.Patch
|
||||||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
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.ClassDef
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
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.Instruction
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Patch(false)
|
@Patch(false)
|
||||||
@Name("Remove screenshot restriction")
|
@Name("Remove screenshot restriction")
|
||||||
@Description("Removes the restriction of taking screenshots in apps that normally wouldn't allow it.")
|
@Description("Removes the restriction of taking screenshots in apps that normally wouldn't allow it.")
|
||||||
@RequiresIntegrations
|
@RequiresIntegrations
|
||||||
internal class RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
class RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX =
|
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.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
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.BytecodePatch
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
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.SwitchPreference
|
||||||
import app.revanced.patches.shared.settings.preference.impl.TextPreference
|
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.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.litho.filter.patch.LithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.PreferenceScreen
|
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
|
@Patch
|
||||||
@Name("Hide layout components")
|
@Name("Hide layout components")
|
||||||
|
@ -26,7 +28,7 @@ import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch.P
|
||||||
@DependsOn([LithoFilterPatch::class, SettingsPatch::class])
|
@DependsOn([LithoFilterPatch::class, SettingsPatch::class])
|
||||||
@HideLayoutComponentsCompatibility
|
@HideLayoutComponentsCompatibility
|
||||||
class HideLayoutComponentsPatch : BytecodePatch(
|
class HideLayoutComponentsPatch : BytecodePatch(
|
||||||
listOf(ConvertElementToFlatBufferFingerprint)
|
listOf(ParseElementFromBufferFingerprint)
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
PreferenceScreen.LAYOUT.addPreferences(
|
PreferenceScreen.LAYOUT.addPreferences(
|
||||||
|
@ -249,30 +251,26 @@ class HideLayoutComponentsPatch : BytecodePatch(
|
||||||
|
|
||||||
// region Mix playlists
|
// region Mix playlists
|
||||||
|
|
||||||
ConvertElementToFlatBufferFingerprint.result?.let {
|
ParseElementFromBufferFingerprint.result?.let { result ->
|
||||||
val returnEmptyComponentIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2
|
val returnEmptyComponentInstruction = result.mutableMethod.getInstructions()
|
||||||
|
.last { it.opcode == Opcode.INVOKE_STATIC }
|
||||||
|
|
||||||
it.mutableMethod.apply {
|
result.mutableMethod.apply {
|
||||||
// The last virtual register (not parameter). Used to store the byte array
|
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex
|
||||||
// that may contain information about a mix playlist.
|
val byteBufferRegister = getInstruction<FiveRegisterInstruction>(consumeByteBufferIndex).registerD
|
||||||
val freeRegister = (implementation!!.registerCount - 1) - parameterTypes.size - 1
|
|
||||||
|
|
||||||
// Check if the byte array contains anything about a mix playlist.
|
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
it.scanResult.patternScanResult!!.startIndex,
|
result.scanResult.patternScanResult!!.startIndex,
|
||||||
"""
|
"""
|
||||||
invoke-static {v$freeRegister}, $FILTER_CLASS_DESCRIPTOR->filterMixPlaylists([B)Z
|
invoke-static {v$byteBufferRegister}, $FILTER_CLASS_DESCRIPTOR->filterMixPlaylists([B)Z
|
||||||
move-result v$freeRegister
|
move-result v0 # Conveniently same register happens to be free.
|
||||||
if-nez v$freeRegister, :return_empty_component
|
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
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
|
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.or
|
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.patches.youtube.layout.hide.shorts.resource.patch.HideShortsComponentsResourcePatch
|
||||||
|
import app.revanced.util.patch.LiteralValueFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
object CreateShortsButtonsFingerprint : LiteralValueFingerprint(
|
object CreateShortsButtonsFingerprint : LiteralValueFingerprint(
|
||||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
parameters = listOf("Z", "Z", "L"),
|
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
|
fun String.getId() = ResourceMappingPatch.resourceMappings.single { it.name == this }.id
|
||||||
|
|
||||||
reelMultipleItemShelfId = "reel_multiple_items_shelf".getId()
|
reelMultipleItemShelfId = "reel_multiple_items_shelf".getId()
|
||||||
reelPlayerRightLargeIconSize = "reel_player_right_large_icon_size".getId()
|
reelPlayerRightCellButtonHeight = "reel_player_right_cell_button_height".getId()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var reelMultipleItemShelfId: Long = -1
|
var reelMultipleItemShelfId = -1L
|
||||||
var reelPlayerRightLargeIconSize = -1L
|
var reelPlayerRightCellButtonHeight = -1L
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,19 +13,17 @@ object TextComponentAtomicReferenceFingerprint : MethodFingerprint(
|
||||||
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
||||||
opcodes = listOf(
|
opcodes = listOf(
|
||||||
Opcode.MOVE_OBJECT_FROM16, // available unused register
|
Opcode.MOVE_OBJECT, // Register A and B is context, use B as context, reuse A as free register
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
Opcode.INVOKE_VIRTUAL, // Register C is atomic reference
|
||||||
null, // move-object/from16 or move/from16
|
Opcode.MOVE_RESULT_OBJECT, // Register A is char sequence
|
||||||
Opcode.MOVE_OBJECT_FROM16,
|
Opcode.MOVE_OBJECT,
|
||||||
Opcode.INVOKE_VIRTUAL, // CharSequence atomic reference
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
Opcode.MOVE_OBJECT, // CharSequence reference, and control flow label. Insert code here.
|
Opcode.MOVE_OBJECT,
|
||||||
null, // invoke-interface or invoke-virtual
|
Opcode.INVOKE_INTERFACE, // Insert hook here
|
||||||
Opcode.MOVE_RESULT,
|
Opcode.MOVE_RESULT,
|
||||||
Opcode.IF_EQZ,
|
Opcode.IF_EQZ,
|
||||||
null, // invoke-interface or invoke-virtual
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.GOTO,
|
Opcode.GOTO
|
||||||
)
|
)
|
||||||
)
|
)
|
|
@ -83,10 +83,8 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
|
||||||
// since the underlying (likes only) text did not change.
|
// 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.
|
// This hook handles all situations, as it's where the created Spans are stored and later reused.
|
||||||
TextComponentContextFingerprint.also {
|
TextComponentContextFingerprint.also {
|
||||||
it.resolve(
|
if (!it.resolve(context, TextComponentConstructorFingerprint.result!!.classDef))
|
||||||
context,
|
throw it.exception
|
||||||
TextComponentConstructorFingerprint.result!!.classDef
|
|
||||||
)
|
|
||||||
}.result?.also { result ->
|
}.result?.also { result ->
|
||||||
if (!TextComponentAtomicReferenceFingerprint.resolve(context, result.method, result.classDef))
|
if (!TextComponentAtomicReferenceFingerprint.resolve(context, result.method, result.classDef))
|
||||||
throw TextComponentAtomicReferenceFingerprint.exception
|
throw TextComponentAtomicReferenceFingerprint.exception
|
||||||
|
@ -96,33 +94,46 @@ class ReturnYouTubeDislikePatch : BytecodePatch(
|
||||||
val atomicReferenceStartIndex = TextComponentAtomicReferenceFingerprint.result!!
|
val atomicReferenceStartIndex = TextComponentAtomicReferenceFingerprint.result!!
|
||||||
.scanResult.patternScanResult!!.startIndex
|
.scanResult.patternScanResult!!.startIndex
|
||||||
|
|
||||||
val insertIndex = atomicReferenceStartIndex + 7
|
val insertIndex = atomicReferenceStartIndex + 6
|
||||||
|
|
||||||
textComponentContextFingerprintResult.mutableMethod.apply {
|
textComponentContextFingerprintResult.mutableMethod.apply {
|
||||||
// Get the conversion context obfuscated field name, and the registers for the AtomicReference and CharSequence
|
// Get the conversion context obfuscated field name, and the registers for the AtomicReference and CharSequence
|
||||||
val conversionContextFieldReference =
|
val conversionContextFieldReference =
|
||||||
getInstruction<ReferenceInstruction>(conversionContextIndex).reference
|
getInstruction<ReferenceInstruction>(conversionContextIndex).reference
|
||||||
|
|
||||||
// any free register
|
// Reuse the free register to make room for the atomic reference register.
|
||||||
val contextRegister =
|
val freeRegister =
|
||||||
getInstruction<TwoRegisterInstruction>(atomicReferenceStartIndex).registerB
|
getInstruction<TwoRegisterInstruction>(atomicReferenceStartIndex).registerB
|
||||||
|
|
||||||
val atomicReferenceRegister =
|
val atomicReferenceRegister =
|
||||||
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 4).registerC
|
getInstruction<FiveRegisterInstruction>(atomicReferenceStartIndex + 1).registerC
|
||||||
|
|
||||||
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
|
val moveCharSequenceInstruction = getInstruction<TwoRegisterInstruction>(insertIndex - 1)
|
||||||
val charSequenceRegister = moveCharSequenceInstruction.registerB
|
val charSequenceSourceRegister = moveCharSequenceInstruction.registerB
|
||||||
|
val charSequenceTargetRegister = moveCharSequenceInstruction.registerA
|
||||||
|
|
||||||
// Insert as first instructions at the control flow label.
|
// In order to preserve the atomic reference register, because it is overwritten,
|
||||||
// Must replace the existing instruction to preserve the label, and then insert the remaining instructions.
|
// use another free register to store it.
|
||||||
replaceInstruction(insertIndex, "move-object/from16 v$contextRegister, p0")
|
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(
|
addInstructions(
|
||||||
insertIndex + 1,
|
insertIndex,
|
||||||
"""
|
"""
|
||||||
iget-object v$contextRegister, v$contextRegister, $conversionContextFieldReference # copy obfuscated context field into free register
|
# Move context to 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;
|
iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference
|
||||||
move-result-object v$charSequenceRegister
|
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-object v${moveCharSequenceInstruction.registerA}, v${charSequenceRegister} # original instruction at the insertion point
|
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.Compatibility
|
||||||
import app.revanced.patcher.annotation.Package
|
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.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
internal abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||||
|
|
||||||
abstract fun filterMap(
|
abstract fun filterMap(
|
||||||
classDef: ClassDef,
|
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.instruction.formats.Instruction35c
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
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 definedClassName: String
|
||||||
val methodName: String
|
val methodName: String
|
||||||
val methodParams: Array<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 ->
|
where E : Enum<E>, E : IMethodCall = enumValues<E>().firstOrNull { search ->
|
||||||
search.definedClassName == methodReference.definingClass
|
search.definedClassName == methodReference.definingClass
|
||||||
&& search.methodName == methodReference.name
|
&& search.methodName == methodReference.name
|
||||||
&& methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
|
&& methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified E> filterMapInstruction35c(
|
inline fun <reified E> filterMapInstruction35c(
|
||||||
integrationsClassDescriptorPrefix: String,
|
integrationsClassDescriptorPrefix: String,
|
||||||
classDef: ClassDef,
|
classDef: ClassDef,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
|
|
Loading…
Reference in a new issue