diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/HideButtonsPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/HideButtonsPatch.java new file mode 100644 index 000000000..5794baa9b --- /dev/null +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/HideButtonsPatch.java @@ -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); + } +} diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCastButtonPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCastButtonPatch.java deleted file mode 100644 index ab03ba8e4..000000000 --- a/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCastButtonPatch.java +++ /dev/null @@ -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); - } -} diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCategoryBarPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCategoryBarPatch.java index 79d772096..f0b090155 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCategoryBarPatch.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/HideCategoryBarPatch.java @@ -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") diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java index a56fb04b9..bebb3f86c 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java @@ -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); diff --git a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java index 10cbda63a..b11ec0875 100644 --- a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java +++ b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java @@ -204,4 +204,4 @@ class SpeedIconDrawable extends Drawable { public int getIntrinsicHeight() { return Dim.dp32; } -} \ No newline at end of file +} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index 551d89753..5d6bb492d 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -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); } /** diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java index 6ae43018e..1044ba424 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ImportExportPreference.java @@ -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 { diff --git a/patches/api/patches.api b/patches/api/patches.api index 9c3b806d3..92a94463e 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -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; } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt new file mode 100644 index 000000000..31c844aea --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/Fingerprints.kt @@ -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 } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt new file mode 100644 index 000000000..578eaebe2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/buttons/HideButtons.kt @@ -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(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(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 + """ + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/Fingerprints.kt deleted file mode 100644 index 718d49479..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/Fingerprints.kt +++ /dev/null @@ -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 } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt index fba3653ce..bb6053f8a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt @@ -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(targetIndex).registerA - - addInstruction( - targetIndex + 1, - "invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCastButton(Landroid/view/View;)V" - ) - } - } +val hideCastButton = bytecodePatch{ + dependsOn(hideButtons) } diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 559367390..eb97fb7d4 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1,4 +1,3 @@ -