diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java index f87708b9a..00d74d8c5 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/PlayerPatch.java @@ -426,9 +426,40 @@ public class PlayerPatch { imageView.setImageAlpha(PLAYER_OVERLAY_OPACITY_LEVEL); } + /** + * Used in YouTube 18.29.38 ~ 20.04.46. + */ + private static boolean isAutoPopupPanel; + + /** + * Used in YouTube 18.29.38 ~ 20.04.46. + */ + public static boolean disableAutoPlayerPopupPanels(boolean isLiveChatOrPlaylistPanel) { + if (!Settings.DISABLE_AUTO_PLAYER_POPUP_PANELS.get()) { + return false; + } + if (isLiveChatOrPlaylistPanel) { + return true; + } + return isAutoPopupPanel && ShortsPlayerState.getCurrent().isClosed(); + } + + /** + * Used in YouTube 18.29.38 ~ 20.04.46. + */ + public static void setInitVideoPanel(boolean initVideoPanel) { + isAutoPopupPanel = initVideoPanel; + } + + /** + * Used in YouTube 20.05.46+. + */ @NonNull private static final AtomicBoolean newVideoStarted = new AtomicBoolean(false); + /** + * Used in YouTube 20.05.46+. + */ public static boolean disableAutoPlayerPopupPanels(boolean isLiveChatOrPlaylistPanel, String panelId) { if (Settings.DISABLE_AUTO_PLAYER_POPUP_PANELS.get()) { return isLiveChatOrPlaylistPanel || (panelId.equals("PAproduct_list") && newVideoStarted.get()); @@ -436,11 +467,14 @@ public class PlayerPatch { return false; } + /** + * Used in YouTube 20.05.46+. + */ public static void disableAutoPlayerPopupPanels(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName, @NonNull String newlyLoadedVideoId, @NonNull String newlyLoadedVideoTitle, final long newlyLoadedVideoLength, boolean newlyLoadedLiveStreamValue) { if (Settings.DISABLE_AUTO_PLAYER_POPUP_PANELS.get() && newVideoStarted.compareAndSet(false, true)) { - Utils.runOnMainThreadDelayed(() -> newVideoStarted.compareAndSet(true, false), 1500L); + Utils.runOnMainThreadDelayed(() -> newVideoStarted.compareAndSet(true, false), 3000L); } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt index f9e6b4460..9665230b9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/Fingerprints.kt @@ -95,6 +95,27 @@ fun indexOfSpannableStringInstruction(method: Method) = method.indexOfFirstInstr getReference()?.toString() == SPANNABLE_STRING_REFERENCE } +internal val startVideoInformerFingerprint = legacyFingerprint( + name = "startVideoInformerFingerprint", + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + opcodes = listOf( + Opcode.INVOKE_INTERFACE, + Opcode.RETURN_VOID + ), + strings = listOf("pc"), + customFingerprint = { method, _ -> + method.implementation + ?.instructions + ?.withIndex() + ?.filter { (_, instruction) -> + instruction.opcode == Opcode.CONST_STRING + } + ?.map { (index, _) -> index } + ?.size == 1 + } +) + internal val videoLengthFingerprint = legacyFingerprint( name = "videoLengthFingerprint", returnType = "V", diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/captions/BaseAutoCaptionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/captions/BaseAutoCaptionsPatch.kt index ae3d48eed..26c19b4cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/captions/BaseAutoCaptionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/captions/BaseAutoCaptionsPatch.kt @@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.extension.Constants.PATCHES_PATH +import app.revanced.patches.shared.startVideoInformerFingerprint import app.revanced.util.fingerprint.methodOrThrow private const val EXTENSION_CLASS_DESCRIPTOR = diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/captions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/captions/Fingerprints.kt index 34efb7b19..0f2bb490e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/captions/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/captions/Fingerprints.kt @@ -3,28 +3,6 @@ package app.revanced.patches.shared.captions import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.or import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal val startVideoInformerFingerprint = legacyFingerprint( - name = "startVideoInformerFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - opcodes = listOf( - Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID - ), - strings = listOf("pc"), - customFingerprint = { method, _ -> - method.implementation - ?.instructions - ?.withIndex() - ?.filter { (_, instruction) -> - instruction.opcode == Opcode.CONST_STRING - } - ?.map { (index, _) -> index } - ?.size == 1 - } -) internal val storyboardRendererDecoderRecommendedLevelFingerprint = legacyFingerprint( name = "storyboardRendererDecoderRecommendedLevelFingerprint", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/Fingerprints.kt index b489f3dd6..a36f776ab 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/Fingerprints.kt @@ -2,12 +2,14 @@ package app.revanced.patches.youtube.player.components +import app.revanced.patches.youtube.utils.resourceid.componentLongClickListener import app.revanced.patches.youtube.utils.resourceid.darkBackground import app.revanced.patches.youtube.utils.resourceid.donationCompanion import app.revanced.patches.youtube.utils.resourceid.easySeekEduContainer import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircle import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo +import app.revanced.patches.youtube.utils.resourceid.offlineActionsVideoDeletedUndoSnackbarText import app.revanced.patches.youtube.utils.resourceid.scrubbing import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing import app.revanced.patches.youtube.utils.resourceid.suggestedAction @@ -207,6 +209,30 @@ internal val layoutVideoFingerprint = legacyFingerprint( literals = listOf(endScreenElementLayoutVideo), ) +internal val lithoComponentOnClickListenerFingerprint = legacyFingerprint( + name = "lithoComponentOnClickListenerFingerprint", + returnType = "V", + accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC, + parameters = listOf("L"), + literals = listOf(componentLongClickListener), +) + +internal val engagementPanelPlaylistSyntheticFingerprint = legacyFingerprint( + name = "engagementPanelPlaylistSyntheticFingerprint", + strings = listOf("engagement-panel-playlist"), + customFingerprint = { _, classDef -> + classDef.interfaces.contains("Landroid/view/View${'$'}OnClickListener;") + } +) + +internal val offlineActionsOnClickListenerFingerprint = legacyFingerprint( + name = "offlineActionsOnClickListenerFingerprint", + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/String;"), + literals = listOf(offlineActionsVideoDeletedUndoSnackbarText), +) + internal val quickSeekOverlayFingerprint = legacyFingerprint( name = "quickSeekOverlayFingerprint", returnType = "V", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt index 326b1e5c6..8cc9ce104 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/components/PlayerComponentsPatch.kt @@ -13,6 +13,7 @@ import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.shared.spans.addSpanFilter import app.revanced.patches.shared.spans.inclusiveSpanPatch +import app.revanced.patches.shared.startVideoInformerFingerprint import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.controlsoverlay.controlsOverlayConfigPatch import app.revanced.patches.youtube.utils.engagement.engagementPanelBuilderMethod @@ -42,6 +43,7 @@ import app.revanced.patches.youtube.utils.youtubeControlsOverlayFingerprint import app.revanced.patches.youtube.video.information.hookVideoInformation import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT +import app.revanced.util.Utils.printWarn import app.revanced.util.findMethodOrThrow import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.injectLiteralInstructionViewCall @@ -51,6 +53,7 @@ import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.fingerprint.resolvable import app.revanced.util.getReference import app.revanced.util.getWalkerMethod +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow @@ -375,19 +378,70 @@ val playerComponentsPatch = bytecodePatch( // region patch for disable auto player popup panels - engagementPanelBuilderMethod.addInstructionsWithLabels( - engagementPanelIdIndex, """ - move/from16 v$engagementPanelFreeRegister, p4 - invoke-static {v$engagementPanelFreeRegister, v$engagementPanelIdRegister}, $PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(ZLjava/lang/String;)Z - move-result v$engagementPanelFreeRegister - if-eqz v$engagementPanelFreeRegister, :shown - const/4 v$engagementPanelFreeRegister, 0x0 - return-object v$engagementPanelFreeRegister - :shown - nop - """ - ) - hookVideoInformation("$PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") + fun MutableMethod.hookInitVideoPanel(initVideoPanel: Int) = + addInstructions( + 0, """ + const/4 v0, $initVideoPanel + invoke-static {v0}, $PLAYER_CLASS_DESCRIPTOR->setInitVideoPanel(Z)V + """ + ) + + if (is_20_05_or_greater) { + engagementPanelBuilderMethod.addInstructionsWithLabels( + engagementPanelIdIndex, """ + move/from16 v$engagementPanelFreeRegister, p4 + invoke-static {v$engagementPanelFreeRegister, v$engagementPanelIdRegister}, $PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(ZLjava/lang/String;)Z + move-result v$engagementPanelFreeRegister + if-eqz v$engagementPanelFreeRegister, :shown + const/4 v$engagementPanelFreeRegister, 0x0 + return-object v$engagementPanelFreeRegister + :shown + nop + """ + ) + hookVideoInformation("$PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") + } else { + arrayOf( + lithoComponentOnClickListenerFingerprint, + offlineActionsOnClickListenerFingerprint, + ).forEach { fingerprint -> + fingerprint.methodOrThrow().apply { + val syntheticIndex = + indexOfFirstInstruction(Opcode.NEW_INSTANCE) + if (syntheticIndex >= 0) { + val syntheticReference = + getInstruction(syntheticIndex).reference.toString() + + findMethodOrThrow(syntheticReference) { + name == "onClick" + }.hookInitVideoPanel(0) + } else { + printWarn("target Opcode not found in ${fingerprint.first}") + } + } + } + + findMethodOrThrow( + engagementPanelPlaylistSyntheticFingerprint.methodOrThrow().definingClass + ) { + name == "onClick" + }.hookInitVideoPanel(0) + + startVideoInformerFingerprint.methodOrThrow().hookInitVideoPanel(1) + + engagementPanelBuilderMethod.addInstructionsWithLabels( + 0, """ + move/from16 v0, p4 + invoke-static {v0}, $PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(Z)Z + move-result v0 + if-eqz v0, :shown + const/4 v0, 0x0 + return-object v0 + :shown + nop + """ + ) + } // endregion diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt index cfb48d045..654130e91 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt @@ -61,6 +61,8 @@ var compactLink = -1L private set var compactListItem = -1L private set +var componentLongClickListener = -1L + private set var contentPill = -1L private set var controlsLayoutStub = -1L @@ -143,6 +145,8 @@ var musicAppDeeplinkButtonView = -1L private set var notificationBigPictureIconWidth = -1L private set +var offlineActionsVideoDeletedUndoSnackbarText = -1L + private set var playerCollapseButton = -1L private set var playerControlPreviousButtonTouchArea = -1L @@ -348,6 +352,10 @@ internal val sharedResourceIdPatch = resourcePatch( LAYOUT, "compact_list_item" ] + componentLongClickListener = resourceMappings[ + ID, + "component_long_click_listener" + ] contentPill = resourceMappings[ LAYOUT, "content_pill" @@ -512,6 +520,10 @@ internal val sharedResourceIdPatch = resourcePatch( DIMEN, "notification_big_picture_icon_width" ] + offlineActionsVideoDeletedUndoSnackbarText = resourceMappings[ + STRING, + "offline_actions_video_deleted_undo_snackbar_text" + ] playerCollapseButton = resourceMappings[ ID, "player_collapse_button"