Loading src/com/android/settings/datetime/timezone/TimeZoneInfo.java +52 −0 Original line number Diff line number Diff line Loading @@ -15,9 +15,16 @@ */ package com.android.settings.datetime.timezone; import android.icu.text.TimeZoneFormat; import android.icu.text.TimeZoneNames; import android.icu.util.TimeZone; import android.text.TextUtils; import com.android.settingslib.datetime.ZoneGetter; import java.util.Date; import java.util.Locale; /** * Data object containing information for displaying a time zone for the user to select. */ Loading Loading @@ -131,6 +138,51 @@ public class TimeZoneInfo { } return new TimeZoneInfo(this); } } public static class Formatter { private final Locale mLocale; private final Date mNow; private final TimeZoneFormat mTimeZoneFormat; public Formatter(Locale locale, Date now) { mLocale = locale; mNow = now; mTimeZoneFormat = TimeZoneFormat.getInstance(locale); } /** * @param timeZoneId Olson time zone id * @return TimeZoneInfo containing time zone names, exemplar locations and GMT offset */ public TimeZoneInfo format(String timeZoneId) { TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId); return format(timeZone); } /** * @param timeZone Olson time zone object * @return TimeZoneInfo containing time zone names, exemplar locations and GMT offset */ public TimeZoneInfo format(TimeZone timeZone) { final String id = timeZone.getID(); final TimeZoneNames timeZoneNames = mTimeZoneFormat.getTimeZoneNames(); final java.util.TimeZone javaTimeZone = android.icu.impl.TimeZoneAdapter.wrap(timeZone); final CharSequence gmtOffset = ZoneGetter.getGmtOffsetText(mTimeZoneFormat, mLocale, javaTimeZone, mNow); return new TimeZoneInfo.Builder(timeZone) .setGenericName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_GENERIC, mNow.getTime())) .setStandardName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_STANDARD, mNow.getTime())) .setDaylightName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_DAYLIGHT, mNow.getTime())) .setExemplarLocation(timeZoneNames.getExemplarLocationName(id)) .setGmtOffset(gmtOffset) // TODO: move Item id to TimeZoneInfoAdapter .setItemId(0) .build(); } } } src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java 0 → 100644 +53 −0 Original line number Diff line number Diff line /* * 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.model; import libcore.util.CountryTimeZones; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * Wrap {@class CountryTimeZones} to filter time zone that are shown in the picker. */ public class FilteredCountryTimeZones { private final CountryTimeZones mCountryTimeZones; private final List<String> mTimeZoneIds; public FilteredCountryTimeZones(CountryTimeZones countryTimeZones) { mCountryTimeZones = countryTimeZones; List<String> timeZoneIds = countryTimeZones.getTimeZoneMappings().stream() .filter(timeZoneMapping -> timeZoneMapping.showInPicker) .map(timeZoneMapping -> timeZoneMapping.timeZoneId) .collect(Collectors.toList()); mTimeZoneIds = Collections.unmodifiableList(timeZoneIds); } public List<String> getTimeZoneIds() { return mTimeZoneIds; } public CountryTimeZones getCountryTimeZones() { return mCountryTimeZones; } public String getRegionId() { return TimeZoneData.normalizeRegionId(mCountryTimeZones.getCountryIso()); } } src/com/android/settings/datetime/timezone/model/TimeZoneData.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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.model; import android.support.annotation.VisibleForTesting; import libcore.util.CountryTimeZones; import libcore.util.CountryZonesFinder; import libcore.util.TimeZoneFinder; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; /** * Wrapper of CountryZonesFinder to normalize the country code and only show the regions that are * has time zone shown in the time zone picker. * The constructor reads the data from underlying file, and this means it should not be called * from the UI thread. */ public class TimeZoneData { private static WeakReference<TimeZoneData> sCache = null; private final CountryZonesFinder mCountryZonesFinder; private final Set<String> mRegionIds; public static synchronized TimeZoneData getInstance() { TimeZoneData data = sCache == null ? null : sCache.get(); if (data != null) { return data; } data = new TimeZoneData(); sCache = new WeakReference<>(data); return data; } public TimeZoneData() { this(TimeZoneFinder.getInstance().getCountryZonesFinder()); } @VisibleForTesting TimeZoneData(CountryZonesFinder countryZonesFinder) { mCountryZonesFinder = countryZonesFinder; mRegionIds = getNormalizedRegionIds(mCountryZonesFinder.lookupAllCountryIsoCodes()); } public Set<String> getRegionIds() { return mRegionIds; } public Set<String> lookupCountryCodesForZoneId(String tzId) { if (tzId == null) { return Collections.emptySet(); } return mCountryZonesFinder.lookupCountryTimeZonesForZoneId(tzId).stream() .filter(countryTimeZones -> countryTimeZones.getTimeZoneMappings().stream() .anyMatch(mapping -> mapping.timeZoneId.equals(tzId) && mapping.showInPicker)) .map(countryTimeZones -> normalizeRegionId(countryTimeZones.getCountryIso())) .collect(Collectors.toSet()); } public FilteredCountryTimeZones lookupCountryTimeZones(String regionId) { CountryTimeZones finder = regionId == null ? null : mCountryZonesFinder.lookupCountryTimeZones(regionId); return finder == null ? null : new FilteredCountryTimeZones(finder); } private static Set<String> getNormalizedRegionIds(List<String> regionIds) { final Set<String> result = new HashSet<>(regionIds.size()); for (String regionId : regionIds) { result.add(normalizeRegionId(regionId)); } return Collections.unmodifiableSet(result); } // Uppercase ASCII is normalized for the purpose of using ICU API public static String normalizeRegionId(String regionId) { return regionId == null ? null : regionId.toUpperCase(Locale.US); } } src/com/android/settings/datetime/timezone/model/TimeZoneDataLoader.java 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * 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.model; import android.app.LoaderManager; import android.content.Context; import android.content.Loader; import android.os.Bundle; import com.android.settingslib.utils.AsyncLoader; public class TimeZoneDataLoader extends AsyncLoader<TimeZoneData> { public TimeZoneDataLoader(Context context) { super(context); } @Override public TimeZoneData loadInBackground() { // Heavy operation due to reading the underlying file return new TimeZoneData(); } @Override protected void onDiscardResult(TimeZoneData result) { // This class doesn't hold resource of the result. } public interface OnDataReadyCallback { void onTimeZoneDataReady(TimeZoneData data); } public static class LoaderCreator implements LoaderManager.LoaderCallbacks<TimeZoneData> { private final Context mContext; private final OnDataReadyCallback mCallback; public LoaderCreator(Context context, OnDataReadyCallback callback) { mContext = context; mCallback = callback; } @Override public Loader onCreateLoader(int id, Bundle args) { return new TimeZoneDataLoader(mContext); } @Override public void onLoadFinished(Loader<TimeZoneData> loader, TimeZoneData data) { if (mCallback != null) { mCallback.onTimeZoneDataReady(data); } } @Override public void onLoaderReset(Loader<TimeZoneData> loader) { //It's okay to keep the time zone data when loader is reset } } } tests/robotests/src/com/android/settings/datetime/timezone/TimeZoneInfoTest.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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 com.android.settings.TestConfig; import com.android.settings.datetime.timezone.TimeZoneInfo.Formatter; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; import java.util.Date; import java.util.Locale; import static com.google.common.truth.Truth.assertThat; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class TimeZoneInfoTest { @Test public void testFormat() { Date now = new Date(0L); // 00:00 1/1/1970 Formatter formatter = new Formatter(Locale.US, now); TimeZoneInfo timeZoneInfo = formatter.format("America/Los_Angeles"); assertThat(timeZoneInfo.getId()).isEqualTo("America/Los_Angeles"); assertThat(timeZoneInfo.getExemplarLocation()).isEqualTo("Los Angeles"); assertThat(timeZoneInfo.getGmtOffset().toString()).isEqualTo("GMT-08:00"); assertThat(timeZoneInfo.getGenericName()).isEqualTo("Pacific Time"); assertThat(timeZoneInfo.getStandardName()).isEqualTo("Pacific Standard Time"); assertThat(timeZoneInfo.getDaylightName()).isEqualTo("Pacific Daylight Time"); } } Loading
src/com/android/settings/datetime/timezone/TimeZoneInfo.java +52 −0 Original line number Diff line number Diff line Loading @@ -15,9 +15,16 @@ */ package com.android.settings.datetime.timezone; import android.icu.text.TimeZoneFormat; import android.icu.text.TimeZoneNames; import android.icu.util.TimeZone; import android.text.TextUtils; import com.android.settingslib.datetime.ZoneGetter; import java.util.Date; import java.util.Locale; /** * Data object containing information for displaying a time zone for the user to select. */ Loading Loading @@ -131,6 +138,51 @@ public class TimeZoneInfo { } return new TimeZoneInfo(this); } } public static class Formatter { private final Locale mLocale; private final Date mNow; private final TimeZoneFormat mTimeZoneFormat; public Formatter(Locale locale, Date now) { mLocale = locale; mNow = now; mTimeZoneFormat = TimeZoneFormat.getInstance(locale); } /** * @param timeZoneId Olson time zone id * @return TimeZoneInfo containing time zone names, exemplar locations and GMT offset */ public TimeZoneInfo format(String timeZoneId) { TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId); return format(timeZone); } /** * @param timeZone Olson time zone object * @return TimeZoneInfo containing time zone names, exemplar locations and GMT offset */ public TimeZoneInfo format(TimeZone timeZone) { final String id = timeZone.getID(); final TimeZoneNames timeZoneNames = mTimeZoneFormat.getTimeZoneNames(); final java.util.TimeZone javaTimeZone = android.icu.impl.TimeZoneAdapter.wrap(timeZone); final CharSequence gmtOffset = ZoneGetter.getGmtOffsetText(mTimeZoneFormat, mLocale, javaTimeZone, mNow); return new TimeZoneInfo.Builder(timeZone) .setGenericName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_GENERIC, mNow.getTime())) .setStandardName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_STANDARD, mNow.getTime())) .setDaylightName(timeZoneNames.getDisplayName(id, TimeZoneNames.NameType.LONG_DAYLIGHT, mNow.getTime())) .setExemplarLocation(timeZoneNames.getExemplarLocationName(id)) .setGmtOffset(gmtOffset) // TODO: move Item id to TimeZoneInfoAdapter .setItemId(0) .build(); } } }
src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java 0 → 100644 +53 −0 Original line number Diff line number Diff line /* * 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.model; import libcore.util.CountryTimeZones; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** * Wrap {@class CountryTimeZones} to filter time zone that are shown in the picker. */ public class FilteredCountryTimeZones { private final CountryTimeZones mCountryTimeZones; private final List<String> mTimeZoneIds; public FilteredCountryTimeZones(CountryTimeZones countryTimeZones) { mCountryTimeZones = countryTimeZones; List<String> timeZoneIds = countryTimeZones.getTimeZoneMappings().stream() .filter(timeZoneMapping -> timeZoneMapping.showInPicker) .map(timeZoneMapping -> timeZoneMapping.timeZoneId) .collect(Collectors.toList()); mTimeZoneIds = Collections.unmodifiableList(timeZoneIds); } public List<String> getTimeZoneIds() { return mTimeZoneIds; } public CountryTimeZones getCountryTimeZones() { return mCountryTimeZones; } public String getRegionId() { return TimeZoneData.normalizeRegionId(mCountryTimeZones.getCountryIso()); } }
src/com/android/settings/datetime/timezone/model/TimeZoneData.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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.model; import android.support.annotation.VisibleForTesting; import libcore.util.CountryTimeZones; import libcore.util.CountryZonesFinder; import libcore.util.TimeZoneFinder; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; /** * Wrapper of CountryZonesFinder to normalize the country code and only show the regions that are * has time zone shown in the time zone picker. * The constructor reads the data from underlying file, and this means it should not be called * from the UI thread. */ public class TimeZoneData { private static WeakReference<TimeZoneData> sCache = null; private final CountryZonesFinder mCountryZonesFinder; private final Set<String> mRegionIds; public static synchronized TimeZoneData getInstance() { TimeZoneData data = sCache == null ? null : sCache.get(); if (data != null) { return data; } data = new TimeZoneData(); sCache = new WeakReference<>(data); return data; } public TimeZoneData() { this(TimeZoneFinder.getInstance().getCountryZonesFinder()); } @VisibleForTesting TimeZoneData(CountryZonesFinder countryZonesFinder) { mCountryZonesFinder = countryZonesFinder; mRegionIds = getNormalizedRegionIds(mCountryZonesFinder.lookupAllCountryIsoCodes()); } public Set<String> getRegionIds() { return mRegionIds; } public Set<String> lookupCountryCodesForZoneId(String tzId) { if (tzId == null) { return Collections.emptySet(); } return mCountryZonesFinder.lookupCountryTimeZonesForZoneId(tzId).stream() .filter(countryTimeZones -> countryTimeZones.getTimeZoneMappings().stream() .anyMatch(mapping -> mapping.timeZoneId.equals(tzId) && mapping.showInPicker)) .map(countryTimeZones -> normalizeRegionId(countryTimeZones.getCountryIso())) .collect(Collectors.toSet()); } public FilteredCountryTimeZones lookupCountryTimeZones(String regionId) { CountryTimeZones finder = regionId == null ? null : mCountryZonesFinder.lookupCountryTimeZones(regionId); return finder == null ? null : new FilteredCountryTimeZones(finder); } private static Set<String> getNormalizedRegionIds(List<String> regionIds) { final Set<String> result = new HashSet<>(regionIds.size()); for (String regionId : regionIds) { result.add(normalizeRegionId(regionId)); } return Collections.unmodifiableSet(result); } // Uppercase ASCII is normalized for the purpose of using ICU API public static String normalizeRegionId(String regionId) { return regionId == null ? null : regionId.toUpperCase(Locale.US); } }
src/com/android/settings/datetime/timezone/model/TimeZoneDataLoader.java 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * 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.model; import android.app.LoaderManager; import android.content.Context; import android.content.Loader; import android.os.Bundle; import com.android.settingslib.utils.AsyncLoader; public class TimeZoneDataLoader extends AsyncLoader<TimeZoneData> { public TimeZoneDataLoader(Context context) { super(context); } @Override public TimeZoneData loadInBackground() { // Heavy operation due to reading the underlying file return new TimeZoneData(); } @Override protected void onDiscardResult(TimeZoneData result) { // This class doesn't hold resource of the result. } public interface OnDataReadyCallback { void onTimeZoneDataReady(TimeZoneData data); } public static class LoaderCreator implements LoaderManager.LoaderCallbacks<TimeZoneData> { private final Context mContext; private final OnDataReadyCallback mCallback; public LoaderCreator(Context context, OnDataReadyCallback callback) { mContext = context; mCallback = callback; } @Override public Loader onCreateLoader(int id, Bundle args) { return new TimeZoneDataLoader(mContext); } @Override public void onLoadFinished(Loader<TimeZoneData> loader, TimeZoneData data) { if (mCallback != null) { mCallback.onTimeZoneDataReady(data); } } @Override public void onLoaderReset(Loader<TimeZoneData> loader) { //It's okay to keep the time zone data when loader is reset } } }
tests/robotests/src/com/android/settings/datetime/timezone/TimeZoneInfoTest.java 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * 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 com.android.settings.TestConfig; import com.android.settings.datetime.timezone.TimeZoneInfo.Formatter; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; import java.util.Date; import java.util.Locale; import static com.google.common.truth.Truth.assertThat; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class TimeZoneInfoTest { @Test public void testFormat() { Date now = new Date(0L); // 00:00 1/1/1970 Formatter formatter = new Formatter(Locale.US, now); TimeZoneInfo timeZoneInfo = formatter.format("America/Los_Angeles"); assertThat(timeZoneInfo.getId()).isEqualTo("America/Los_Angeles"); assertThat(timeZoneInfo.getExemplarLocation()).isEqualTo("Los Angeles"); assertThat(timeZoneInfo.getGmtOffset().toString()).isEqualTo("GMT-08:00"); assertThat(timeZoneInfo.getGenericName()).isEqualTo("Pacific Time"); assertThat(timeZoneInfo.getStandardName()).isEqualTo("Pacific Standard Time"); assertThat(timeZoneInfo.getDaylightName()).isEqualTo("Pacific Daylight Time"); } }