Skip to content

Commit 7a18ebc

Browse files
authored
feat(YouTube Music): Add Hide buttons patch (#6255)
1 parent 475197a commit 7a18ebc

File tree

13 files changed

+272
-141
lines changed

13 files changed

+272
-141
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package app.revanced.extension.music.patches;
2+
3+
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
4+
5+
import android.view.View;
6+
import android.view.ViewGroup;
7+
8+
import app.revanced.extension.music.settings.Settings;
9+
10+
@SuppressWarnings("unused")
11+
public class HideButtonsPatch {
12+
13+
/**
14+
* Injection point
15+
*/
16+
public static int hideCastButton(int original) {
17+
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
18+
}
19+
20+
/**
21+
* Injection point
22+
*/
23+
public static void hideCastButton(View view) {
24+
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
25+
}
26+
27+
/**
28+
* Injection point
29+
*/
30+
public static boolean hideHistoryButton(boolean original) {
31+
return original && !Settings.HIDE_HISTORY_BUTTON.get();
32+
}
33+
34+
/**
35+
* Injection point
36+
*/
37+
public static void hideNotificationButton(View view) {
38+
if (view.getParent() instanceof ViewGroup viewGroup) {
39+
hideViewBy0dpUnderCondition(Settings.HIDE_NOTIFICATION_BUTTON, viewGroup);
40+
}
41+
}
42+
43+
/**
44+
* Injection point
45+
*/
46+
public static void hideSearchButton(View view) {
47+
hideViewBy0dpUnderCondition(Settings.HIDE_SEARCH_BUTTON, view);
48+
}
49+
}

extensions/music/src/main/java/app/revanced/extension/music/patches/HideCastButtonPatch.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

extensions/music/src/main/java/app/revanced/extension/music/patches/HideCategoryBarPatch.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
44

55
import android.view.View;
6+
67
import app.revanced.extension.music.settings.Settings;
78

89
@SuppressWarnings("unused")

extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ public class Settings extends BaseSettings {
1616
public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true);
1717

1818
// General
19-
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, false);
19+
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, true);
2020
public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true);
21+
public static final BooleanSetting HIDE_HISTORY_BUTTON = new BooleanSetting("revanced_music_hide_history_button", FALSE, true);
22+
public static final BooleanSetting HIDE_SEARCH_BUTTON = new BooleanSetting("revanced_music_hide_search_button", FALSE, true);
23+
public static final BooleanSetting HIDE_NOTIFICATION_BUTTON = new BooleanSetting("revanced_music_hide_notification_button", FALSE, true);
2124
public static final BooleanSetting HIDE_NAVIGATION_BAR_HOME_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_home_button", FALSE, true);
2225
public static final BooleanSetting HIDE_NAVIGATION_BAR_SAMPLES_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_samples_button", FALSE, true);
2326
public static final BooleanSetting HIDE_NAVIGATION_BAR_EXPLORE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_explore_button", FALSE, true);

extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,4 @@ public int getIntrinsicWidth() {
204204
public int getIntrinsicHeight() {
205205
return Dim.dp32;
206206
}
207-
}
207+
}

extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@
3232
import android.view.WindowManager;
3333
import android.view.animation.Animation;
3434
import android.view.animation.AnimationUtils;
35-
import android.widget.FrameLayout;
36-
import android.widget.LinearLayout;
37-
import android.widget.RelativeLayout;
3835
import android.widget.Toast;
39-
import android.widget.Toolbar;
4036

4137
import androidx.annotation.ColorInt;
4238
import androidx.annotation.NonNull;
@@ -759,31 +755,25 @@ public static NetworkType getNetworkType() {
759755
}
760756

