feat(YouTube Music): Add Hide buttons patch (#6255)

This commit is contained in:
MarcaD
2025-11-09 09:05:31 +02:00
committed by GitHub
parent 475197af45
commit 7a18ebc7ab
13 changed files with 272 additions and 141 deletions

View File

@@ -0,0 +1,49 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import android.view.ViewGroup;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideButtonsPatch {
/**
* Injection point
*/
public static int hideCastButton(int original) {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point
*/
public static void hideCastButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
}
/**
* Injection point
*/
public static boolean hideHistoryButton(boolean original) {
return original && !Settings.HIDE_HISTORY_BUTTON.get();
}
/**
* Injection point
*/
public static void hideNotificationButton(View view) {
if (view.getParent() instanceof ViewGroup viewGroup) {
hideViewBy0dpUnderCondition(Settings.HIDE_NOTIFICATION_BUTTON, viewGroup);
}
}
/**
* Injection point
*/
public static void hideSearchButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_SEARCH_BUTTON, view);
}
}

View File

@@ -1,24 +0,0 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideCastButtonPatch {
/**
* Injection point
*/
public static int hideCastButton(int original) {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point
*/
public static void hideCastButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
}
}

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")

View File

@@ -16,8 +16,11 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true);
// General
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, false);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, true);
public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true);
public static final BooleanSetting HIDE_HISTORY_BUTTON = new BooleanSetting("revanced_music_hide_history_button", FALSE, true);
public static final BooleanSetting HIDE_SEARCH_BUTTON = new BooleanSetting("revanced_music_hide_search_button", FALSE, true);
public static final BooleanSetting HIDE_NOTIFICATION_BUTTON = new BooleanSetting("revanced_music_hide_notification_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_HOME_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_home_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_SAMPLES_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_samples_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_EXPLORE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_explore_button", FALSE, true);

View File

@@ -204,4 +204,4 @@ class SpeedIconDrawable extends Drawable {
public int getIntrinsicHeight() {
return Dim.dp32;
}
}
}

View File

