Merge "Update the time zone picker to work with location containing diacritics" into main

This commit is contained in:
Treehugger Robot
2024-09-04 12:02:26 +00:00
committed by Android (Google) Code Review
3 changed files with 43 additions and 17 deletions

View File

@@ -33,9 +33,11 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.datetime.timezone.BaseTimeZonePicker.OnListItemClickListener; import com.android.settings.datetime.timezone.BaseTimeZonePicker.OnListItemClickListener;
import java.text.Normalizer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Pattern;
/** /**
* Used with {@class BaseTimeZonePicker}. It renders text in each item into list view. A list of * Used with {@class BaseTimeZonePicker}. It renders text in each item into list view. A list of
@@ -48,6 +50,9 @@ public class BaseTimeZoneAdapter<T extends BaseTimeZoneAdapter.AdapterItem>
@VisibleForTesting @VisibleForTesting
static final int TYPE_ITEM = 1; static final int TYPE_ITEM = 1;
private static final Pattern PATTERN_REMOVE_DIACRITICS = Pattern.compile(
"\\p{InCombiningDiacriticalMarks}+");
private final List<T> mOriginalItems; private final List<T> mOriginalItems;
private final OnListItemClickListener<T> mOnListItemClickListener; private final OnListItemClickListener<T> mOnListItemClickListener;
private final Locale mLocale; private final Locale mLocale;
@@ -183,6 +188,19 @@ public class BaseTimeZoneAdapter<T extends BaseTimeZoneAdapter.AdapterItem>
} }
} }
/**
* Removes diacritics (e.g. accents) from a string
*/
private static String removeDiacritics(final String str) {
if (str == null || str.isEmpty()) {
return str;
}
// decomposes the original characters into a base character and a diacritic sign
final String decomposed = Normalizer.normalize(str, Normalizer.Form.NFKD);
// replaces the diacritic signs with empty strings
return PATTERN_REMOVE_DIACRITICS.matcher(decomposed).replaceAll("");
}
@VisibleForTesting @VisibleForTesting
public static class ItemViewHolder<T extends BaseTimeZoneAdapter.AdapterItem> public static class ItemViewHolder<T extends BaseTimeZoneAdapter.AdapterItem>
extends RecyclerView.ViewHolder implements View.OnClickListener { extends RecyclerView.ViewHolder implements View.OnClickListener {
@@ -241,13 +259,14 @@ public class BaseTimeZoneAdapter<T extends BaseTimeZoneAdapter.AdapterItem>
if (TextUtils.isEmpty(prefix)) { if (TextUtils.isEmpty(prefix)) {
newItems = mOriginalItems; newItems = mOriginalItems;
} else { } else {
final String prefixString = prefix.toString().toLowerCase(mLocale); final String prefixString = removeDiacritics(
prefix.toString().toLowerCase(mLocale));
newItems = new ArrayList<>(); newItems = new ArrayList<>();
for (T item : mOriginalItems) { for (T item : mOriginalItems) {
outer: outer:
for (String searchKey : item.getSearchKeys()) { for (String searchKey : item.getSearchKeys()) {
searchKey = searchKey.toLowerCase(mLocale); searchKey = removeDiacritics(searchKey.toLowerCase(mLocale));
// First match against the whole, non-splitted value // First match against the whole, non-splitted value
if (searchKey.startsWith(prefixString)) { if (searchKey.startsWith(prefixString)) {
newItems.add(item); newItems.add(item);

View File

@@ -111,7 +111,7 @@ public class RegionZonePicker extends BaseTimeZoneInfoPicker {
/** /**
* Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for * Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for
* display in the locale.It may be smaller than the input collection, if equivalent IDs are * display in the locale. It may be smaller than the input collection, if equivalent IDs are
* passed in. * passed in.
* *
* @param timeZoneIds a list of Olson IDs. * @param timeZoneIds a list of Olson IDs.

View File

@@ -22,39 +22,43 @@ import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class BaseTimeZoneAdapterTest { public class BaseTimeZoneAdapterTest {
@Test @Test
public void testFilter() throws InterruptedException { public void testFilter() throws InterruptedException {
TestItem US = new TestItem("United States"); TestItem unitedStates = new TestItem("United States");
TestItem HK = new TestItem("Hong Kong"); TestItem hongKong = new TestItem("Hong Kong");
TestItem UK = new TestItem("United Kingdom", new String[] { "United Kingdom", TestItem unitedKingdom = new TestItem("United Kingdom", new String[]{"United Kingdom",
"Great Britain"}); "Great Britain"});
TestItem secretCountry = new TestItem("no name", new String[] { "Secret"}); TestItem reunion = new TestItem("Réunion");
TestItem secretCountry = new TestItem("no name", new String[]{"Secret"});
List<TestItem> items = new ArrayList<>(); List<TestItem> items = new ArrayList<>();
items.add(US); items.add(unitedStates);
items.add(HK); items.add(hongKong);
items.add(UK); items.add(unitedKingdom);
items.add(reunion);
items.add(secretCountry); items.add(secretCountry);
TestTimeZoneAdapter adapter = new TestTimeZoneAdapter(items); TestTimeZoneAdapter adapter = new TestTimeZoneAdapter(items);
assertSearch(adapter, "", items.toArray(new TestItem[0])); assertSearch(adapter, "", items.toArray(new TestItem[0]));
assertSearch(adapter, "Unit", US, UK); assertSearch(adapter, "Unit", unitedStates, unitedKingdom);
assertSearch(adapter, "kon", HK); assertSearch(adapter, "kon", hongKong);
assertSearch(adapter, "brit", UK); assertSearch(adapter, "brit", unitedKingdom);
assertSearch(adapter, "sec", secretCountry); assertSearch(adapter, "sec", secretCountry);
assertSearch(adapter, "reun", reunion); // no accent in search, accent in result
assertSearch(adapter, "Réunion", reunion); // accents in search and result
} }
private void assertSearch(TestTimeZoneAdapter adapter , String searchText, TestItem... items) private void assertSearch(TestTimeZoneAdapter adapter, String searchText, TestItem... items)
throws InterruptedException { throws InterruptedException {
Observer observer = new Observer(adapter); Observer observer = new Observer(adapter);
adapter.getFilter().filter(searchText); adapter.getFilter().filter(searchText);
@@ -89,7 +93,10 @@ public class BaseTimeZoneAdapterTest {
private static class TestTimeZoneAdapter extends BaseTimeZoneAdapter<TestItem> { private static class TestTimeZoneAdapter extends BaseTimeZoneAdapter<TestItem> {
private TestTimeZoneAdapter(List<TestItem> items) { private TestTimeZoneAdapter(List<TestItem> items) {
super(items, position -> {}, Locale.US, false /* showItemSummary */, super(items,
position -> {},
Locale.US,
false /* showItemSummary */,
null /* headerText */); null /* headerText */);
} }
} }
@@ -100,7 +107,7 @@ public class BaseTimeZoneAdapterTest {
private final String[] mSearchKeys; private final String[] mSearchKeys;
TestItem(String title) { TestItem(String title) {
this(title, new String[] { title }); this(title, new String[]{title});
} }
TestItem(String title, String[] searchKeys) { TestItem(String title, String[] searchKeys) {