feat(YouTube): Support versions 19.25 and 19.34 (#3629)

Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
Zain 2024-10-19 19:26:39 +07:00 committed by GitHub
parent c9c7f01a2f
commit 049e7f0813
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
141 changed files with 2284 additions and 2261 deletions

View File

@ -1850,10 +1850,6 @@ public final class app/revanced/patches/youtube/layout/startpage/ChangeStartPage
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/layout/startpage/fingerprints/StartActivityFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startpage/fingerprints/StartActivityFingerprint;
}
public final class app/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/DisableResumingShortsOnStartupPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -2203,12 +2199,27 @@ public final class app/revanced/util/BytecodeUtilsKt {
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 findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
public static final fun forEachLiteralValueInstruction (Lapp/revanced/patcher/data/BytecodeContext;JLkotlin/jvm/functions/Function2;)V
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;ILcom/android/tools/smali/dexlib2/Opcode;)I
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;Lcom/android/tools/smali/dexlib2/Opcode;)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;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)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;ILcom/android/tools/smali/dexlib2/Opcode;)I
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I
public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)I
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)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 indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
public static final fun indexOfFirstInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
public static synthetic fun indexOfFirstInstructionReversed$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;)I
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/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 indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I

View File

@ -13,7 +13,7 @@ import app.revanced.patches.tiktok.shared.fingerprints.OnRenderFirstFrameFingerp
import app.revanced.util.exception
import app.revanced.util.indexOfFirstInstructionOrThrow
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.TwoRegisterInstruction
@Patch(
name = "Remember clear display",
@ -34,8 +34,8 @@ object RememberClearDisplayPatch : BytecodePatch(
OnClearDisplayEventFingerprint.result?.mutableMethod?.let {
// region Hook the "Clear display" configuration save event to remember the state of clear display.
val isEnabledIndex = it.indexOfFirstInstructionOrThrow { opcode == Opcode.IGET_BOOLEAN } + 1
val isEnabledRegister = it.getInstruction<Instruction22c>(isEnabledIndex - 1).registerA
val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1
val isEnabledRegister = it.getInstruction<TwoRegisterInstruction>(isEnabledIndex - 1).registerA
it.addInstructions(
isEnabledIndex,

View File

@ -26,30 +26,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -20,30 +20,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,30 +25,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,24 +19,11 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,24 +25,11 @@ import app.revanced.util.resultOrThrow
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import app.revanced.util.alsoResolve
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -27,27 +27,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
// 18.38.44 patches but crashes on startup.
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -4,46 +4,37 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
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.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DoubleSpeedSeekNoticeFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardLegacyFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardGestureFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.DisableFastForwardNoticeFingerprint
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SlideToSeekFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.getReference
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.reference.MethodReference
@Patch(
name = "Enable slide to seek",
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with tapping or double tapping the video player overlay.",
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with the video player overlay, such as missing buttons and ignored taps and double taps.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
],
@ -53,10 +44,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
object EnableSlideToSeekPatch : BytecodePatch(
setOf(
SlideToSeekFingerprint,
DoubleSpeedSeekNoticeFingerprint
DisableFastForwardLegacyFingerprint,
DisableFastForwardGestureFingerprint,
DisableFastForwardNoticeFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/SlideToSeekPatch;"
private const val INTEGRATIONS_METHOD_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/SlideToSeekPatch;->isSlideToSeekDisabled(Z)Z"
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -65,27 +59,74 @@ object EnableSlideToSeekPatch : BytecodePatch(
SwitchPreference("revanced_slide_to_seek")
)
arrayOf(
// Restore the behaviour to slide to seek.
SlideToSeekFingerprint,
// Disable the double speed seek notice.
DoubleSpeedSeekNoticeFingerprint
).map {
it.result ?: throw it.exception
}.forEach {
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
var modifiedMethods = false
// Restore the behaviour to slide to seek.
SlideToSeekFingerprint.resultOrThrow().let {
val checkIndex = it.scanResult.patternScanResult!!.startIndex
val checkReference = it.mutableMethod
.getInstruction(checkIndex).getReference<MethodReference>()!!
// A/B check method was only called on this class.
it.mutableClass.methods.forEach { method ->
method.implementation!!.instructions.forEachIndexed { index, instruction ->
if (instruction.opcode == Opcode.INVOKE_VIRTUAL &&
instruction.getReference<MethodReference>() == checkReference
) {
method.apply {
val targetRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructions(
index + 2,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
modifiedMethods = true
}
}
}
}
if (!modifiedMethods) throw PatchException("Could not find methods to modify")
// Disable the double speed seek gesture.
if (!VersionCheckPatch.is_19_17_or_greater) {
DisableFastForwardLegacyFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val isEnabledRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
val targetRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isSlideToSeekDisabled()Z
move-result v$isEnabledRegister
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
}
} else {
arrayOf(
DisableFastForwardGestureFingerprint,
DisableFastForwardNoticeFingerprint
).forEach { it.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_METHOD_DESCRIPTOR
move-result v$targetRegister
"""
)
}
}}
}
}
}

View File

@ -0,0 +1,21 @@
package app.revanced.patches.youtube.interaction.seekbar.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 DisableFastForwardGestureFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT
),
customFingerprint = { methodDef, classDef ->
methodDef.implementation!!.instructions.count() > 30 &&
classDef.type.endsWith("/NextGenWatchLayout;")
}
)

View File

@ -3,7 +3,7 @@ package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object DoubleSpeedSeekNoticeFingerprint : LiteralValueFingerprint(
internal object DisableFastForwardLegacyFingerprint : LiteralValueFingerprint(
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(Opcode.MOVE_RESULT),

View File

@ -0,0 +1,19 @@
package app.revanced.patches.youtube.interaction.seekbar.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 DisableFastForwardNoticeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = emptyList(),
opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT
),
strings = listOf("Failed to easy seek haptics vibrate")
)

View File

@ -2,10 +2,9 @@ package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
internal object SeekbarTappingFingerprint : MethodFingerprint(
returnType = "Z",
@ -21,14 +20,6 @@ internal object SeekbarTappingFingerprint : MethodFingerprint(
customFingerprint = custom@{ methodDef, _ ->
if (methodDef.name != "onTouchEvent") return@custom false
methodDef.implementation!!.instructions.any { instruction ->
if (instruction.opcode != Opcode.CONST) return@any false
val literal = (instruction as NarrowLiteralInstruction).narrowLiteral
// onTouchEvent method contains a CONST instruction
// with this literal making it unique with the rest of the properties of this fingerprint.
literal == Integer.MAX_VALUE
}
methodDef.containsWideLiteralInstructionValue(Integer.MAX_VALUE.toLong())
}
)

View File

@ -1,11 +1,19 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object SlideToSeekFingerprint : LiteralValueFingerprint(
returnType = "Z",
parameters = emptyList(),
opcodes = listOf(Opcode.MOVE_RESULT),
literalSupplier = { 45411329 }
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/view/View;", "F"),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.GOTO_16
),
literalSupplier = { 67108864 }
)

View File

@ -1,11 +1,8 @@
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object SwipingUpGestureParentFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf(),
literalSupplier = { 45379021 }

View File

@ -26,30 +26,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import app.revanced.util.exception
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
],

View File

@ -23,30 +23,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -34,30 +34,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import com.android.tools.smali.dexlib2.Opcode
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -37,30 +37,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -1,20 +1,23 @@
package app.revanced.patches.youtube.layout.buttons.player.hide
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
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.layout.buttons.player.hide.HidePlayerButtonsPatch.ParameterOffsets.HAS_NEXT
import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsPatch.ParameterOffsets.HAS_PREVIOUS
import app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints.PlayerControlsVisibilityModelFingerprint
import app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints.PlayerControlsPreviousNextOverlayTouchFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Hide player buttons",
@ -22,43 +25,25 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class
AddResourcesPatch::class,
HidePlayerButtonsResourcePatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
)
@Suppress("unused")
object HidePlayerButtonsPatch : BytecodePatch(
setOf(PlayerControlsVisibilityModelFingerprint)
setOf(PlayerControlsPreviousNextOverlayTouchFingerprint)
) {
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -67,29 +52,23 @@ object HidePlayerButtonsPatch : BytecodePatch(
SwitchPreference("revanced_hide_player_buttons")
)
PlayerControlsVisibilityModelFingerprint.result?.apply {
val callIndex = scanResult.patternScanResult!!.endIndex
val callInstruction = mutableMethod.getInstruction<Instruction3rc>(callIndex)
// overriding this parameter register hides the previous and next buttons
val hasNextParameterRegister = callInstruction.startRegister + HAS_NEXT
val hasPreviousParameterRegister = callInstruction.startRegister + HAS_PREVIOUS
mutableMethod.addInstructions(
callIndex,
"""
invoke-static { v$hasNextParameterRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;->previousOrNextButtonIsVisible(Z)Z
move-result v$hasNextParameterRegister
invoke-static { v$hasPreviousParameterRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;->previousOrNextButtonIsVisible(Z)Z
move-result v$hasPreviousParameterRegister
"""
PlayerControlsPreviousNextOverlayTouchFingerprint.resultOrThrow().mutableMethod.apply {
val resourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea
)
} ?: throw PlayerControlsVisibilityModelFingerprint.exception
val insertIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
opcode == Opcode.INVOKE_STATIC
&& getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Landroid/view/View;"
}
private object ParameterOffsets {
const val HAS_NEXT = 5
const val HAS_PREVIOUS = 6
val viewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, Lapp/revanced/integrations/youtube/patches/HidePlayerButtonsPatch;" +
"->hidePreviousNextButtons(Landroid/view/View;)V"
)
}
}
}

View File

@ -0,0 +1,24 @@
package app.revanced.patches.youtube.layout.buttons.player.hide
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 HidePlayerButtonsResourcePatch : ResourcePatch() {
var playerControlPreviousButtonTouchArea = -1L
var playerControlNextButtonTouchArea = -1L
override fun execute(context: ResourceContext) {
playerControlPreviousButtonTouchArea = ResourceMappingPatch[
"id",
"player_control_previous_button_touch_area"
]
playerControlNextButtonTouchArea = ResourceMappingPatch[
"id",
"player_control_next_button_touch_area"
]
}
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.buttons.player.hide.HidePlayerButtonsResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlayerControlsPreviousNextOverlayTouchFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
strings = listOf("1.0x"),
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionValue(
HidePlayerButtonsResourcePatch.playerControlPreviousButtonTouchArea
) && methodDef.containsWideLiteralInstructionValue(
HidePlayerButtonsResourcePatch.playerControlNextButtonTouchArea
)
}
)

View File

@ -1,9 +0,0 @@
package app.revanced.patches.youtube.layout.buttons.player.hide.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerControlsVisibilityModelFingerprint : MethodFingerprint(
opcodes = listOf(Opcode.INVOKE_DIRECT_RANGE),
strings = listOf("Missing required properties:", "hasNext", "hasPrevious")
)

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -25,30 +25,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,30 +18,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,29 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -24,6 +24,7 @@ import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.alsoResolve
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -45,30 +46,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
@ -202,9 +184,10 @@ object HideLayoutComponentsPatch : BytecodePatch(
// region Watermark (legacy code for old versions of YouTube)
ShowWatermarkFingerprint.also {
it.resolve(context, PlayerOverlayFingerprint.resultOrThrow().classDef)
}.resultOrThrow().mutableMethod.apply {
ShowWatermarkFingerprint.alsoResolve(
context,
PlayerOverlayFingerprint
).mutableMethod.apply {
val index = implementation!!.instructions.size - 5
removeInstruction(index)

View File

@ -13,6 +13,7 @@ import app.revanced.patches.youtube.layout.hide.infocards.fingerprints.Infocards
import app.revanced.patches.youtube.layout.hide.infocards.fingerprints.InfocardsMethodCallFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.util.alsoResolve
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
@ -29,30 +30,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -68,9 +50,7 @@ object HideInfoCardsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/components/HideInfoCardsFilterPatch;"
override fun execute(context: BytecodeContext) {
InfocardsIncognitoFingerprint.also {
it.resolve(context, InfocardsIncognitoParentFingerprint.result!!.classDef)
}.result!!.mutableMethod.apply {
InfocardsIncognitoFingerprint.alsoResolve(context, InfocardsIncognitoParentFingerprint).mutableMethod.apply {
val invokeInstructionIndex = implementation!!.instructions.indexOfFirst {
it.opcode.ordinal == Opcode.INVOKE_VIRTUAL.ordinal &&
((it as ReferenceInstruction).reference.toString() == "Landroid/view/View;->setVisibility(I)V")

View File

@ -23,30 +23,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -23,27 +23,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
// 18.43 is the earliest target this patch works.
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.SeekbarFingerprint
import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.util.alsoResolve
@Patch(
name = "Hide seekbar",
@ -25,30 +26,11 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -65,9 +47,10 @@ object HideSeekbarPatch : BytecodePatch(
SwitchPreference("revanced_hide_seekbar_thumbnail")
)
SeekbarFingerprint.result!!.let {
SeekbarOnDrawFingerprint.apply { resolve(context, it.mutableClass) }
}.result!!.mutableMethod.addInstructionsWithLabels(
SeekbarOnDrawFingerprint.alsoResolve(
context,
SeekbarFingerprint
).mutableMethod.addInstructionsWithLabels(
0,
"""
const/4 v0, 0x0

View File

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.layout.hide.shorts
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
@ -13,10 +14,15 @@ import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch
import app.revanced.util.exception
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.alsoResolve
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
import app.revanced.util.indexOfIdResourceOrThrow
import app.revanced.util.injectHideViewCall
import app.revanced.util.resultOrThrow
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
@ -32,35 +38,17 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
HideShortsComponentsResourcePatch::class,
ResourceMappingPatch::class,
NavigationBarHookPatch::class,
VersionCheckPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
@ -70,7 +58,8 @@ object HideShortsComponentsPatch : BytecodePatch(
setOf(
CreateShortsButtonsFingerprint,
ReelConstructorFingerprint,
BottomNavigationBarFingerprint,
ShortsBottomBarContainerFingerprint,
LegacyRenderBottomNavigationBarParentFingerprint,
RenderBottomNavigationBarParentFingerprint,
SetPivotBarVisibilityParentFingerprint,
),
@ -95,7 +84,7 @@ object HideShortsComponentsPatch : BytecodePatch(
// region Hide the Shorts shelf.
// This patch point is not present in 19.03.x and greater.
// If 19.02.x and lower is dropped, then this section of code and the fingerprint should be removed.
if (!VersionCheckPatch.is_19_03_or_greater) {
ReelConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
@ -108,16 +97,17 @@ object HideShortsComponentsPatch : BytecodePatch(
"hideShortsShelf",
)
}
} // Do not throw an exception if not resolved.
}
}
// endregion
// region Hide the Shorts buttons in older versions of YouTube.
// Some Shorts buttons are views, hide them by setting their visibility to GONE.
CreateShortsButtonsFingerprint.result?.let {
CreateShortsButtonsFingerprint.resultOrThrow().let {
ShortsButtons.entries.forEach { button -> button.injectHideCall(it.mutableMethod) }
} ?: throw CreateShortsButtonsFingerprint.exception
}
// endregion
@ -125,54 +115,68 @@ object HideShortsComponentsPatch : BytecodePatch(
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
context.forEachLiteralValueInstruction(
HideShortsComponentsResourcePatch.reelPlayerRightPivotV2Size
) { literalInstructionIndex ->
val targetIndex = indexOfFirstInstructionOrThrow(literalInstructionIndex) {
getReference<MethodReference>()?.name == "getDimensionPixelSize"
} + 1
val sizeRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(targetIndex + 1, """
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
move-result v$sizeRegister
"""
)
}
// endregion
// region Hide the navigation bar.
// Hook to get the pivotBar view.
SetPivotBarVisibilityParentFingerprint.result?.let {
if (!SetPivotBarVisibilityFingerprint.resolve(context, it.classDef)) {
throw SetPivotBarVisibilityFingerprint.exception
}
SetPivotBarVisibilityFingerprint.result!!.let { result ->
SetPivotBarVisibilityFingerprint.alsoResolve(
context,
SetPivotBarVisibilityParentFingerprint
).let { result->
result.mutableMethod.apply {
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val viewRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
addInstruction(
insertIndex,
"sput-object v$viewRegister, $FILTER_CLASS_DESCRIPTOR->pivotBar:" +
"Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;",
addInstruction(insertIndex, "invoke-static {v$viewRegister}," +
" $FILTER_CLASS_DESCRIPTOR->setNavigationBar(Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;)V"
)
}
}
} ?: throw SetPivotBarVisibilityParentFingerprint.exception
// Hook to hide the navigation bar when Shorts are being played.
RenderBottomNavigationBarParentFingerprint.result?.let {
if (!RenderBottomNavigationBarFingerprint.resolve(context, it.classDef)) {
throw RenderBottomNavigationBarFingerprint.exception
}
// Hook to hide the shared navigation bar when the Shorts player is opened.
RenderBottomNavigationBarFingerprint.alsoResolve(
context,
if (VersionCheckPatch.is_19_41_or_greater)
RenderBottomNavigationBarParentFingerprint
else
LegacyRenderBottomNavigationBarParentFingerprint
).mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar(Ljava/lang/String;)V"
)
RenderBottomNavigationBarFingerprint.result!!.mutableMethod.apply {
addInstruction(0, "invoke-static { }, $FILTER_CLASS_DESCRIPTOR->hideNavigationBar()V")
}
} ?: throw RenderBottomNavigationBarParentFingerprint.exception
// Hide the bottom bar container of the Shorts player.
ShortsBottomBarContainerFingerprint.resultOrThrow().mutableMethod.apply {
val resourceIndex = indexOfFirstWideLiteralInstructionValue(HideShortsComponentsResourcePatch.bottomBarContainer)
// Required to prevent a black bar from appearing at the bottom of the screen.
BottomNavigationBarFingerprint.result?.let {
it.mutableMethod.apply {
val moveResultIndex = it.scanResult.patternScanResult!!.startIndex + 2
val viewRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
val insertIndex = moveResultIndex + 1
val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
getReference<MethodReference>()?.name == "getHeight"
} + 1
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $FILTER_CLASS_DESCRIPTOR->" +
"hideNavigationBar(Landroid/view/View;)Landroid/view/View;",
val heightRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(targetIndex + 1, """
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
move-result v$heightRegister
"""
)
}
} ?: throw BottomNavigationBarFingerprint.exception
// endregion
}
@ -187,14 +191,12 @@ object HideShortsComponentsPatch : BytecodePatch(
fun injectHideCall(method: MutableMethod) {
val referencedIndex = method.indexOfIdResourceOrThrow(resourceName)
val instruction = method.implementation!!.instructions
.subList(referencedIndex, referencedIndex + 20)
.first {
it.opcode == Opcode.INVOKE_VIRTUAL && it.getReference<MethodReference>()?.name == "setId"
val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setId"
}
val setIdIndex = instruction.location.index
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
}
}

View File

@ -8,13 +8,23 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.findElementByAttributeValueOrThrow
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class])
@Patch(
dependencies = [
SettingsPatch::class,
ResourceMappingPatch::class,
AddResourcesPatch::class,
VersionCheckPatch::class
]
)
object HideShortsComponentsResourcePatch : ResourcePatch() {
internal var reelMultipleItemShelfId = -1L
internal var reelPlayerRightCellButtonHeight = -1L
internal var bottomBarContainer = -1L
internal var reelPlayerRightPivotV2Size = -1L
override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class)
@ -86,14 +96,21 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
"reel_player_right_cell_button_height",
]
// Resource not present in new versions of the app.
try {
ResourceMappingPatch[
bottomBarContainer = ResourceMappingPatch[
"id",
"bottom_bar_container"
]
reelPlayerRightPivotV2Size = ResourceMappingPatch[
"dimen",
"reel_player_right_pivot_v2_size"
]
if (!VersionCheckPatch.is_19_03_or_greater) {
reelMultipleItemShelfId = ResourceMappingPatch[
"dimen",
"reel_player_right_cell_button_height",
]
} catch (e: NoSuchElementException) {
return
}.also { reelPlayerRightCellButtonHeight = it }
}
}
}

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.layout.hide.shorts.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 BottomNavigationBarFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/os/Bundle;"),
opcodes = listOf(
Opcode.CONST, // R.id.app_engagement_panel_wrapper
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
),
strings = listOf(
"ReelWatchPaneFragmentViewModelKey"
),
)

View File

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object LegacyRenderBottomNavigationBarParentFingerprint : MethodFingerprint(
parameters = listOf(
"I",
"I",
"L",
"L",
"J",
"L",
),
strings = listOf("aa")
)

View File

@ -1,19 +1,13 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object ReelConstructorFingerprint : MethodFingerprint(
internal object ReelConstructorFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(Opcode.INVOKE_VIRTUAL),
customFingerprint = { methodDef, _ ->
// Cannot use LiteralValueFingerprint, because the resource id may not be present.
val reelMultipleItemShelfId = HideShortsComponentsResourcePatch.reelMultipleItemShelfId
reelMultipleItemShelfId != -1L
&& methodDef.containsWideLiteralInstructionValue(reelMultipleItemShelfId)
}
literalSupplier = { HideShortsComponentsResourcePatch.reelMultipleItemShelfId }
)

View File

@ -4,6 +4,8 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object RenderBottomNavigationBarFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Ljava/lang/String;"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.MONITOR_ENTER,

View File

@ -1,8 +1,23 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Identical to [LegacyRenderBottomNavigationBarParentFingerprint]
* except this has an extra parameter.
*/
internal object RenderBottomNavigationBarParentFingerprint : MethodFingerprint(
parameters = listOf("I", "I", "L", "L", "J", "L"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(
"I",
"I",
"L", // ReelWatchEndpointOuterClass
"L",
"J",
"Ljava/lang/String;",
"L"
),
strings = listOf("aa")
)

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.hide.shorts.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ShortsBottomBarContainerFingerprint : LiteralValueFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/os/Bundle;"),
strings = listOf(
"r_pfvc"
),
literalSupplier = { HideShortsComponentsResourcePatch.bottomBarContainer }
)

View File

@ -18,29 +18,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -19,29 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -1,17 +1,20 @@
@file:Suppress("SpellCheckingInspection")
package app.revanced.patches.youtube.layout.miniplayer
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
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.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
@ -26,6 +29,7 @@ import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.sc
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerMinimumSizeFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint
@ -42,30 +46,32 @@ import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayer
import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.alsoResolve
import app.revanced.util.findOpcodeIndicesReversed
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.patch.LiteralValueFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
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.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
// YT uses "Miniplayer" without a space between 'mini' and 'player: https://support.google.com/youtube/answer/9162927.
// YT uses "Miniplayer" without a space between 'mini' and 'player': https://support.google.com/youtube/answer/9162927.
@Patch(
name = "Miniplayer",
description = "Adds options to change the in app minimized player, " +
"and if patching target 19.16+ adds options to use modern miniplayers.",
description = "Adds options to change the in app minimized player. " +
"Patching target 19.16+ adds modern miniplayers.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
@ -75,32 +81,28 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
// 19.14 is left out, as it has incomplete miniplayer code and missing some UI resources.
// It's simpler to not bother with supporting this single old version.
// 19.15 has a different code for handling sub title texts,
// and also probably not worth making changes just to support this single old version.
"19.16.39" // Earliest supported version with modern miniplayers.
// 19.14.43 // Incomplete code for modern miniplayers.
// 19.15.36 // Different code for handling subtitle texts and not worth supporting.
"19.16.39", // First with modern miniplayers.
// 19.17.41 // Works without issues, but no reason to recommend over 19.16.
// 19.18.41 // Works without issues, but no reason to recommend over 19.16.
// 19.19.39 // Last bug free version with smaller Modern 1 miniplayer, but no reason to recommend over 19.16.
// 19.20.35 // Cannot swipe to expand.
// 19.21.40 // Cannot swipe to expand.
// 19.22.43 // Cannot swipe to expand.
// 19.23.40 // First with Modern 1 drag and drop, Cannot swipe to expand.
// 19.24.45 // First with larger Modern 1, Cannot swipe to expand.
"19.25.37", // First with double tap, last with skip forward/back buttons, last with swipe to expand/close, and last before double tap to expand seems to be required.
// 19.26.42 // Modern 1 Pause/play button are always hidden. Unusable.
// 19.28.42 // First with custom miniplayer size, screen flickers when swiping to maximize Modern 1. Swipe to close miniplayer is broken.
// 19.29.42 // All modern players are broken and ignore tapping the miniplayer video.
// 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off.
// 19.31.36 // All Modern 1 buttons are missing. Unusable.
// 19.32.36 // 19.32+ and beyond all work without issues.
// 19.33.35
"19.34.42",
]
)
]
@ -113,6 +115,7 @@ object MiniplayerPatch : BytecodePatch(
MiniplayerOverrideFingerprint,
MiniplayerModernConstructorFingerprint,
MiniplayerModernViewParentFingerprint,
MiniplayerMinimumSizeFingerprint,
YouTubePlayerOverlaysLayoutFingerprint
)
) {
@ -121,48 +124,71 @@ object MiniplayerPatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
// Modern mini player is only present and functional in 19.15+.
// Resource is not present in older versions. Using it to determine, if patching an old version.
val isPatchingOldVersion = ytOutlinePictureInPictureWhite24 < 0
val preferences = mutableSetOf<BasePreference>()
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = "revanced_miniplayer_screen",
sorting = Sorting.UNSORTED,
preferences =
if (isPatchingOldVersion) {
setOf(
ListPreference(
if (!VersionCheckPatch.is_19_16_or_greater) {
preferences += ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_legacy_entries",
entryValuesKey = "revanced_miniplayer_type_legacy_entry_values"
)
)
} else {
setOf(
ListPreference(
preferences += ListPreference(
"revanced_miniplayer_type",
summaryKey = null,
entriesKey = "revanced_miniplayer_type_19_15_entries",
entryValuesKey = "revanced_miniplayer_type_19_15_entry_values"
),
SwitchPreference("revanced_miniplayer_hide_expand_close"),
SwitchPreference("revanced_miniplayer_hide_subtext"),
SwitchPreference("revanced_miniplayer_hide_rewind_forward"),
TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
)
if (VersionCheckPatch.is_19_25_or_greater) {
if (!VersionCheckPatch.is_19_29_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_double_tap_action")
}
preferences += SwitchPreference("revanced_miniplayer_drag_and_drop")
}
if (VersionCheckPatch.is_19_36_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_rounded_corners")
}
preferences += SwitchPreference("revanced_miniplayer_hide_subtext")
preferences +=
if (VersionCheckPatch.is_19_26_or_greater) {
SwitchPreference("revanced_miniplayer_hide_expand_close")
} else {
SwitchPreference(
key = "revanced_miniplayer_hide_expand_close",
titleKey = "revanced_miniplayer_hide_expand_close_legacy_title",
summaryOnKey = "revanced_miniplayer_hide_expand_close_legacy_summary_on",
summaryOffKey = "revanced_miniplayer_hide_expand_close_legacy_summary_off",
)
}
if (!VersionCheckPatch.is_19_26_or_greater) {
preferences += SwitchPreference("revanced_miniplayer_hide_rewind_forward")
}
if (VersionCheckPatch.is_19_26_or_greater) {
preferences += TextPreference("revanced_miniplayer_width_dip", inputType = InputType.NUMBER)
}
preferences += TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER)
}
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = "revanced_miniplayer_screen",
sorting = Sorting.UNSORTED,
preferences = preferences
)
)
// region Enable tablet miniplayer.
MiniplayerOverrideNoContextFingerprint.resolve(
MiniplayerOverrideNoContextFingerprint.alsoResolve(
context,
MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef
)
MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply {
MiniplayerDimensionsCalculatorParentFingerprint
).mutableMethod.apply {
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
}
@ -188,8 +214,8 @@ object MiniplayerPatch : BytecodePatch(
it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.scanResult.patternScanResult!!.endIndex)
}
if (isPatchingOldVersion) {
// Return here, as patch below is only intended for new versions of the app.
if (!VersionCheckPatch.is_19_16_or_greater) {
// Return here, as patch below is only for the current versions of the app.
return
}
@ -212,18 +238,93 @@ object MiniplayerPatch : BytecodePatch(
}
}
if (VersionCheckPatch.is_19_23_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL,
"enableMiniplayerDragAndDrop"
)
}
if (VersionCheckPatch.is_19_25_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY,
"getModernMiniplayerOverride"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL,
"getModernFeatureFlagsActiveOverride"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL,
"enableMiniplayerDoubleTapAction"
)
}
if (VersionCheckPatch.is_19_26_or_greater) {
MiniplayerModernConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
MiniplayerModernConstructorFingerprint.INITIAL_SIZE_FEATURE_KEY_LITERAL
)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I
move-result v$register
"""
)
}
// Override a mininimum miniplayer size constant.
MiniplayerMinimumSizeFingerprint.resultOrThrow().mutableMethod.apply {
val index = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST_16 && (this as NarrowLiteralInstruction).narrowLiteral == 192
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
// Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller.
// The 170 initial limit probably could be patched to allow even smaller initial sizes,
// but 170 is already half the horizontal space and smaller does not seem useful.
replaceInstruction(index, "const/16 v$register, 170")
}
}
if (VersionCheckPatch.is_19_32_or_greater) {
// Feature is not exposed in the settings, and currently only for debugging.
MiniplayerModernConstructorFingerprint.insertLiteralValueFloatOverride(
MiniplayerModernConstructorFingerprint.ANIMATION_INTERPOLATION_FEATURE_KEY,
"setMovementBoundFactor"
)
}
if (VersionCheckPatch.is_19_36_or_greater) {
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.DROP_SHADOW_FEATURE_KEY,
"setDropShadow"
)
MiniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
MiniplayerModernConstructorFingerprint.ROUNDED_CORNERS_FEATURE_KEY,
"setRoundedCorners"
)
}
// endregion
// region Fix 19.16 using mixed up drawables for tablet modern.
// YT fixed this mistake in 19.17.
// Fix this, by swapping the drawable resource values with each other.
MiniplayerModernExpandCloseDrawablesFingerprint.apply {
resolve(
if (ytOutlinePictureInPictureWhite24 >= 0) {
MiniplayerModernExpandCloseDrawablesFingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.apply {
MiniplayerModernViewParentFingerprint
).mutableMethod.apply {
listOf(
ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24,
ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24,
@ -234,37 +335,53 @@ object MiniplayerPatch : BytecodePatch(
replaceInstruction(imageResourceIndex, "const v$register, $replacementResource")
}
}
}
// endregion
// region Add hooks to hide tablet modern miniplayer buttons.
// region Add hooks to hide modern miniplayer buttons.
listOf(
Triple(MiniplayerModernExpandButtonFingerprint, modernMiniplayerExpand,"hideMiniplayerExpandClose"),
Triple(MiniplayerModernCloseButtonFingerprint, modernMiniplayerClose, "hideMiniplayerExpandClose"),
Triple(MiniplayerModernRewindButtonFingerprint, modernMiniplayerRewindButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernForwardButtonFingerprint, modernMiniplayerForwardButton, "hideMiniplayerRewindForward"),
Triple(MiniplayerModernOverlayViewFingerprint, scrimOverlay, "adjustMiniplayerOpacity")
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.resolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
Triple(
MiniplayerModernExpandButtonFingerprint,
modernMiniplayerExpand,
"hideMiniplayerExpandClose"
),
Triple(
MiniplayerModernCloseButtonFingerprint,
modernMiniplayerClose,
"hideMiniplayerExpandClose"
),
Triple(
MiniplayerModernRewindButtonFingerprint,
modernMiniplayerRewindButton,
"hideMiniplayerRewindForward"
),
Triple(
MiniplayerModernForwardButtonFingerprint,
modernMiniplayerForwardButton,
"hideMiniplayerRewindForward"
),
Triple(
MiniplayerModernOverlayViewFingerprint,
scrimOverlay,
"adjustMiniplayerOpacity"
)
fingerprint.hookInflatedView(
).forEach { (fingerprint, literalValue, methodName) ->
fingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint
).mutableMethod.hookInflatedView(
literalValue,
"Landroid/widget/ImageView;",
"$INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
)
}
MiniplayerModernAddViewListenerFingerprint.apply {
resolve(
MiniplayerModernAddViewListenerFingerprint.alsoResolve(
context,
MiniplayerModernViewParentFingerprint.resultOrThrow().classDef
)
}.resultOrThrow().mutableMethod.addInstruction(
MiniplayerModernViewParentFingerprint
).mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" +
"hideMiniplayerSubTexts(Landroid/view/View;)V"
@ -275,6 +392,9 @@ object MiniplayerPatch : BytecodePatch(
// Modern 2 uses the same overlay controls as the regular video player,
// and the overlay views are added at runtime.
// Add a hook to the overlay class, and pass the added views to integrations.
//
// NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller.
// This patch code could be used to hide other player overlays that do not use Litho.
YouTubePlayerOverlaysLayoutFingerprint.resultOrThrow().mutableClass.methods.add(
ImmutableMethod(
YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME,
@ -319,6 +439,37 @@ object MiniplayerPatch : BytecodePatch(
insertBooleanOverride(index, "getModernMiniplayerOverride")
}
private fun MethodFingerprint.insertLiteralValueBooleanOverride(
literal: Long,
integrationsMethod: String
) {
resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
insertBooleanOverride(targetIndex + 1, integrationsMethod)
}
}
private fun MethodFingerprint.insertLiteralValueFloatOverride(
literal: Long,
integrationsMethod: String
) {
resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.DOUBLE_TO_FLOAT)
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1,
"""
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$integrationsMethod(F)F
move-result v$register
"""
)
}
}
private fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
@ -335,25 +486,20 @@ object MiniplayerPatch : BytecodePatch(
*/
private fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
val targetReference = (targetInstruction as ReferenceInstruction).reference
addInstructions(
iPutIndex + 1, """
addInstructionsAtControlFlowLabel(
iPutIndex, """
invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v${targetInstruction.registerA}
# Original instruction
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
"""
)
removeInstruction(iPutIndex)
}
private fun LiteralValueFingerprint.hookInflatedView(
private fun MutableMethod.hookInflatedView(
literalValue: Long,
hookedClassType: String,
integrationsMethodName: String,
) {
resultOrThrow().mutableMethod.apply {
val imageViewIndex = indexOfFirstInstructionOrThrow(
indexOfFirstWideLiteralInstructionValueOrThrow(literalValue)
) {
@ -367,4 +513,3 @@ object MiniplayerPatch : BytecodePatch(
)
}
}
}

View File

@ -1,12 +1,12 @@
package app.revanced.patches.youtube.layout.miniplayer
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
@Patch(dependencies = [ResourceMappingPatch::class])
@Patch(dependencies = [ResourceMappingPatch::class, VersionCheckPatch::class])
internal object MiniplayerResourcePatch : ResourcePatch() {
var floatyBarButtonTopMargin = -1L
@ -19,6 +19,7 @@ internal object MiniplayerResourcePatch : ResourcePatch() {
var modernMiniplayerRewindButton = -1L
var modernMiniplayerForwardButton = -1L
var playerOverlays = -1L
var miniplayerMaxSize = -1L
override fun execute(context: ResourceContext) {
floatyBarButtonTopMargin = ResourceMappingPatch[
@ -26,26 +27,17 @@ internal object MiniplayerResourcePatch : ResourcePatch() {
"floaty_bar_button_top_margin"
]
try {
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_picture_in_picture_white_24"
]
} catch (exception: PatchException) {
// Ignore, and assume the app is 19.14 or earlier.
return
}
ytOutlineXWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_x_white_24"
]
scrimOverlay = ResourceMappingPatch[
"id",
"scrim_overlay"
]
playerOverlays = ResourceMappingPatch[
"layout",
"player_overlays"
]
if (VersionCheckPatch.is_19_16_or_greater) {
modernMiniplayerClose = ResourceMappingPatch[
"id",
"modern_miniplayer_close"
@ -66,16 +58,32 @@ internal object MiniplayerResourcePatch : ResourcePatch() {
"modern_miniplayer_forward_button"
]
playerOverlays = ResourceMappingPatch[
"layout",
"player_overlays"
]
// Resource id is not used during patching, but is used by integrations.
// Verify the resource is present while patching.
ResourceMappingPatch[
"id",
"modern_miniplayer_subtitle_text"
]
// Only required for exactly 19.16
if (!VersionCheckPatch.is_19_17_or_greater) {
ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_picture_in_picture_white_24"
]
ytOutlineXWhite24 = ResourceMappingPatch[
"drawable",
"yt_outline_x_white_24"
]
}
if (VersionCheckPatch.is_19_26_or_greater) {
miniplayerMaxSize = ResourceMappingPatch[
"dimen",
"miniplayer_max_size"
]
}
}
}
}

View File

@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.miniplayer.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch
import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerMinimumSizeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { methodDef, _ ->
methodDef.containsWideLiteralInstructionValue(192)
&& methodDef.containsWideLiteralInstructionValue(128)
&& methodDef.containsWideLiteralInstructionValue(MiniplayerResourcePatch.miniplayerMaxSize)
}
)

View File

@ -8,4 +8,14 @@ internal object MiniplayerModernConstructorFingerprint : LiteralValueFingerprint
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
literalSupplier = { 45623000L } // Magic number found in the constructor.
)
) {
const val MODERN_FEATURE_FLAGS_ENABLED_KEY_LITERAL = 45622882L
// In later targets this feature flag does nothing and is dead code.
const val MODERN_MINIPLAYER_ENABLED_OLD_TARGETS_FEATURE_KEY = 45630429L
const val DOUBLE_TAP_ENABLED_FEATURE_KEY_LITERAL = 45628823L
const val DRAG_DROP_ENABLED_FEATURE_KEY_LITERAL = 45628752L
const val INITIAL_SIZE_FEATURE_KEY_LITERAL = 45640023L
const val ANIMATION_INTERPOLATION_FEATURE_KEY = 45647018L
const val DROP_SHADOW_FEATURE_KEY = 45652223L
const val ROUNDED_CORNERS_FEATURE_KEY = 45652224L
}

