Files
app_Settings/src/com/android/settings/datetime/timezone/RegionZonePicker.java
Geoffrey Boullanger 766c7951fb Update the time zone picker to work with location containing diacritics
Currently, looking for a location containing diacritics (e.g. accents) requires the user to type in exactly those characters. With this change, diacritics are ignored and the strings are returned if they match (using startsWith).
For example, looking for "reun" will show you "Réunion".

Bug: b/364245352
Test: atest tests/robotests/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapterTest.java
Change-Id: I507a9ebc1c830ad3162fb2382814935fc337328d
Flag: EXEMPT bugfix
2024-09-04 09:23:20 +00:00

166 lines
6.1 KiB
Java

/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.datetime.timezone;
import static com.android.settingslib.datetime.ZoneGetter.capitalizeForStandaloneDisplay;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.icu.text.Collator;
import android.icu.text.LocaleDisplayNames;
import android.icu.util.TimeZone;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
import com.android.settings.datetime.timezone.model.TimeZoneData;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;
/**
* Given a region, render a list of time zone {@class TimeZoneInfo} into a list view.
*/
public class RegionZonePicker extends BaseTimeZoneInfoPicker {
public static final String EXTRA_REGION_ID =
"com.android.settings.datetime.timezone.region_id";
private @Nullable String mRegionName;
public RegionZonePicker() {
super(R.string.date_time_set_timezone_title, R.string.search_settings, true, false);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_ZONE_PICKER_TIME_ZONE;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LocaleDisplayNames localeDisplayNames = LocaleDisplayNames.getInstance(getLocale());
final String regionId =
getArguments() == null ? null : getArguments().getString(EXTRA_REGION_ID);
mRegionName = regionId == null ? null : capitalizeForStandaloneDisplay(
mLocale, localeDisplayNames.regionDisplayName(regionId));
}
@Override
protected @Nullable CharSequence getHeaderText() {
return mRegionName;
}
/**
* Add the extra region id into the result.
*/
@Override
protected Intent prepareResultData(TimeZoneInfo selectedTimeZoneInfo) {
final Intent intent = super.prepareResultData(selectedTimeZoneInfo);
intent.putExtra(EXTRA_RESULT_REGION_ID, getArguments().getString(EXTRA_REGION_ID));
return intent;
}
@Override
public List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData) {
if (getArguments() == null) {
Log.e(TAG, "getArguments() == null");
getActivity().finish();
return Collections.emptyList();
}
String regionId = getArguments().getString(EXTRA_REGION_ID);
FilteredCountryTimeZones filteredCountryTimeZones = timeZoneData.lookupCountryTimeZones(
regionId);
if (filteredCountryTimeZones == null) {
Log.e(TAG, "region id is not valid: " + regionId);
getActivity().finish();
return Collections.emptyList();
}
// It could be a timely operations if there are many time zones. A region in time zone data
// contains a maximum of 29 time zones currently. It may change in the future, but it's
// unlikely to be changed drastically.
return getRegionTimeZoneInfo(filteredCountryTimeZones.getPreferredTimeZoneIds());
}
/**
* 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
* passed in.
*
* @param timeZoneIds a list of Olson IDs.
*/
public List<TimeZoneInfo> getRegionTimeZoneInfo(Collection<String> timeZoneIds) {
final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(),
new Date());
final TreeSet<TimeZoneInfo> timeZoneInfos =
new TreeSet<>(new TimeZoneInfoComparator(Collator.getInstance(getLocale()),
new Date()));
for (final String timeZoneId : timeZoneIds) {
final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
// Skip time zone ICU isn't aware.
if (timeZone.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
continue;
}
timeZoneInfos.add(formatter.format(timeZone));
}
return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
}
@VisibleForTesting
static class TimeZoneInfoComparator implements Comparator<TimeZoneInfo> {
private Collator mCollator;
private final Date mNow;
@VisibleForTesting
TimeZoneInfoComparator(Collator collator, Date now) {
mCollator = collator;
mNow = now;
}
@Override
public int compare(TimeZoneInfo tzi1, TimeZoneInfo tzi2) {
int result = Integer.compare(tzi1.getTimeZone().getOffset(mNow.getTime()),
tzi2.getTimeZone().getOffset(mNow.getTime()));
if (result == 0) {
result = Integer.compare(tzi1.getTimeZone().getRawOffset(),
tzi2.getTimeZone().getRawOffset());
}
if (result == 0) {
result = mCollator.compare(tzi1.getExemplarLocation(), tzi2.getExemplarLocation());
}
if (result == 0 && tzi1.getGenericName() != null && tzi2.getGenericName() != null) {
result = mCollator.compare(tzi1.getGenericName(), tzi2.getGenericName());
}
return result;
}
}
}