mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 01:51:27 +01:00
fix(YouTube - Settings): Resolve settings search crash when searching for specific words (#6231)
This commit is contained in:
@@ -45,6 +45,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.text.Bidi;
|
||||
import java.text.Collator;
|
||||
import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -79,6 +81,15 @@ public class Utils {
|
||||
@Nullable
|
||||
private static Boolean isDarkModeEnabled;
|
||||
|
||||
// Cached Collator instance with its locale.
|
||||
@Nullable
|
||||
private static Locale cachedCollatorLocale;
|
||||
@Nullable
|
||||
private static Collator cachedCollator;
|
||||
|
||||
private static final Pattern PUNCTUATION_PATTERN = Pattern.compile("\\p{P}+");
|
||||
private static final Pattern DIACRITICS_PATTERN = Pattern.compile("\\p{M}");
|
||||
|
||||
private Utils() {
|
||||
} // utility class
|
||||
|
||||
@@ -976,30 +987,60 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern punctuationPattern = Pattern.compile("\\p{P}+");
|
||||
|
||||
/**
|
||||
* Strips all punctuation and converts to lower case. A null parameter returns an empty string.
|
||||
* Removes punctuation and converts text to lowercase. Returns an empty string if input is null.
|
||||
*/
|
||||
public static String removePunctuationToLowercase(@Nullable CharSequence original) {
|
||||
if (original == null) return "";
|
||||
return punctuationPattern.matcher(original).replaceAll("")
|
||||
return PUNCTUATION_PATTERN.matcher(original).replaceAll("")
|
||||
.toLowerCase(BaseSettings.REVANCED_LANGUAGE.get().getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a PreferenceGroup and all it's sub groups by title or key.
|
||||
* Normalizes text for search: applies NFD, removes diacritics, and lowercases (locale-neutral).
|
||||
* Returns an empty string if input is null.
|
||||
*/
|
||||
public static String normalizeTextToLowercase(@Nullable CharSequence original) {
|
||||
if (original == null) return "";
|
||||
return DIACRITICS_PATTERN.matcher(Normalizer.normalize(original, Normalizer.Form.NFD))
|
||||
.replaceAll("").toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cached Collator for the current locale, or creates a new one if locale changed.
|
||||
*/
|
||||
private static Collator getCollator() {
|
||||
Locale currentLocale = BaseSettings.REVANCED_LANGUAGE.get().getLocale();
|
||||
|
||||
if (cachedCollator == null || !currentLocale.equals(cachedCollatorLocale)) {
|
||||
cachedCollatorLocale = currentLocale;
|
||||
cachedCollator = Collator.getInstance(currentLocale);
|
||||
cachedCollator.setStrength(Collator.SECONDARY); // Case-insensitive, diacritic-insensitive.
|
||||
}
|
||||
|
||||
return cachedCollator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a {@link PreferenceGroup} and all nested subgroups by title or key.
|
||||
* <p>
|
||||
* Sort order is determined by the preferences key {@link Sort} suffix.
|
||||
* The sort order is controlled by the {@link Sort} suffix present in the preference key.
|
||||
* Preferences without a key or without a {@link Sort} suffix remain in their original order.
|
||||
* <p>
|
||||
* If a preference has no key or no {@link Sort} suffix,
|
||||
* then the preferences are left unsorted.
|
||||
* Sorting is performed using {@link Collator} with the current user locale,
|
||||
* ensuring correct alphabetical ordering for all supported languages
|
||||
* (e.g., Ukrainian "і", German "ß", French accented characters, etc.).
|
||||
*
|
||||
* @param group the {@link PreferenceGroup} to sort
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sortPreferenceGroups(PreferenceGroup group) {
|
||||
Sort groupSort = Sort.fromKey(group.getKey(), Sort.UNSORTED);
|
||||
List<Pair<String, Preference>> preferences = new ArrayList<>();
|
||||
|
||||
// Get cached Collator for locale-aware string comparison.
|
||||
Collator collator = getCollator();
|
||||
|
||||
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
|
||||
Preference preference = group.getPreference(i);
|
||||
|
||||
@@ -1030,10 +1071,11 @@ public class Utils {
|
||||
preferences.add(new Pair<>(sortValue, preference));
|
||||
}
|
||||
|
||||
//noinspection ComparatorCombinators
|
||||
// Sort the list using locale-specific collation rules.
|
||||
Collections.sort(preferences, (pair1, pair2)
|
||||
-> pair1.first.compareTo(pair2.first));
|
||||
-> collator.compare(pair1.first, pair2.first));
|
||||
|
||||
// Reassign order values to reflect the new sorted sequence
|
||||
int index = 0;
|
||||
for (Pair<String, Preference> pair : preferences) {
|
||||
int order = index++;
|
||||
|
||||
@@ -392,10 +392,13 @@ public abstract class Setting<T> {
|
||||
|
||||
/**
|
||||
* Get the parent Settings that this setting depends on.
|
||||
* @return List of parent Settings (e.g., BooleanSetting or EnumSetting), or empty list if no dependencies exist.
|
||||
* @return List of parent Settings, or empty list if no dependencies exist.
|
||||
* Defensive: handles null availability or missing getParentSettings() override.
|
||||
*/
|
||||
public List<Setting<?>> getParentSettings() {
|
||||
return availability == null ? Collections.emptyList() : availability.getParentSettings();
|
||||
return availability == null
|
||||
? Collections.emptyList()
|
||||
: Objects.requireNonNullElse(availability.getParentSettings(), Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,7 +75,7 @@ public abstract class BaseSearchResultItem {
|
||||
|
||||
// Shared method for highlighting text with search query.
|
||||
protected static CharSequence highlightSearchQuery(CharSequence text, Pattern queryPattern) {
|
||||
if (TextUtils.isEmpty(text)) return text;
|
||||
if (TextUtils.isEmpty(text) || queryPattern == null) return text;
|
||||
|
||||
final int adjustedColor = Utils.adjustColorBrightness(
|
||||
Utils.getAppBackgroundColor(), 0.95f, 1.20f);
|
||||
@@ -84,7 +84,10 @@ public abstract class BaseSearchResultItem {
|
||||
|
||||
Matcher matcher = queryPattern.matcher(text);
|
||||
while (matcher.find()) {
|
||||
spannable.setSpan(highlightSpan, matcher.start(), matcher.end(),
|
||||
int start = matcher.start();
|
||||
int end = matcher.end();
|
||||
if (start == end) continue; // Skip zero matches.
|
||||
spannable.setSpan(highlightSpan, start, end,
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
@@ -224,10 +227,14 @@ public abstract class BaseSearchResultItem {
|
||||
return searchBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends normalized searchable text to the builder.
|
||||
* Uses full Unicode normalization for accurate search across all languages.
|
||||
*/
|
||||
private void appendText(StringBuilder builder, CharSequence text) {
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
if (builder.length() > 0) builder.append(" ");
|
||||
builder.append(Utils.removePunctuationToLowercase(text));
|
||||
builder.append(Utils.normalizeTextToLowercase(text));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +279,7 @@ public abstract class BaseSearchResultItem {
|
||||
*/
|
||||
@Override
|
||||
boolean matchesQuery(String query) {
|
||||
return searchableText.contains(Utils.removePunctuationToLowercase(query));
|
||||
return searchableText.contains(Utils.normalizeTextToLowercase(query));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -450,7 +450,7 @@ public abstract class BaseSearchViewController {
|
||||
|
||||
filteredSearchItems.clear();
|
||||
|
||||
String queryLower = Utils.removePunctuationToLowercase(query);
|
||||
String queryLower = Utils.normalizeTextToLowercase(query);
|
||||
Pattern queryPattern = Pattern.compile(Pattern.quote(queryLower), Pattern.CASE_INSENSITIVE);
|
||||
|
||||
// Clear highlighting only for items that were previously visible.
|
||||
|
||||
@@ -22,6 +22,11 @@ public class SpoofVideoStreamsPatch {
|
||||
return Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.isAvailable()
|
||||
&& Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ANDROID_VR_1_43_32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Setting<?>> getParentSettings() {
|
||||
return List.of(Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user