761757
/**
762-
* Hide a view by setting its layout params to 0x0
763-
* @param view The view to hide.
758+
* Hides a view by setting its layout width and height to 0dp.
759+
* Handles null layout params safely.
760+
*
761+
* @param view The view to hide. If null, does nothing.
764762
*/
765-
public static void hideViewByLayoutParams(View view) {
766-
if (view instanceof LinearLayout) {
767-
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
768-
view.setLayoutParams(layoutParams);
769-
} else if (view instanceof FrameLayout) {
770-
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
771-
view.setLayoutParams(layoutParams2);
772-
} else if (view instanceof RelativeLayout) {
773-
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
774-
view.setLayoutParams(layoutParams3);
775-
} else if (view instanceof Toolbar) {
776-
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
777-
view.setLayoutParams(layoutParams4);
778-
} else if (view instanceof ViewGroup) {
779-
ViewGroup.LayoutParams layoutParams5 = new ViewGroup.LayoutParams(0, 0);
780-
view.setLayoutParams(layoutParams5);
763+
public static void hideViewByLayoutParams(@Nullable View view) {
764+
if (view == null) return;
765+
766+
ViewGroup.LayoutParams params = view.getLayoutParams();
767+
768+
if (params == null) {
769+
// Create generic 0x0 layout params accepted by all ViewGroups.
770+
params = new ViewGroup.LayoutParams(0, 0);
781771
} else {
782-
ViewGroup.LayoutParams params = view.getLayoutParams();
783772
params.width = 0;
784773
params.height = 0;
785-
view.setLayoutParams(params);
786774
}
775+
776+
view.setLayoutParams(params);
787777
}
788778

789779
/**

extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import app.revanced.extension.shared.Utils;
2020
import app.revanced.extension.shared.settings.Setting;
2121
import app.revanced.extension.shared.ui.CustomDialog;
22-
import app.revanced.extension.shared.ui.Dim;
2322

2423
@SuppressWarnings({"unused", "deprecation"})
2524
public class ImportExportPreference extends EditTextPreference implements Preference.OnPreferenceClickListener {

patches/api/patches.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,10 @@ public final class app/revanced/patches/music/layout/branding/CustomBrandingPatc
404404
public static final fun getCustomBrandingPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
405405
}
406406

407+
public final class app/revanced/patches/music/layout/buttons/HideButtonsKt {
408+
public static final fun getHideButtons ()Lapp/revanced/patcher/patch/BytecodePatch;
409+
}
410+
407411
public final class app/revanced/patches/music/layout/castbutton/HideCastButtonKt {
408412
public static final fun getHideCastButton ()Lapp/revanced/patcher/patch/BytecodePatch;
409413
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package app.revanced.patches.music.layout.buttons
2+
3+
import app.revanced.patcher.fingerprint
4+
import app.revanced.util.containsLiteralInstruction
5+
import app.revanced.util.literal
6+
import com.android.tools.smali.dexlib2.AccessFlags
7+
import com.android.tools.smali.dexlib2.Opcode
8+
9+
internal val mediaRouteButtonFingerprint = fingerprint {
10+
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
11+
returns("Z")
12+
strings("MediaRouteButton")
13+
}
14+
15+
internal val playerOverlayChipFingerprint = fingerprint {
16+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
17+
returns("L")
18+
literal { playerOverlayChip }
19+
}
20+
21+
internal val historyMenuItemFingerprint = fingerprint {
22+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
23+
returns("V")
24+
parameters("Landroid/view/Menu;")
25+
opcodes(
26+
Opcode.INVOKE_INTERFACE,
27+
Opcode.RETURN_VOID
28+
)
29+
literal { historyMenuItem }
30+
custom { _, classDef ->
31+
classDef.methods.count() == 5
32+
}
33+
}
34+
35+
internal val historyMenuItemOfflineTabFingerprint = fingerprint {
36+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
37+
returns("V")
38+
parameters("Landroid/view/Menu;")
39+
opcodes(
40+
Opcode.INVOKE_INTERFACE,
41+
Opcode.RETURN_VOID
42+
)
43+
custom { method, _ ->
44+
method.containsLiteralInstruction(historyMenuItem) &&
45+
method.containsLiteralInstruction(offlineSettingsMenuItem)
46+
}
47+
}
48+
49+
internal val searchActionViewFingerprint = fingerprint {
50+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
51+
returns("Landroid/view/View;")
52+
parameters()
53+
literal { searchButton }
54+
custom { _, classDef ->
55+
classDef.type.endsWith("/SearchActionProvider;")
56+
}
57+
}
58+
59+
internal val topBarMenuItemImageViewFingerprint = fingerprint {
60+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
61+
returns("Landroid/view/View;")
62+
parameters()
63+
literal { topBarMenuItemImageView }
64+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package app.revanced.patches.music.layout.buttons
2+
3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
4+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
5+
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
6+
import app.revanced.patcher.patch.bytecodePatch
7+
import app.revanced.patches.all.misc.resources.addResources
8+
import app.revanced.patches.all.misc.resources.addResourcesPatch
9+
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
10+
import app.revanced.patches.music.misc.settings.PreferenceScreen
11+
import app.revanced.patches.music.misc.settings.settingsPatch
12+
import app.revanced.patches.shared.misc.mapping.get
13+
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
14+
import app.revanced.patches.shared.misc.mapping.resourceMappings
15+
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
16+
import app.revanced.util.indexOfFirstInstructionOrThrow
17+
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
18+
import com.android.tools.smali.dexlib2.Opcode
19+
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
20+
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
21+
22+
internal var playerOverlayChip = -1L
23+
private set
24+
internal var historyMenuItem = -1L
25+
private set
26+
internal var offlineSettingsMenuItem = -1L
27+
private set
28+
internal var searchButton = -1L
29+
private set
30+
internal var topBarMenuItemImageView = -1L
31+
private set
32+
33+
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideButtonsPatch;"
34+
35+
@Suppress("unused")
36+
val hideButtons = bytecodePatch(
37+
name = "Hide buttons",
38+
description = "Adds options to hide the cast, history, notification, and search buttons."
39+
) {
40+
dependsOn(
41+
sharedExtensionPatch,
42+
settingsPatch,
43+
addResourcesPatch,
44+
resourceMappingPatch
45+
)
46+
47+
compatibleWith(
48+
"com.google.android.apps.youtube.music"(
49+
"7.29.52",
50+
"8.10.52"
51+
)
52+
)
53+
54+
execute {
55+
playerOverlayChip = resourceMappings["id", "player_overlay_chip"]
56+
historyMenuItem = resourceMappings["id", "history_menu_item"]
57+
offlineSettingsMenuItem = resourceMappings["id", "offline_settings_menu_item"]
58+
searchButton = resourceMappings["layout", "search_button"]
59+
topBarMenuItemImageView = resourceMappings["id", "top_bar_menu_item_image_view"]
60+
61+
addResources("music", "layout.buttons.hideButtons")
62+
63+
PreferenceScreen.GENERAL.addPreferences(
64+
SwitchPreference("revanced_music_hide_cast_button"),
65+
SwitchPreference("revanced_music_hide_history_button"),
66+
SwitchPreference("revanced_music_hide_notification_button"),
67+
SwitchPreference("revanced_music_hide_search_button")
68+
)
69+
70+
// Region for hide history button in the top bar.
71+
arrayOf(
72+
historyMenuItemFingerprint,
73+
historyMenuItemOfflineTabFingerprint
74+
).forEach { fingerprint ->
75+
fingerprint.method.apply {
76+
val targetIndex = fingerprint.patternMatch!!.startIndex
77+
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerD
78+
79+
addInstructions(
80+
targetIndex,
81+
"""
82+
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideHistoryButton(Z)Z
83+
move-result v$targetRegister
84+
"""
85+
)
86+
}
87+
}
88+
89+
// Region for hide cast, search and notification buttons in the top bar.
90+
arrayOf(
91+
Triple(playerOverlayChipFingerprint, playerOverlayChip, "hideCastButton"),
92+
Triple(searchActionViewFingerprint, searchButton, "hideSearchButton"),
93+
Triple(topBarMenuItemImageViewFingerprint, topBarMenuItemImageView, "hideNotificationButton")
94+
).forEach { (fingerprint, resourceIdLiteral, methodName) ->
95+
fingerprint.method.apply {
96+
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(resourceIdLiteral)
97+
val targetIndex = indexOfFirstInstructionOrThrow(
98+
resourceIndex, Opcode.MOVE_RESULT_OBJECT
99+
)
100+
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
101+
102+
addInstruction(
103+
targetIndex + 1,
104+
"invoke-static { v$targetRegister }, " +
105+
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/view/View;)V"
106+
)
107+
}
108+
}
109+
110+
// Region for hide cast button in the player.
111+
mediaRouteButtonFingerprint.classDef.methods.single { method ->
112+
method.name == "setVisibility"
113+
}.addInstructions(
114+
0,
115+
"""
116+
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(I)I
117+
move-result p1
118+
"""
119+
)
120+
}
121+
}

0 commit comments

Comments
 (0)