chore: Merge branch dev to main (#3266)

This commit is contained in:
oSumAtrIX 2024-06-02 19:41:49 +02:00 committed by GitHub
commit 792e045e45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
94 changed files with 1012 additions and 254 deletions

View file

@ -1,3 +1,52 @@
# [4.9.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.6...v4.9.0-dev.7) (2024-06-02)
### Features
* **YouTube - Playback speed:** Add option to show speed dialog button in video player ([#3197](https://github.com/ReVanced/revanced-patches/issues/3197)) ([ad00305](https://github.com/ReVanced/revanced-patches/commit/ad00305ff57d5e8041de7375bea7d3ad6f18c4e2))
# [4.9.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.5...v4.9.0-dev.6) (2024-06-02)
### Bug Fixes
* **YouTube - Spoof client:** Restore playback speed menu when spoofing to an iOS client ([95f290f](https://github.com/ReVanced/revanced-patches/commit/95f290f1139cc8679beecac53c623847668f885e))
# [4.9.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.4...v4.9.0-dev.5) (2024-06-01)
### Features
* **YouTube:** Support version `19.12`, `19.13`, `19.14`, `19.15` and `19.16` ([#3239](https://github.com/ReVanced/revanced-patches/issues/3239)) ([99b07e0](https://github.com/ReVanced/revanced-patches/commit/99b07e0e18574668f36bb3c962c8d11222114be4))
# [4.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.3...v4.9.0-dev.4) (2024-06-01)
### Features
* **Messenger:** Add `Hide inbox subtabs` patch ([#3163](https://github.com/ReVanced/revanced-patches/issues/3163)) ([24e4ebd](https://github.com/ReVanced/revanced-patches/commit/24e4ebd77ad0f349b479926bf3983b72c2683496))
# [4.9.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.2...v4.9.0-dev.3) (2024-06-01)
### Features
* **YouTube Music:** Support version `7.03` ([#3272](https://github.com/ReVanced/revanced-patches/issues/3272)) ([d1ceca3](https://github.com/ReVanced/revanced-patches/commit/d1ceca39984f7933b28d81802d04bb3ead327595))
# [4.9.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.9.0-dev.1...v4.9.0-dev.2) (2024-06-01)
### Bug Fixes
* **YouTube - Spoof client:** Allow swipe gestures to enter/exit fullscreen when spoofing with `Android VR` client ([#3259](https://github.com/ReVanced/revanced-patches/issues/3259)) ([5114900](https://github.com/ReVanced/revanced-patches/commit/5114900b1b5572c04ba6759eedab77f0a934b058))
# [4.9.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.8.3...v4.9.0-dev.1) (2024-05-31)
### Features
* **YouTube - Hide layout components:** Disable like / subscribe button glow animation ([#3265](https://github.com/ReVanced/revanced-patches/issues/3265)) ([68d35ea](https://github.com/ReVanced/revanced-patches/commit/68d35eafc15513c23cd5220260023e7ec5b7978a))
## [4.8.3](https://github.com/ReVanced/revanced-patches/compare/v4.8.2...v4.8.3) (2024-05-31) ## [4.8.3](https://github.com/ReVanced/revanced-patches/compare/v4.8.2...v4.8.3) (2024-05-31)

View file

@ -277,20 +277,26 @@ public final class app/revanced/patches/memegenerator/misc/pro/UnlockProVersionP
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/messenger/ads/inbox/patch/HideInboxAdsPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/messenger/inbox/HideInboxAdsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/messenger/ads/inbox/patch/HideInboxAdsPatch; public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxAdsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/messenger/inputfield/patch/DisableSwitchingEmojiToStickerPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/messenger/inbox/HideInboxSubtabsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/patch/DisableSwitchingEmojiToStickerPatch; public static final field INSTANCE Lapp/revanced/patches/messenger/inbox/HideInboxSubtabsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/messenger/inputfield/patch/DisableTypingIndicatorPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/patch/DisableTypingIndicatorPatch; public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableSwitchingEmojiToStickerPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/messenger/inputfield/DisableTypingIndicatorPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
@ -1834,6 +1840,12 @@ public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch :
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/button/PlaybackSpeedButtonPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch; public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -1883,7 +1895,11 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException; public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException;
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstruction$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I

View file

@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 4.8.3 version = 4.9.0-dev.7

View file

@ -1,21 +1,21 @@
package app.revanced.patches.messenger.ads.inbox.patch package app.revanced.patches.messenger.inbox
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.messenger.ads.inbox.fingerprints.LoadInboxAdsFingerprint import app.revanced.patches.messenger.inbox.fingerprints.LoadInboxAdsFingerprint
import app.revanced.util.exception
@Patch( @Patch(
name = "Hide inbox ads", name = "Hide inbox ads",
description = "Hides ads in inbox.", description = "Hides ads in inbox.",
compatiblePackages = [CompatiblePackage("com.facebook.orca")] compatiblePackages = [CompatiblePackage("com.facebook.orca")],
) )
@Suppress("unused") @Suppress("unused")
object HideInboxAdsPatch : BytecodePatch( object HideInboxAdsPatch : BytecodePatch(
setOf(LoadInboxAdsFingerprint) setOf(LoadInboxAdsFingerprint),
) { ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
LoadInboxAdsFingerprint.result?.mutableMethod?.apply { LoadInboxAdsFingerprint.result?.mutableMethod?.apply {
@ -23,4 +23,3 @@ object HideInboxAdsPatch : BytecodePatch(
} ?: throw LoadInboxAdsFingerprint.exception } ?: throw LoadInboxAdsFingerprint.exception
} }
} }

View file

@ -0,0 +1,24 @@
package app.revanced.patches.messenger.inbox
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.messenger.inbox.fingerprints.CreateInboxSubTabsFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide inbox subtabs",
description = "Hides Home and Channels tabs between active now tray and chats.",
compatiblePackages = [CompatiblePackage("com.facebook.orca")],
)
@Suppress("unused")
object HideInboxSubtabsPatch : BytecodePatch(
setOf(CreateInboxSubTabsFingerprint),
) {
// Set InboxSubtabsItemSupplierImplementation boolean attribute to false.
override fun execute(context: BytecodeContext) = CreateInboxSubTabsFingerprint.result?.mutableMethod
?.replaceInstruction(2, "const/4 v0, 0x0")
?: throw CreateInboxSubTabsFingerprint.exception
}

View file

@ -0,0 +1,23 @@
package app.revanced.patches.messenger.inbox.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
internal object CreateInboxSubTabsFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID,
),
customFingerprint = { methodDef, classDef ->
methodDef.name == "run" && classDef.fields.any any@{ field ->
if (field.name != "__redex_internal_original_name") return@any false
(field.initialValue as? StringEncodedValue)?.value == "InboxSubtabsItemSupplierImplementation\$onSubscribe\$1"
}
},
)

View file

@ -1,4 +1,4 @@
package app.revanced.patches.messenger.ads.inbox.fingerprints package app.revanced.patches.messenger.inbox.fingerprints
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
@ -8,11 +8,10 @@ internal object LoadInboxAdsFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
strings = listOf( strings = listOf(
"ads_load_begin", "ads_load_begin",
"inbox_ads_fetch_start" "inbox_ads_fetch_start",
), ),
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
customFingerprint = { methodDef, _ -> customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/facebook/messaging/business/inboxads/plugins/inboxads/itemsupplier/InboxAdsItemSupplierImplementation;" methodDef.definingClass == "Lcom/facebook/messaging/business/inboxads/plugins/inboxads/itemsupplier/InboxAdsItemSupplierImplementation;"
} },
) )

View file

@ -1,6 +1,5 @@
package app.revanced.patches.messenger.inputfield.patch package app.revanced.patches.messenger.inputfield
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
@ -8,16 +7,17 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.messenger.inputfield.fingerprints.SwitchMessangeInputEmojiButtonFingerprint import app.revanced.patches.messenger.inputfield.fingerprints.SwitchMessangeInputEmojiButtonFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch( @Patch(
name = "Disable switching emoji to sticker", name = "Disable switching emoji to sticker",
description = "Disables switching from emoji to sticker search mode in message input field.", description = "Disables switching from emoji to sticker search mode in message input field.",
compatiblePackages = [CompatiblePackage("com.facebook.orca")] compatiblePackages = [CompatiblePackage("com.facebook.orca")],
) )
@Suppress("unused") @Suppress("unused")
object DisableSwitchingEmojiToStickerPatch : BytecodePatch( object DisableSwitchingEmojiToStickerPatch : BytecodePatch(
setOf(SwitchMessangeInputEmojiButtonFingerprint) setOf(SwitchMessangeInputEmojiButtonFingerprint),
) { ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
SwitchMessangeInputEmojiButtonFingerprint.result?.let { SwitchMessangeInputEmojiButtonFingerprint.result?.let {
@ -28,7 +28,7 @@ object DisableSwitchingEmojiToStickerPatch : BytecodePatch(
replaceInstruction( replaceInstruction(
setStringIndex, setStringIndex,
"const-string v$targetRegister, \"expression\"" "const-string v$targetRegister, \"expression\"",
) )
} }
} ?: throw SwitchMessangeInputEmojiButtonFingerprint.exception } ?: throw SwitchMessangeInputEmojiButtonFingerprint.exception

View file

@ -1,22 +1,22 @@
package app.revanced.patches.messenger.inputfield.patch package app.revanced.patches.messenger.inputfield
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.messenger.inputfield.fingerprints.SendTypingIndicatorFingerprint import app.revanced.patches.messenger.inputfield.fingerprints.SendTypingIndicatorFingerprint
import app.revanced.util.exception
@Patch( @Patch(
name = "Disable typing indicator", name = "Disable typing indicator",
description = "Disables the indicator while typing a message.", description = "Disables the indicator while typing a message.",
compatiblePackages = [CompatiblePackage("com.facebook.orca")] compatiblePackages = [CompatiblePackage("com.facebook.orca")],
) )
@Suppress("unused") @Suppress("unused")
object DisableTypingIndicatorPatch : BytecodePatch( object DisableTypingIndicatorPatch : BytecodePatch(
setOf(SendTypingIndicatorFingerprint) setOf(SendTypingIndicatorFingerprint),
){ ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
SendTypingIndicatorFingerprint.result?.mutableMethod?.replaceInstruction(0, "return-void") SendTypingIndicatorFingerprint.result?.mutableMethod?.replaceInstruction(0, "return-void")
?: throw SendTypingIndicatorFingerprint.exception ?: throw SendTypingIndicatorFingerprint.exception

View file

@ -11,7 +11,18 @@ import app.revanced.util.exception
@Patch( @Patch(
name = "Hide music video ads", name = "Hide music video ads",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
],
) )
@Suppress("unused") @Suppress("unused")
object HideMusicVideoAds : BytecodePatch( object HideMusicVideoAds : BytecodePatch(

View file

@ -11,7 +11,18 @@ import com.android.tools.smali.dexlib2.Opcode
@Patch( @Patch(
description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.", description = "Adds more audio codec options. The new audio codecs usually result in better audio quality.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Deprecated("This patch is no longer needed as the feature is now enabled by default.") @Deprecated("This patch is no longer needed as the feature is now enabled by default.")
object CodecsUnlockPatch : BytecodePatch( object CodecsUnlockPatch : BytecodePatch(

View file

@ -11,7 +11,18 @@ import app.revanced.util.exception
@Patch( @Patch(
name = "Enable exclusive audio playback", name = "Enable exclusive audio playback",
description = "Enables the option to play audio without video.", description = "Enables the option to play audio without video.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Suppress("unused") @Suppress("unused")
object EnableExclusiveAudioPlayback : BytecodePatch( object EnableExclusiveAudioPlayback : BytecodePatch(

View file

@ -13,7 +13,18 @@ import app.revanced.patches.music.interaction.permanentrepeat.fingerprints.Repea
@Patch( @Patch(
name = "Permanent repeat", name = "Permanent repeat",
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.", description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
],
use = false use = false
) )
@Suppress("unused") @Suppress("unused")
@ -23,7 +34,7 @@ object PermanentRepeatPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
RepeatTrackFingerprint.result?.let { RepeatTrackFingerprint.result?.let {
val startIndex = it.scanResult.patternScanResult!!.endIndex val startIndex = it.scanResult.patternScanResult!!.endIndex
val repeatIndex = startIndex + 3 val repeatIndex = startIndex + 1
it.mutableMethod.apply { it.mutableMethod.apply {
addInstructionsWithLabels( addInstructionsWithLabels(

View file

@ -6,17 +6,16 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
internal object RepeatTrackFingerprint : MethodFingerprint( internal object RepeatTrackFingerprint : MethodFingerprint(
"V", returnType = "V",
AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("L", "L"), parameters = listOf("L", "L"),
listOf( opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.INVOKE_INTERFACE,
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
Opcode.SGET_OBJECT, Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT, Opcode.MOVE_RESULT,
Opcode.IF_NEZ Opcode.IF_NEZ
) ),
strings = listOf("w_st")
) )

View file

@ -12,7 +12,18 @@ import app.revanced.util.exception
name = "Permanent shuffle", name = "Permanent shuffle",
description = "Permanently remember your shuffle preference " + description = "Permanently remember your shuffle preference " +
"even if the playlist ends or another track is played.", "even if the playlist ends or another track is played.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
],
use = false, use = false,
) )
@Suppress("unused") @Suppress("unused")

View file

@ -13,7 +13,18 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch( @Patch(
name = "Hide category bar", name = "Hide category bar",
description = "Hides the category bar at the top of the homepage.", description = "Hides the category bar at the top of the homepage.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
],
use = false, use = false,
) )
@Suppress("unused") @Suppress("unused")

View file

@ -13,7 +13,18 @@ import app.revanced.util.exception
@Patch( @Patch(
name = "Minimized playback", name = "Minimized playback",
description = "Unlocks options for picture-in-picture and background playback.", description = "Unlocks options for picture-in-picture and background playback.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Suppress("unused") @Suppress("unused")
object MinimizedPlaybackPatch : BytecodePatch( object MinimizedPlaybackPatch : BytecodePatch(

View file

@ -16,7 +16,18 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch( @Patch(
name = "Hide 'Get Music Premium' label", name = "Hide 'Get Music Premium' label",
description = "Hides the \"Get Music Premium\" label from the account menu and settings.", description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Suppress("unused") @Suppress("unused")
object HideGetPremiumPatch : BytecodePatch( object HideGetPremiumPatch : BytecodePatch(

View file

@ -22,7 +22,18 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch( @Patch(
name = "Remove upgrade button", name = "Remove upgrade button",
description = "Removes the upgrade tab from the pivot bar.", description = "Removes the upgrade tab from the pivot bar.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")], compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Suppress("unused") @Suppress("unused")
object RemoveUpgradeButtonPatch : BytecodePatch( object RemoveUpgradeButtonPatch : BytecodePatch(

View file

@ -12,7 +12,18 @@ import app.revanced.patches.music.misc.androidauto.fingerprints.CheckCertificate
@Patch( @Patch(
name = "Bypass certificate checks", name = "Bypass certificate checks",
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.", description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.youtube.music")] compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music",
[
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
]
)
]
) )
@Suppress("unused") @Suppress("unused")
object BypassCertificateChecksPatch : BytecodePatch(setOf(CheckCertificateFingerprint)) { object BypassCertificateChecksPatch : BytecodePatch(setOf(CheckCertificateFingerprint)) {

View file

@ -23,7 +23,18 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
mainActivityOnCreateFingerprint = MusicActivityOnCreateFingerprint, mainActivityOnCreateFingerprint = MusicActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class, integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(CompatiblePackage("com.google.android.apps.youtube.music")), compatiblePackages = setOf(
CompatiblePackage(
"com.google.android.apps.youtube.music",
setOf(
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
)
)
),
fingerprints = setOf( fingerprints = setOf(
ServiceCheckFingerprint, ServiceCheckFingerprint,
GooglePlayUtilityFingerprint, GooglePlayUtilityFingerprint,

View file

@ -11,7 +11,7 @@ import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.Ba
import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.BuildAuthorizationStringFingerprint import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.BuildAuthorizationStringFingerprint
import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.GetUserAgentFingerprint import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.GetUserAgentFingerprint
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Suppress("unused") @Suppress("unused")
@ -68,7 +68,7 @@ object SpoofClientPatch : BaseSpoofClientPatch(
// Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login. // Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login.
// Replace old.reddit.com with ssl.reddit.com to fix this. // Replace old.reddit.com with ssl.reddit.com to fix this.
BuildAuthorizationStringFingerprint.result!!.mutableMethod.apply { BuildAuthorizationStringFingerprint.result!!.mutableMethod.apply {
val index = indexOfFirstInstruction { val index = indexOfFirstInstructionOrThrow {
getReference<StringReference>()?.contains("old.reddit.com") == true getReference<StringReference>()?.contains("old.reddit.com") == true
} }

View file

@ -11,7 +11,7 @@ import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnClearDisplayEventFingerprint
import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnRenderFirstFrameFingerprint import app.revanced.patches.tiktok.interaction.cleardisplay.fingerprints.OnRenderFirstFrameFingerprint
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
@ -34,7 +34,7 @@ object RememberClearDisplayPatch : BytecodePatch(
OnClearDisplayEventFingerprint.result?.mutableMethod?.let { OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
// region Hook the "Clear display" configuration save event to remember the state of clear display. // region Hook the "Clear display" configuration save event to remember the state of clear display.
val isEnabledIndex = it.indexOfFirstInstruction { opcode == Opcode.IGET_BOOLEAN } + 1 val isEnabledIndex = it.indexOfFirstInstructionOrThrow { opcode == Opcode.IGET_BOOLEAN } + 1
val isEnabledRegister = it.getInstruction<Instruction22c>(isEnabledIndex - 1).registerA val isEnabledRegister = it.getInstruction<Instruction22c>(isEnabledIndex - 1).registerA
it.addInstructions( it.addInstructions(

View file

@ -18,7 +18,7 @@ import app.revanced.patches.tiktok.misc.integrations.IntegrationsPatch
import app.revanced.patches.tiktok.misc.settings.SettingsPatch import app.revanced.patches.tiktok.misc.settings.SettingsPatch
import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint import app.revanced.patches.tiktok.misc.settings.fingerprints.SettingsStatusLoadFingerprint
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
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
@ -81,16 +81,16 @@ object DownloadsPatch : BytecodePatch(
}, },
// Change the download path patch. // Change the download path patch.
DownloadPathParentFingerprint to { DownloadPathParentFingerprint to {
val targetIndex = indexOfFirstInstruction { opcode == Opcode.INVOKE_STATIC } val targetIndex = indexOfFirstInstructionOrThrow { opcode == Opcode.INVOKE_STATIC }
val downloadUriMethod = context val downloadUriMethod = context
.toMethodWalker(this) .toMethodWalker(this)
.nextMethod(targetIndex, true) .nextMethod(targetIndex, true)
.getMethod() as MutableMethod .getMethod() as MutableMethod
val firstIndex = downloadUriMethod.indexOfFirstInstruction { val firstIndex = downloadUriMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && ((this as Instruction35c).reference as MethodReference).name == "<init>" opcode == Opcode.INVOKE_DIRECT && ((this as Instruction35c).reference as MethodReference).name == "<init>"
} }
val secondIndex = downloadUriMethod.indexOfFirstInstruction { val secondIndex = downloadUriMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC && ((this as Instruction35c).reference as MethodReference).returnType.contains( opcode == Opcode.INVOKE_STATIC && ((this as Instruction35c).reference as MethodReference).returnType.contains(
"Uri" "Uri"
) )

View file

@ -14,6 +14,7 @@ import app.revanced.patches.tiktok.interaction.speed.fingerprints.SetSpeedFinger
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11x
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -38,7 +39,7 @@ object PlaybackSpeedPatch : BytecodePatch(
SetSpeedFingerprint.result?.let { onVideoSwiped -> SetSpeedFingerprint.result?.let { onVideoSwiped ->
// Remember the playback speed of the current video. // Remember the playback speed of the current video.
GetSpeedFingerprint.result?.mutableMethod?.apply { GetSpeedFingerprint.result?.mutableMethod?.apply {
val injectIndex = indexOfFirstInstruction { getReference<MethodReference>()?.returnType == "F" } + 2 val injectIndex = indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.returnType == "F" } + 2
val register = getInstruction<Instruction11x>(injectIndex - 1).registerA val register = getInstruction<Instruction11x>(injectIndex - 1).registerA
addInstruction( addInstruction(

View file

@ -44,7 +44,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -38,7 +38,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -43,7 +43,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -31,7 +31,12 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -37,7 +37,12 @@ import app.revanced.util.resultOrThrow
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -42,7 +42,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -38,7 +38,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
], ],

View file

@ -44,10 +44,12 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", // 19.12.x has an issue with opening YT using external links, "19.11.43",
// and the app then crashes if double tap to skip forward/back is immediately used. "19.12.41",
// The stack trace shows a call coming from integrations SwipeController, "19.13.37",
// but it may be a bug in YT itself as other target versions do not have this issue. "19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -42,7 +42,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
], ],

View file

@ -41,7 +41,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -13,11 +13,12 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint
import app.revanced.util.exception import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfIdResourceOrThrow import app.revanced.util.indexOfIdResourceOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.Instruction import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction 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.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch( @Patch(
@ -52,6 +53,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],
@ -60,6 +66,10 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
object HideAutoplayButtonPatch : BytecodePatch( object HideAutoplayButtonPatch : BytecodePatch(
setOf(LayoutConstructorFingerprint), setOf(LayoutConstructorFingerprint),
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/HideAutoplayButtonPatch;"
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
@ -67,33 +77,27 @@ object HideAutoplayButtonPatch : BytecodePatch(
SwitchPreference("revanced_hide_autoplay_button"), SwitchPreference("revanced_hide_autoplay_button"),
) )
LayoutConstructorFingerprint.result?.mutableMethod?.apply { LayoutConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val layoutGenMethodInstructions = implementation!!.instructions val constIndex = indexOfIdResourceOrThrow("autonav_toggle")
val constRegister = getInstruction<OneRegisterInstruction>(constIndex).registerA
// resolve the offsets of where to insert the branch instructions and ... // Add a conditional branch around the code that inflates and adds the auto repeat button.
val insertIndex = indexOfIdResourceOrThrow("autonav_preview_stub") val gotoIndex = indexOfFirstInstructionOrThrow(constIndex) {
val parameterTypes = getReference<MethodReference>()?.parameterTypes
// where to branch away opcode == Opcode.INVOKE_VIRTUAL &&
val branchIndex = parameterTypes?.size == 2 &&
layoutGenMethodInstructions.subList(insertIndex + 1, layoutGenMethodInstructions.size - 1) parameterTypes.first() == "Landroid/view/ViewStub;"
.indexOfFirst { } + 1
((it as? ReferenceInstruction)?.reference as? MethodReference)?.name == "addOnLayoutChangeListener"
} + 2
val jumpInstruction = layoutGenMethodInstructions[insertIndex + branchIndex] as Instruction
// can be clobbered because this register is overwritten after the injected code
val clobberRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructionsWithLabels( addInstructionsWithLabels(
insertIndex, constIndex,
""" """
invoke-static {}, Lapp/revanced/integrations/youtube/patches/HideAutoplayButtonPatch;->isButtonShown()Z invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->hideAutoPlayButton()Z
move-result v$clobberRegister move-result v$constRegister
if-eqz v$clobberRegister, :hidden if-nez v$constRegister, :hidden
""", """,
ExternalLabel("hidden", jumpInstruction), ExternalLabel("hidden", getInstruction(gotoIndex)),
) )
} ?: throw LayoutConstructorFingerprint.exception }
} }
} }

View file

@ -42,7 +42,12 @@ import com.android.tools.smali.dexlib2.Opcode
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -19,7 +19,7 @@ import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -56,6 +56,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],
@ -111,7 +116,7 @@ object NavigationButtonsPatch : BytecodePatch(
// Hide navigation button labels. // Hide navigation button labels.
CreatePivotBarFingerprint.result?.mutableMethod?.apply { CreatePivotBarFingerprint.result?.mutableMethod?.apply {
val setTextIndex = indexOfFirstInstruction { val setTextIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "setText" getReference<MethodReference>()?.name == "setText"
} }

View file

@ -46,7 +46,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -40,7 +40,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -43,7 +43,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -36,7 +36,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -36,7 +36,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -23,7 +23,6 @@ import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@ -59,7 +58,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],
@ -102,6 +106,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
SwitchPreference("revanced_hide_expandable_chip"), SwitchPreference("revanced_hide_expandable_chip"),
SwitchPreference("revanced_hide_info_panels"), SwitchPreference("revanced_hide_info_panels"),
SwitchPreference("revanced_hide_join_membership_button"), SwitchPreference("revanced_hide_join_membership_button"),
SwitchPreference("revanced_disable_like_subscribe_glow"),
SwitchPreference("revanced_hide_medical_panels"), SwitchPreference("revanced_hide_medical_panels"),
SwitchPreference("revanced_hide_quick_actions"), SwitchPreference("revanced_hide_quick_actions"),
SwitchPreference("revanced_hide_related_videos"), SwitchPreference("revanced_hide_related_videos"),
@ -163,20 +168,21 @@ object HideLayoutComponentsPatch : BytecodePatch(
// region Mix playlists // region Mix playlists
ParseElementFromBufferFingerprint.resultOrThrow().let { result -> ParseElementFromBufferFingerprint.resultOrThrow().let { result ->
val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex val startIndex = result.scanResult.patternScanResult!!.startIndex
result.mutableMethod.apply { result.mutableMethod.apply {
val conversionContextRegister = val freeRegister = "v0"
getInstruction<TwoRegisterInstruction>(consumeByteBufferIndex - 2).registerA val byteArrayParameter = "p3"
val byteBufferRegister = getInstruction<FiveRegisterInstruction>(consumeByteBufferIndex).registerD val conversionContextRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC } val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC }
addInstructionsWithLabels( addInstructionsWithLabels(
consumeByteBufferIndex, startIndex + 1,
""" """
invoke-static {v$conversionContextRegister, v$byteBufferRegister}, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z invoke-static { v$conversionContextRegister, $byteArrayParameter }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
move-result v0 # Conveniently same register happens to be free. move-result $freeRegister
if-nez v0, :return_empty_component if-nez $freeRegister, :return_empty_component
const/4 $freeRegister, 0x0 # Restore register, required for 19.16
""", """,
ExternalLabel("return_empty_component", returnEmptyComponentInstruction), ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
) )

View file

@ -4,7 +4,11 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
internal object ParseElementFromBufferFingerprint : MethodFingerprint( internal object ParseElementFromBufferFingerprint : MethodFingerprint(
parameters = listOf("L","L","[B", "L","L"), parameters = listOf("L", "L", "[B", "L", "L"),
opcodes = listOf(Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT), opcodes = listOf(
strings = listOf("Failed to parse Element") Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("Failed to parse Element") // String is a partial match.
) )

View file

@ -47,7 +47,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -41,7 +41,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -38,7 +38,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -43,7 +43,12 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -55,6 +55,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -35,7 +35,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -36,7 +36,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -37,7 +37,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -32,7 +32,12 @@ import org.w3c.dom.Element
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -31,7 +31,7 @@ import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAni
import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -66,7 +66,12 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]
@ -142,11 +147,10 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply { TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply {
// Find the instruction for creating the text data object. // Find the instruction for creating the text data object.
val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type
val insertIndex = indexOfFirstInstruction { val insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE && opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == textDataClassType getReference<TypeReference>()?.type == textDataClassType
} }
if (insertIndex < 0) throw PatchException("Could not find data creation instruction")
val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// Find the instruction that sets the span to an instance field. // Find the instruction that sets the span to an instance field.
@ -335,7 +339,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
realTimeUpdateTextViewMethod realTimeUpdateTextViewMethod
).forEach { insertMethod -> ).forEach { insertMethod ->
insertMethod.apply { insertMethod.apply {
val setTextIndex = indexOfFirstInstruction { val setTextIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "setText" getReference<MethodReference>()?.name == "setText"
} }

View file

@ -41,7 +41,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -37,7 +37,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -52,6 +52,11 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -40,7 +40,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -5,7 +5,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
@ -15,7 +14,7 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -46,7 +45,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]
@ -67,12 +71,11 @@ object DisableResumingShortsOnStartupPatch : BytecodePatch(
) )
UserWasInShortsFingerprint.result?.mutableMethod?.apply { UserWasInShortsFingerprint.result?.mutableMethod?.apply {
val listenableInstructionIndex = indexOfFirstInstruction { val listenableInstructionIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_INTERFACE && opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" && getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
getReference<MethodReference>()?.name == "isDone" getReference<MethodReference>()?.name == "isDone"
} }
if (listenableInstructionIndex < 0) throw PatchException("Could not find instruction index")
val originalInstructionRegister = getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC val originalInstructionRegister = getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC
val freeRegister = getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA val freeRegister = getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA

View file

@ -45,7 +45,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
) )
) )
] ]

View file

@ -52,7 +52,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -63,7 +63,12 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -41,7 +41,12 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -36,7 +36,12 @@ import app.revanced.util.exception
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -10,18 +10,15 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.SetPlayerRequestClientTypeFingerprint
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -63,17 +60,29 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],
) )
object SpoofClientPatch : BytecodePatch( object SpoofClientPatch : BytecodePatch(
setOf( setOf(
// Client type spoof.
BuildInitPlaybackRequestFingerprint, BuildInitPlaybackRequestFingerprint,
BuildPlayerRequestURIFingerprint, BuildPlayerRequestURIFingerprint,
SetPlayerRequestClientTypeFingerprint, SetPlayerRequestClientTypeFingerprint,
CreatePlayerRequestBodyFingerprint, CreatePlayerRequestBodyFingerprint,
CreatePlayerRequestBodyWithModelFingerprint, CreatePlayerRequestBodyWithModelFingerprint,
// Player gesture config.
PlayerGestureConfigSyntheticFingerprint,
// Player speed menu item.
CreatePlaybackSpeedMenuItemFingerprint,
), ),
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = private const val INTEGRATIONS_CLASS_DESCRIPTOR =
@ -87,7 +96,7 @@ object SpoofClientPatch : BytecodePatch(
SettingsPatch.PreferenceScreen.MISC.addPreferences( SettingsPatch.PreferenceScreen.MISC.addPreferences(
PreferenceScreen( PreferenceScreen(
key = "revanced_spoof_client_screen", key = "revanced_spoof_client_screen",
sorting = Sorting.UNSORTED, sorting = PreferenceScreen.Sorting.UNSORTED,
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_spoof_client"), SwitchPreference("revanced_spoof_client"),
SwitchPreference("revanced_spoof_client_use_ios"), SwitchPreference("revanced_spoof_client_use_ios"),
@ -162,15 +171,14 @@ object SpoofClientPatch : BytecodePatch(
val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().let { val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().let {
val getClientModelIndex = CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction(it.method) val getClientModelIndex = CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction(it.method)
val instructions = it.mutableMethod.getInstructions()
// The next IPUT_OBJECT instruction after getting the client model is setting the client model field. // The next IPUT_OBJECT instruction after getting the client model is setting the client model field.
instructions.subList( val index = it.mutableMethod.indexOfFirstInstructionOrThrow(getClientModelIndex) {
getClientModelIndex, opcode == Opcode.IPUT_OBJECT
instructions.size, }
).find { instruction ->
instruction.opcode == Opcode.IPUT_OBJECT it.mutableMethod.getInstruction(index).getReference<FieldReference>()
}?.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoClientModelField") ?: throw PatchException("Could not find clientInfoClientModelField")
} }
// endregion // endregion
@ -243,5 +251,56 @@ object SpoofClientPatch : BytecodePatch(
// endregion // endregion
// region Fix player gesture if spoofing to iOS.
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
val downAndOutLandscapeAllowedIndex = endIndex - 3
val downAndOutPortraitAllowedIndex = endIndex - 9
arrayOf(
downAndOutLandscapeAllowedIndex,
downAndOutPortraitAllowedIndex,
).forEach { index ->
val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod)
.nextMethod(index, true)
.getMethod() as MutableMethod
gestureAllowedMethod.apply {
val isAllowedIndex = getInstructions().lastIndex
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
addInstructions(
isAllowedIndex,
"""
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
move-result v$isAllowed
""",
)
}
}
}
// endregion
// Fix playback speed menu item if spoofing to iOS.
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
val shouldCreateMenuIndex = it.scanResult.patternScanResult!!.endIndex
it.mutableMethod.apply {
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
addInstructions(
shouldCreateMenuIndex,
"""
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
move-result v$shouldCreateMenuRegister
""",
)
}
}
// endregion
} }
} }

View file

@ -0,0 +1,22 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("[L", "F"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT, // Return value controls the creation of the playback speed menu item.
Opcode.IF_EQZ, // If the return value is false, the playback speed menu item is not created.
),
)

View file

@ -0,0 +1,49 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"),
opcodes = listOf(
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
Opcode.MOVE_RESULT,
Opcode.CHECK_CAST,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
Opcode.MOVE_RESULT,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID,
),
customFingerprint = { methodDef, classDef ->
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
reference.parameterTypes.isEmpty() &&
reference.returnType == "Z"
}
// This method is always called "a" because this kind of class always has a single method.
methodDef.name == "a" && classDef.methods.count() == 2 &&
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
},
)

View file

@ -52,6 +52,11 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43", "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
), ),
), ),
), ),

View file

@ -12,7 +12,7 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.links.fingerprints.ABUriParserFingerprint import app.revanced.patches.youtube.misc.links.fingerprints.ABUriParserFingerprint
import app.revanced.patches.youtube.misc.links.fingerprints.HTTPUriParserFingerprint import app.revanced.patches.youtube.misc.links.fingerprints.HTTPUriParserFingerprint
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch( @Patch(
@ -38,27 +38,32 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
] "19.12.41",
) "19.13.37",
] "19.14.43",
"19.15.36",
"19.16.39",
],
),
],
) )
@Suppress("unused") @Suppress("unused")
object BypassURLRedirectsPatch : BytecodePatch( object BypassURLRedirectsPatch : BytecodePatch(
setOf(ABUriParserFingerprint, HTTPUriParserFingerprint) setOf(ABUriParserFingerprint, HTTPUriParserFingerprint),
) { ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.MISC.addPreferences( SettingsPatch.PreferenceScreen.MISC.addPreferences(
SwitchPreference("revanced_bypass_url_redirects") SwitchPreference("revanced_bypass_url_redirects"),
) )
mapOf( mapOf(
ABUriParserFingerprint to 7, // Offset to Uri.parse. ABUriParserFingerprint to 7, // Offset to Uri.parse.
HTTPUriParserFingerprint to 0 // Offset to Uri.parse. HTTPUriParserFingerprint to 0, // Offset to Uri.parse.
).map { (fingerprint, offset) -> ).map { (fingerprint, offset) ->
(fingerprint.result ?: throw fingerprint.exception) to offset fingerprint.resultOrThrow() to offset
}.forEach { (result, offset) -> }.forEach { (result, offset) ->
result.mutableMethod.apply { result.mutableMethod.apply {
val insertIndex = result.scanResult.patternScanResult!!.startIndex + offset val insertIndex = result.scanResult.patternScanResult!!.startIndex + offset
@ -69,7 +74,7 @@ object BypassURLRedirectsPatch : BytecodePatch(
"invoke-static {v$uriStringRegister}," + "invoke-static {v$uriStringRegister}," +
"Lapp/revanced/integrations/youtube/patches/BypassURLRedirectsPatch;" + "Lapp/revanced/integrations/youtube/patches/BypassURLRedirectsPatch;" +
"->" + "->" +
"parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;" "parseRedirectUri(Ljava/lang/String;)Landroid/net/Uri;",
) )
} }
} }

View file

@ -42,7 +42,12 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -20,10 +20,14 @@ internal object ABUriParserFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT, Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT, Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST Opcode.CHECK_CAST,
), ),
customFingerprint = { methodDef, classDef -> customFingerprint = custom@{ methodDef, classDef ->
// This method is always called "a" because this kind of class always has a single method. // This method is always called "a" because this kind of class always has a single (non synthetic) method.
methodDef.name == "a" && classDef.methods.count() == 3
} if (methodDef.name != "a") return@custom false
val count = classDef.methods.count()
count == 2 || count == 3
},
) )

View file

@ -10,20 +10,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.fingerprints.* import app.revanced.patches.youtube.misc.litho.filter.fingerprints.*
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction 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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import java.io.Closeable import java.io.Closeable
@Patch( @Patch(
@ -108,11 +106,10 @@ object LithoFilterPatch : BytecodePatch(
val emptyComponentFieldIndex = builderMethodIndex + 2 val emptyComponentFieldIndex = builderMethodIndex + 2
bytesToComponentContextMethod.mutableMethod.apply { bytesToComponentContextMethod.mutableMethod.apply {
val insertHookIndex = indexOfFirstInstruction { val insertHookIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT && opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;" getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;"
} + 1 } + 1
if (insertHookIndex <= 0) throw PatchException("Could not find insert index")
// region Get free registers that this patch uses. // region Get free registers that this patch uses.
// Registers are overwritten right after they are used in this patch, therefore free to clobber. // Registers are overwritten right after they are used in this patch, therefore free to clobber.

View file

@ -13,7 +13,6 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsParentFingerprint
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch import app.revanced.patches.youtube.video.information.VideoInformationPatch
@ -25,6 +24,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
name = "Minimized playback", name = "Minimized playback",
description = "Unlocks options for picture-in-picture and background playback.", description = "Unlocks options for picture-in-picture and background playback.",
dependencies = [ dependencies = [
MinimizedPlaybackResourcePatch::class,
IntegrationsPatch::class, IntegrationsPatch::class,
PlayerTypeHookPatch::class, PlayerTypeHookPatch::class,
VideoInformationPatch::class, VideoInformationPatch::class,
@ -47,7 +47,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]
@ -56,7 +61,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
object MinimizedPlaybackPatch : BytecodePatch( object MinimizedPlaybackPatch : BytecodePatch(
setOf( setOf(
MinimizedPlaybackManagerFingerprint, MinimizedPlaybackManagerFingerprint,
MinimizedPlaybackSettingsParentFingerprint, MinimizedPlaybackSettingsFingerprint,
KidsMinimizedPlaybackPolicyControllerFingerprint KidsMinimizedPlaybackPolicyControllerFingerprint
) )
) { ) {
@ -82,11 +87,6 @@ object MinimizedPlaybackPatch : BytecodePatch(
} ?: throw MinimizedPlaybackManagerFingerprint.exception } ?: throw MinimizedPlaybackManagerFingerprint.exception
// Enable minimized playback option in YouTube settings // Enable minimized playback option in YouTube settings
MinimizedPlaybackSettingsParentFingerprint.result ?: throw MinimizedPlaybackSettingsParentFingerprint.exception
MinimizedPlaybackSettingsFingerprint.resolve(
context,
MinimizedPlaybackSettingsParentFingerprint.result!!.classDef
)
MinimizedPlaybackSettingsFingerprint.result?.apply { MinimizedPlaybackSettingsFingerprint.result?.apply {
val booleanCalls = method.implementation!!.instructions.withIndex() val booleanCalls = method.implementation!!.instructions.withIndex()
.filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" } .filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" }

View file

@ -0,0 +1,17 @@
package app.revanced.patches.youtube.misc.minimizedplayback
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(
dependencies = [ResourceMappingPatch::class],
)
internal object MinimizedPlaybackResourcePatch : ResourcePatch() {
internal var prefBackgroundAndOfflineCategoryId: Long = -1
override fun execute(context: ResourceContext) {
prefBackgroundAndOfflineCategoryId = ResourceMappingPatch["string", "pref_background_and_offline_category"]
}
}

View file

@ -1,11 +1,12 @@
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patches.youtube.misc.minimizedplayback.MinimizedPlaybackResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
internal object MinimizedPlaybackSettingsFingerprint : MethodFingerprint( internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint(
returnType = "Ljava/lang/String;", returnType = "Ljava/lang/String;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(), parameters = listOf(),
@ -16,8 +17,7 @@ internal object MinimizedPlaybackSettingsFingerprint : MethodFingerprint(
Opcode.MOVE_RESULT, Opcode.MOVE_RESULT,
Opcode.IF_EQZ, Opcode.IF_EQZ,
Opcode.IF_NEZ, Opcode.IF_NEZ,
Opcode.GOTO, Opcode.GOTO
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST
), ),
literalSupplier = { MinimizedPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId }
) )

View file

@ -1,15 +0,0 @@
package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Class fingerprint for [MinimizedPlaybackSettingsFingerprint]
*/
internal object MinimizedPlaybackSettingsParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
returnType = "I",
parameters = listOf(),
strings = listOf("BiometricManager", "Failure in canAuthenticate(). FingerprintManager was null.")
)

View file

@ -12,7 +12,7 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.navigation.fingerprints.* import app.revanced.patches.youtube.misc.navigation.fingerprints.*
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -121,7 +121,7 @@ object NavigationBarHookPatch : BytecodePatch(
// Insert before the first ViewGroup method call after inflating, // Insert before the first ViewGroup method call after inflating,
// so this works regardless which layout is used. // so this works regardless which layout is used.
ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply { ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply {
val instructionIndex = indexOfFirstInstruction { val instructionIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setLayoutDirection" opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setLayoutDirection"
} }

View file

@ -43,7 +43,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -47,7 +47,12 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
] ]
) )
] ]

View file

@ -4,13 +4,19 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.video.speed.button.PlaybackSpeedButtonPatch
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPatch import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPatch
@Patch( @Patch(
name = "Playback speed", name = "Playback speed",
description = "Adds options to customize available playback speeds and to remember the last playback speed selected.", description = "Adds options to customize available playback speeds, remember the last playback speed selected " +
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class], "and show a speed dialog button to the video player.",
dependencies = [
PlaybackSpeedButtonPatch::class,
CustomPlaybackSpeedPatch::class,
RememberPlaybackSpeedPatch::class,
],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
"com.google.android.youtube", "com.google.android.youtube",
@ -27,7 +33,12 @@ import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPa
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
], ],
), ),
], ],

View file

@ -0,0 +1,38 @@
package app.revanced.patches.youtube.video.speed.button
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsBytecodePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.speed.custom.CustomPlaybackSpeedPatch
@Patch(
description = "Adds the option to display playback speed dialog button in the video player.",
dependencies = [
PlaybackSpeedButtonResourcePatch::class,
CustomPlaybackSpeedPatch::class,
PlayerControlsBytecodePatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
],
)
@Suppress("unused")
object PlaybackSpeedButtonPatch : BytecodePatch(emptySet()) {
private const val SPEED_BUTTON_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/videoplayer/PlaybackSpeedDialogButton;"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_playback_speed_dialog_button"),
)
PlayerControlsBytecodePatch.initializeControl("$SPEED_BUTTON_CLASS_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$SPEED_BUTTON_CLASS_DESCRIPTOR->changeVisibility(Z)V")
}
}

View file

@ -0,0 +1,25 @@
package app.revanced.patches.youtube.video.speed.button
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
@Patch(
dependencies = [BottomControlsResourcePatch::class],
)
internal object PlaybackSpeedButtonResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.copyResources(
"speedbutton",
ResourceGroup(
"drawable",
"revanced_playback_speed_dialog_button.xml",
),
)
BottomControlsResourcePatch.addControls("speedbutton")
}
}

View file

@ -2,12 +2,12 @@ package app.revanced.patches.youtube.video.videoqualitymenu
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.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.RecyclerViewTreeHookPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.RecyclerViewTreeHookPatch
@ -23,7 +23,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
IntegrationsPatch::class, IntegrationsPatch::class,
RestoreOldVideoQualityMenuResourcePatch::class, RestoreOldVideoQualityMenuResourcePatch::class,
LithoFilterPatch::class, LithoFilterPatch::class,
RecyclerViewTreeHookPatch::class RecyclerViewTreeHookPatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
@ -47,14 +47,19 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.08.36", "19.08.36",
"19.09.38", "19.09.38",
"19.10.39", "19.10.39",
"19.11.43" "19.11.43",
] "19.12.41",
) "19.13.37",
] "19.14.43",
"19.15.36",
"19.16.39",
],
),
],
) )
@Suppress("unused") @Suppress("unused")
object RestoreOldVideoQualityMenuPatch : BytecodePatch( object RestoreOldVideoQualityMenuPatch : BytecodePatch(
setOf(VideoQualityMenuViewInflateFingerprint, VideoQualityMenuOptionsFingerprint) setOf(VideoQualityMenuViewInflateFingerprint, VideoQualityMenuOptionsFingerprint),
) { ) {
private const val FILTER_CLASS_DESCRIPTOR = private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/components/VideoQualityMenuFilterPatch;" "Lapp/revanced/integrations/youtube/patches/components/VideoQualityMenuFilterPatch;"
@ -76,30 +81,29 @@ object RestoreOldVideoQualityMenuPatch : BytecodePatch(
checkCastIndex + 1, checkCastIndex + 1,
"invoke-static { v$listViewRegister }, " + "invoke-static { v$listViewRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->" + "$INTEGRATIONS_CLASS_DESCRIPTOR->" +
"showOldVideoQualityMenu(Landroid/widget/ListView;)V" "showOldVideoQualityMenu(Landroid/widget/ListView;)V",
) )
} }
} }
// Force YT to add the 'advanced' quality menu for Shorts. // Force YT to add the 'advanced' quality menu for Shorts.
VideoQualityMenuOptionsFingerprint.resultOrThrow().let { VideoQualityMenuOptionsFingerprint.resultOrThrow().let {
val result = it.scanResult.patternScanResult!! val scanResult = it.scanResult.patternScanResult!!
val startIndex = result.startIndex val startIndex = scanResult.startIndex
val endIndex = result.endIndex if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex")
val insertIndex = scanResult.endIndex
it.mutableMethod.apply { it.mutableMethod.apply {
val freeRegister = getInstruction<OneRegisterInstruction>(startIndex).registerA val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// A condition controls whether to show the three or four items quality menu. // A condition controls whether to show the three or four items quality menu.
// Force the four items quality menu to make the "Advanced" item visible, necessary for the patch. // Force the four items quality menu to make the "Advanced" item visible, necessary for the patch.
addInstructionsWithLabels( addInstructions(
startIndex + 1, insertIndex,
""" """
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation()Z invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z
move-result v$freeRegister move-result v$register
if-nez v$freeRegister, :includeAdvancedMenu
""", """,
ExternalLabel("includeAdvancedMenu", getInstruction(endIndex))
) )
} }
} }