View File

@ -7,6 +7,5 @@ import com.android.tools.smali.dexlib2.AccessFlags
internal object MiniplayerOverrideFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("appName")
)

View File

@ -19,30 +19,11 @@ import app.revanced.util.exception
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -14,30 +14,11 @@ import org.w3c.dom.Element
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
@ -27,11 +28,14 @@ import app.revanced.patches.youtube.layout.returnyoutubedislike.fingerprints.Tex
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.shared.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.alsoResolve
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -51,27 +55,15 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
VideoIdPatch::class,
ReturnYouTubeDislikeResourcePatch::class,
PlayerTypeHookPatch::class,
VersionCheckPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -117,7 +109,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
DislikeFingerprint.toPatch(Vote.DISLIKE),
RemoveLikeFingerprint.toPatch(Vote.REMOVE_LIKE)
).forEach { (fingerprint, vote) ->
fingerprint.result?.mutableMethod?.apply {
fingerprint.resultOrThrow().mutableMethod.apply {
addInstructions(
0,
"""
@ -125,7 +117,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->sendVote(I)V
"""
)
} ?: throw fingerprint.exception
}
}
// endregion
@ -136,7 +128,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// And it works in all situations except it fails to update the Span when the user dislikes,
// since the underlying (likes only) text did not change.
// This hook handles all situations, as it's where the created Spans are stored and later reused.
TextComponentConstructorFingerprint.result?.let { textConstructorResult ->
TextComponentConstructorFingerprint.resultOrThrow().let { textConstructorResult ->
// Find the field name of the conversion context.
val conversionContextClassType = ConversionContextFingerprint.resultOrThrow().classDef.type
val conversionContextField = textConstructorResult.classDef.fields.find {
@ -147,26 +139,44 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
TextComponentLookupFingerprint.resultOrThrow().mutableMethod.apply {
// Find the instruction for creating the text data object.
val textDataClassType = TextComponentDataFingerprint.resultOrThrow().classDef.type
val insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == textDataClassType
val insertIndex : Int
val tempRegister : Int
val charSequenceRegister : Int
if (VersionCheckPatch.is_19_33_or_greater) {
insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC_RANGE &&
getReference<MethodReference>()?.returnType == textDataClassType
}
val tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
tempRegister = getInstruction<OneRegisterInstruction>(insertIndex + 1).registerA
// Find the instruction that sets the span to an instance field.
// The instruction is only a few lines after the creation of the instance.
// The method has multiple iput-object instructions using a CharSequence,
// so verify the found instruction is in the expected location.
val putFieldInstruction = implementation!!.instructions
.subList(insertIndex, insertIndex + 20)
.find {
it.opcode == Opcode.IPUT_OBJECT &&
it.getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
} ?: throw PatchException("Could not find put object instruction")
val charSequenceRegister = (putFieldInstruction as TwoRegisterInstruction).registerA
charSequenceRegister = getInstruction<FiveRegisterInstruction>(
indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.parameterTypes?.firstOrNull() == "Ljava/lang/CharSequence;"
}
).registerD
} else {
insertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == textDataClassType
}
addInstructions(
insertIndex,
tempRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
charSequenceRegister = getInstruction<TwoRegisterInstruction>(
indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/CharSequence;"
}
).registerA
}
addInstructionsAtControlFlowLabel(insertIndex,
"""
# Copy conversion context
move-object/from16 v$tempRegister, p0
@ -176,26 +186,33 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw TextComponentConstructorFingerprint.exception
}
// endregion
// region Hook for non-litho Short videos.
ShortsTextViewFingerprint.result?.let {
ShortsTextViewFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val patternResult = it.scanResult.patternScanResult!!
val insertIndex = it.scanResult.patternScanResult!!.endIndex + 1
// If the field is true, the TextView is for a dislike button.
val isDisLikesBooleanReference = getInstruction<ReferenceInstruction>(patternResult.endIndex).reference
val isDisLikesBooleanInstruction = getInstructions().first { instruction ->
instruction.opcode == Opcode.IGET_BOOLEAN
} as ReferenceInstruction
val textViewFieldReference = // Like/Dislike button TextView field
getInstruction<ReferenceInstruction>(patternResult.endIndex - 1).reference
val isDisLikesBooleanReference = isDisLikesBooleanInstruction.reference
// Like/Dislike button TextView field.
val textViewFieldInstruction = getInstructions().first { instruction ->
instruction.opcode == Opcode.IGET_OBJECT
} as ReferenceInstruction
val textViewFieldReference = textViewFieldInstruction.reference
// Check if the hooked TextView object is that of the dislike button.
// If RYD is disabled, or the TextView object is not that of the dislike button, the execution flow is not interrupted.
// Otherwise, the TextView object is modified, and the execution flow is interrupted to prevent it from being changed afterward.
val insertIndex = patternResult.startIndex + 6
addInstructionsWithLabels(
insertIndex,
"""
@ -216,7 +233,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw ShortsTextViewFingerprint.exception
}
// endregion
@ -251,20 +268,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// region Hook rolling numbers.
// Do this last to allow patching old unsupported versions (if the user really wants),
// On older unsupported version this will fail to resolve and throw an exception,
// but everything will still work correctly anyways.
RollingNumberSetterFingerprint.result?.let {
RollingNumberSetterFingerprint.resultOrThrow().let {
val dislikesIndex = it.scanResult.patternScanResult!!.endIndex
it.mutableMethod.apply {
val insertIndex = 1
val charSequenceInstanceRegister =
getInstruction<OneRegisterInstruction>(0).registerA
val charSequenceFieldReference =
getInstruction<ReferenceInstruction>(dislikesIndex).reference
val charSequenceInstanceRegister = getInstruction<OneRegisterInstruction>(0).registerA
val charSequenceFieldReference = getInstruction<ReferenceInstruction>(dislikesIndex).reference
val registerCount = implementation!!.registerCount
@ -282,7 +293,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw RollingNumberSetterFingerprint.exception
}
// Rolling Number text views use the measured width of the raw string for layout.
// Modify the measure text calculation to include the left drawable separator if needed.
@ -306,9 +317,12 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
// Additional text measurement method. Used if YouTube decides not to animate the likes count
// and sometimes used for initial video load.
RollingNumberMeasureStaticLabelFingerprint.resolve(context, RollingNumberMeasureStaticLabelParentFingerprint.resultOrThrow().classDef)
RollingNumberMeasureStaticLabelFingerprint.result?.also {
RollingNumberMeasureStaticLabelFingerprint.alsoResolve(
context,
RollingNumberMeasureStaticLabelParentFingerprint
).let {
val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1
it.mutableMethod.apply {
val freeRegister = getInstruction<TwoRegisterInstruction>(0).registerA
@ -320,19 +334,18 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
"""
)
}
} ?: throw RollingNumberMeasureStaticLabelFingerprint.exception
}
// The rolling number Span is missing styling since it's initially set as a String.
// Modify the UI text view and use the styled like/dislike Span.
RollingNumberTextViewFingerprint.result?.let {
RollingNumberTextViewFingerprint.resultOrThrow().let {
// Initial TextView is set in this method.
val initiallyCreatedTextViewMethod = it.mutableMethod
// Videos less than 24 hours after uploaded, like counts will be updated in real time.
// Whenever like counts are updated, TextView is set in this method.
val realTimeUpdateTextViewMethod =
RollingNumberTextViewAnimationUpdateFingerprint.result?.mutableMethod
?: throw RollingNumberTextViewAnimationUpdateFingerprint.exception
RollingNumberTextViewAnimationUpdateFingerprint.resultOrThrow().mutableMethod
arrayOf(
initiallyCreatedTextViewMethod,
@ -357,7 +370,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
)
}
}
} ?: throw RollingNumberTextViewFingerprint.exception
}
// endregion

View File

@ -10,9 +10,6 @@ internal object ConversionContextFingerprint : MethodFingerprint(
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
// 18.37.36 and after this String is: ConversionContext{containerInternal=
// and before it is: ConversionContext{container=
// Use a partial string to match both.
"ConversionContext{container"
"ConversionContext{containerInternal="
)
)

View File

@ -8,5 +8,6 @@ internal object RollingNumberSetterFingerprint : MethodFingerprint(
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT
),
strings = listOf("RollingNumberType required properties missing! Need updateCount, fontName, color and fontSize.")
// Partial string match.
strings = listOf("RollingNumberType required properties missing! Need")
)

View File

@ -18,6 +18,7 @@ internal object RollingNumberTextViewFingerprint : MethodFingerprint(
Opcode.RETURN_VOID
),
customFingerprint = { _, classDef ->
classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;"
classDef.superclass == "Landroid/support/v7/widget/AppCompatTextView;" ||
classDef.superclass == "Lcom/google/android/libraries/youtube/rendering/ui/spec/typography/YouTubeAppCompatTextView;"
}
)

View File

@ -15,18 +15,6 @@ internal object ShortsTextViewFingerprint : MethodFingerprint(
null,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.SGET_OBJECT, // insertion point, must be after constructor call to parent class
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.CONST_4,
Opcode.IF_EQ,
Opcode.CONST_4,
Opcode.IF_EQ,
Opcode.RETURN_VOID,
Opcode.IGET_OBJECT, // TextView field
Opcode.IGET_BOOLEAN, // boolean field
Opcode.CHECK_CAST
)
)

View File

@ -9,8 +9,6 @@ internal object TextComponentDataFingerprint : MethodFingerprint(
parameters = listOf("L", "L"),
strings = listOf("text"),
customFingerprint = { _, classDef ->
val fields = classDef.fields
fields.find { it.type == "Ljava/util/BitSet;" } != null &&
fields.find { it.type == "[Ljava/lang/String;" } != null
classDef.fields.find { it.type == "Ljava/util/BitSet;" } != null
}
)

View File

@ -24,29 +24,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,6 +18,6 @@ internal object SetWordmarkHeaderFingerprint : MethodFingerprint(
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.CONST,
Opcode.INVOKE_STATIC,
null // invoke-static or invoke-virtual
)
)

View File

@ -4,45 +4,28 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
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.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.seekbar.fingerprints.FullscreenSeekbarThumbnailsFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
@Patch(
name = "Restore old seekbar thumbnails",
description = "Adds an option to restore the old seekbar thumbnails that appear above the seekbar while seeking instead of fullscreen thumbnails.",
dependencies = [IntegrationsPatch::class, AddResourcesPatch::class],
dependencies = [IntegrationsPatch::class, AddResourcesPatch::class, VersionCheckPatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.16.39"
// 19.17+ is not supported.
]
)
]
@ -55,6 +38,11 @@ object RestoreOldSeekbarThumbnailsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/RestoreOldSeekbarThumbnailsPatch;"
override fun execute(context: BytecodeContext) {
if (VersionCheckPatch.is_19_17_or_greater) {
// Give a more informative error, if the user has turned off version checks.
throw PatchException("'Restore old seekbar thumbnails' cannot be patched to any version after 19.16.39")
}
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.SEEKBAR.addPreferences(

View File

@ -1,20 +1,26 @@
package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.layout.seekbar.fingerprints.LithoLinearGradientFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarColorFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarGradientConfigFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.SetSeekbarClickedColorFingerprint
import app.revanced.patches.youtube.layout.seekbar.fingerprints.ShortsSeekbarColorFingerprint
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.util.exception
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
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.TwoRegisterInstruction
@ -24,7 +30,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
compatiblePackages = [CompatiblePackage("com.google.android.youtube")]
)
internal object SeekbarColorBytecodePatch : BytecodePatch(
setOf(PlayerSeekbarColorFingerprint, ShortsSeekbarColorFingerprint, SetSeekbarClickedColorFingerprint)
setOf(
PlayerSeekbarColorFingerprint,
ShortsSeekbarColorFingerprint,
SetSeekbarClickedColorFingerprint,
PlayerSeekbarGradientConfigFingerprint,
LithoLinearGradientFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/theme/SeekbarColorPatch;"
@ -32,6 +44,7 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
addInstructions(
registerIndex + 1,
"""
@ -41,16 +54,16 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
)
}
PlayerSeekbarColorFingerprint.result?.mutableMethod?.apply {
PlayerSeekbarColorFingerprint.resultOrThrow().mutableMethod.apply {
addColorChangeInstructions(SeekbarColorResourcePatch.inlineTimeBarColorizedBarPlayedColorDarkId)
addColorChangeInstructions(SeekbarColorResourcePatch.inlineTimeBarPlayedNotHighlightedColorId)
} ?: throw PlayerSeekbarColorFingerprint.exception
}
ShortsSeekbarColorFingerprint.result?.mutableMethod?.apply {
ShortsSeekbarColorFingerprint.resultOrThrow().mutableMethod.apply {
addColorChangeInstructions(SeekbarColorResourcePatch.reelTimeBarPlayedColorId)
} ?: throw ShortsSeekbarColorFingerprint.exception
}
SetSeekbarClickedColorFingerprint.result?.let { result ->
SetSeekbarClickedColorFingerprint.resultOrThrow().let { result ->
result.mutableMethod.let {
val setColorMethodIndex = result.scanResult.patternScanResult!!.startIndex + 1
val method = context
@ -69,7 +82,31 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
)
}
}
} ?: throw SetSeekbarClickedColorFingerprint.exception
}
if (VersionCheckPatch.is_19_23_or_greater) {
PlayerSeekbarGradientConfigFingerprint.resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
PlayerSeekbarGradientConfigFingerprint.PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG
)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstructions(
resultIndex + 1,
"""
invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z
move-result v$register
"""
)
}
LithoLinearGradientFingerprint.resultOrThrow().mutableMethod.apply {
addInstruction(0, "invoke-static/range { p4 .. p5 }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->setLinearGradient([I[F)V"
)
}
}
lithoColorOverrideHook(INTEGRATIONS_CLASS_DESCRIPTOR, "getLithoColor")
}

