From 70178a09420c7e79982ec7918d928c0b4d980a27 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 20 Jan 2025 02:34:35 +0900 Subject: [PATCH] feat(YouTube - Hide action buttons): Add patch option `Hide action buttons by index` (Experimental) --- .../patches/player/ActionButtonsPatch.java | 62 ++++++++++++++ .../extension/youtube/settings/Settings.java | 17 ++++ .../player/action/ActionButtonsPatch.kt | 84 ++++++++++++++++++- .../youtube/player/action/Fingerprints.kt | 21 +++++ .../youtube/settings/host/values/strings.xml | 37 ++++++++ .../youtube/settings/xml/revanced_prefs.xml | 25 +++++- 6 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/ActionButtonsPatch.java create mode 100644 patches/src/main/kotlin/app/revanced/patches/youtube/player/action/Fingerprints.kt diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/ActionButtonsPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/ActionButtonsPatch.java new file mode 100644 index 000000000..9cd6f6a0c --- /dev/null +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/ActionButtonsPatch.java @@ -0,0 +1,62 @@ +package app.revanced.extension.youtube.patches.player; + +import androidx.annotation.Nullable; + +import java.util.List; + +import app.revanced.extension.shared.settings.BooleanSetting; +import app.revanced.extension.shared.utils.Logger; +import app.revanced.extension.youtube.settings.Settings; +import app.revanced.extension.youtube.shared.VideoInformation; + +@SuppressWarnings("unused") +public class ActionButtonsPatch { + + public enum ActionButton { + INDEX_7(Settings.HIDE_ACTION_BUTTON_INDEX_7, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_7, 7), + INDEX_6(Settings.HIDE_ACTION_BUTTON_INDEX_6, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_6, 6), + INDEX_5(Settings.HIDE_ACTION_BUTTON_INDEX_5, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_5, 5), + INDEX_4(Settings.HIDE_ACTION_BUTTON_INDEX_4, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_4, 4), + INDEX_3(Settings.HIDE_ACTION_BUTTON_INDEX_3, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_3, 3), + INDEX_2(Settings.HIDE_ACTION_BUTTON_INDEX_2, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_2, 2), + INDEX_1(Settings.HIDE_ACTION_BUTTON_INDEX_1, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_1, 1), + INDEX_0(Settings.HIDE_ACTION_BUTTON_INDEX_0, Settings.HIDE_ACTION_BUTTON_INDEX_LIVE_0, 0); + + private final BooleanSetting generalSetting; + private final BooleanSetting liveSetting; + private final int index; + + ActionButton(final BooleanSetting generalSetting, final BooleanSetting liveSetting, final int index) { + this.generalSetting = generalSetting; + this.liveSetting = liveSetting; + this.index = index; + } + } + + private static final String TARGET_COMPONENT_TYPE = "LazilyConvertedElement"; + private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml"; + + public static List hideActionButtonByIndex(@Nullable List list, @Nullable String identifier) { + try { + if (identifier != null && + identifier.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) && + list != null && + !list.isEmpty() && + list.get(0).toString().equals(TARGET_COMPONENT_TYPE) + ) { + final int size = list.size(); + final boolean isLive = VideoInformation.getLiveStreamState(); + for (ActionButton button : ActionButton.values()) { + if (size > button.index && (isLive ? button.liveSetting.get() : button.generalSetting.get())) { + list.remove(button.index); + } + } + } + } catch (Exception ex) { + Logger.printException(() -> "hideActionButtonByIndex failure", ex); + } + + return list; + } + +} diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 5bfbf8bc4..ffb880a56 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -285,6 +285,23 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_SHOP_BUTTON = new BooleanSetting("revanced_hide_shop_button", FALSE); public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_0 = new BooleanSetting("revanced_hide_action_button_index_0", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_1 = new BooleanSetting("revanced_hide_action_button_index_1", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_2 = new BooleanSetting("revanced_hide_action_button_index_2", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_3 = new BooleanSetting("revanced_hide_action_button_index_3", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_4 = new BooleanSetting("revanced_hide_action_button_index_4", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_5 = new BooleanSetting("revanced_hide_action_button_index_5", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_6 = new BooleanSetting("revanced_hide_action_button_index_6", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_7 = new BooleanSetting("revanced_hide_action_button_index_7", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_0 = new BooleanSetting("revanced_hide_action_button_index_live_0", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_1 = new BooleanSetting("revanced_hide_action_button_index_live_1", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_2 = new BooleanSetting("revanced_hide_action_button_index_live_2", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_3 = new BooleanSetting("revanced_hide_action_button_index_live_3", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_4 = new BooleanSetting("revanced_hide_action_button_index_live_4", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_5 = new BooleanSetting("revanced_hide_action_button_index_live_5", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_6 = new BooleanSetting("revanced_hide_action_button_index_live_6", FALSE); + public static final BooleanSetting HIDE_ACTION_BUTTON_INDEX_LIVE_7 = new BooleanSetting("revanced_hide_action_button_index_live_7", FALSE); + // PreferenceScreen: Player - Ambient mode public static final BooleanSetting BYPASS_AMBIENT_MODE_RESTRICTIONS = new BooleanSetting("revanced_bypass_ambient_mode_restrictions", FALSE); public static final BooleanSetting DISABLE_AMBIENT_MODE = new BooleanSetting("revanced_disable_ambient_mode", FALSE, true); diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/ActionButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/ActionButtonsPatch.kt index 91f46f888..d1d695fdd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/ActionButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/ActionButtonsPatch.kt @@ -1,16 +1,37 @@ package app.revanced.patches.youtube.player.action +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.booleanOption import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.litho.addLithoFilter +import app.revanced.patches.shared.litho.emptyComponentsFingerprint import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH +import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_PATH import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_ACTION_BUTTONS import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference import app.revanced.patches.youtube.utils.settings.settingsPatch +import app.revanced.patches.youtube.video.information.videoInformationPatch +import app.revanced.util.Utils.trimIndentMultiline +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.findMethodOrThrow +import app.revanced.util.fingerprint.methodOrThrow +import app.revanced.util.getReference +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.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.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference private const val FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/ActionButtonsFilter;" +private const val ACTION_BUTTONS_CLASS_DESCRIPTOR = + "$PLAYER_PATH/ActionButtonsPatch;" @Suppress("unused") val actionButtonsPatch = bytecodePatch( @@ -22,18 +43,73 @@ val actionButtonsPatch = bytecodePatch( dependsOn( settingsPatch, lithoFilterPatch, + videoInformationPatch, + ) + + val hideActionButtonByIndex by booleanOption( + key = "hideActionButtonByIndex", + default = false, + title = "Hide action buttons by index", + description = """ + Add an option to hide action buttons by index. + + This setting is still experimental, so use it only for debugging purposes. + """.trimIndentMultiline(), + required = true ) execute { addLithoFilter(FILTER_CLASS_DESCRIPTOR) + var settingArray = arrayOf( + "PREFERENCE_SCREEN: PLAYER", + "SETTINGS: HIDE_ACTION_BUTTONS" + ) + + if (hideActionButtonByIndex == true) { + componentListFingerprint.methodOrThrow(emptyComponentsFingerprint).apply { + val conversionContextToStringMethod = + findMethodOrThrow(parameters[1].type) { + name == "toString" + } + val identifierReference = with (conversionContextToStringMethod) { + val identifierStringIndex = + indexOfFirstStringInstructionOrThrow(", identifierProperty=") + val identifierStringAppendIndex = + indexOfFirstInstructionOrThrow(identifierStringIndex, Opcode.INVOKE_VIRTUAL) + val identifierStringAppendIndexRegister = getInstruction(identifierStringAppendIndex).registerD + val identifierAppendIndex = + indexOfFirstInstructionOrThrow(identifierStringAppendIndex + 1, Opcode.INVOKE_VIRTUAL) + val identifierRegister = getInstruction(identifierAppendIndex).registerD + val identifierIndex = indexOfFirstInstructionReversedOrThrow(identifierAppendIndex) { + opcode == Opcode.IGET_OBJECT && + getReference()?.type == "Ljava/lang/String;" && + (this as? TwoRegisterInstruction)?.registerA == identifierRegister + } + getInstruction(identifierIndex).reference + } + + val listIndex = implementation!!.instructions.lastIndex + val listRegister = getInstruction(listIndex).registerA + val identifierRegister = listRegister + 1 + + addInstructionsAtControlFlowLabel( + listIndex, """ + move-object/from16 v$identifierRegister, p2 + iget-object v$identifierRegister, v$identifierRegister, $identifierReference + invoke-static {v$listRegister, v$identifierRegister}, $ACTION_BUTTONS_CLASS_DESCRIPTOR->hideActionButtonByIndex(Ljava/util/List;Ljava/lang/String;)Ljava/util/List; + move-result-object v$listRegister + """ + ) + + settingArray += "SETTINGS: HIDE_BUTTONS_BY_INDEX" + } + } + // region add settings addPreference( - arrayOf( - "PREFERENCE_SCREEN: PLAYER", - "SETTINGS: HIDE_ACTION_BUTTONS" - ), + settingArray, HIDE_ACTION_BUTTONS ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/Fingerprints.kt new file mode 100644 index 000000000..a3735638f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/action/Fingerprints.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.player.action + +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 componentListFingerprint = legacyFingerprint( + name = "componentListFingerprint", + returnType = "Ljava/util/List;", + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + customFingerprint = { method, _ -> + method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_STATIC && + getReference()?.name == "nCopies" + } >= 0 + } +) diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml index e722f2ca6..ce771f372 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -848,6 +848,43 @@ Settings → Autoplay / Playback → Autoplay next video" Thanks button is hidden. Thanks button is shown. + + Hide by index + + Hide first button + First button is hidden. + First button is shown. + Hide second button + Second button is hidden. + Second button is shown. + Hide third button + Third button is hidden. + Third button is shown. + Hide fourth button + Fourth button is hidden. + Fourth button is shown. + Hide fifth button + Fifth button is hidden. + Fifth button is shown. + Hide sixth button + Sixth button is hidden. + Sixth button is shown. + Hide seventh button + Seventh button is hidden. + Seventh button is shown. + Hide eighth button + Eighth button is hidden. + Eighth button is shown. + + + Hide by index in live stream + + About Hide action button by index + "Hide the action buttons by index before the action buttons are initialized. + +- Hiding the action buttons leaves no empty space. +- Index of the action buttons may not always be the same button." + Ambient mode Disable Ambient mode or bypass Ambient mode restrictions. diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml index bac38b8c6..549793c1d 100644 --- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -331,7 +331,30 @@ - + SETTINGS: HIDE_ACTION_BUTTONS --> + + + +