feat(YouTube): Add support version 20.02.38

This commit is contained in:
inotia00 2025-01-17 14:58:20 +09:00
parent 316038cd75
commit c8cc8c4bda
22 changed files with 358 additions and 178 deletions

View File

@ -61,6 +61,10 @@ public class ShortsPatch {
return Settings.DISABLE_RESUMING_SHORTS_PLAYER.get(); return Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
} }
public static boolean disableResumingStartupShortsPlayer(boolean original) {
return !Settings.DISABLE_RESUMING_SHORTS_PLAYER.get() && original;
}
public static boolean enableShortsTimeStamp(boolean original) { public static boolean enableShortsTimeStamp(boolean original) {
return ENABLE_TIME_STAMP || original; return ENABLE_TIME_STAMP || original;
} }

View File

@ -16,6 +16,7 @@ import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstStringInstruction
import app.revanced.util.indexOfFirstStringInstructionOrThrow import app.revanced.util.indexOfFirstStringInstructionOrThrow
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
@ -27,6 +28,7 @@ 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.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
@ -42,6 +44,8 @@ private var filterCount = 0
internal lateinit var addLithoFilter: (String) -> Unit internal lateinit var addLithoFilter: (String) -> Unit
private set private set
internal var emptyComponentLabel = ""
val lithoFilterPatch = bytecodePatch( val lithoFilterPatch = bytecodePatch(
description = "lithoFilterPatch", description = "lithoFilterPatch",
) { ) {
@ -73,6 +77,8 @@ val lithoFilterPatch = bytecodePatch(
return-object v0 return-object v0
""" """
emptyComponentLabel = label
Pair(this, label) Pair(this, label)
} }
} }
@ -121,17 +127,25 @@ val lithoFilterPatch = bytecodePatch(
val stringBuilderRegister = val stringBuilderRegister =
getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
val emptyStringIndex = indexOfFirstStringInstructionOrThrow("") val emptyStringIndex = indexOfFirstStringInstruction("")
val relativeIndex = if (emptyStringIndex > -1) {
emptyStringIndex
} else {
val separatorIndex = indexOfFirstStringInstructionOrThrow("|")
indexOfFirstInstructionOrThrow(separatorIndex) {
opcode == Opcode.NEW_INSTANCE &&
getReference<TypeReference>()?.type == "Ljava/lang/StringBuilder;"
}
}
val identifierRegister = getInstruction<TwoRegisterInstruction>( val identifierRegister = getInstruction<TwoRegisterInstruction>(
indexOfFirstInstructionReversedOrThrow(emptyStringIndex) { indexOfFirstInstructionReversedOrThrow(relativeIndex) {
opcode == Opcode.IPUT_OBJECT opcode == Opcode.IPUT_OBJECT
&& getReference<FieldReference>()?.type == "Ljava/lang/String;" && getReference<FieldReference>()?.type == "Ljava/lang/String;"
} }
).registerA ).registerA
val objectRegister = getInstruction<FiveRegisterInstruction>( val objectRegister = getInstruction<FiveRegisterInstruction>(
indexOfFirstInstructionOrThrow(emptyStringIndex) { indexOfFirstInstructionOrThrow(relativeIndex, Opcode.INVOKE_VIRTUAL)
opcode == Opcode.INVOKE_VIRTUAL
}
).registerC ).registerC
val insertIndex = stringBuilderIndex + 1 val insertIndex = stringBuilderIndex + 1

View File

@ -16,6 +16,7 @@ import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction 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.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@ -40,11 +41,11 @@ val textComponentPatch = bytecodePatch(
spannedIndex = indexOfSpannableStringInstruction(this) spannedIndex = indexOfSpannableStringInstruction(this)
spannedRegister = getInstruction<FiveRegisterInstruction>(spannedIndex).registerC spannedRegister = getInstruction<FiveRegisterInstruction>(spannedIndex).registerC
spannedContextRegister = spannedContextRegister =
getInstruction<TwoRegisterInstruction>(0).registerA getInstruction<OneRegisterInstruction>(spannedIndex + 1).registerA
replaceInstruction( replaceInstruction(
spannedIndex, spannedIndex,
"nop" "move-object/from16 v$spannedContextRegister, p0"
) )
addInstruction( addInstruction(
++spannedIndex, ++spannedIndex,

View File

@ -10,6 +10,7 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.emptyComponentLabel
import app.revanced.patches.shared.mainactivity.onCreateMethod import app.revanced.patches.shared.mainactivity.onCreateMethod
import app.revanced.patches.youtube.utils.bottomsheet.bottomSheetHookPatch import app.revanced.patches.youtube.utils.bottomsheet.bottomSheetHookPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
@ -21,6 +22,9 @@ import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_FEED_COMPONENTS import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_FEED_COMPONENTS
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.bar import app.revanced.patches.youtube.utils.resourceid.bar
import app.revanced.patches.youtube.utils.resourceid.captionToggleContainer import app.revanced.patches.youtube.utils.resourceid.captionToggleContainer
import app.revanced.patches.youtube.utils.resourceid.channelListSubMenu import app.revanced.patches.youtube.utils.resourceid.channelListSubMenu
@ -78,6 +82,7 @@ val feedComponentsPatch = bytecodePatch(
sharedResourceIdPatch, sharedResourceIdPatch,
settingsPatch, settingsPatch,
bottomSheetHookPatch, bottomSheetHookPatch,
versionCheckPatch,
) )
execute { execute {
@ -223,19 +228,22 @@ val feedComponentsPatch = bytecodePatch(
// region patch for hide subscriptions channel section for tablet // region patch for hide subscriptions channel section for tablet
arrayOf( // Integrated as a litho component since YouTube 20.02.
channelListSubMenuTabletFingerprint, if (!is_20_02_or_greater) {
channelListSubMenuTabletSyntheticFingerprint arrayOf(
).forEach { fingerprint -> channelListSubMenuTabletFingerprint,
fingerprint.methodOrThrow().apply { channelListSubMenuTabletSyntheticFingerprint
addInstructionsWithLabels( ).forEach { fingerprint ->
0, """ fingerprint.methodOrThrow().apply {
invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideSubscriptionsChannelSection()Z addInstructionsWithLabels(
move-result v0 0, """
if-eqz v0, :show invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideSubscriptionsChannelSection()Z
return-void move-result v0
""", ExternalLabel("show", getInstruction(0)) if-eqz v0, :show
) return-void
""", ExternalLabel("show", getInstruction(0))
)
}
} }
} }
@ -287,29 +295,27 @@ val feedComponentsPatch = bytecodePatch(
it.method.apply { it.method.apply {
val freeRegister = implementation!!.registerCount - parameters.size - 2 val freeRegister = implementation!!.registerCount - parameters.size - 2
val insertIndex = indexOfFirstInstructionOrThrow { val insertIndex = indexOfFirstInstructionOrThrow {
val reference = ((this as? ReferenceInstruction)?.reference as? MethodReference) val reference = getReference<MethodReference>()
reference?.parameterTypes?.size == 1 && reference?.parameterTypes?.size == 1 &&
reference.parameterTypes.first() == "[B" && reference.parameterTypes.first() == "[B" &&
reference.returnType.startsWith("L") reference.returnType.startsWith("L")
} }
val objectIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_OBJECT) val objectOpcode = if (is_18_34_or_greater)
Opcode.IGET_OBJECT
else
Opcode.MOVE_OBJECT
val objectIndex = indexOfFirstInstructionReversedOrThrow(insertIndex, objectOpcode)
val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA val objectRegister = getInstruction<TwoRegisterInstruction>(objectIndex).registerA
val jumpIndex = it.patternMatch!!.startIndex
addInstructionsWithLabels( addInstructionsWithLabels(
insertIndex, """ insertIndex, """
invoke-static {v$objectRegister, v$freeRegister}, $FEED_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z invoke-static {v$objectRegister, p3}, $FEED_COMPONENTS_FILTER_CLASS_DESCRIPTOR->filterMixPlaylists(Ljava/lang/Object;[B)Z
move-result v$freeRegister move-result v$freeRegister
if-nez v$freeRegister, :filter if-eqz v$freeRegister, :ignore
""", ExternalLabel("filter", getInstruction(jumpIndex)) """ + emptyComponentLabel,
) ExternalLabel("ignore", getInstruction(insertIndex))
addInstruction(
0,
"move-object/from16 v$freeRegister, p3"
) )
} }
} }

View File

@ -87,7 +87,6 @@ internal val floatingMicrophoneFingerprint = legacyFingerprint(
opcodes = listOf( opcodes = listOf(
Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ, Opcode.IF_EQZ,
Opcode.RETURN_VOID
), ),
literals = listOf(fab), literals = listOf(fab),
) )

View File

@ -20,6 +20,7 @@ import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_LAYOUT_COMPONENTS
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.accountSwitcherAccessibility import app.revanced.patches.youtube.utils.resourceid.accountSwitcherAccessibility
import app.revanced.patches.youtube.utils.resourceid.fab
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
@ -154,18 +155,17 @@ val layoutComponentsPatch = bytecodePatch(
// region patch for hide floating microphone // region patch for hide floating microphone
floatingMicrophoneFingerprint.matchOrThrow().let { floatingMicrophoneFingerprint.methodOrThrow().apply {
it.method.apply { val literalIndex = indexOfFirstLiteralInstructionOrThrow(fab)
val insertIndex = it.patternMatch!!.startIndex val booleanIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.IGET_BOOLEAN)
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA val insertRegister = getInstruction<TwoRegisterInstruction>(booleanIndex).registerA
addInstructions( addInstructions(
insertIndex + 1, """ booleanIndex + 1, """
invoke-static {v$register}, $GENERAL_CLASS_DESCRIPTOR->hideFloatingMicrophone(Z)Z invoke-static {v$insertRegister}, $GENERAL_CLASS_DESCRIPTOR->hideFloatingMicrophone(Z)Z
move-result v$register move-result v$insertRegister
""" """
) )
}
} }
// endregion // endregion

View File

@ -15,6 +15,7 @@ import app.revanced.patches.youtube.utils.castbutton.hookToolBarCastButton
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.TOOLBAR_COMPONENTS import app.revanced.patches.youtube.utils.patch.PatchList.TOOLBAR_COMPONENTS
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.actionBarRingoBackground import app.revanced.patches.youtube.utils.resourceid.actionBarRingoBackground
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
@ -265,7 +266,11 @@ val toolBarComponentsPatch = bytecodePatch(
// region patch for hide search term thumbnail // region patch for hide search term thumbnail
createSearchSuggestionsFingerprint.methodOrThrow().apply { createSearchSuggestionsFingerprint.methodOrThrow().apply {
val relativeIndex = indexOfFirstLiteralInstructionOrThrow(40L) val literal = if (is_19_46_or_greater)
32L
else
40L
val relativeIndex = indexOfFirstLiteralInstructionOrThrow(literal)
val replaceIndex = indexOfFirstInstructionReversedOrThrow(relativeIndex) { val replaceIndex = indexOfFirstInstructionReversedOrThrow(relativeIndex) {
opcode == Opcode.INVOKE_VIRTUAL && opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() == "Landroid/widget/ImageView;->setVisibility(I)V" getReference<MethodReference>()?.toString() == "Landroid/widget/ImageView;->setVisibility(I)V"

View File

@ -23,6 +23,8 @@ import app.revanced.patches.youtube.utils.extension.Constants.SPANS_PATH
import app.revanced.patches.youtube.utils.fix.suggestedvideoendscreen.suggestedVideoEndScreenPatch import app.revanced.patches.youtube.utils.fix.suggestedvideoendscreen.suggestedVideoEndScreenPatch
import app.revanced.patches.youtube.utils.patch.PatchList.PLAYER_COMPONENTS import app.revanced.patches.youtube.utils.patch.PatchList.PLAYER_COMPONENTS
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.darkBackground import app.revanced.patches.youtube.utils.resourceid.darkBackground
import app.revanced.patches.youtube.utils.resourceid.fadeDurationFast import app.revanced.patches.youtube.utils.resourceid.fadeDurationFast
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
@ -278,9 +280,15 @@ val playerComponentsPatch = bytecodePatch(
speedOverlayPatch, speedOverlayPatch,
suggestedVideoEndScreenPatch, suggestedVideoEndScreenPatch,
videoInformationPatch, videoInformationPatch,
versionCheckPatch,
) )
execute { execute {
var settingArray = arrayOf(
"PREFERENCE_SCREEN: PLAYER",
"SETTINGS: PLAYER_COMPONENTS"
)
fun MutableMethod.getAllLiteralComponent( fun MutableMethod.getAllLiteralComponent(
startIndex: Int, startIndex: Int,
endIndex: Int endIndex: Int
@ -563,30 +571,34 @@ val playerComponentsPatch = bytecodePatch(
) )
} }
youtubeControlsOverlayFingerprint.methodOrThrow().apply { if (!is_20_02_or_greater) {
val insertIndex = youtubeControlsOverlayFingerprint.methodOrThrow().apply {
indexOfFirstLiteralInstructionOrThrow(seekUndoEduOverlayStub) val insertIndex =
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA indexOfFirstLiteralInstructionOrThrow(seekUndoEduOverlayStub)
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
val onClickListenerIndex = indexOfFirstInstructionOrThrow(insertIndex) { val onClickListenerIndex = indexOfFirstInstructionOrThrow(insertIndex) {
opcode == Opcode.INVOKE_VIRTUAL && opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setOnClickListener" getReference<MethodReference>()?.name == "setOnClickListener"
} }
val constComponent = getFirstLiteralComponent(insertIndex, onClickListenerIndex - 1) val constComponent = getFirstLiteralComponent(insertIndex, onClickListenerIndex - 1)
if (constComponent.isNotEmpty()) { if (constComponent.isNotEmpty()) {
addInstruction( addInstruction(
onClickListenerIndex + 2, onClickListenerIndex + 2,
constComponent constComponent
)
}
addInstructionsWithLabels(
insertIndex, """
invoke-static {}, $PLAYER_CLASS_DESCRIPTOR->hideSeekUndoMessage()Z
move-result v$insertRegister
if-nez v$insertRegister, :default
""", ExternalLabel("default", getInstruction(onClickListenerIndex + 1))
) )
settingArray += "SETTINGS: HIDE_SEEK_UNDO_MESSAGE"
} }
addInstructionsWithLabels(
insertIndex, """
invoke-static {}, $PLAYER_CLASS_DESCRIPTOR->hideSeekUndoMessage()Z
move-result v$insertRegister
if-nez v$insertRegister, :default
""", ExternalLabel("default", getInstruction(onClickListenerIndex + 1))
)
} }
// endregion // endregion
@ -652,10 +664,7 @@ val playerComponentsPatch = bytecodePatch(
// region add settings // region add settings
addPreference( addPreference(
arrayOf( settingArray,
"PREFERENCE_SCREEN: PLAYER",
"SETTINGS: PLAYER_COMPONENTS"
),
PLAYER_COMPONENTS PLAYER_COMPONENTS
) )

View File

@ -7,6 +7,7 @@ import app.revanced.patches.youtube.utils.resourceid.quickActionsElementContaine
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.or import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.util.MethodUtil
internal val broadcastReceiverFingerprint = legacyFingerprint( internal val broadcastReceiverFingerprint = legacyFingerprint(
name = "broadcastReceiverFingerprint", name = "broadcastReceiverFingerprint",
@ -45,12 +46,15 @@ internal val playerTitleViewFingerprint = legacyFingerprint(
literals = listOf(playerVideoTitleView), literals = listOf(playerVideoTitleView),
) )
internal val quickActionsElementFingerprint = legacyFingerprint( internal val quickActionsElementSyntheticFingerprint = legacyFingerprint(
name = "quickActionsElementFingerprint", name = "quickActionsElementSyntheticFingerprint",
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;"), parameters = listOf("Landroid/view/View;"),
literals = listOf(quickActionsElementContainer), literals = listOf(quickActionsElementContainer),
customFingerprint = { _, classDef ->
AccessFlags.SYNTHETIC.isSet(classDef.accessFlags)
}
) )
internal val relatedEndScreenResultsFingerprint = legacyFingerprint( internal val relatedEndScreenResultsFingerprint = legacyFingerprint(

View File

@ -19,6 +19,7 @@ import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_PATH import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_PATH
import app.revanced.patches.youtube.utils.fullscreen.fullscreenButtonHookPatch import app.revanced.patches.youtube.utils.fullscreen.fullscreenButtonHookPatch
import app.revanced.patches.youtube.utils.indexOfFocusableInTouchModeInstruction
import app.revanced.patches.youtube.utils.layoutConstructorFingerprint import app.revanced.patches.youtube.utils.layoutConstructorFingerprint
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.patch.PatchList.FULLSCREEN_COMPONENTS import app.revanced.patches.youtube.utils.patch.PatchList.FULLSCREEN_COMPONENTS
@ -101,7 +102,6 @@ val fullscreenComponentsPatch = bytecodePatch(
"invoke-static {v$targetRegister}, " + "invoke-static {v$targetRegister}, " +
"$PLAYER_CLASS_DESCRIPTOR->disableEngagementPanels(Landroidx/coordinatorlayout/widget/CoordinatorLayout;)V" "$PLAYER_CLASS_DESCRIPTOR->disableEngagementPanels(Landroidx/coordinatorlayout/widget/CoordinatorLayout;)V"
) )
} }
playerTitleViewFingerprint.methodOrThrow().apply { playerTitleViewFingerprint.methodOrThrow().apply {
@ -190,7 +190,7 @@ val fullscreenComponentsPatch = bytecodePatch(
// region patch for quick actions // region patch for quick actions
quickActionsElementFingerprint.methodOrThrow().apply { quickActionsElementSyntheticFingerprint.methodOrThrow().apply {
val containerCalls = implementation!!.instructions.withIndex() val containerCalls = implementation!!.instructions.withIndex()
.filter { instruction -> .filter { instruction ->
(instruction.value as? WideLiteralInstruction)?.wideLiteral == quickActionsElementContainer (instruction.value as? WideLiteralInstruction)?.wideLiteral == quickActionsElementContainer
@ -219,11 +219,13 @@ val fullscreenComponentsPatch = bytecodePatch(
// region patch for compact control overlay // region patch for compact control overlay
youtubeControlsOverlayFingerprint.methodOrThrow().apply { youtubeControlsOverlayFingerprint.methodOrThrow().apply {
val targetIndex = indexOfFirstInstructionOrThrow { val targetIndex = indexOfFocusableInTouchModeInstruction(this)
opcode == Opcode.INVOKE_VIRTUAL && val walkerIndex = indexOfFirstInstructionOrThrow(targetIndex) {
getReference<MethodReference>()?.name == "setFocusableInTouchMode" val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
reference?.returnType == "Z" &&
reference.parameterTypes.size == 1
} }
val walkerIndex = indexOfFirstInstructionOrThrow(targetIndex, Opcode.INVOKE_STATIC)
val walkerMethod = getWalkerMethod(walkerIndex) val walkerMethod = getWalkerMethod(walkerIndex)
walkerMethod.apply { walkerMethod.apply {

View File

@ -10,7 +10,7 @@ import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
/** /**
* This fingerprint is compatible with all YouTube versions after v18.15.40. * YouTube v18.15.40 ~ YouTube 19.46.42
*/ */
internal val userWasInShortsABConfigFingerprint = legacyFingerprint( internal val userWasInShortsABConfigFingerprint = legacyFingerprint(
name = "userWasInShortsABConfigFingerprint", name = "userWasInShortsABConfigFingerprint",
@ -27,10 +27,35 @@ internal fun indexOfOptionalInstruction(method: Method) =
getReference<MethodReference>().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;" getReference<MethodReference>().toString() == "Lj${'$'}/util/Optional;->of(Ljava/lang/Object;)Lj${'$'}/util/Optional;"
} }
/**
* YouTube 19.47.53 ~
*/
internal val userWasInShortsABConfigAlternativeFingerprint = legacyFingerprint(
name = "userWasInShortsABConfigAlternativeFingerprint",
returnType = "V",
parameters = listOf("I"),
opcodes = listOf(Opcode.OR_INT_LIT8),
strings = listOf("alias", "null"),
)
/**
* ~ YouTube 19.50.42
*/
internal val userWasInShortsFingerprint = legacyFingerprint( internal val userWasInShortsFingerprint = legacyFingerprint(
name = "userWasInShortsFingerprint", name = "userWasInShortsFingerprint",
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"), parameters = listOf("Ljava/lang/Object;"),
strings = listOf("Failed to read user_was_in_shorts proto after successful warmup") strings = listOf("Failed to read user_was_in_shorts proto after successful warmup")
)
/**
* YouTube 20.02.08 ~
*/
internal val userWasInShortsAlternativeFingerprint = legacyFingerprint(
name = "userWasInShortsAlternativeFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"),
strings = listOf("userIsInShorts: ")
) )

View File

@ -1,20 +1,28 @@
package app.revanced.patches.youtube.shorts.startupshortsreset package app.revanced.patches.youtube.shorts.startupshortsreset
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_RESUMING_SHORTS_ON_STARTUP import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_RESUMING_SHORTS_ON_STARTUP
import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstStringInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -27,12 +35,14 @@ val resumingShortsOnStartupPatch = bytecodePatch(
) { ) {
compatibleWith(COMPATIBLE_PACKAGE) compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(settingsPatch) dependsOn(
settingsPatch,
versionCheckPatch,
)
execute { execute {
userWasInShortsABConfigFingerprint.methodOrThrow().apply { fun MutableMethod.hookUserWasInShortsABConfig(startIndex: Int) {
val startIndex = indexOfOptionalInstruction(this)
val walkerIndex = implementation!!.instructions.let { val walkerIndex = implementation!!.instructions.let {
val subListIndex = val subListIndex =
it.subList(startIndex, startIndex + 20).indexOfFirst { instruction -> it.subList(startIndex, startIndex + 20).indexOfFirst { instruction ->
@ -64,29 +74,65 @@ val resumingShortsOnStartupPatch = bytecodePatch(
} }
} }
userWasInShortsFingerprint.methodOrThrow().apply { if (is_19_46_or_greater) {
val listenableInstructionIndex = indexOfFirstInstructionOrThrow { userWasInShortsABConfigAlternativeFingerprint.methodOrThrow().apply {
opcode == Opcode.INVOKE_INTERFACE && val stringIndex = indexOfFirstStringInstructionOrThrow("null")
getReference<MethodReference>()?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" && val startIndex = indexOfFirstInstructionOrThrow(stringIndex, Opcode.OR_INT_LIT8)
getReference<MethodReference>()?.name == "isDone" hookUserWasInShortsABConfig(startIndex)
} }
val originalInstructionRegister = } else {
getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC userWasInShortsABConfigFingerprint.methodOrThrow().apply {
val freeRegister = val startIndex = indexOfOptionalInstruction(this)
getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA hookUserWasInShortsABConfig(startIndex)
}
}
addInstructionsWithLabels( if (is_20_02_or_greater) {
listenableInstructionIndex + 1, userWasInShortsAlternativeFingerprint.matchOrThrow().let {
""" it.method.apply {
invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z val stringIndex = it.stringMatches!!.first().index
move-result v$freeRegister val booleanValueIndex = indexOfFirstInstructionReversedOrThrow(stringIndex) {
if-eqz v$freeRegister, :show opcode == Opcode.INVOKE_VIRTUAL &&
return-void getReference<MethodReference>()?.name == "booleanValue"
:show }
invoke-interface {v$originalInstructionRegister}, Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z val booleanValueRegister =
getInstruction<OneRegisterInstruction>(booleanValueIndex + 1).registerA
addInstructions(
booleanValueIndex + 2, """
invoke-static {v$booleanValueRegister}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer(Z)Z
move-result v$booleanValueRegister
"""
)
}
}
} else {
userWasInShortsFingerprint.methodOrThrow().apply {
val listenableInstructionIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_INTERFACE &&
reference?.definingClass == "Lcom/google/common/util/concurrent/ListenableFuture;" &&
reference.name == "isDone"
}
val originalInstructionRegister =
getInstruction<FiveRegisterInstruction>(listenableInstructionIndex).registerC
val freeRegister =
getInstruction<OneRegisterInstruction>(listenableInstructionIndex + 1).registerA
addInstructionsWithLabels(
listenableInstructionIndex + 1,
""" """
) invoke-static {}, $SHORTS_CLASS_DESCRIPTOR->disableResumingStartupShortsPlayer()Z
removeInstruction(listenableInstructionIndex) move-result v$freeRegister
if-eqz v$freeRegister, :show
return-void
:show
invoke-interface {v$originalInstructionRegister}, Lcom/google/common/util/concurrent/ListenableFuture;->isDone()Z
"""
)
removeInstruction(listenableInstructionIndex)
}
} }
// region add settings // region add settings

View File

@ -27,14 +27,12 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val bottomSheetMenuItemBuilderFingerprint = legacyFingerprint( internal val bottomSheetMenuItemBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuItemBuilderFingerprint", name = "bottomSheetMenuItemBuilderFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L", returnType = "L",
parameters = listOf("L"), parameters = listOf("L"),
opcodes = listOf( opcodes = listOf(
Opcode.INVOKE_STATIC, Opcode.IGET,
Opcode.MOVE_RESULT_OBJECT, Opcode.AND_INT_LIT16,
Opcode.INVOKE_STATIC, Opcode.IF_EQZ,
Opcode.MOVE_RESULT_OBJECT
), ),
strings = listOf("Text missing for BottomSheetMenuItem."), strings = listOf("Text missing for BottomSheetMenuItem."),
customFingerprint = { method, _ -> customFingerprint = { method, _ ->
@ -251,9 +249,19 @@ internal val youtubeControlsOverlayFingerprint = legacyFingerprint(
fadeDurationFast, fadeDurationFast,
insetOverlayViewLayout, insetOverlayViewLayout,
scrimOverlay, scrimOverlay,
seekUndoEduOverlayStub // Removed in YouTube 20.02.38+
// seekUndoEduOverlayStub
), ),
customFingerprint = { method, _ ->
indexOfFocusableInTouchModeInstruction(method) >= 0
}
) )
internal fun indexOfFocusableInTouchModeInstruction(method: Method) =
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setFocusableInTouchMode"
}
const val PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR = const val PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR =
"Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"

View File

@ -15,7 +15,8 @@ internal object Constants {
"18.48.39", // This is the last version that do not use Rolling Number. "18.48.39", // This is the last version that do not use Rolling Number.
"19.05.36", // This is the last version with the least YouTube experimental flag. "19.05.36", // This is the last version with the least YouTube experimental flag.
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works. "19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
"19.44.39", // This is the latest version supported by the RVX patch. "19.44.39", // This is the last supported version for 2024.
"20.02.38", // This is the latest version supported by the RVX patch.
) )
) )
} }

View File

@ -7,56 +7,91 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH import app.revanced.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.playservice.is_20_02_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.util.addStaticFieldToExtension import app.revanced.util.addStaticFieldToExtension
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
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.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import kotlin.collections.mutableListOf
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR = private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;" "$EXTENSION_PATH/utils/VideoUtils;"
internal lateinit var enterFullscreenMethod: MutableMethod internal var enterFullscreenMethods = mutableListOf<MutableMethod>()
val fullscreenButtonHookPatch = bytecodePatch( val fullscreenButtonHookPatch = bytecodePatch(
description = "fullscreenButtonHookPatch" description = "fullscreenButtonHookPatch"
) { ) {
dependsOn(sharedExtensionPatch) dependsOn(
sharedExtensionPatch,
versionCheckPatch,
)
execute { execute {
val (referenceClass, fullscreenActionClass) = with( fun getParameters(): Pair<MutableMethod, String> {
nextGenWatchLayoutFullscreenModeFingerprint.methodOrThrow() nextGenWatchLayoutFullscreenModeFingerprint.methodOrThrow().apply {
) { val methodIndex = indexOfFirstInstructionReversedOrThrow {
val targetIndex = indexOfFirstInstructionReversedOrThrow { opcode == Opcode.INVOKE_DIRECT &&
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.parameterTypes?.size == 2
getReference<MethodReference>()?.parameterTypes?.size == 2 }
} val fieldIndex =
val targetReference = indexOfFirstInstructionReversedOrThrow(methodIndex, Opcode.IGET_OBJECT)
getInstruction<ReferenceInstruction>(targetIndex).reference as MethodReference val fullscreenActionClass =
(getInstruction<ReferenceInstruction>(fieldIndex).reference as FieldReference).type
Pair(targetReference.definingClass, targetReference.parameterTypes[1].toString()) if (is_20_02_or_greater) {
val setAnimatorListenerIndex =
indexOfFirstInstructionOrThrow(methodIndex, Opcode.INVOKE_VIRTUAL)
getWalkerMethod(setAnimatorListenerIndex).apply {
val addListenerIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "addListener"
}
val animatorListenerAdapterClass = getInstruction<ReferenceInstruction>(
indexOfFirstInstructionReversedOrThrow(addListenerIndex, Opcode.NEW_INSTANCE)
).reference.toString()
return Pair(
findMethodOrThrow(animatorListenerAdapterClass) { parameters.isEmpty() },
fullscreenActionClass
)
}
} else {
val animatorListenerClass =
(getInstruction<ReferenceInstruction>(methodIndex).reference as MethodReference).definingClass
return Pair(
findMethodOrThrow(animatorListenerClass) { parameters == listOf("I") },
fullscreenActionClass
)
}
}
} }
val (animatorListenerMethod, fullscreenActionClass) = getParameters()
val (enterFullscreenReference, exitFullscreenReference, opcodeName) = val (enterFullscreenReference, exitFullscreenReference, opcodeName) =
with(findMethodOrThrow(referenceClass) { parameters == listOf("I") }) { with(animatorListenerMethod) {
val enterFullscreenIndex = indexOfFirstInstructionOrThrow { val enterFullscreenIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>() val reference = getReference<MethodReference>()
reference?.returnType == "V" && reference?.returnType == "V" &&
reference.definingClass == fullscreenActionClass && reference.definingClass == fullscreenActionClass &&
reference.parameterTypes.size == 0 reference.parameterTypes.isEmpty()
} }
val exitFullscreenIndex = indexOfFirstInstructionReversedOrThrow { val exitFullscreenIndex = indexOfFirstInstructionReversedOrThrow {
val reference = getReference<MethodReference>() val reference = getReference<MethodReference>()
reference?.returnType == "V" && reference?.returnType == "V" &&
reference.definingClass == fullscreenActionClass && reference.definingClass == fullscreenActionClass &&
reference.parameterTypes.size == 0 reference.parameterTypes.isEmpty()
} }
val enterFullscreenReference = val enterFullscreenReference =
@ -68,18 +103,28 @@ val fullscreenButtonHookPatch = bytecodePatch(
val enterFullscreenClass = val enterFullscreenClass =
(enterFullscreenReference as MethodReference).definingClass (enterFullscreenReference as MethodReference).definingClass
enterFullscreenMethod = if (opcode == Opcode.INVOKE_INTERFACE) { if (opcode == Opcode.INVOKE_INTERFACE) {
classes.find { classDef -> classDef.interfaces.contains(enterFullscreenClass) } classes.forEach { classDef ->
?.let { classDef -> if (enterFullscreenMethods.size >= 2)
return@forEach
if (!classDef.interfaces.contains(enterFullscreenClass))
return@forEach
val enterFullscreenMethod =
proxy(classDef) proxy(classDef)
.mutableClass .mutableClass
.methods .methods
.find { method -> method.name == enterFullscreenReference.name } .find { method -> method.name == enterFullscreenReference.name }
} ?: throw PatchException("No matching classes: $enterFullscreenClass") ?: throw PatchException("No matching classes: $enterFullscreenClass")
} else {
findMethodOrThrow(enterFullscreenClass) { enterFullscreenMethods.add(enterFullscreenMethod)
name == enterFullscreenReference.name
} }
} else {
val enterFullscreenMethod =
findMethodOrThrow(enterFullscreenClass) {
name == enterFullscreenReference.name
}
enterFullscreenMethods.add(enterFullscreenMethod)
} }
Triple( Triple(

View File

@ -9,7 +9,7 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.fullscreen.enterFullscreenMethod import app.revanced.patches.youtube.utils.fullscreen.enterFullscreenMethods
import app.revanced.patches.youtube.utils.fullscreen.fullscreenButtonHookPatch import app.revanced.patches.youtube.utils.fullscreen.fullscreenButtonHookPatch
import app.revanced.patches.youtube.utils.playerButtonsResourcesFingerprint import app.revanced.patches.youtube.utils.playerButtonsResourcesFingerprint
import app.revanced.patches.youtube.utils.playerButtonsVisibilityFingerprint import app.revanced.patches.youtube.utils.playerButtonsVisibilityFingerprint
@ -122,10 +122,12 @@ private val playerControlsBytecodePatch = bytecodePatch(
// Reproduced only in RVX // Reproduced only in RVX
if (is_19_23_or_greater) { if (is_19_23_or_greater) {
enterFullscreenMethod.addInstruction( enterFullscreenMethods.forEach { method ->
0, method.addInstruction(
"invoke-static {}, $EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediately()V" 0,
) "invoke-static {}, $EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediately()V"
)
}
} }
// endregion // endregion

View File

@ -51,6 +51,8 @@ var is_19_44_or_greater = false
private set private set
var is_19_46_or_greater = false var is_19_46_or_greater = false
private set private set
var is_20_02_or_greater = false
private set
val versionCheckPatch = resourcePatch( val versionCheckPatch = resourcePatch(
description = "versionCheckPatch", description = "versionCheckPatch",
@ -89,5 +91,6 @@ val versionCheckPatch = resourcePatch(
is_19_43_or_greater = 244405000 <= playStoreServicesVersion is_19_43_or_greater = 244405000 <= playStoreServicesVersion
is_19_44_or_greater = 244505000 <= playStoreServicesVersion is_19_44_or_greater = 244505000 <= playStoreServicesVersion
is_19_46_or_greater = 244705000 <= playStoreServicesVersion is_19_46_or_greater = 244705000 <= playStoreServicesVersion
is_20_02_or_greater = 250299000 <= playStoreServicesVersion
} }
} }

View File

@ -2,11 +2,9 @@ package app.revanced.patches.youtube.utils.settings
import app.revanced.patches.youtube.utils.resourceid.appearance import app.revanced.patches.youtube.utils.resourceid.appearance
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal val themeSetterSystemFingerprint = legacyFingerprint( internal val themeSetterSystemFingerprint = legacyFingerprint(
name = "themeSetterSystemFingerprint", name = "themeSetterSystemFingerprint",
returnType = "L", returnType = "L",
opcodes = listOf(Opcode.RETURN_OBJECT),
literals = listOf(appearance), literals = listOf(appearance),
) )

View File

@ -1,12 +1,9 @@
package app.revanced.patches.youtube.utils.settings package app.revanced.patches.youtube.utils.settings
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_CLASS_DESCRIPTOR import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_CLASS_DESCRIPTOR
import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_PATH import app.revanced.patches.shared.extension.Constants.EXTENSION_UTILS_PATH
import app.revanced.patches.shared.mainactivity.injectConstructorMethodCall import app.revanced.patches.shared.mainactivity.injectConstructorMethodCall
@ -23,11 +20,14 @@ import app.revanced.patches.youtube.utils.patch.PatchList.SETTINGS_FOR_YOUTUBE
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.util.ResourceGroup import app.revanced.util.ResourceGroup
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode import app.revanced.util.copyXmlNode
import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.removeStringsElements import app.revanced.util.removeStringsElements
import app.revanced.util.valueOrThrow import app.revanced.util.valueOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import org.w3c.dom.Element import org.w3c.dom.Element
import java.nio.file.Files import java.nio.file.Files
@ -50,23 +50,16 @@ private val settingsBytecodePatch = bytecodePatch(
) )
execute { execute {
fun MutableMethod.injectCall(index: Int) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1, """
invoke-static {v$register}, $EXTENSION_THEME_METHOD_DESCRIPTOR
return-object v$register
"""
)
removeInstruction(index)
}
// apply the current theme of the settings page // apply the current theme of the settings page
themeSetterSystemFingerprint.matchOrThrow().let { themeSetterSystemFingerprint.methodOrThrow().apply {
it.method.apply { findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
injectCall(implementation!!.instructions.size - 1) val register = getInstruction<OneRegisterInstruction>(index).registerA
injectCall(it.patternMatch!!.startIndex)
addInstructionsAtControlFlowLabel(
index,
"invoke-static { v$register }, $EXTENSION_THEME_METHOD_DESCRIPTOR"
)
} }
} }

View File

@ -249,11 +249,13 @@ val videoInformationPatch = bytecodePatch(
) )
val literalIndex = indexOfFirstLiteralInstructionOrThrow(45368273L) val literalIndex = indexOfFirstLiteralInstructionOrThrow(45368273L)
val walkerIndex = val walkerIndex = indexOfFirstInstructionReversedOrThrow(literalIndex) {
indexOfFirstInstructionReversedOrThrow( val reference = getReference<MethodReference>()
literalIndex, (opcode == Opcode.INVOKE_VIRTUAL || opcode == Opcode.INVOKE_VIRTUAL_RANGE) &&
Opcode.INVOKE_VIRTUAL_RANGE reference?.definingClass == definingClass &&
) reference.parameterTypes.isEmpty() &&
reference.returnType == "V"
}
videoEndMethod = getWalkerMethod(walkerIndex) videoEndMethod = getWalkerMethod(walkerIndex)
} }

View File

@ -13,20 +13,12 @@ private val PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST = listOf(
"I", "I",
"I" "I"
) )
private val PLAYER_PARAMETER_ENDS_WITH_PARAMETER_LIST = listOf(
"Ljava/util/Set;",
"Ljava/lang/String;",
"Ljava/lang/String;",
"L",
"Z", // Appears to indicate if the video id is being opened or is currently playing.
"Z",
"Z"
)
internal val playerParameterBuilderFingerprint = legacyFingerprint( internal val playerParameterBuilderFingerprint = legacyFingerprint(
name = "playerParameterBuilderFingerprint", name = "playerParameterBuilderFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L", returnType = "L",
strings = listOf("psps"),
// 19.22 and earlier parameters are: // 19.22 and earlier parameters are:
// "Ljava/lang/String;", // VideoId. // "Ljava/lang/String;", // VideoId.
// "[B", // "[B",
@ -60,20 +52,41 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
customFingerprint = custom@{ method, _ -> customFingerprint = custom@{ method, _ ->
val parameterTypes = method.parameterTypes val parameterTypes = method.parameterTypes
val parameterSize = parameterTypes.size val parameterSize = parameterTypes.size
if (parameterSize != 13 && parameterSize != 14) { if (parameterSize < 13) {
return@custom false return@custom false
} }
val startsWithMethodParameterList = parameterTypes.slice(0..5) val startsWithMethodParameterList = parameterTypes.slice(0..5)
val endsWithMethodParameterList = parameterTypes.slice(parameterSize - 7..<parameterSize)
parametersEqual( parametersEqual(
PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST, PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST,
startsWithMethodParameterList startsWithMethodParameterList
) && )
parametersEqual(
PLAYER_PARAMETER_ENDS_WITH_PARAMETER_LIST,
endsWithMethodParameterList
)
} }
)
/**
* For targets 19.22 and earlier.
*/
private val PLAYER_PARAMETER_LEGACY_LIST = listOf(
"Ljava/lang/String;", // VideoId.
"[B",
"Ljava/lang/String;", // Player parameters proto buffer.
"Ljava/lang/String;",
"I",
"I",
"Ljava/util/Set;",
"Ljava/lang/String;",
"Ljava/lang/String;",
"L",
"Z", // Appears to indicate if the video id is being opened or is currently playing.
"Z",
"Z",
)
internal val playerParameterBuilderLegacyFingerprint = legacyFingerprint(
name = "playerParameterBuilderLegacyFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L",
parameters = PLAYER_PARAMETER_LEGACY_LIST,
) )

View File

@ -35,7 +35,7 @@ val playerResponseMethodHookPatch = bytecodePatch(
execute { execute {
playerParameterBuilderFingerprint.methodOrThrow().apply { playerParameterBuilderFingerprint.methodOrThrow().apply {
playerResponseMethod = this playerResponseMethod = this
parameterIsShortAndOpeningOrPlaying = parameters.size - 2 parameterIsShortAndOpeningOrPlaying = parameterTypes.indexOfFirst { it == "Z" } + 1
// On some app targets the method has too many registers pushing the parameters past v15. // On some app targets the method has too many registers pushing the parameters past v15.
// If needed, move the parameters to 4-bit registers so they can be passed to extension. // If needed, move the parameters to 4-bit registers so they can be passed to extension.
playerResponseMethodCopyRegisters = implementation!!.registerCount - playerResponseMethodCopyRegisters = implementation!!.registerCount -