View File

@ -0,0 +1,10 @@
package app.revanced.patches.youtube.layout.seekbar.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object LithoLinearGradientFingerprint : MethodFingerprint(
accessFlags = AccessFlags.STATIC.value,
returnType = "Landroid/graphics/LinearGradient;",
parameters = listOf("F", "F", "F", "F", "[I", "[F"),
)

View File

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.seekbar.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.layout.seekbar.fingerprints.PlayerSeekbarGradientConfigFingerprint.PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlayerSeekbarGradientConfigFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf(),
literalSupplier = { PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG },
) {
const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
}

View File

@ -23,13 +23,16 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarFingerprint
import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.exception
import app.revanced.util.alsoResolve
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@ -41,24 +44,11 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
@ -92,12 +82,6 @@ object SponsorBlockBytecodePatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/sponsorblock/ui/SponsorBlockViewController;"
override fun execute(context: BytecodeContext) {
LayoutConstructorFingerprint.result?.let {
if (!ControlsOverlayFingerprint.resolve(context, it.classDef)) {
throw ControlsOverlayFingerprint.exception
}
} ?: throw LayoutConstructorFingerprint.exception
/*
* Hook the video time methods
*/
@ -108,67 +92,46 @@ object SponsorBlockBytecodePatch : BytecodePatch(
)
}
/*
* Set current video id.
*/
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
VideoIdPatch.hookBackgroundPlayVideoId(
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
/*
* Seekbar drawing
*/
val seekbarSignatureResult = SeekbarFingerprint.result!!.let {
SeekbarOnDrawFingerprint.apply { resolve(context, it.mutableClass) }
}.result!!
val seekbarMethod = seekbarSignatureResult.mutableMethod
val seekbarMethodInstructions = seekbarMethod.implementation!!.instructions
/*
* Get left and right of seekbar rectangle
*/
val moveRectangleToRegisterIndex = seekbarMethodInstructions.indexOfFirst {
it.opcode == Opcode.MOVE_OBJECT_FROM16
}
// Seekbar drawing
SeekbarOnDrawFingerprint.alsoResolve(context, SeekbarFingerprint).mutableMethod.apply {
// Get left and right of seekbar rectangle.
val moveRectangleToRegisterIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT_FROM16)
seekbarMethod.addInstruction(
addInstruction(
moveRectangleToRegisterIndex + 1,
"invoke-static/range { p0 .. p0 }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarRect(Ljava/lang/Object;)V",
)
for ((index, instruction) in seekbarMethodInstructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_STATIC) continue
val invokeInstruction = instruction as Instruction35c
if ((invokeInstruction.reference as MethodReference).name != "round") continue
val insertIndex = index + 2
// set the thickness of the segment
seekbarMethod.addInstruction(
insertIndex,
"invoke-static {v${invokeInstruction.registerC}}, " +
// Set the thickness of the segment.
val thicknessIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC && getReference<MethodReference>()?.name == "round"
}
val thicknessRegister = getInstruction<FiveRegisterInstruction>(thicknessIndex).registerC
addInstruction(
thicknessIndex + 2,
"invoke-static { v$thicknessRegister }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setSponsorBarThickness(I)V",
)
break
}
/*
* Draw segment
*/
// Find the drawCircle call and draw the segment before it
for (i in seekbarMethodInstructions.size - 1 downTo 0) {
val invokeInstruction = seekbarMethodInstructions[i] as? ReferenceInstruction ?: continue
if ((invokeInstruction.reference as MethodReference).name != "drawCircle") continue
val (canvasInstance, centerY) = (invokeInstruction as FiveRegisterInstruction).let {
it.registerC to it.registerE
// Find the drawCircle call and draw the segment before it.
val drawCircleIndex = indexOfFirstInstructionReversedOrThrow {
getReference<MethodReference>()?.name == "drawCircle"
}
seekbarMethod.addInstruction(
i,
"invoke-static {v$canvasInstance, v$centerY}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->drawSponsorTimeBars(Landroid/graphics/Canvas;F)V",
val drawCircleInstruction = getInstruction<FiveRegisterInstruction>(drawCircleIndex)
val canvasInstanceRegister = drawCircleInstruction.registerC
val centerYRegister = drawCircleInstruction.registerE
addInstruction(
drawCircleIndex,
"invoke-static { v$canvasInstanceRegister, v$centerYRegister }, " +
"$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->" +
"drawSponsorTimeBars(Landroid/graphics/Canvas;F)V",
)
break
}
// Change visibility of the buttons.
@ -179,24 +142,26 @@ object SponsorBlockBytecodePatch : BytecodePatch(
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
// Append the new time to the player layout.
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
val targetRegister =
(appendTimeFingerprintResult.method.implementation!!.instructions.elementAt(appendTimePatternScanStartIndex + 1) as OneRegisterInstruction).registerA
AppendTimeFingerprint.resultOrThrow().let {
val appendTimePatternScanStartIndex = it.scanResult.patternScanResult!!.startIndex
it.mutableMethod.apply {
val register = getInstruction<OneRegisterInstruction>(appendTimePatternScanStartIndex + 1).registerA
appendTimeFingerprintResult.mutableMethod.addInstructions(
addInstructions(
appendTimePatternScanStartIndex + 2,
"""
invoke-static {v$targetRegister}, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$targetRegister
""",
invoke-static { v$register }, $INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->appendTimeWithoutSegments(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
// initialize the player controller
// Initialize the player controller.
VideoInformationPatch.onCreateHook(INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR, "initialize")
// initialize the sponsorblock view
ControlsOverlayFingerprint.result?.let {
// Initialize the SponsorBlock view.
ControlsOverlayFingerprint.alsoResolve(context, LayoutConstructorFingerprint).let {
val startIndex = it.scanResult.patternScanResult!!.startIndex
it.mutableMethod.apply {
val frameLayoutRegister = (getInstruction(startIndex + 2) as OneRegisterInstruction).registerA
@ -205,14 +170,13 @@ object SponsorBlockBytecodePatch : BytecodePatch(
"invoke-static {v$frameLayoutRegister}, $INTEGRATIONS_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/ViewGroup;)V",
)
}
} ?: throw ControlsOverlayFingerprint.exception
}
// get rectangle field name
RectangleFieldInvalidatorFingerprint.resolve(context, seekbarSignatureResult.classDef)
val rectangleFieldInvalidatorInstructions =
RectangleFieldInvalidatorFingerprint.result!!.method.implementation!!.instructions
val rectangleFieldName =
((rectangleFieldInvalidatorInstructions.elementAt(rectangleFieldInvalidatorInstructions.count() - 3) as ReferenceInstruction).reference as FieldReference).name
// Set seekbar draw rectangle.
RectangleFieldInvalidatorFingerprint.alsoResolve(context, SeekbarOnDrawFingerprint).mutableMethod.apply {
val fieldIndex = implementation!!.instructions.count() - 3
val fieldReference = getInstruction<ReferenceInstruction>(fieldIndex).reference as FieldReference
// replace the "replaceMeWith*" strings
context
@ -235,23 +199,21 @@ object SponsorBlockBytecodePatch : BytecodePatch(
"replaceMeWithsetSponsorBarRect" -> method.replaceStringInstruction(
index,
it,
rectangleFieldName,
fieldReference.name,
)
}
}
} ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings")
}
// The vote and create segment buttons automatically change their visibility when appropriate,
// but if buttons are showing when the end of the video is reached then they will not automatically hide.
// Add a hook to forcefully hide when the end of the video is reached.
AutoRepeatParentFingerprint.result ?: throw AutoRepeatParentFingerprint.exception
AutoRepeatFingerprint.also {
it.resolve(context, AutoRepeatParentFingerprint.result!!.classDef)
}.result?.mutableMethod?.addInstruction(
AutoRepeatFingerprint.alsoResolve(context, AutoRepeatParentFingerprint).mutableMethod.addInstruction(
0,
"invoke-static {}, $INTEGRATIONS_SPONSORBLOCK_VIEW_CONTROLLER_CLASS_DESCRIPTOR->endOfVideoReached()V",
) ?: throw AutoRepeatFingerprint.exception
)
// TODO: isSBChannelWhitelisting implementation
// TODO: isSBChannelWhitelisting implementation?
}
}

View File

@ -22,30 +22,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -2,30 +2,47 @@ package app.revanced.patches.youtube.layout.startpage
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.layout.startpage.fingerprints.StartActivityFingerprint
import app.revanced.patches.youtube.layout.startpage.fingerprints.BrowseIdFingerprint
import app.revanced.patches.youtube.layout.startpage.fingerprints.IntentActionFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.shared.fingerprints.HomeActivityFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Patch(
name = "Change start page",
description = "Adds an option to set which page the app opens in instead of the homepage.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube"
"com.google.android.youtube",
[
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
)
@Suppress("unused")
object ChangeStartPagePatch : BytecodePatch(
setOf(HomeActivityFingerprint)
setOf(BrowseIdFingerprint, IntentActionFingerprint)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/ChangeStartPagePatch;"
@ -35,19 +52,31 @@ object ChangeStartPagePatch : BytecodePatch(
SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference(
key = "revanced_start_page",
key = "revanced_change_start_page",
summaryKey = null,
)
)
StartActivityFingerprint.resolve(
context,
HomeActivityFingerprint.result?.classDef ?: throw HomeActivityFingerprint.exception
)
// Hook browseId.
BrowseIdFingerprint.resultOrThrow().mutableMethod.apply {
val browseIdIndex = indexOfFirstInstructionOrThrow {
getReference<StringReference>()?.string == "FEwhat_to_watch"
}
val browseIdRegister = getInstruction<OneRegisterInstruction>(browseIdIndex).registerA
StartActivityFingerprint.result?.mutableMethod?.addInstruction(
addInstructions(
browseIdIndex + 1, """
invoke-static { v$browseIdRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$browseIdRegister
"""
)
}
// There is no browserId assigned to Shorts and Search.
// Just hook the Intent action.
IntentActionFingerprint.resultOrThrow().mutableMethod.addInstruction(
0,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->changeIntent(Landroid/content/Intent;)V"
) ?: throw StartActivityFingerprint.exception
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideIntentAction(Landroid/content/Intent;)V"
)
}
}

View File

@ -0,0 +1,15 @@
package app.revanced.patches.youtube.layout.startpage.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object BrowseIdFingerprint : MethodFingerprint(
returnType = "Lcom/google/android/apps/youtube/app/common/ui/navigation/PaneDescriptor;",
parameters = listOf(),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
),
strings = listOf("FEwhat_to_watch")
)

View File

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.layout.startpage.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
object StartActivityFingerprint : MethodFingerprint(
internal object IntentActionFingerprint : MethodFingerprint(
parameters = listOf("Landroid/content/Intent;"),
strings = listOf("has_handled_intent"),
)

View File

@ -3,20 +3,22 @@ package app.revanced.patches.youtube.layout.startupshortsreset
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
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.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.UserWasInShortsConfigFingerprint
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.UserWasInShortsConfigFingerprint.indexOfOptionalInstruction
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.UserWasInShortsFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.resultOrThrow
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.reference.MethodReference
@ -27,37 +29,18 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
)
@Suppress("unused")
object DisableResumingShortsOnStartupPatch : BytecodePatch(
setOf(UserWasInShortsFingerprint)
setOf(UserWasInShortsConfigFingerprint, UserWasInShortsFingerprint)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
@ -70,30 +53,54 @@ object DisableResumingShortsOnStartupPatch : BytecodePatch(
SwitchPreference("revanced_disable_resuming_shorts_player")
)
UserWasInShortsFingerprint.result?.mutableMethod?.apply {
UserWasInShortsConfigFingerprint.resultOrThrow().mutableMethod.apply {
val startIndex = indexOfOptionalInstruction(this)
val walkerIndex = indexOfFirstInstructionOrThrow(startIndex) {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL
&& reference?.returnType == "Z"
&& reference.definingClass != "Lj${'$'}/util/Optional;"
&& reference.parameterTypes.size == 0
}
val walkerMethod = context.toMethodWalker(this)
.nextMethod(walkerIndex, true)
.getMethod() as MutableMethod
// Presumably a method that processes the ProtoDataStore value (boolean) for the 'user_was_in_shorts' key.
walkerMethod.addInstructionsWithLabels(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
move-result v0
if-eqz v0, :show
const/4 v0, 0x0
return v0
:show
nop
"""
)
}
UserWasInShortsFingerprint.resultOrThrow().mutableMethod.apply {
val listenableInstructionIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_INTERFACE &&
getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
getReference<MethodReference>()?.name == "isDone"
}
val originalInstructionRegister = getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC
val freeRegister = getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA
// Replace original instruction to preserve control flow label.
replaceInstruction(
addInstructionsAtControlFlowLabel(
listenableInstructionIndex,
"invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z"
)
addInstructionsWithLabels(
listenableInstructionIndex + 1,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
move-result v$freeRegister
if-eqz v$freeRegister, :show_startup_shorts_player
return-void
:show_startup_shorts_player
invoke-interface {v$originalInstructionRegister}, Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z
nop
"""
)
} ?: throw UserWasInShortsFingerprint.exception
}
}
}

View File

@ -0,0 +1,35 @@
package app.revanced.patches.youtube.layout.startupshortsreset.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.UserWasInShortsConfigFingerprint.indexOfOptionalInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
/**
* 18.15.40+
*/
internal object UserWasInShortsConfigFingerprint : MethodFingerprint(
returnType = "V",
strings = listOf("Failed to get offline response: "),
customFingerprint = { methodDef, _ ->
indexOfOptionalInstruction(methodDef) >= 0
}
) {
private val optionalOfMethodReference = ImmutableMethodReference(
"Lj${'$'}/util/Optional;",
"of",
listOf("Ljava/lang/Object;"),
"Lj${'$'}/util/Optional;",
)
fun indexOfOptionalInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
MethodUtil.methodSignaturesMatch(reference, optionalOfMethodReference)
}
}

View File

@ -26,30 +26,11 @@ import app.revanced.util.resultOrThrow
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", arrayOf(
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39"
"19.16.39",
"19.25.37",
"19.34.42",
)
)
]

