From dd2a68575620ecdfb923c2a656a26c2d025da4ed Mon Sep 17 00:00:00 2001 From: Almaz Mingaleev Date: Thu, 25 Mar 2021 15:22:30 +0000 Subject: [PATCH] Allow alternative time zone ID to be set device's zone. If America/Godthab is set as device time zone, display it as if America/Nuuk is chosen. If time zone is known, but is not an alternative and not shown in time zone picker, region will de derived from user's locale. Bug: 155738410 Test: atest com.android.settings.datetime.timezone.model Test: set persist.sys.timezone via adb and checked Date & Time screen Change-Id: I168fb4319e144dbe9e2496d06a681d4c09b411c0 --- .../datetime/timezone/RegionSearchPicker.java | 4 +- .../datetime/timezone/RegionZonePicker.java | 2 +- .../datetime/timezone/TimeZoneSettings.java | 8 ++-- .../model/FilteredCountryTimeZones.java | 45 +++++++++++++------ .../datetime/timezone/model/TimeZoneData.java | 2 +- .../timezone/model/TimeZoneDataTest.java | 19 ++++++++ .../timezone/model/TimeZoneDataTest.java | 11 ++--- 7 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/com/android/settings/datetime/timezone/RegionSearchPicker.java b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java index 07986e29a9f..85d5d705b49 100644 --- a/src/com/android/settings/datetime/timezone/RegionSearchPicker.java +++ b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java @@ -71,14 +71,14 @@ public class RegionSearchPicker extends BaseTimeZonePicker { final FilteredCountryTimeZones countryTimeZones = mTimeZoneData.lookupCountryTimeZones( regionId); final Activity activity = getActivity(); - if (countryTimeZones == null || countryTimeZones.getTimeZoneIds().isEmpty()) { + if (countryTimeZones == null || countryTimeZones.getPreferredTimeZoneIds().isEmpty()) { Log.e(TAG, "Region has no time zones: " + regionId); activity.setResult(Activity.RESULT_CANCELED); activity.finish(); return; } - List timeZoneIds = countryTimeZones.getTimeZoneIds(); + List timeZoneIds = countryTimeZones.getPreferredTimeZoneIds(); // Choose the time zone associated the region if there is only one time zone in that region if (timeZoneIds.size() == 1) { final Intent resultData = new Intent() diff --git a/src/com/android/settings/datetime/timezone/RegionZonePicker.java b/src/com/android/settings/datetime/timezone/RegionZonePicker.java index 8e4aa055322..7f988cd0ef0 100644 --- a/src/com/android/settings/datetime/timezone/RegionZonePicker.java +++ b/src/com/android/settings/datetime/timezone/RegionZonePicker.java @@ -103,7 +103,7 @@ public class RegionZonePicker extends BaseTimeZoneInfoPicker { // 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.getTimeZoneIds()); + return getRegionTimeZoneInfo(filteredCountryTimeZones.getPreferredTimeZoneIds()); } /** diff --git a/src/com/android/settings/datetime/timezone/TimeZoneSettings.java b/src/com/android/settings/datetime/timezone/TimeZoneSettings.java index 60cd6363ec2..6c779b54a0c 100644 --- a/src/com/android/settings/datetime/timezone/TimeZoneSettings.java +++ b/src/com/android/settings/datetime/timezone/TimeZoneSettings.java @@ -214,10 +214,11 @@ public class TimeZoneSettings extends DashboardFragment { mTimeZoneData.lookupCountryTimeZones(regionId); use(RegionZonePreferenceController.class).setTimeZoneInfo(tzInfo); - // Only clickable when the region has more than 1 time zones or no time zone is selected. + // Only clickable when the region has more than 1 time zones or no time zone is selected. use(RegionZonePreferenceController.class).setClickable(tzInfo == null || - (countryTimeZones != null && countryTimeZones.getTimeZoneIds().size() > 1)); + (countryTimeZones != null + && countryTimeZones.getPreferredTimeZoneIds().size() > 1)); use(TimeZoneInfoPreferenceController.class).setTimeZoneInfo(tzInfo); updatePreferenceStates(); @@ -244,7 +245,8 @@ public class TimeZoneSettings extends DashboardFragment { FilteredCountryTimeZones countryTimeZones = timeZoneData.lookupCountryTimeZones(regionId); - if (countryTimeZones == null || !countryTimeZones.getTimeZoneIds().contains(tzId)) { + if (countryTimeZones == null + || !countryTimeZones.getPreferredTimeZoneIds().contains(tzId)) { Log.e(TAG, "Unknown time zone id is selected: " + tzId); return; } diff --git a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java index 7a3885326ba..05a542d9bcd 100644 --- a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java +++ b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java @@ -16,11 +16,15 @@ package com.android.settings.datetime.timezone.model; +import android.util.ArraySet; + import com.android.i18n.timezone.CountryTimeZones; +import java.time.Instant; +import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; /** * Wrap {@class CountryTimeZones} to filter time zone that are shown in the picker. @@ -39,31 +43,46 @@ public class FilteredCountryTimeZones { * a timestamp known to be in the recent past is used. This should be updated occasionally but * it doesn't have to be very often. */ - private static final long MIN_USE_DATE_OF_TIMEZONE = 1546300800000L; // 1/1/2019 00:00 UTC + private static final Instant MIN_USE_DATE_OF_TIMEZONE = + Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC private final CountryTimeZones mCountryTimeZones; - private final List mTimeZoneIds; + private final List mPreferredTimeZoneIds; + private final Set mAlternativeTimeZoneIds; public FilteredCountryTimeZones(CountryTimeZones countryTimeZones) { mCountryTimeZones = countryTimeZones; - List timeZoneIds = countryTimeZones.getTimeZoneMappings().stream() - .filter(timeZoneMapping -> - timeZoneMapping.isShownInPicker() - && (timeZoneMapping.getNotUsedAfter() == null - || timeZoneMapping.getNotUsedAfter() >= MIN_USE_DATE_OF_TIMEZONE)) - .map(timeZoneMapping -> timeZoneMapping.getTimeZoneId()) - .collect(Collectors.toList()); - mTimeZoneIds = Collections.unmodifiableList(timeZoneIds); + List timeZoneIds = new ArrayList<>(); + Set alternativeTimeZoneIds = new ArraySet<>(); + for (CountryTimeZones.TimeZoneMapping timeZoneMapping : + countryTimeZones.getTimeZoneMappings()) { + if (timeZoneMapping.isShownInPickerAt(MIN_USE_DATE_OF_TIMEZONE)) { + String timeZoneId = timeZoneMapping.getTimeZoneId(); + timeZoneIds.add(timeZoneId); + alternativeTimeZoneIds.addAll(timeZoneMapping.getAlternativeIds()); + } + } + mPreferredTimeZoneIds = Collections.unmodifiableList(timeZoneIds); + mAlternativeTimeZoneIds = Collections.unmodifiableSet(alternativeTimeZoneIds); } - public List getTimeZoneIds() { - return mTimeZoneIds; + public List getPreferredTimeZoneIds() { + return mPreferredTimeZoneIds; } public CountryTimeZones getCountryTimeZones() { return mCountryTimeZones; } + /** + * Returns whether {@code timeZoneId} is currently used in the country or is an alternative + * name of a currently used time zone. + */ + public boolean matches(String timeZoneId) { + return mPreferredTimeZoneIds.contains(timeZoneId) + || mAlternativeTimeZoneIds.contains(timeZoneId); + } + public String getRegionId() { return TimeZoneData.normalizeRegionId(mCountryTimeZones.getCountryIso()); } diff --git a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java index 335e6e2cd6a..06f2de0f139 100644 --- a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java +++ b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java @@ -71,7 +71,7 @@ public class TimeZoneData { Set regionIds = new ArraySet<>(); for (CountryTimeZones countryTimeZone : countryTimeZones) { FilteredCountryTimeZones filteredZones = new FilteredCountryTimeZones(countryTimeZone); - if (filteredZones.getTimeZoneIds().contains(tzId)) { + if (filteredZones.matches(tzId)) { regionIds.add(filteredZones.getRegionId()); } } diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java index d21aa04f3dc..784b3bb06c8 100644 --- a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java +++ b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java @@ -92,4 +92,23 @@ public class TimeZoneDataTest { .containsExactly("US", "GB"); assertThat(timeZoneData.lookupCountryCodesForZoneId("Unknown/Secret_City2")).isEmpty(); } + + @Test + public void lookupCountryCodesForNonCanonicalZoneId_returnsCurrentZone() { + TimeZoneData timeZoneData = new TimeZoneData(mCountryZonesFinder); + + CountryTimeZones greenland = mock(CountryTimeZones.class); + when(greenland.getCountryIso()).thenReturn("gl"); + when(greenland.getTimeZoneMappings()).thenReturn(Arrays.asList( + TimeZoneMapping.createForTests( + "America/Nuuk", + true /* showInPicker */, + null /* notUsedAfter */, + Arrays.asList("America/Godthab")))); + when(mCountryZonesFinder.lookupCountryTimeZonesForZoneId("America/Godthab")) + .thenReturn(Arrays.asList(greenland)); + + assertThat(timeZoneData.lookupCountryCodesForZoneId("America/Godthab")) + .containsExactly("GL"); + } } diff --git a/tests/unit/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java b/tests/unit/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java index a04c08d37e7..1d2194b037f 100644 --- a/tests/unit/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java +++ b/tests/unit/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java @@ -43,7 +43,7 @@ public class TimeZoneDataTest { FilteredCountryTimeZones countryTimeZones = mTimeZoneData.lookupCountryTimeZones(regionId); assertThat(countryTimeZones).isNotNull(); - assertThat(countryTimeZones.getTimeZoneIds().size()).isGreaterThan(0); + assertThat(countryTimeZones.getPreferredTimeZoneIds().size()).isGreaterThan(0); } } @@ -54,11 +54,8 @@ public class TimeZoneDataTest { 1) because we specifically exclude it with the picker attribute, and 2) because it's the same as Moscow after Oct 2014. */ - assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/Simferopol").isEmpty()) - .isTrue(); - assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/London").isEmpty()) - .isFalse(); - assertThat(mTimeZoneData.lookupCountryCodesForZoneId("America/Los_Angeles").isEmpty()) - .isFalse(); + assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/Simferopol")).isEmpty(); + assertThat(mTimeZoneData.lookupCountryCodesForZoneId("Europe/London")).isNotEmpty(); + assertThat(mTimeZoneData.lookupCountryCodesForZoneId("America/Los_Angeles")).isNotEmpty(); } }