View file

@ -10,13 +10,11 @@ internal object VideoQualityMenuOptionsFingerprint : LiteralValueFingerprint(
parameters = listOf("Landroid/content/Context", "L", "L"), parameters = listOf("Landroid/content/Context", "L", "L"),
returnType = "[L", returnType = "[L",
opcodes = listOf( opcodes = listOf(
Opcode.IF_EQZ, // Check if advanced menu should be shown. Opcode.CONST_4, // First instruction of method.
Opcode.NEW_ARRAY, Opcode.CONST_4,
Opcode.APUT_OBJECT, Opcode.IF_EQZ,
Opcode.APUT_OBJECT, Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu.
Opcode.APUT_OBJECT, Opcode.IF_NEZ
Opcode.RETURN_OBJECT,
Opcode.CONST_4 // Advanced menu code path.
), ),
literalSupplier = { RestoreOldVideoQualityMenuResourcePatch.videoQualityQuickMenuAdvancedMenuDescription } literalSupplier = { RestoreOldVideoQualityMenuResourcePatch.videoQualityQuickMenuAdvancedMenuDescription }
) )

View file

@ -143,8 +143,41 @@ inline fun <reified T : Reference> Instruction.getReference() = (this as? Refere
* @param predicate The predicate to match. * @param predicate The predicate to match.
* @return The index of the first [Instruction] that matches the predicate. * @return The index of the first [Instruction] that matches the predicate.
*/ */
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = // TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes.
this.implementation!!.instructions.indexOfFirst(predicate) @Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)"))
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
*
* @param startIndex Optional starting index to start searching from.
* @return -1 if the instruction is not found.
* @see indexOfFirstInstructionOrThrow
*/
fun Method.indexOfFirstInstruction(startIndex: Int = 0, predicate: Instruction.() -> Boolean): Int {
val index = this.implementation!!.instructions.drop(startIndex).indexOfFirst(predicate)
return if (index >= 0) {
startIndex + index
} else {
-1
}
}
/**
* Get the index of the first [Instruction] that matches the predicate, starting from [startIndex].
*
* @return the index of the instruction
* @throws PatchException
* @see indexOfFirstInstruction
*/
fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, predicate: Instruction.() -> Boolean): Int {
val index = indexOfFirstInstruction(startIndex, predicate)
if (index < 0) {
throw PatchException("Could not find instruction index")
}
return index
}
/** /**
* Return the resolved methods of [MethodFingerprint]s early. * Return the resolved methods of [MethodFingerprint]s early.

View file

@ -91,6 +91,9 @@
<string name="revanced_debug_toast_on_error_user_dialog_message">Turning off error toasts hides all ReVanced error notifications.\n\nYou will not be notified of any unexpected events.</string> <string name="revanced_debug_toast_on_error_user_dialog_message">Turning off error toasts hides all ReVanced error notifications.\n\nYou will not be notified of any unexpected events.</string>
</patch> </patch>
<patch id="layout.hide.general.HideLayoutComponentsPatch"> <patch id="layout.hide.general.HideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">Disable like / subscribe button glow</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">Like and subscribe button will not glow when mentioned</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">Like and subscribe button will glow when mentioned</string>
<string name="revanced_hide_gray_separator_title">Hide gray separator</string> <string name="revanced_hide_gray_separator_title">Hide gray separator</string>
<string name="revanced_hide_gray_separator_summary_on">Gray separators are hidden</string> <string name="revanced_hide_gray_separator_summary_on">Gray separators are hidden</string>
<string name="revanced_hide_gray_separator_summary_off">Gray separators are shown</string> <string name="revanced_hide_gray_separator_summary_off">Gray separators are shown</string>
@ -1062,6 +1065,11 @@
<string name="revanced_remember_video_quality_wifi">wifi</string> <string name="revanced_remember_video_quality_wifi">wifi</string>
<string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string> <string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string>
</patch> </patch>
<patch id="video.speed.button.PlaybackSpeedButtonPatch">
<string name="revanced_playback_speed_dialog_button_title">Show speed dialog button</string>
<string name="revanced_playback_speed_dialog_button_summary_on">Button is shown</string>
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch"> <patch id="video.speed.custom.CustomPlaybackSpeedPatch">
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string> <string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string> <string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
@ -1093,8 +1101,8 @@
<string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string> <string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string> <string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
<string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string> <string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
<string name="revanced_spoof_client_use_ios_summary_on">Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Speed menu is missing\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices</string> <string name="revanced_spoof_client_use_ios_summary_on">Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices</string>
<string name="revanced_spoof_client_use_ios_summary_off">Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Swipe to enter/exit fullscreen does not work\n• Kids videos do not playback\n• Paused videos can randomly resume</string> <string name="revanced_spoof_client_use_ios_summary_off">Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Kids videos do not playback\n• Paused videos can randomly resume</string>
<string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string> <string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string> <string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
</patch> </patch>

View file

@ -0,0 +1,30 @@
<!--
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/slow_motion_video/materialsymbolsoutlined/slow_motion_video_wght200gradN25_24px.xml
The icon has been resized
Copyright 2022 Google
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#FFFFFF"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 6.0798492,16.70271 Q 5.3667836,15.77782 4.9972798,14.87317 4.6275665,13.96852 4.5,12.83942 h 0.7949215 q 0.1275665,0.9249 0.4677271,1.7656 0.3401706,0.8407 0.8797497,1.53346 z M 4.5,11.16058 Q 4.6373409,10.04469 5.002167,9.14154 5.3667836,8.2386 6.0798492,7.3137 L 6.6423983,7.87793 Q 6.1028192,8.5707 5.7626486,9.40308 5.422488,10.23568 5.2949215,11.16058 Z M 11.110466,19.5 Q 9.8992727,19.32286 9.0431213,18.95346 8.1871793,18.58406 7.2572507,17.89299 l 0.5627586,-0.57382 q 0.6818173,0.52458 1.5126748,0.89527 0.8308479,0.37047 1.7777819,0.49836 z M 7.8625281,6.66442 7.2899951,6.09059 Q 8.2197042,5.39953 9.0758557,5.03823 9.9320071,4.67714 11.152995,4.5 V 5.2872 Q 10.19861,5.41509 9.3677624,5.77746 8.5371243,6.13983 7.8625281,6.66442 Z M 10.32873,14.97101 V 9.02899 L 14.953488,12 Z M 12.827456,19.5 v -0.7872 q 2.53571,-0.36237 4.211668,-2.26097 1.675948,-1.89882 1.675948,-4.45183 0,-2.55301 -1.675948,-4.45183 Q 15.363166,5.64957 12.827456,5.2872 V 4.5 Q 15.70589,4.80844 17.60294,6.95069 19.5,9.09294 19.5,12 q 0,2.8964 -1.89706,5.04398 -1.89705,2.14758 -4.775484,2.45602 z"/>
</vector>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_playback_speed_dialog_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_playback_speed_dialog_button" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
</android.support.constraint.ConstraintLayout>