View File

@ -35,29 +35,11 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
CompatiblePackage(
"com.google.android.youtube",
[
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -11,6 +11,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.theme.ThemeBytecodePatch.darkThemeBackgroundColor
import app.revanced.patches.youtube.layout.theme.ThemeBytecodePatch.lightThemeBackgroundColor
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import org.w3c.dom.Element
@ -19,6 +20,7 @@ import org.w3c.dom.Element
SettingsPatch::class,
ResourceMappingPatch::class,
AddResourcesPatch::class,
VersionCheckPatch::class,
],
)
internal object ThemeResourcePatch : ResourcePatch() {
@ -89,12 +91,35 @@ internal object ThemeResourcePatch : ResourcePatch() {
return@editSplashScreen
}
}
throw PatchException("Failed to modify launch screen")
}
}
// Fix the splash screen dark mode background color.
// In earlier versions of the app this is white and makes no sense for dark mode.
// This is only required for 19.32 and greater, but is applied to all targets.
// Only dark mode needs this fix as light mode correctly uses the custom color.
context.xmlEditor["res/values-night/styles.xml"].use { editor ->
val document = editor.file
// Create a night mode specific override for the splash screen background.
val style = document.createElement("style")
style.setAttribute("name", "Theme.YouTube.Home")
style.setAttribute("parent", "@style/Base.V23.Theme.YouTube.Home")
val windowItem = document.createElement("item")
windowItem.setAttribute("name", "android:windowBackground")
windowItem.textContent = "@color/$SPLASH_BACKGROUND_COLOR"
style.appendChild(windowItem)
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
resourcesNode.appendChild(style)
}
}
}
@Suppress("SameParameterValue")
private fun addColorResource(
context: ResourceContext,
resourceFile: String,

View File

@ -28,30 +28,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -24,30 +24,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -23,30 +23,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -3,6 +3,7 @@ package app.revanced.patches.youtube.misc.backgroundplayback
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
@ -14,7 +15,11 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findOpcodeIndicesReversed
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.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -32,24 +37,11 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]
@ -66,14 +58,19 @@ object BackgroundPlaybackPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/BackgroundPlaybackPatch;"
override fun execute(context: BytecodeContext) {
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.apply {
findOpcodeIndicesReversed(Opcode.RETURN).forEach{ index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructionsAtControlFlowLabel(
index,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
move-result v0
return v0
invoke-static { v$register }, $INTEGRATIONS_CLASS_DESCRIPTOR->allowBackgroundPlayback(Z)Z
move-result v$register
"""
)
}
}
// Enable background playback option in YouTube settings
BackgroundPlaybackSettingsFingerprint.resultOrThrow().mutableMethod.apply {

View File

@ -17,13 +17,7 @@ internal object KidsBackgroundPlaybackPolicyControllerFingerprint : LiteralValue
Opcode.IGET,
Opcode.CONST_4,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IF_EQ,
Opcode.GOTO,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
Opcode.IGET_OBJECT
),
literalSupplier = { 5 },
)

View File

@ -21,27 +21,10 @@ import app.revanced.util.exception
"com.google.android.youtube",
[
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -18,30 +18,11 @@ import app.revanced.util.resultOrThrow
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -0,0 +1,56 @@
package app.revanced.patches.youtube.misc.fix.cairo
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch
import app.revanced.patches.youtube.misc.fix.cairo.fingerprints.CarioFragmentConfigFingerprint
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
description = "Disables Cairo Fragment from being used.",
dependencies = [
VersionCheckPatch::class
]
)
internal object DisableCairoSettingsPatch : BytecodePatch(
setOf(CarioFragmentConfigFingerprint)
) {
override fun execute(context: BytecodeContext) {
if (!VersionCheckPatch.is_19_04_or_greater) {
return
}
/**
* <pre>
* Cairo Fragment was added since YouTube v19.04.38.
*
* Disable this for the following reasons:
* 1. [BackgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment.
* 2. Some patches do not yet support Cairo Fragments (ie: custom Seekbar color).
* 3. Settings preferences added by ReVanced are missing.
*
* Screenshots of the Cairo Fragment:
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
*/
CarioFragmentConfigFingerprint.resultOrThrow().mutableMethod.apply {
val literalIndex = indexOfFirstWideLiteralInstructionValueOrThrow(
CarioFragmentConfigFingerprint.CAIRO_CONFIG_LITERAL_VALUE
)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstruction(
resultIndex + 1,
"const/16 v$register, 0x0"
)
}
}
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.misc.fix.cairo.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.misc.fix.cairo.fingerprints.CarioFragmentConfigFingerprint.CAIRO_CONFIG_LITERAL_VALUE
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Added in YouTube v19.04.38
*
* When this value is TRUE, Cairo Fragment is used.
* In this case, some of patches may be broken, so set this value to FALSE.
*/
internal object CarioFragmentConfigFingerprint : LiteralValueFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
literalSupplier = { CAIRO_CONFIG_LITERAL_VALUE }
) {
const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
}

View File

@ -49,29 +49,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
CompatiblePackage(
"com.google.android.youtube",
[
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],

View File

@ -3,7 +3,7 @@ package app.revanced.patches.youtube.misc.gms
import app.revanced.patches.shared.fingerprints.CastContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
import app.revanced.patches.youtube.layout.buttons.cast.HideCastButtonPatch
import app.revanced.patches.youtube.misc.fix.playback.SpoofClientPatch
import app.revanced.patches.youtube.misc.fix.playback.SpoofVideoStreamsPatch
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
@ -25,36 +25,18 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
integrationsPatchDependency = IntegrationsPatch::class,
dependencies = setOf(
HideCastButtonPatch::class,
SpoofClientPatch::class,
SpoofVideoStreamsPatch::class,
),
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(
CompatiblePackage(
"com.google.android.youtube",
setOf(
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
),
),
),

View File

@ -97,9 +97,8 @@ object CronetImageUrlHook : BytecodePatch(
// The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field.
RequestFingerprint.resultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().single {
if (it.opcode != Opcode.IPUT_OBJECT) return@single false
val urlFieldInstruction = mutableMethod.getInstructions().first {
if (it.opcode != Opcode.IPUT_OBJECT) return@first false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"

View File

@ -2,17 +2,11 @@ package app.revanced.patches.youtube.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
import app.revanced.patches.youtube.misc.integrations.fingerprints.*
import app.revanced.patches.youtube.misc.integrations.fingerprints.ApplicationInitFingerprint
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(
ApplicationInitFingerprint,
StandalonePlayerActivityFingerprint,
RemoteEmbeddedPlayerFingerprint,
RemoteEmbedFragmentFingerprint,
EmbeddedPlayerControlsOverlayFingerprint,
EmbeddedPlayerFingerprint,
APIPlayerServiceFingerprint,
),
)

View File

@ -1,17 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* For embedded playback.
* It appears this hook may no longer be needed as one of the constructor parameters is the already hooked
* [EmbeddedPlayerControlsOverlayFingerprint]
*/
internal object APIPlayerServiceFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/google/android/apps/youtube/embeddedplayer/service/service/jar/ApiPlayerService;" },
// Integrations context is the first method parameter.
contextRegisterResolver = { it.implementation!!.registerCount - it.parameters.size }
)

View File

@ -1,22 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* For embedded playback inside Google Play store (and probably other situations as well).
*
* Note: this fingerprint may no longer be needed, as it appears
* [RemoteEmbedFragmentFingerprint] may be set before this hook is called.
*/
internal object EmbeddedPlayerControlsOverlayFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.CONSTRUCTOR,
returnType = "V",
parameters = listOf("Landroid/content/Context;", "L", "L"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass.startsWith("Lcom/google/android/apps/youtube/embeddedplayer/service/ui/overlays/controlsoverlay/remoteloaded/")
},
// Integrations context is the first method parameter.
contextRegisterResolver = { it.implementation!!.registerCount - it.parameters.size }
)

View File

@ -1,20 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* For embedded playback inside the Google app (such as the in app 'discover' tab).
*
* Note: this fingerprint may or may not be needed, as
* [RemoteEmbedFragmentFingerprint] might be set before this is called.
*/
internal object EmbeddedPlayerFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "L",
parameters = listOf("L", "L", "Landroid/content/Context;"),
strings = listOf("android.hardware.type.television"), // String is also found in other classes
// Integrations context is the third method parameter.
contextRegisterResolver = { it.implementation!!.registerCount - it.parameters.size + 2 }
)

View File

@ -1,19 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* For embedded playback. Likely covers Google Play store and other Google products.
*/
internal object RemoteEmbedFragmentFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
parameters = listOf("Landroid/content/Context;", "L", "L"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/google/android/apps/youtube/embeddedplayer/service/jar/client/RemoteEmbedFragment;"
},
// Integrations context is the first method parameter.
contextRegisterResolver = { it.implementation!!.registerCount - it.parameters.size }
)

View File

@ -1,19 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* For embedded playback inside 3rd party android app (such as 3rd party Reddit apps).
*/
internal object RemoteEmbeddedPlayerFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.CONSTRUCTOR,
returnType = "V",
parameters = listOf("Landroid/content/Context;", "L", "L", "Z"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/google/android/youtube/api/jar/client/RemoteEmbeddedPlayer;"
},
// Integrations context is the first method parameter.
contextRegisterResolver = { it.implementation!!.registerCount - it.parameters.size }
)

View File

@ -1,23 +0,0 @@
package app.revanced.patches.youtube.misc.integrations.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Old API activity to embed YouTube into 3rd party Android apps.
*
* In 2023 supported was ended and is no longer available,
* but this may still be used by older apps:
* https://developers.google.com/youtube/android/player
*/
internal object StandalonePlayerActivityFingerprint : IntegrationsFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("L"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/google/android/youtube/api/StandalonePlayerActivity;"
&& methodDef.name == "onCreate"
},
// Integrations context is the Activity itself.
)

View File

@ -10,47 +10,48 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
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.ABUriParserLegacyFingerprint
import app.revanced.patches.youtube.misc.links.fingerprints.HTTPUriParserFingerprint
import app.revanced.patches.youtube.misc.links.fingerprints.HTTPUriParserLegacyFingerprint
import app.revanced.patches.youtube.misc.playservice.VersionCheckPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Bypass URL redirects",
description = "Adds an option to bypass URL redirects and open the original URL directly.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
VersionCheckPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
[
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.38.44",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
],
),
],
)
@Suppress("unused")
object BypassURLRedirectsPatch : BytecodePatch(
setOf(ABUriParserFingerprint, HTTPUriParserFingerprint),
setOf(
ABUriParserFingerprint,
ABUriParserLegacyFingerprint,
HTTPUriParserFingerprint,
HTTPUriParserLegacyFingerprint,
),
) {
override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class)
@ -59,14 +60,22 @@ object BypassURLRedirectsPatch : BytecodePatch(
SwitchPreference("revanced_bypass_url_redirects"),
)
mapOf(
ABUriParserFingerprint to 7, // Offset to Uri.parse.
HTTPUriParserFingerprint to 0, // Offset to Uri.parse.
).map { (fingerprint, offset) ->
fingerprint.resultOrThrow() to offset
}.forEach { (result, offset) ->
result.mutableMethod.apply {
val insertIndex = result.scanResult.patternScanResult!!.startIndex + offset
val fingerprints =
if (VersionCheckPatch.is_19_33_or_greater)
arrayOf(
ABUriParserFingerprint,
HTTPUriParserFingerprint
)
else arrayOf(
ABUriParserLegacyFingerprint,
HTTPUriParserLegacyFingerprint
)
fingerprints.forEach { fingerprint ->
fingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex = findUriParseIndex()
val uriStringRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
replaceInstruction(
@ -80,3 +89,10 @@ object BypassURLRedirectsPatch : BytecodePatch(
}
}
}
internal fun Method.findUriParseIndex(): Int = indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.returnType == "Landroid/net/Uri;" &&
reference.name == "parse"
}
}

View File

@ -24,30 +24,11 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
CompatiblePackage(
"com.google.android.youtube",
[
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.43",
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.38",
"19.10.39",
"19.11.43",
"19.12.41",
"19.13.37",
"19.14.43",
"19.15.36",
"19.16.39",
"19.25.37",
"19.34.42",
]
)
]

View File

@ -2,32 +2,21 @@ package app.revanced.patches.youtube.misc.links.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.misc.links.BypassURLRedirectsPatch.findUriParseIndex
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Target 19.33+
*/
internal object ABUriParserFingerprint : MethodFingerprint(
returnType = "Ljava/lang/Object",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object"),
opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
Opcode.CHECK_CAST,
strings = listOf(
"Found entityKey=`",
"` that does not contain a PlaylistVideoEntityId message as it's identifier."
),
customFingerprint = custom@{ methodDef, classDef ->
// This method is always called "a" because this kind of class always has a single (non synthetic) method.
if (methodDef.name != "a") return@custom false
val count = classDef.methods.count()
count == 2 || count == 3
},
customFingerprint = { methodDef, _ ->
methodDef.findUriParseIndex() >= 0
}
)

Some files were not shown because too many files have changed in this diff Show More