@@ -32,11 +32,7 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.Toolbar;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
@@ -759,31 +755,25 @@ public class Utils {
}
/**
* Hide a view by setting its layout params to 0x0
* @param view The view to hide.
* Hides a view by setting its layout width and height to 0dp.
* Handles null layout params safely.
*
* @param view The view to hide. If null, does nothing.
*/
public static void hideViewByLayoutParams(View view) {
if (view instanceof LinearLayout) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams);
} else if (view instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams2);
} else if (view instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams3);
} else if (view instanceof Toolbar) {
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
view.setLayoutParams(layoutParams4);
} else if (view instanceof ViewGroup) {
ViewGroup.LayoutParams layoutParams5 = new ViewGroup.LayoutParams(0, 0);
view.setLayoutParams(layoutParams5);
public static void hideViewByLayoutParams(@Nullable View view) {
if (view == null) return;
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) {
// Create generic 0x0 layout params accepted by all ViewGroups.
params = new ViewGroup.LayoutParams(0, 0);
} else {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = 0;
params.height = 0;
view.setLayoutParams(params);
}
view.setLayoutParams(params);
}
/**

View File

@@ -19,7 +19,6 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
@SuppressWarnings({"unused", "deprecation"})
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {

View File

@@ -404,6 +404,10 @@ public final class app/revanced/patches/music/layout/branding/CustomBrandingPatc
public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/music/layout/buttons/HideButtonsKt {
public static final fun getHideButtons ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/layout/castbutton/HideCastButtonKt {
public static final fun getHideCastButton ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -0,0 +1,64 @@
package app.revanced.patches.music.layout.buttons
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val mediaRouteButtonFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("Z")
strings("MediaRouteButton")
}
internal val playerOverlayChipFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
literal { playerOverlayChip }
}
internal val historyMenuItemFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Landroid/view/Menu;")
opcodes(
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID
)
literal { historyMenuItem }
custom { _, classDef ->
classDef.methods.count() == 5
}
}
internal val historyMenuItemOfflineTabFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Landroid/view/Menu;")
opcodes(
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID
)
custom { method, _ ->
method.containsLiteralInstruction(historyMenuItem) &&
method.containsLiteralInstruction(offlineSettingsMenuItem)
}
}
internal val searchActionViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters()
literal { searchButton }
custom { _, classDef ->
classDef.type.endsWith("/SearchActionProvider;")
}
}
internal val topBarMenuItemImageViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters()
literal { topBarMenuItemImageView }
}

View File

@@ -0,0 +1,121 @@
package app.revanced.patches.music.layout.buttons
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
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
internal var playerOverlayChip = -1L
private set
internal var historyMenuItem = -1L
private set
internal var offlineSettingsMenuItem = -1L
private set
internal var searchButton = -1L
private set
internal var topBarMenuItemImageView = -1L
private set
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideButtonsPatch;"
@Suppress("unused")
val hideButtons = bytecodePatch(
name = "Hide buttons",
description = "Adds options to hide the cast, history, notification, and search buttons."
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
resourceMappingPatch
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52",
"8.10.52"
)
)
execute {
playerOverlayChip = resourceMappings["id", "player_overlay_chip"]
historyMenuItem = resourceMappings["id", "history_menu_item"]
offlineSettingsMenuItem = resourceMappings["id", "offline_settings_menu_item"]
searchButton = resourceMappings["layout", "search_button"]
topBarMenuItemImageView = resourceMappings["id", "top_bar_menu_item_image_view"]
addResources("music", "layout.buttons.hideButtons")
PreferenceScreen.GENERAL.addPreferences(
SwitchPreference("revanced_music_hide_cast_button"),
SwitchPreference("revanced_music_hide_history_button"),
SwitchPreference("revanced_music_hide_notification_button"),
SwitchPreference("revanced_music_hide_search_button")
)
// Region for hide history button in the top bar.
arrayOf(
historyMenuItemFingerprint,
historyMenuItemOfflineTabFingerprint
).forEach { fingerprint ->
fingerprint.method.apply {
val targetIndex = fingerprint.patternMatch!!.startIndex
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerD
addInstructions(
targetIndex,
"""
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideHistoryButton(Z)Z
move-result v$targetRegister
"""
)
}
}
// Region for hide cast, search and notification buttons in the top bar.
arrayOf(
Triple(playerOverlayChipFingerprint, playerOverlayChip, "hideCastButton"),
Triple(searchActionViewFingerprint, searchButton, "hideSearchButton"),
Triple(topBarMenuItemImageViewFingerprint, topBarMenuItemImageView, "hideNotificationButton")
).forEach { (fingerprint, resourceIdLiteral, methodName) ->
fingerprint.method.apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(resourceIdLiteral)
val targetIndex = indexOfFirstInstructionOrThrow(
resourceIndex, Opcode.MOVE_RESULT_OBJECT
)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
targetIndex + 1,
"invoke-static { v$targetRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V"
)
}
}
// Region for hide cast button in the player.
mediaRouteButtonFingerprint.classDef.methods.single { method ->
method.name == "setVisibility"
}.addInstructions(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(I)I
move-result p1
"""
)
}
}

View File

@@ -1,17 +0,0 @@
package app.revanced.patches.music.layout.castbutton
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
internal val mediaRouteButtonFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("Z")
strings("MediaRouteButton")
}
internal val playerOverlayChipFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
literal { playerOverlayChip }
}

View File

@@ -1,77 +1,10 @@
package app.revanced.patches.music.layout.castbutton
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var playerOverlayChip = -1L
private set
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideCastButtonPatch;"
import app.revanced.patches.music.layout.buttons.hideButtons
@Deprecated("Patch was moved", ReplaceWith("hideButtons"))
@Suppress("unused")
val hideCastButton = bytecodePatch(
name = "Hide cast button",
description = "Adds an option to hide the cast button."
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
resourceMappingPatch
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52",
"8.10.52"
)
)
execute {
playerOverlayChip = resourceMappings["id", "player_overlay_chip"]
addResources("music", "layout.castbutton.hideCastButton")
PreferenceScreen.GENERAL.addPreferences(
SwitchPreference("revanced_music_hide_cast_button"),
)
mediaRouteButtonFingerprint.classDef.apply {
val setVisibilityMethod = methods.first { method -> method.name == "setVisibility" }
setVisibilityMethod.addInstructions(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(I)I
move-result p1
"""
)
}
playerOverlayChipFingerprint.method.apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(playerOverlayChip)
val targetIndex = indexOfFirstInstructionOrThrow(resourceIndex, Opcode.MOVE_RESULT_OBJECT)
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
targetIndex + 1,
"invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(Landroid/view/View;)V"
)
}
}
val hideCastButton = bytecodePatch{
dependsOn(hideButtons)
}

View File

@@ -1,4 +1,3 @@
<!-- Copyright 2024 ReVanced. See https://github.com/ReVanced/revanced-branding -->
<!--
All strings must have a unique path, even if the same string is declared in two different apps.
@@ -1700,10 +1699,19 @@ Video playback with AV1 may stutter or drop frames."</string>
<string name="revanced_music_play_permanent_repeat_summary_on">Permanent repeat is enabled</string>
<string name="revanced_music_play_permanent_repeat_summary_off">Permanent repeat is disabled</string>
</patch>
<patch id="layout.castbutton.hideCastButton">
<patch id="layout.buttons.hideButtons">
<string name="revanced_music_hide_cast_button_title">Hide cast button</string>
<string name="revanced_music_hide_cast_button_summary_on">Cast button is hidden</string>
<string name="revanced_music_hide_cast_button_summary_off">Cast button is shown</string>
<string name="revanced_music_hide_history_button_title">Hide history button</string>
<string name="revanced_music_hide_history_button_summary_on">History button is hidden</string>
<string name="revanced_music_hide_history_button_summary_off">History button is shown</string>
<string name="revanced_music_hide_notification_button_title">Hide notification button</string>
<string name="revanced_music_hide_notification_button_summary_on">Notification button is hidden</string>
<string name="revanced_music_hide_notification_button_summary_off">Notification button is shown</string>
<string name="revanced_music_hide_search_button_title">Hide search button</string>
<string name="revanced_music_hide_search_button_summary_on">Search button is hidden</string>
<string name="revanced_music_hide_search_button_summary_off">Search button is shown</string>
</patch>
<patch id="layout.compactheader.hideCategoryBar">
<string name="revanced_music_hide_category_bar_title">Hide category bar</string>