Merge "Update the time zone picker to work with location containing diacritics" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
ab1f6480c2
@@ -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);
|
||||||
|
@@ -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.
|
||||||
|
@@ -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) {
|
||||||
|
Reference in New Issue
Block a user