mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2025-05-11 21:25:40 +02:00
refactor
This commit is contained in:
parent
828013155a
commit
208a8e2660
2 changed files with 295 additions and 283 deletions
|
@ -19,6 +19,13 @@ import android.widget.SearchView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.AppLanguage;
|
import app.revanced.extension.shared.settings.AppLanguage;
|
||||||
|
@ -26,19 +33,15 @@ import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
import app.revanced.extension.youtube.ThemeHelper;
|
||||||
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for managing the search view in ReVanced settings.
|
* Controller for managing the search view in ReVanced settings.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"deprecated", "DiscouragedApi"})
|
||||||
public class SearchViewController {
|
public class SearchViewController {
|
||||||
private final SearchView searchView;
|
private final SearchView searchView;
|
||||||
private final FrameLayout searchContainer;
|
private final FrameLayout searchContainer;
|
||||||
private final Toolbar toolbar;
|
private final Toolbar toolbar;
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
private final ReVancedPreferenceFragment fragment;
|
|
||||||
private boolean isSearchActive;
|
private boolean isSearchActive;
|
||||||
private final CharSequence originalTitle;
|
private final CharSequence originalTitle;
|
||||||
private final SharedPreferences searchHistoryPrefs;
|
private final SharedPreferences searchHistoryPrefs;
|
||||||
|
@ -81,7 +84,6 @@ public class SearchViewController {
|
||||||
private SearchViewController(Activity activity, Toolbar toolbar, ReVancedPreferenceFragment fragment) {
|
private SearchViewController(Activity activity, Toolbar toolbar, ReVancedPreferenceFragment fragment) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.toolbar = toolbar;
|
this.toolbar = toolbar;
|
||||||
this.fragment = fragment;
|
|
||||||
this.originalTitle = toolbar.getTitle();
|
this.originalTitle = toolbar.getTitle();
|
||||||
this.searchHistoryPrefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
this.searchHistoryPrefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
@ -107,8 +109,13 @@ public class SearchViewController {
|
||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
if (!query.trim().isEmpty()) {
|
try {
|
||||||
saveSearchQuery(query.trim());
|
String queryTrimmed = query.trim();
|
||||||
|
if (!queryTrimmed.isEmpty()) {
|
||||||
|
saveSearchQuery(queryTrimmed);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "onQueryTextSubmit failure", ex);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -181,11 +188,10 @@ public class SearchViewController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the search history from SharedPreferences.
|
* Retrieves the search history from SharedPreferences.
|
||||||
* @return List of search history entries, up to MAX_HISTORY_SIZE.
|
* @return Set of search history entries, up to MAX_HISTORY_SIZE.
|
||||||
*/
|
*/
|
||||||
private ArrayList<String> getSearchHistory() {
|
private Set<String> getSearchHistory() {
|
||||||
Set<String> historySet = searchHistoryPrefs.getStringSet(KEY_SEARCH_HISTORY, new LinkedHashSet<>());
|
return searchHistoryPrefs.getStringSet(KEY_SEARCH_HISTORY, new LinkedHashSet<>());
|
||||||
return new ArrayList<>(historySet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,14 +199,15 @@ public class SearchViewController {
|
||||||
* @param query The search query to save.
|
* @param query The search query to save.
|
||||||
*/
|
*/
|
||||||
private void saveSearchQuery(String query) {
|
private void saveSearchQuery(String query) {
|
||||||
LinkedHashSet<String> historySet = new LinkedHashSet<>(getSearchHistory());
|
Set<String> historySet = getSearchHistory();
|
||||||
historySet.remove(query); // Remove if already exists to update position
|
historySet.remove(query); // Remove if already exists to update position
|
||||||
historySet.add(query); // Add to the end (most recent)
|
historySet.add(query); // Add to the end (most recent)
|
||||||
|
|
||||||
// Keep only the last MAX_HISTORY_SIZE entries
|
// Keep only the last MAX_HISTORY_SIZE entries
|
||||||
|
Iterator<String> iterator = historySet.iterator();
|
||||||
while (historySet.size() > MAX_HISTORY_SIZE) {
|
while (historySet.size() > MAX_HISTORY_SIZE) {
|
||||||
String first = historySet.iterator().next();
|
iterator.next();
|
||||||
historySet.remove(first);
|
iterator.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save to SharedPreferences
|
// Save to SharedPreferences
|
||||||
|
@ -217,7 +224,7 @@ public class SearchViewController {
|
||||||
* @param query The search query to remove.
|
* @param query The search query to remove.
|
||||||
*/
|
*/
|
||||||
private void removeSearchQuery(String query) {
|
private void removeSearchQuery(String query) {
|
||||||
LinkedHashSet<String> historySet = new LinkedHashSet<>(getSearchHistory());
|
Set<String> historySet = getSearchHistory();
|
||||||
historySet.remove(query);
|
historySet.remove(query);
|
||||||
|
|
||||||
// Save to SharedPreferences
|
// Save to SharedPreferences
|
||||||
|
@ -233,8 +240,8 @@ public class SearchViewController {
|
||||||
* Updates the search history adapter with the latest history.
|
* Updates the search history adapter with the latest history.
|
||||||
*/
|
*/
|
||||||
private void updateSearchHistoryAdapter() {
|
private void updateSearchHistoryAdapter() {
|
||||||
AutoCompleteTextView autoCompleteTextView = searchView.findViewById(
|
AutoCompleteTextView autoCompleteTextView = searchView.findViewById(searchView.getContext()
|
||||||
searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null));
|
.getResources().getIdentifier("android:id/search_src_text", null, null));
|
||||||
if (autoCompleteTextView != null) {
|
if (autoCompleteTextView != null) {
|
||||||
SearchHistoryAdapter adapter = (SearchHistoryAdapter) autoCompleteTextView.getAdapter();
|
SearchHistoryAdapter adapter = (SearchHistoryAdapter) autoCompleteTextView.getAdapter();
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
|
@ -283,17 +290,16 @@ public class SearchViewController {
|
||||||
* Custom ArrayAdapter for search history.
|
* Custom ArrayAdapter for search history.
|
||||||
*/
|
*/
|
||||||
private class SearchHistoryAdapter extends ArrayAdapter<String> {
|
private class SearchHistoryAdapter extends ArrayAdapter<String> {
|
||||||
private final ArrayList<String> history;
|
public SearchHistoryAdapter(Context context, Set<String> history) {
|
||||||
|
super(context, 0, new ArrayList<>(history));
|
||||||
public SearchHistoryAdapter(Context context, ArrayList<String> history) {
|
|
||||||
super(context, 0, history);
|
|
||||||
this.history = history;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView, android.view.ViewGroup parent) {
|
public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) {
|
||||||
if (convertView == null) {
|
if (convertView == null) {
|
||||||
convertView = LinearLayout.inflate(getContext(), getResourceIdentifier("revanced_search_suggestion_item", "layout"), null);
|
convertView = LinearLayout.inflate(getContext(), getResourceIdentifier(
|
||||||
|
"revanced_search_suggestion_item", "layout"), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply rounded corners programmatically
|
// Apply rounded corners programmatically
|
||||||
|
@ -301,7 +307,8 @@ public class SearchViewController {
|
||||||
String query = getItem(position);
|
String query = getItem(position);
|
||||||
|
|
||||||
// Set query text
|
// Set query text
|
||||||
TextView textView = convertView.findViewById(getResourceIdentifier("suggestion_text", "id"));
|
TextView textView = convertView.findViewById(getResourceIdentifier(
|
||||||
|
"suggestion_text", "id"));
|
||||||
if (textView != null) {
|
if (textView != null) {
|
||||||
textView.setText(query);
|
textView.setText(query);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,263 +54,6 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGrou
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||||
|
|
||||||
private abstract static class AbstractPreferenceSearchData<T extends Preference> {
|
|
||||||
/**
|
|
||||||
* @return The navigation path for the given preference, such as "Player > Action buttons".
|
|
||||||
*/
|
|
||||||
private static String getPreferenceNavigationString(Preference preference) {
|
|
||||||
Deque<CharSequence> pathElements = new ArrayDeque<>();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
preference = preference.getParent();
|
|
||||||
|
|
||||||
if (preference == null) {
|
|
||||||
if (pathElements.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return Utils.getTextDirectionString() + String.join(" > ", pathElements);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(preference instanceof NoTitlePreferenceCategory)
|
|
||||||
&& !(preference instanceof SponsorBlockPreferenceGroup)) {
|
|
||||||
CharSequence title = preference.getTitle();
|
|
||||||
if (title != null && title.length() > 0) {
|
|
||||||
pathElements.addFirst(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highlights the search query in the given text by applying bold and underline style spans.
|
|
||||||
* @param text The original text to process.
|
|
||||||
* @param queryPattern The search query to highlight.
|
|
||||||
* @return The text with highlighted query matches as a SpannableStringBuilder.
|
|
||||||
*/
|
|
||||||
static CharSequence highlightSearchQuery(CharSequence text, Pattern queryPattern) {
|
|
||||||
if (TextUtils.isEmpty(text)) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int baseColor = ThemeHelper.getBackgroundColor();
|
|
||||||
final int adjustedColor = ThemeHelper.isDarkTheme()
|
|
||||||
? ThemeHelper.adjustColorBrightness(baseColor, 1.20f) // Lighten for dark theme
|
|
||||||
: ThemeHelper.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme
|
|
||||||
|
|
||||||
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
|
|
||||||
Matcher matcher = queryPattern.matcher(text);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
|
||||||
spannable.setSpan(
|
|
||||||
new BackgroundColorSpan(adjustedColor), // Highlight color
|
|
||||||
matcher.start(),
|
|
||||||
matcher.end(),
|
|
||||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return spannable;
|
|
||||||
}
|
|
||||||
|
|
||||||
final T preference;
|
|
||||||
final String key;
|
|
||||||
final String navigationPath;
|
|
||||||
boolean highlightingApplied;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
CharSequence originalTitle;
|
|
||||||
@Nullable
|
|
||||||
String searchTitle;
|
|
||||||
|
|
||||||
AbstractPreferenceSearchData(T pref) {
|
|
||||||
preference = pref;
|
|
||||||
key = Utils.removePunctuationToLowercase(pref.getKey());
|
|
||||||
navigationPath = getPreferenceNavigationString(pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void updateSearchDataIfNeeded() {
|
|
||||||
if (highlightingApplied) {
|
|
||||||
// Must clear, otherwise old highlighting is still applied.
|
|
||||||
clearHighlighting();
|
|
||||||
}
|
|
||||||
|
|
||||||
CharSequence title = preference.getTitle();
|
|
||||||
if (originalTitle != title) { // Check using reference equality.
|
|
||||||
originalTitle = title;
|
|
||||||
searchTitle = Utils.removePunctuationToLowercase(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
boolean matchesSearchQuery(String query) {
|
|
||||||
updateSearchDataIfNeeded();
|
|
||||||
|
|
||||||
return key.contains(query)
|
|
||||||
|| searchTitle != null && searchTitle.contains(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void applyHighlighting(String query, Pattern queryPattern) {
|
|
||||||
preference.setTitle(highlightSearchQuery(originalTitle, queryPattern));
|
|
||||||
highlightingApplied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void clearHighlighting() {
|
|
||||||
if (highlightingApplied) {
|
|
||||||
preference.setTitle(originalTitle);
|
|
||||||
highlightingApplied = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Regular preference type that only uses the base preference summary.
|
|
||||||
* Should only be used if a more specific data class does not exist.
|
|
||||||
*/
|
|
||||||
private static class PreferenceSearchData extends AbstractPreferenceSearchData<Preference> {
|
|
||||||
@Nullable
|
|
||||||
CharSequence originalSummary;
|
|
||||||
@Nullable
|
|
||||||
String searchSummary;
|
|
||||||
|
|
||||||
PreferenceSearchData(Preference pref) {
|
|
||||||
super(pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSearchDataIfNeeded() {
|
|
||||||
super.updateSearchDataIfNeeded();
|
|
||||||
|
|
||||||
CharSequence summary = preference.getSummary();
|
|
||||||
if (originalSummary != summary) {
|
|
||||||
originalSummary = summary;
|
|
||||||
searchSummary = Utils.removePunctuationToLowercase(summary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matchesSearchQuery(String query) {
|
|
||||||
return super.matchesSearchQuery(query)
|
|
||||||
|| searchSummary != null && searchSummary.contains(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void applyHighlighting(String query, Pattern queryPattern) {
|
|
||||||
super.applyHighlighting(query, queryPattern);
|
|
||||||
|
|
||||||
preference.setSummary(highlightSearchQuery(originalSummary, queryPattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void clearHighlighting() {
|
|
||||||
super.clearHighlighting();
|
|
||||||
|
|
||||||
preference.setSummary(originalSummary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switch preference type that uses summaryOn and summaryOff.
|
|
||||||
*/
|
|
||||||
private static class SwitchPreferenceSearchData extends AbstractPreferenceSearchData<SwitchPreference> {
|
|
||||||
@Nullable
|
|
||||||
CharSequence originalSummaryOn, originalSummaryOff;
|
|
||||||
@Nullable
|
|
||||||
String searchSummaryOn, searchSummaryOff;
|
|
||||||
|
|
||||||
SwitchPreferenceSearchData(SwitchPreference pref) {
|
|
||||||
super(pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSearchDataIfNeeded() {
|
|
||||||
super.updateSearchDataIfNeeded();
|
|
||||||
|
|
||||||
CharSequence summaryOn = preference.getSummaryOn();
|
|
||||||
if (originalSummaryOn != summaryOn) {
|
|
||||||
originalSummaryOn = summaryOn;
|
|
||||||
searchSummaryOn = Utils.removePunctuationToLowercase(summaryOn);
|
|
||||||
}
|
|
||||||
|
|
||||||
CharSequence summaryOff = preference.getSummaryOff();
|
|
||||||
if (originalSummaryOff != summaryOff) {
|
|
||||||
originalSummaryOff = summaryOff;
|
|
||||||
searchSummaryOff = Utils.removePunctuationToLowercase(summaryOff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matchesSearchQuery(String query) {
|
|
||||||
return super.matchesSearchQuery(query)
|
|
||||||
|| searchSummaryOn != null && searchSummaryOn.contains(query)
|
|
||||||
|| searchSummaryOff != null && searchSummaryOff.contains(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void applyHighlighting(String query, Pattern queryPattern) {
|
|
||||||
super.applyHighlighting(query, queryPattern);
|
|
||||||
|
|
||||||
preference.setSummaryOn(highlightSearchQuery(originalSummaryOn, queryPattern));
|
|
||||||
preference.setSummaryOff(highlightSearchQuery(originalSummaryOff, queryPattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void clearHighlighting() {
|
|
||||||
super.clearHighlighting();
|
|
||||||
|
|
||||||
preference.setSummaryOn(originalSummaryOn);
|
|
||||||
preference.setSummaryOff(originalSummaryOff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List preference type that uses entries.
|
|
||||||
*/
|
|
||||||
private static class ListPreferenceSearchData extends AbstractPreferenceSearchData<ListPreference> {
|
|
||||||
@Nullable
|
|
||||||
CharSequence[] originalEntries;
|
|
||||||
@Nullable
|
|
||||||
String searchEntries;
|
|
||||||
|
|
||||||
ListPreferenceSearchData(ListPreference pref) {
|
|
||||||
super(pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateSearchDataIfNeeded() {
|
|
||||||
super.updateSearchDataIfNeeded();
|
|
||||||
|
|
||||||
CharSequence[] entries = preference.getEntries();
|
|
||||||
if (originalEntries != entries) {
|
|
||||||
originalEntries = entries;
|
|
||||||
searchEntries = Utils.removePunctuationToLowercase(String.join(" ", entries));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean matchesSearchQuery(String query) {
|
|
||||||
return super.matchesSearchQuery(query)
|
|
||||||
|| searchEntries != null && searchEntries.contains(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void applyHighlighting(String query, Pattern queryPattern) {
|
|
||||||
super.applyHighlighting(query, queryPattern);
|
|
||||||
|
|
||||||
if (originalEntries != null) {
|
|
||||||
final int length = originalEntries.length;
|
|
||||||
CharSequence[] highlightedEntries = new CharSequence[length];
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
highlightedEntries[i] = highlightSearchQuery(originalEntries[i], queryPattern);
|
|
||||||
}
|
|
||||||
preference.setEntries(highlightedEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
void clearHighlighting() {
|
|
||||||
super.clearHighlighting();
|
|
||||||
|
|
||||||
preference.setEntries(originalEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main PreferenceScreen used to display the current set of preferences.
|
* The main PreferenceScreen used to display the current set of preferences.
|
||||||
* This screen is manipulated during initialization and filtering to show or hide preferences.
|
* This screen is manipulated during initialization and filtering to show or hide preferences.
|
||||||
|
@ -595,3 +338,265 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class AbstractPreferenceSearchData<T extends Preference> {
|
||||||
|
/**
|
||||||
|
* @return The navigation path for the given preference, such as "Player > Action buttons".
|
||||||
|
*/
|
||||||
|
private static String getPreferenceNavigationString(Preference preference) {
|
||||||
|
Deque<CharSequence> pathElements = new ArrayDeque<>();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
preference = preference.getParent();
|
||||||
|
|
||||||
|
if (preference == null) {
|
||||||
|
if (pathElements.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return Utils.getTextDirectionString() + String.join(" > ", pathElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(preference instanceof NoTitlePreferenceCategory)
|
||||||
|
&& !(preference instanceof SponsorBlockPreferenceGroup)) {
|
||||||
|
CharSequence title = preference.getTitle();
|
||||||
|
if (title != null && title.length() > 0) {
|
||||||
|
pathElements.addFirst(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights the search query in the given text by applying bold and underline style spans.
|
||||||
|
* @param text The original text to process.
|
||||||
|
* @param queryPattern The search query to highlight.
|
||||||
|
* @return The text with highlighted query matches as a SpannableStringBuilder.
|
||||||
|
*/
|
||||||
|
static CharSequence highlightSearchQuery(CharSequence text, Pattern queryPattern) {
|
||||||
|
if (TextUtils.isEmpty(text)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int baseColor = ThemeHelper.getBackgroundColor();
|
||||||
|
final int adjustedColor = ThemeHelper.isDarkTheme()
|
||||||
|
? ThemeHelper.adjustColorBrightness(baseColor, 1.20f) // Lighten for dark theme
|
||||||
|
: ThemeHelper.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme
|
||||||
|
|
||||||
|
SpannableStringBuilder spannable = new SpannableStringBuilder(text);
|
||||||
|
Matcher matcher = queryPattern.matcher(text);
|
||||||
|
|
||||||
|
while (matcher.find()) {
|
||||||
|
spannable.setSpan(
|
||||||
|
new BackgroundColorSpan(adjustedColor), // Highlight color
|
||||||
|
matcher.start(),
|
||||||
|
matcher.end(),
|
||||||
|
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spannable;
|
||||||
|
}
|
||||||
|
|
||||||
|
final T preference;
|
||||||
|
final String key;
|
||||||
|
final String navigationPath;
|
||||||
|
boolean highlightingApplied;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
CharSequence originalTitle;
|
||||||
|
@Nullable
|
||||||
|
String searchTitle;
|
||||||
|
|
||||||
|
AbstractPreferenceSearchData(T pref) {
|
||||||
|
preference = pref;
|
||||||
|
key = Utils.removePunctuationToLowercase(pref.getKey());
|
||||||
|
navigationPath = getPreferenceNavigationString(pref);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void updateSearchDataIfNeeded() {
|
||||||
|
if (highlightingApplied) {
|
||||||
|
// Must clear, otherwise old highlighting is still applied.
|
||||||
|
clearHighlighting();
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence title = preference.getTitle();
|
||||||
|
if (originalTitle != title) { // Check using reference equality.
|
||||||
|
originalTitle = title;
|
||||||
|
searchTitle = Utils.removePunctuationToLowercase(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
boolean matchesSearchQuery(String query) {
|
||||||
|
updateSearchDataIfNeeded();
|
||||||
|
|
||||||
|
return key.contains(query)
|
||||||
|
|| searchTitle != null && searchTitle.contains(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void applyHighlighting(String query, Pattern queryPattern) {
|
||||||
|
preference.setTitle(highlightSearchQuery(originalTitle, queryPattern));
|
||||||
|
highlightingApplied = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void clearHighlighting() {
|
||||||
|
if (highlightingApplied) {
|
||||||
|
preference.setTitle(originalTitle);
|
||||||
|
highlightingApplied = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular preference type that only uses the base preference summary.
|
||||||
|
* Should only be used if a more specific data class does not exist.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class PreferenceSearchData extends AbstractPreferenceSearchData<Preference> {
|
||||||
|
@Nullable
|
||||||
|
CharSequence originalSummary;
|
||||||
|
@Nullable
|
||||||
|
String searchSummary;
|
||||||
|
|
||||||
|
PreferenceSearchData(Preference pref) {
|
||||||
|
super(pref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSearchDataIfNeeded() {
|
||||||
|
super.updateSearchDataIfNeeded();
|
||||||
|
|
||||||
|
CharSequence summary = preference.getSummary();
|
||||||
|
if (originalSummary != summary) {
|
||||||
|
originalSummary = summary;
|
||||||
|
searchSummary = Utils.removePunctuationToLowercase(summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean matchesSearchQuery(String query) {
|
||||||
|
return super.matchesSearchQuery(query)
|
||||||
|
|| searchSummary != null && searchSummary.contains(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void applyHighlighting(String query, Pattern queryPattern) {
|
||||||
|
super.applyHighlighting(query, queryPattern);
|
||||||
|
|
||||||
|
preference.setSummary(highlightSearchQuery(originalSummary, queryPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void clearHighlighting() {
|
||||||
|
super.clearHighlighting();
|
||||||
|
|
||||||
|
preference.setSummary(originalSummary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch preference type that uses summaryOn and summaryOff.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class SwitchPreferenceSearchData extends AbstractPreferenceSearchData<SwitchPreference> {
|
||||||
|
@Nullable
|
||||||
|
CharSequence originalSummaryOn, originalSummaryOff;
|
||||||
|
@Nullable
|
||||||
|
String searchSummaryOn, searchSummaryOff;
|
||||||
|
|
||||||
|
SwitchPreferenceSearchData(SwitchPreference pref) {
|
||||||
|
super(pref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSearchDataIfNeeded() {
|
||||||
|
super.updateSearchDataIfNeeded();
|
||||||
|
|
||||||
|
CharSequence summaryOn = preference.getSummaryOn();
|
||||||
|
if (originalSummaryOn != summaryOn) {
|
||||||
|
originalSummaryOn = summaryOn;
|
||||||
|
searchSummaryOn = Utils.removePunctuationToLowercase(summaryOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence summaryOff = preference.getSummaryOff();
|
||||||
|
if (originalSummaryOff != summaryOff) {
|
||||||
|
originalSummaryOff = summaryOff;
|
||||||
|
searchSummaryOff = Utils.removePunctuationToLowercase(summaryOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean matchesSearchQuery(String query) {
|
||||||
|
return super.matchesSearchQuery(query)
|
||||||
|
|| searchSummaryOn != null && searchSummaryOn.contains(query)
|
||||||
|
|| searchSummaryOff != null && searchSummaryOff.contains(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void applyHighlighting(String query, Pattern queryPattern) {
|
||||||
|
super.applyHighlighting(query, queryPattern);
|
||||||
|
|
||||||
|
preference.setSummaryOn(highlightSearchQuery(originalSummaryOn, queryPattern));
|
||||||
|
preference.setSummaryOff(highlightSearchQuery(originalSummaryOff, queryPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void clearHighlighting() {
|
||||||
|
super.clearHighlighting();
|
||||||
|
|
||||||
|
preference.setSummaryOn(originalSummaryOn);
|
||||||
|
preference.setSummaryOff(originalSummaryOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List preference type that uses entries.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
class ListPreferenceSearchData extends AbstractPreferenceSearchData<ListPreference> {
|
||||||
|
@Nullable
|
||||||
|
CharSequence[] originalEntries;
|
||||||
|
@Nullable
|
||||||
|
String searchEntries;
|
||||||
|
|
||||||
|
ListPreferenceSearchData(ListPreference pref) {
|
||||||
|
super(pref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSearchDataIfNeeded() {
|
||||||
|
super.updateSearchDataIfNeeded();
|
||||||
|
|
||||||
|
CharSequence[] entries = preference.getEntries();
|
||||||
|
if (originalEntries != entries) {
|
||||||
|
originalEntries = entries;
|
||||||
|
searchEntries = Utils.removePunctuationToLowercase(String.join(" ", entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean matchesSearchQuery(String query) {
|
||||||
|
return super.matchesSearchQuery(query)
|
||||||
|
|| searchEntries != null && searchEntries.contains(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void applyHighlighting(String query, Pattern queryPattern) {
|
||||||
|
super.applyHighlighting(query, queryPattern);
|
||||||
|
|
||||||
|
if (originalEntries != null) {
|
||||||
|
final int length = originalEntries.length;
|
||||||
|
CharSequence[] highlightedEntries = new CharSequence[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
highlightedEntries[i] = highlightSearchQuery(originalEntries[i], queryPattern);
|
||||||
|
}
|
||||||
|
preference.setEntries(highlightedEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
void clearHighlighting() {
|
||||||
|
super.clearHighlighting();
|
||||||
|
|
||||||
|
preference.setEntries(originalEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue