diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/GoogleApiActivityHook.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/GoogleApiActivityHook.java
deleted file mode 100644
index 336ea890f..000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/GoogleApiActivityHook.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package app.revanced.extension.music.settings;
-
-import android.app.Activity;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.preference.PreferenceFragment;
-import android.view.View;
-
-import app.revanced.extension.music.settings.preference.ReVancedPreferenceFragment;
-import app.revanced.extension.shared.Logger;
-import app.revanced.extension.shared.Utils;
-import app.revanced.extension.shared.settings.BaseActivityHook;
-
-/**
- * Hooks GoogleApiActivity to inject a custom ReVancedPreferenceFragment with a toolbar.
- */
-public class GoogleApiActivityHook extends BaseActivityHook {
- /**
- * Injection point
- *
- * Creates an instance of GoogleApiActivityHook for use in static initialization.
- */
- @SuppressWarnings("unused")
- public static GoogleApiActivityHook createInstance() {
- // Must touch the Music settings to ensure the class is loaded and
- // the values can be found when setting the UI preferences.
- // Logging anything under non debug ensures this is set.
- Logger.printInfo(() -> "Permanent repeat enabled: " + Settings.PERMANENT_REPEAT.get());
-
- // YT Music always uses dark mode.
- Utils.setIsDarkModeEnabled(true);
-
- return new GoogleApiActivityHook();
- }
-
- /**
- * Sets the fixed theme for the activity.
- */
- @Override
- protected void customizeActivityTheme(Activity activity) {
- // Override the default YouTube Music theme to increase start padding of list items.
- // Custom style located in resources/music/values/style.xml
- activity.setTheme(Utils.getResourceIdentifier("Theme.ReVanced.YouTubeMusic.Settings", "style"));
- }
-
- /**
- * Returns the resource ID for the YouTube Music settings layout.
- */
- @Override
- protected int getContentViewResourceId() {
- return Utils.getResourceIdentifier("revanced_music_settings_with_toolbar", "layout");
- }
-
- /**
- * Returns the fixed background color for the toolbar.
- */
- @Override
- protected int getToolbarBackgroundColor() {
- return Utils.getResourceColor("ytm_color_black");
- }
-
- /**
- * Returns the navigation icon with a color filter applied.
- */
- @Override
- protected Drawable getNavigationIcon() {
- Drawable navigationIcon = ReVancedPreferenceFragment.getBackButtonDrawable();
- navigationIcon.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN);
- return navigationIcon;
- }
-
- /**
- * Returns the click listener that finishes the activity when the navigation icon is clicked.
- */
- @Override
- protected View.OnClickListener getNavigationClickListener(Activity activity) {
- return view -> activity.finish();
- }
-
- /**
- * Creates a new ReVancedPreferenceFragment for the activity.
- */
- @Override
- protected PreferenceFragment createPreferenceFragment() {
- return new ReVancedPreferenceFragment();
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java
new file mode 100644
index 000000000..bb19d2497
--- /dev/null
+++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java
@@ -0,0 +1,126 @@
+package app.revanced.extension.music.settings;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.preference.PreferenceFragment;
+import android.view.View;
+import android.widget.Toolbar;
+
+import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
+import app.revanced.extension.music.settings.search.MusicSearchViewController;
+import app.revanced.extension.shared.Logger;
+import app.revanced.extension.shared.Utils;
+import app.revanced.extension.shared.settings.BaseActivityHook;
+
+/**
+ * Hooks GoogleApiActivity to inject a custom {@link MusicPreferenceFragment} with a toolbar and search.
+ */
+public class MusicActivityHook extends BaseActivityHook {
+
+ @SuppressLint("StaticFieldLeak")
+ public static MusicSearchViewController searchViewController;
+
+ /**
+ * Injection point.
+ */
+ @SuppressWarnings("unused")
+ public static void initialize(Activity parentActivity) {
+ // Must touch the Music settings to ensure the class is loaded and
+ // the values can be found when setting the UI preferences.
+ // Logging anything under non debug ensures this is set.
+ Logger.printInfo(() -> "Permanent repeat enabled: " + Settings.PERMANENT_REPEAT.get());
+
+ // YT Music always uses dark mode.
+ Utils.setIsDarkModeEnabled(true);
+
+ BaseActivityHook.initialize(new MusicActivityHook(), parentActivity);
+ }
+
+ /**
+ * Sets the fixed theme for the activity.
+ */
+ @Override
+ protected void customizeActivityTheme(Activity activity) {
+ // Override the default YouTube Music theme to increase start padding of list items.
+ // Custom style located in resources/music/values/style.xml
+ activity.setTheme(Utils.getResourceIdentifierOrThrow(
+ "Theme.ReVanced.YouTubeMusic.Settings", "style"));
+ }
+
+ /**
+ * Returns the resource ID for the YouTube Music settings layout.
+ */
+ @Override
+ protected int getContentViewResourceId() {
+ return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR;
+ }
+
+ /**
+ * Returns the fixed background color for the toolbar.
+ */
+ @Override
+ protected int getToolbarBackgroundColor() {
+ return Utils.getResourceColor("ytm_color_black");
+ }
+
+ /**
+ * Returns the navigation icon with a color filter applied.
+ */
+ @Override
+ protected Drawable getNavigationIcon() {
+ Drawable navigationIcon = MusicPreferenceFragment.getBackButtonDrawable();
+ navigationIcon.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN);
+ return navigationIcon;
+ }
+
+ /**
+ * Returns the click listener that finishes the activity when the navigation icon is clicked.
+ */
+ @Override
+ protected View.OnClickListener getNavigationClickListener(Activity activity) {
+ return view -> {
+ if (searchViewController != null && searchViewController.isSearchActive()) {
+ searchViewController.closeSearch();
+ } else {
+ activity.finish();
+ }
+ };
+ }
+
+ /**
+ * Adds search view components to the toolbar for {@link MusicPreferenceFragment}.
+ *
+ * @param activity The activity hosting the toolbar.
+ * @param toolbar The configured toolbar.
+ * @param fragment The PreferenceFragment associated with the activity.
+ */
+ @Override
+ protected void onPostToolbarSetup(Activity activity, Toolbar toolbar, PreferenceFragment fragment) {
+ if (fragment instanceof MusicPreferenceFragment) {
+ searchViewController = MusicSearchViewController.addSearchViewComponents(
+ activity, toolbar, (MusicPreferenceFragment) fragment);
+ }
+ }
+
+ /**
+ * Creates a new {@link MusicPreferenceFragment} for the activity.
+ */
+ @Override
+ protected PreferenceFragment createPreferenceFragment() {
+ return new MusicPreferenceFragment();
+ }
+
+ /**
+ * Injection point.
+ *
+ * Overrides {@link Activity#finish()} of the injection Activity.
+ *
+ * @return if the original activity finish method should be allowed to run.
+ */
+ @SuppressWarnings("unused")
+ public static boolean handleFinish() {
+ return MusicSearchViewController.handleFinish(searchViewController);
+ }
+}
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 832118829..4feb13d9c 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
@@ -2,7 +2,6 @@ package app.revanced.extension.music.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
-
import static app.revanced.extension.shared.settings.Setting.parent;
import app.revanced.extension.shared.settings.BaseSettings;
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java
new file mode 100644
index 000000000..1ebae16df
--- /dev/null
+++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java
@@ -0,0 +1,80 @@
+package app.revanced.extension.music.settings.preference;
+
+import android.app.Dialog;
+import android.preference.PreferenceScreen;
+import android.widget.Toolbar;
+
+import app.revanced.extension.music.settings.MusicActivityHook;
+import app.revanced.extension.shared.Logger;
+import app.revanced.extension.shared.Utils;
+import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment;
+
+/**
+ * Preference fragment for ReVanced settings.
+ */
+@SuppressWarnings("deprecation")
+public class MusicPreferenceFragment extends ToolbarPreferenceFragment {
+ /**
+ * The main PreferenceScreen used to display the current set of preferences.
+ */
+ private PreferenceScreen preferenceScreen;
+
+ /**
+ * Initializes the preference fragment.
+ */
+ @Override
+ protected void initialize() {
+ super.initialize();
+
+ try {
+ preferenceScreen = getPreferenceScreen();
+ Utils.sortPreferenceGroups(preferenceScreen);
+ setPreferenceScreenToolbar(preferenceScreen);
+ } catch (Exception ex) {
+ Logger.printException(() -> "initialize failure", ex);
+ }
+ }
+
+ /**
+ * Called when the fragment starts.
+ */
+ @Override
+ public void onStart() {
+ super.onStart();
+ try {
+ // Initialize search controller if needed
+ if (MusicActivityHook.searchViewController != null) {
+ // Trigger search data collection after fragment is ready.
+ MusicActivityHook.searchViewController.initializeSearchData();
+ }
+ } catch (Exception ex) {
+ Logger.printException(() -> "onStart failure", ex);
+ }
+ }
+
+ /**
+ * Sets toolbar for all nested preference screens.
+ */
+ @Override
+ protected void customizeToolbar(Toolbar toolbar) {
+ MusicActivityHook.setToolbarLayoutParams(toolbar);
+ }
+
+ /**
+ * Perform actions after toolbar setup.
+ */
+ @Override
+ protected void onPostToolbarSetup(Toolbar toolbar, Dialog preferenceScreenDialog) {
+ if (MusicActivityHook.searchViewController != null
+ && MusicActivityHook.searchViewController.isSearchActive()) {
+ toolbar.post(() -> MusicActivityHook.searchViewController.closeSearch());
+ }
+ }
+
+ /**
+ * Returns the preference screen for external access by SearchViewController.
+ */
+ public PreferenceScreen getPreferenceScreenForSearch() {
+ return preferenceScreen;
+ }
+}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java
deleted file mode 100644
index 67ca69ba4..000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/ReVancedPreferenceFragment.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package app.revanced.extension.music.settings.preference;
-
-import android.widget.Toolbar;
-
-import app.revanced.extension.music.settings.GoogleApiActivityHook;
-import app.revanced.extension.shared.Logger;
-import app.revanced.extension.shared.Utils;
-import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment;
-
-/**
- * Preference fragment for ReVanced settings.
- */
-@SuppressWarnings({"deprecation", "NewApi"})
-public class ReVancedPreferenceFragment extends ToolbarPreferenceFragment {
-
- /**
- * Initializes the preference fragment.
- */
- @Override
- protected void initialize() {
- super.initialize();
-
- try {
- Utils.sortPreferenceGroups(getPreferenceScreen());
- setPreferenceScreenToolbar(getPreferenceScreen());
- } catch (Exception ex) {
- Logger.printException(() -> "initialize failure", ex);
- }
- }
-
- /**
- * Sets toolbar for all nested preference screens.
- */
- @Override
- protected void customizeToolbar(Toolbar toolbar) {
- GoogleApiActivityHook.setToolbarLayoutParams(toolbar);
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java
new file mode 100644
index 000000000..65ccd4ea1
--- /dev/null
+++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java
@@ -0,0 +1,28 @@
+package app.revanced.extension.music.settings.search;
+
+import android.content.Context;
+import android.preference.PreferenceScreen;
+
+import app.revanced.extension.shared.settings.search.BaseSearchResultsAdapter;
+import app.revanced.extension.shared.settings.search.BaseSearchViewController;
+import app.revanced.extension.shared.settings.search.BaseSearchResultItem;
+
+import java.util.List;
+
+/**
+ * Music-specific search results adapter.
+ */
+@SuppressWarnings("deprecation")
+public class MusicSearchResultsAdapter extends BaseSearchResultsAdapter {
+
+ public MusicSearchResultsAdapter(Context context, List items,
+ BaseSearchViewController.BasePreferenceFragment fragment,
+ BaseSearchViewController searchViewController) {
+ super(context, items, fragment, searchViewController);
+ }
+
+ @Override
+ protected PreferenceScreen getMainPreferenceScreen() {
+ return fragment.getPreferenceScreenForSearch();
+ }
+}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java
new file mode 100644
index 000000000..6681a2f02
--- /dev/null
+++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java
@@ -0,0 +1,71 @@
+package app.revanced.extension.music.settings.search;
+
+import android.app.Activity;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.view.View;
+import android.widget.Toolbar;
+
+import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
+import app.revanced.extension.shared.settings.search.*;
+
+/**
+ * Music-specific search view controller implementation.
+ */
+@SuppressWarnings("deprecation")
+public class MusicSearchViewController extends BaseSearchViewController {
+
+ public static MusicSearchViewController addSearchViewComponents(Activity activity, Toolbar toolbar,
+ MusicPreferenceFragment fragment) {
+ return new MusicSearchViewController(activity, toolbar, fragment);
+ }
+
+ private MusicSearchViewController(Activity activity, Toolbar toolbar, MusicPreferenceFragment fragment) {
+ super(activity, toolbar, new PreferenceFragmentAdapter(fragment));
+ }
+
+ @Override
+ protected BaseSearchResultsAdapter createSearchResultsAdapter() {
+ return new MusicSearchResultsAdapter(activity, filteredSearchItems, fragment, this);
+ }
+
+ @Override
+ protected boolean isSpecialPreferenceGroup(Preference preference) {
+ // Music doesn't have SponsorBlock, so no special groups.
+ return false;
+ }
+
+ @Override
+ protected void setupSpecialPreferenceListeners(BaseSearchResultItem item) {
+ // Music doesn't have special preferences.
+ // This method can be empty or handle music-specific preferences if any.
+ }
+
+ // Static method for handling Activity finish
+ public static boolean handleFinish(MusicSearchViewController searchViewController) {
+ if (searchViewController != null && searchViewController.isSearchActive()) {
+ searchViewController.closeSearch();
+ return true;
+ }
+ return false;
+ }
+
+ // Adapter to wrap MusicPreferenceFragment to BasePreferenceFragment interface.
+ private record PreferenceFragmentAdapter(MusicPreferenceFragment fragment) implements BasePreferenceFragment {
+
+ @Override
+ public PreferenceScreen getPreferenceScreenForSearch() {
+ return fragment.getPreferenceScreenForSearch();
+ }
+
+ @Override
+ public View getView() {
+ return fragment.getView();
+ }
+
+ @Override
+ public Activity getActivity() {
+ return fragment.getActivity();
+ }
+ }
+}
diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
index 3f9f0af11..978ee7131 100644
--- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
@@ -27,6 +27,7 @@ import java.util.Locale;
import app.revanced.extension.shared.requests.Requester;
import app.revanced.extension.shared.requests.Route;
+import app.revanced.extension.shared.ui.CustomDialog;
@SuppressWarnings("unused")
public class GmsCoreSupport {
@@ -80,17 +81,17 @@ public class GmsCoreSupport {
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
Utils.runOnMainThreadDelayed(() -> {
// Create the custom dialog.
- Pair