feat(YouTube - Description components): Remove Title in video description panel setting (No more necessary)

This commit is contained in:
inotia00 2025-02-10 17:16:40 +09:00
parent d1154db77e
commit 37e5a48665
15 changed files with 200 additions and 256 deletions

View File

@ -1,32 +1,18 @@
package app.revanced.extension.youtube.patches.feed; package app.revanced.extension.youtube.patches.feed;
import androidx.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.BottomSheetState; import app.revanced.extension.youtube.shared.BottomSheetState;
import app.revanced.extension.youtube.shared.EngagementPanel;
import app.revanced.extension.youtube.shared.RootView; import app.revanced.extension.youtube.shared.RootView;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class RelatedVideoPatch { public final class RelatedVideoPatch {
private static final boolean HIDE_RELATED_VIDEOS = Settings.HIDE_RELATED_VIDEOS.get(); private static final boolean HIDE_RELATED_VIDEOS = Settings.HIDE_RELATED_VIDEOS.get();
private static final int OFFSET = Settings.RELATED_VIDEOS_OFFSET.get(); private static final int OFFSET = Settings.RELATED_VIDEOS_OFFSET.get();
// video title,channel bar, video action bar, comment // video title,channel bar, video action bar, comment
private static final int MAX_ITEM_COUNT = 4 + OFFSET; private static final int MAX_ITEM_COUNT = 4 + OFFSET;
private static final AtomicBoolean engagementPanelOpen = new AtomicBoolean(false);
public static void showEngagementPanel(@Nullable Object object) {
engagementPanelOpen.set(object != null);
}
public static void hideEngagementPanel() {
engagementPanelOpen.compareAndSet(true, false);
}
public static int overrideItemCounts(int itemCounts) { public static int overrideItemCounts(int itemCounts) {
if (!HIDE_RELATED_VIDEOS) { if (!HIDE_RELATED_VIDEOS) {
return itemCounts; return itemCounts;
@ -40,7 +26,7 @@ public final class RelatedVideoPatch {
if (BottomSheetState.getCurrent().isOpen()) { if (BottomSheetState.getCurrent().isOpen()) {
return itemCounts; return itemCounts;
} }
if (engagementPanelOpen.get()) { if (EngagementPanel.isOpen()) {
return itemCounts; return itemCounts;
} }
return MAX_ITEM_COUNT; return MAX_ITEM_COUNT;

View File

@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.BooleanSetting;
@ -30,6 +31,7 @@ import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.patches.utils.InitializationPatch; import app.revanced.extension.youtube.patches.utils.InitializationPatch;
import app.revanced.extension.youtube.patches.utils.PatchStatus; import app.revanced.extension.youtube.patches.utils.PatchStatus;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.EngagementPanel;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.RootView; import app.revanced.extension.youtube.shared.RootView;
import app.revanced.extension.youtube.shared.ShortsPlayerState; import app.revanced.extension.youtube.shared.ShortsPlayerState;
@ -118,37 +120,6 @@ public class PlayerPatch {
*/ */
private static final int contentId = ResourceUtils.getIdIdentifier("content"); private static final int contentId = ResourceUtils.getIdIdentifier("content");
private static final boolean EXPAND_VIDEO_DESCRIPTION = Settings.EXPAND_VIDEO_DESCRIPTION.get(); private static final boolean EXPAND_VIDEO_DESCRIPTION = Settings.EXPAND_VIDEO_DESCRIPTION.get();
private static final StringSetting EXPAND_VIDEO_DESCRIPTION_STRINGS = Settings.EXPAND_VIDEO_DESCRIPTION_STRINGS;
private static final String EXPAND_VIDEO_DESCRIPTION_STRINGS_DEFAULT_VALUE = "revanced_expand_video_description_strings_default_value";
static {
final String descriptionString = EXPAND_VIDEO_DESCRIPTION_STRINGS.get();
if (descriptionString.equals(EXPAND_VIDEO_DESCRIPTION_STRINGS_DEFAULT_VALUE) &&
Utils.getContext() != null) {
String defaultValue = ResourceUtils.getString(EXPAND_VIDEO_DESCRIPTION_STRINGS_DEFAULT_VALUE);
if (!descriptionString.equals(defaultValue)) {
EXPAND_VIDEO_DESCRIPTION_STRINGS.save(defaultValue);
}
}
}
private static boolean isDescriptionPanel = false;
public static void setContentDescription(String contentDescription) {
if (!EXPAND_VIDEO_DESCRIPTION) {
return;
}
if (contentDescription == null || contentDescription.isEmpty()) {
isDescriptionPanel = false;
return;
}
final String descriptionString = EXPAND_VIDEO_DESCRIPTION_STRINGS.get();
if (descriptionString.isEmpty()) {
isDescriptionPanel = false;
return;
}
isDescriptionPanel = descriptionString.equals(contentDescription);
}
/** /**
* The last time the clickDescriptionView method was called. * The last time the clickDescriptionView method was called.
@ -172,9 +143,8 @@ public class PlayerPatch {
if (contentView.getId() != contentId) { if (contentView.getId() != contentId) {
return; return;
} }
// This method is invoked whenever the Engagement panel is opened. (Description, Chapters, Comments, etc.) // Check description panel opened.
// Check the title of the Engagement panel to prevent unnecessary clicking. if (!EngagementPanel.isDescription()) {
if (!isDescriptionPanel) {
return; return;
} }
// The first view group contains information such as the video's title, like count, and number of views. // The first view group contains information such as the video's title, like count, and number of views.
@ -456,20 +426,22 @@ public class PlayerPatch {
imageView.setImageAlpha(PLAYER_OVERLAY_OPACITY_LEVEL); imageView.setImageAlpha(PLAYER_OVERLAY_OPACITY_LEVEL);
} }
private static boolean isAutoPopupPanel; @NonNull
private static final AtomicBoolean newVideoStarted = new AtomicBoolean(false);
public static boolean disableAutoPlayerPopupPanels(boolean isLiveChatOrPlaylistPanel) { public static boolean disableAutoPlayerPopupPanels(boolean isLiveChatOrPlaylistPanel, String panelId) {
if (!Settings.DISABLE_AUTO_PLAYER_POPUP_PANELS.get()) { if (Settings.DISABLE_AUTO_PLAYER_POPUP_PANELS.get()) {
return isLiveChatOrPlaylistPanel || (panelId.equals("PAproduct_list") && newVideoStarted.get());
}
return false; return false;
} }
if (isLiveChatOrPlaylistPanel) {
return true;
}
return isAutoPopupPanel && ShortsPlayerState.getCurrent().isClosed();
}
public static void setInitVideoPanel(boolean initVideoPanel) { public static void disableAutoPlayerPopupPanels(@NonNull String newlyLoadedChannelId, @NonNull String newlyLoadedChannelName,
isAutoPopupPanel = initVideoPanel; @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);
}
} }
@NonNull @NonNull
@ -493,7 +465,7 @@ public class PlayerPatch {
return; return;
} }
VideoUtils.pauseMedia(); VideoUtils.pauseMedia();
VideoUtils.openVideo(videoId); VideoUtils.openVideo(newlyLoadedVideoId);
} }
public static boolean disableSpeedOverlay() { public static boolean disableSpeedOverlay() {

View File

@ -345,7 +345,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_PLAYER_FLYOUT_MENU_YT_MUSIC = new BooleanSetting("revanced_hide_player_flyout_menu_listen_with_youtube_music", TRUE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_MENU_YT_MUSIC = new BooleanSetting("revanced_hide_player_flyout_menu_listen_with_youtube_music", TRUE);
// PreferenceScreen: Player - Fullscreen // PreferenceScreen: Player - Fullscreen
public static final BooleanSetting DISABLE_ENGAGEMENT_PANEL = new BooleanSetting("revanced_disable_engagement_panel", FALSE, true); public static final BooleanSetting DISABLE_ENGAGEMENT_PANEL = new BooleanSetting("revanced_disable_engagement_panel", FALSE);
public static final BooleanSetting ENTER_FULLSCREEN = new BooleanSetting("revanced_enter_fullscreen", FALSE); public static final BooleanSetting ENTER_FULLSCREEN = new BooleanSetting("revanced_enter_fullscreen", FALSE);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED); public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting SHOW_VIDEO_TITLE_SECTION = new BooleanSetting("revanced_show_video_title_section", TRUE, true, parent(DISABLE_ENGAGEMENT_PANEL)); public static final BooleanSetting SHOW_VIDEO_TITLE_SECTION = new BooleanSetting("revanced_show_video_title_section", TRUE, true, parent(DISABLE_ENGAGEMENT_PANEL));
@ -428,7 +428,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", FALSE); public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", FALSE);
public static final BooleanSetting DISABLE_VIDEO_DESCRIPTION_INTERACTION = new BooleanSetting("revanced_disable_video_description_interaction", FALSE, true); public static final BooleanSetting DISABLE_VIDEO_DESCRIPTION_INTERACTION = new BooleanSetting("revanced_disable_video_description_interaction", FALSE, true);
public static final BooleanSetting EXPAND_VIDEO_DESCRIPTION = new BooleanSetting("revanced_expand_video_description", FALSE, true); public static final BooleanSetting EXPAND_VIDEO_DESCRIPTION = new BooleanSetting("revanced_expand_video_description", FALSE, true);
public static final StringSetting EXPAND_VIDEO_DESCRIPTION_STRINGS = new StringSetting("revanced_expand_video_description_strings", str("revanced_expand_video_description_strings_default_value"), true, parent(EXPAND_VIDEO_DESCRIPTION));
// PreferenceScreen: Shorts // PreferenceScreen: Shorts

View File

@ -0,0 +1,46 @@
package app.revanced.extension.youtube.shared;
import androidx.annotation.Nullable;
import java.util.concurrent.atomic.AtomicReference;
import app.revanced.extension.shared.utils.Logger;
@SuppressWarnings("unused")
public final class EngagementPanel {
private static final AtomicReference<String> engagementPanelId = new AtomicReference<>("");
/**
* Injection point.
*/
public static void setId(@Nullable String panelId) {
if (panelId != null) {
Logger.printDebug(() -> "engagementPanel open\npanelId: " + panelId);
engagementPanelId.set(panelId);
}
}
/**
* Injection point.
*/
public static void hide() {
String panelId = getId();
if (!panelId.isEmpty()) {
Logger.printDebug(() -> "engagementPanel closed\npanelId: " + panelId);
engagementPanelId.set("");
}
}
public static boolean isOpen() {
return !getId().isEmpty();
}
public static boolean isDescription() {
return getId().equals("video-description-ep-identifier");
}
public static String getId() {
return engagementPanelId.get();
}
}

View File

@ -14,7 +14,6 @@ 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
import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch
import app.revanced.patches.youtube.utils.engagement.hookEngagementPanelState
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.extension.Constants.FEED_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.FEED_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.FEED_PATH import app.revanced.patches.youtube.utils.extension.Constants.FEED_PATH
@ -190,8 +189,6 @@ val feedComponentsPatch = bytecodePatch(
} }
} }
hookEngagementPanelState(RELATED_VIDEO_CLASS_DESCRIPTOR)
// endregion // endregion
// region patch for hide subscriptions channel section for tablet // region patch for hide subscriptions channel section for tablet

View File

@ -2,14 +2,12 @@
package app.revanced.patches.youtube.player.components 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.darkBackground
import app.revanced.patches.youtube.utils.resourceid.donationCompanion import app.revanced.patches.youtube.utils.resourceid.donationCompanion
import app.revanced.patches.youtube.utils.resourceid.easySeekEduContainer import app.revanced.patches.youtube.utils.resourceid.easySeekEduContainer
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircle import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutCircle
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutIcon
import app.revanced.patches.youtube.utils.resourceid.endScreenElementLayoutVideo 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.scrubbing
import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing import app.revanced.patches.youtube.utils.resourceid.seekEasyHorizontalTouchOffsetToStartScrubbing
import app.revanced.patches.youtube.utils.resourceid.suggestedAction import app.revanced.patches.youtube.utils.resourceid.suggestedAction
@ -209,30 +207,6 @@ internal val layoutVideoFingerprint = legacyFingerprint(
literals = listOf(endScreenElementLayoutVideo), 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( internal val quickSeekOverlayFingerprint = legacyFingerprint(
name = "quickSeekOverlayFingerprint", name = "quickSeekOverlayFingerprint",
returnType = "V", returnType = "V",

View File

@ -13,10 +13,13 @@ import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.shared.litho.lithoFilterPatch
import app.revanced.patches.shared.spans.addSpanFilter import app.revanced.patches.shared.spans.addSpanFilter
import app.revanced.patches.shared.spans.inclusiveSpanPatch 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.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.controlsoverlay.controlsOverlayConfigPatch import app.revanced.patches.youtube.utils.controlsoverlay.controlsOverlayConfigPatch
import app.revanced.patches.youtube.utils.engagementPanelBuilderFingerprint import app.revanced.patches.youtube.utils.engagement.engagementPanelBuilderMethod
import app.revanced.patches.youtube.utils.engagement.engagementPanelFreeRegister
import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch
import app.revanced.patches.youtube.utils.engagement.engagementPanelIdIndex
import app.revanced.patches.youtube.utils.engagement.engagementPanelIdRegister
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
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.SPANS_PATH import app.revanced.patches.youtube.utils.extension.Constants.SPANS_PATH
@ -39,7 +42,6 @@ import app.revanced.patches.youtube.utils.youtubeControlsOverlayFingerprint
import app.revanced.patches.youtube.video.information.hookVideoInformation import app.revanced.patches.youtube.video.information.hookVideoInformation
import app.revanced.patches.youtube.video.information.videoInformationPatch import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.Utils.printWarn
import app.revanced.util.findMethodOrThrow import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.injectLiteralInstructionViewCall import app.revanced.util.fingerprint.injectLiteralInstructionViewCall
@ -49,7 +51,6 @@ import app.revanced.util.fingerprint.mutableClassOrThrow
import app.revanced.util.fingerprint.resolvable import app.revanced.util.fingerprint.resolvable
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getWalkerMethod import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
@ -286,6 +287,7 @@ val playerComponentsPatch = bytecodePatch(
suggestedVideoEndScreenPatch, suggestedVideoEndScreenPatch,
videoInformationPatch, videoInformationPatch,
versionCheckPatch, versionCheckPatch,
engagementPanelHookPatch,
) )
execute { execute {
@ -373,54 +375,19 @@ val playerComponentsPatch = bytecodePatch(
// region patch for disable auto player popup panels // region patch for disable auto player popup panels
fun MutableMethod.hookInitVideoPanel(initVideoPanel: Int) = engagementPanelBuilderMethod.addInstructionsWithLabels(
addInstructions( engagementPanelIdIndex, """
0, """ move/from16 v$engagementPanelFreeRegister, p4
const/4 v0, $initVideoPanel invoke-static {v$engagementPanelFreeRegister, v$engagementPanelIdRegister}, $PLAYER_CLASS_DESCRIPTOR->disableAutoPlayerPopupPanels(ZLjava/lang/String;)Z
invoke-static {v0}, $PLAYER_CLASS_DESCRIPTOR->setInitVideoPanel(Z)V 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")
arrayOf(
lithoComponentOnClickListenerFingerprint,
offlineActionsOnClickListenerFingerprint,
).forEach { fingerprint ->
fingerprint.methodOrThrow().apply {
val syntheticIndex =
indexOfFirstInstruction(Opcode.NEW_INSTANCE)
if (syntheticIndex >= 0) {
val syntheticReference =
getInstruction<ReferenceInstruction>(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)
engagementPanelBuilderFingerprint.methodOrThrow().apply {
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
""", ExternalLabel("shown", getInstruction(0))
)
}
// endregion // endregion

View File

@ -9,6 +9,7 @@ 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.lithoFilterPatch import app.revanced.patches.shared.litho.lithoFilterPatch
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.engagement.engagementPanelHookPatch
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
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.patch.PatchList.DESCRIPTION_COMPONENTS import app.revanced.patches.youtube.utils.patch.PatchList.DESCRIPTION_COMPONENTS
@ -46,6 +47,7 @@ val descriptionComponentsPatch = bytecodePatch(
lithoFilterPatch, lithoFilterPatch,
playerTypeHookPatch, playerTypeHookPatch,
recyclerViewTreeObserverPatch, recyclerViewTreeObserverPatch,
engagementPanelHookPatch,
sharedResourceIdPatch, sharedResourceIdPatch,
versionCheckPatch, versionCheckPatch,
) )
@ -104,19 +106,6 @@ val descriptionComponentsPatch = bytecodePatch(
) )
} }
engagementPanelTitleFingerprint.methodOrThrow(engagementPanelTitleParentFingerprint)
.apply {
val contentDescriptionIndex = indexOfContentDescriptionInstruction(this)
val contentDescriptionRegister =
getInstruction<FiveRegisterInstruction>(contentDescriptionIndex).registerD
addInstruction(
contentDescriptionIndex,
"invoke-static {v$contentDescriptionRegister}," +
"$PLAYER_CLASS_DESCRIPTOR->setContentDescription(Ljava/lang/String;)V"
)
}
recyclerViewTreeObserverHook("$PLAYER_CLASS_DESCRIPTOR->onVideoDescriptionCreate(Landroid/support/v7/widget/RecyclerView;)V") recyclerViewTreeObserverHook("$PLAYER_CLASS_DESCRIPTOR->onVideoDescriptionCreate(Landroid/support/v7/widget/RecyclerView;)V")
settingArray += "SETTINGS: DESCRIPTION_INTERACTION" settingArray += "SETTINGS: DESCRIPTION_INTERACTION"

View File

@ -3,30 +3,10 @@ package app.revanced.patches.youtube.player.descriptions
import app.revanced.util.fingerprint.legacyFingerprint import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionReversed
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method 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
internal val engagementPanelTitleFingerprint = legacyFingerprint(
name = "engagementPanelTitleFingerprint",
strings = listOf(". "),
customFingerprint = { method, _ ->
indexOfContentDescriptionInstruction(method) >= 0
}
)
internal val engagementPanelTitleParentFingerprint = legacyFingerprint(
name = "engagementPanelTitleParentFingerprint",
strings = listOf("[EngagementPanelTitleHeader] Cannot remove action buttons from header as the child count is out of sync. Buttons to remove exceed current header child count.")
)
internal fun indexOfContentDescriptionInstruction(method: Method) =
method.indexOfFirstInstructionReversed {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setContentDescription"
}
/** /**
* This fingerprint is compatible with YouTube v18.35.xx~ * This fingerprint is compatible with YouTube v18.35.xx~
* Nonetheless, the patch works in YouTube v19.02.xx~ * Nonetheless, the patch works in YouTube v19.02.xx~

View File

@ -1,24 +1,30 @@
package app.revanced.patches.youtube.utils.engagement package app.revanced.patches.youtube.utils.engagement
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction 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.getInstruction
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.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.engagementPanelBuilderFingerprint import app.revanced.patches.youtube.utils.extension.Constants.SHARED_PATH
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.util.fingerprint.matchOrThrow 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.indexOfFirstInstructionReversed import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private lateinit var engagementPanelBuilderMethod: MutableMethod private const val EXTENSION_CLASS_DESCRIPTOR =
private lateinit var hideEngagementPanelMethod: MutableMethod "$SHARED_PATH/EngagementPanel;"
private var showEngagementPanelMethods = mutableListOf<MutableMethod>()
internal lateinit var engagementPanelBuilderMethod: MutableMethod
internal var engagementPanelFreeRegister = 0
internal var engagementPanelIdIndex = 0
internal var engagementPanelIdRegister = 0
val engagementPanelHookPatch = bytecodePatch( val engagementPanelHookPatch = bytecodePatch(
description = "engagementPanelHookPatch" description = "engagementPanelHookPatch"
@ -26,45 +32,66 @@ val engagementPanelHookPatch = bytecodePatch(
dependsOn(sharedResourceIdPatch) dependsOn(sharedResourceIdPatch)
execute { execute {
engagementPanelBuilderFingerprint.matchOrThrow().let { fun Method.setFreeIndex(startIndex: Int) {
engagementPanelBuilderMethod = it.method val startRegister = engagementPanelIdRegister
var index = startIndex
var register = startRegister
it.classDef.methods.filter { method -> while (register == startRegister) {
method.indexOfEngagementPanelBuilderInstruction() >= 0 index = indexOfFirstInstruction(index + 1, Opcode.IGET_OBJECT)
}.forEach { method -> register = getInstruction<TwoRegisterInstruction>(index).registerA
showEngagementPanelMethods.add(method)
}
} }
hideEngagementPanelMethod = engagementPanelFreeRegister = register
engagementPanelUpdateFingerprint.methodOrThrow(engagementPanelBuilderFingerprint)
} }
}
private fun Method.indexOfEngagementPanelBuilderInstruction() = val engagementPanelInfoClass = engagementPanelLayoutFingerprint
indexOfFirstInstructionReversed { .methodOrThrow()
opcode == Opcode.INVOKE_DIRECT && .parameters[0]
MethodUtil.methodSignaturesMatch( .toString()
engagementPanelBuilderMethod,
getReference<MethodReference>()!! val (engagementPanelIdReference, engagementPanelObjectReference) =
with(findMethodOrThrow(engagementPanelInfoClass)) {
val engagementPanelIdIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;"
}
val engagementPanelObjectIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type != "Ljava/lang/String;"
}
Pair(
getInstruction<ReferenceInstruction>(engagementPanelIdIndex).reference.toString(),
getInstruction<ReferenceInstruction>(engagementPanelObjectIndex).reference.toString(),
) )
} }
internal fun hookEngagementPanelState(classDescriptor: String) { engagementPanelBuilderFingerprint.methodOrThrow().apply {
showEngagementPanelMethods.forEach { method -> val insertIndex = indexOfFirstInstructionOrThrow {
method.apply { opcode == Opcode.IGET_OBJECT &&
val index = indexOfEngagementPanelBuilderInstruction() getReference<FieldReference>()?.toString() == engagementPanelObjectReference
val register = getInstruction<OneRegisterInstruction>(index + 1).registerA }
val insertInstruction = getInstruction<TwoRegisterInstruction>(insertIndex)
val classRegister = insertInstruction.registerB
engagementPanelIdRegister = insertInstruction.registerA
addInstruction( setFreeIndex(insertIndex)
index + 2,
"invoke-static {v$register}, $classDescriptor->showEngagementPanel(Ljava/lang/Object;)V" addInstructions(
insertIndex, """
iget-object v$engagementPanelIdRegister, v$classRegister, $engagementPanelIdReference
invoke-static {v$engagementPanelIdRegister}, $EXTENSION_CLASS_DESCRIPTOR->setId(Ljava/lang/String;)V
"""
) )
} engagementPanelIdIndex = insertIndex + 1
engagementPanelBuilderMethod = this
} }
hideEngagementPanelMethod.addInstruction( engagementPanelUpdateFingerprint
.methodOrThrow(engagementPanelBuilderFingerprint)
.addInstruction(
0, 0,
"invoke-static {}, $classDescriptor->hideEngagementPanel()V" "invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hide()V"
) )
}
} }

View File

@ -1,22 +0,0 @@
package app.revanced.patches.youtube.utils.engagement
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val engagementPanelUpdateFingerprint = legacyFingerprint(
name = "engagementPanelUpdateFingerprint",
returnType = "V",
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = listOf("L", "Z"),
customFingerprint = { method, _ ->
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Ljava/util/ArrayDeque;->pop()Ljava/lang/Object;"
} >= 0
}
)

View File

@ -0,0 +1,46 @@
package app.revanced.patches.youtube.utils.engagement
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val engagementPanelBuilderFingerprint = legacyFingerprint(
name = "engagementPanelBuilderFingerprint",
returnType = "L",
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = listOf("L", "L", "Z", "Z"),
strings = listOf(
"EngagementPanelController: cannot show EngagementPanel before EngagementPanelController.init() has been called.",
"[EngagementPanel] Cannot show EngagementPanel before EngagementPanelController.init() has been called."
)
)
internal val engagementPanelLayoutFingerprint = legacyFingerprint(
name = "engagementPanelLayoutFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "L", "I"),
customFingerprint = { method, _ ->
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Landroid/widget/FrameLayout;->indexOfChild(Landroid/view/View;)I"
} >= 0
}
)
internal val engagementPanelUpdateFingerprint = legacyFingerprint(
name = "engagementPanelUpdateFingerprint",
returnType = "V",
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = listOf("L", "Z"),
customFingerprint = { method, _ ->
method.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>().toString() == "Ljava/util/ArrayDeque;->pop()Ljava/lang/Object;"
} >= 0
}
)

View File

@ -61,8 +61,6 @@ var compactLink = -1L
private set private set
var compactListItem = -1L var compactListItem = -1L
private set private set
var componentLongClickListener = -1L
private set
var contentPill = -1L var contentPill = -1L
private set private set
var controlsLayoutStub = -1L var controlsLayoutStub = -1L
@ -145,8 +143,6 @@ var musicAppDeeplinkButtonView = -1L
private set private set
var notificationBigPictureIconWidth = -1L var notificationBigPictureIconWidth = -1L
private set private set
var offlineActionsVideoDeletedUndoSnackbarText = -1L
private set
var playerCollapseButton = -1L var playerCollapseButton = -1L
private set private set
var playerControlPreviousButtonTouchArea = -1L var playerControlPreviousButtonTouchArea = -1L
@ -352,10 +348,6 @@ internal val sharedResourceIdPatch = resourcePatch(
LAYOUT, LAYOUT,
"compact_list_item" "compact_list_item"
] ]
componentLongClickListener = resourceMappings[
ID,
"component_long_click_listener"
]
contentPill = resourceMappings[ contentPill = resourceMappings[
LAYOUT, LAYOUT,
"content_pill" "content_pill"
@ -520,10 +512,6 @@ internal val sharedResourceIdPatch = resourcePatch(
DIMEN, DIMEN,
"notification_big_picture_icon_width" "notification_big_picture_icon_width"
] ]
offlineActionsVideoDeletedUndoSnackbarText = resourceMappings[
STRING,
"offline_actions_video_deleted_undo_snackbar_text"
]
playerCollapseButton = resourceMappings[ playerCollapseButton = resourceMappings[
ID, ID,
"player_collapse_button" "player_collapse_button"

View File

@ -1323,10 +1323,6 @@ This feature works best with a very fast internet connection."</string>
<string name="revanced_expand_video_description_title">Expand video descriptions</string> <string name="revanced_expand_video_description_title">Expand video descriptions</string>
<string name="revanced_expand_video_description_summary_on">Video descriptions are expanded automatically.</string> <string name="revanced_expand_video_description_summary_on">Video descriptions are expanded automatically.</string>
<string name="revanced_expand_video_description_summary_off">Video descriptions are not expanded automatically.</string> <string name="revanced_expand_video_description_summary_off">Video descriptions are not expanded automatically.</string>
<string name="revanced_expand_video_description_strings_title">Title in video description panel</string>
<string name="revanced_expand_video_description_strings_summary">"Enter the title of the video description panel in your language.
The Expand video description option may not work if the entered string does not match the video description panel title."</string>
<string name="revanced_expand_video_description_strings_default_value">Description</string>
<!-- PreferenceScreen: Shorts --> <!-- PreferenceScreen: Shorts -->

View File

@ -535,7 +535,6 @@
<PreferenceCategory android:title="@string/revanced_preference_category_experimental_flag" android:layout="@layout/revanced_settings_preferences_category"> <PreferenceCategory android:title="@string/revanced_preference_category_experimental_flag" android:layout="@layout/revanced_settings_preferences_category">
<SwitchPreference android:title="@string/revanced_disable_video_description_interaction_title" android:key="revanced_disable_video_description_interaction" android:summary="@string/revanced_disable_video_description_interaction_summary" /> <SwitchPreference android:title="@string/revanced_disable_video_description_interaction_title" android:key="revanced_disable_video_description_interaction" android:summary="@string/revanced_disable_video_description_interaction_summary" />
<SwitchPreference android:title="@string/revanced_expand_video_description_title" android:key="revanced_expand_video_description" android:summaryOn="@string/revanced_expand_video_description_summary_on" android:summaryOff="@string/revanced_expand_video_description_summary_off" /> <SwitchPreference android:title="@string/revanced_expand_video_description_title" android:key="revanced_expand_video_description" android:summaryOn="@string/revanced_expand_video_description_summary_on" android:summaryOff="@string/revanced_expand_video_description_summary_off" />
<app.revanced.extension.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_expand_video_description_strings_title" android:key="revanced_expand_video_description_strings" android:summary="@string/revanced_expand_video_description_strings_summary" android:inputType="text" />
</PreferenceCategory>SETTINGS: DESCRIPTION_INTERACTION --> </PreferenceCategory>SETTINGS: DESCRIPTION_INTERACTION -->
<!-- SETTINGS: DESCRIPTION_COMPONENTS <!-- SETTINGS: DESCRIPTION_COMPONENTS