New time zone picker page

- Show current selected region and time zone in a 2 rows.
  Defailed info of current selected time zone in footer
- Show option menu to switch to select UTC offset
- This picker will be changed to the default picker in a later CL

Bug: 73952488
Test: m RunSettingsRoboTests
Change-Id: Ia81bb022e1021369612f5bd60c2c1f4d08db2af8
(cherry picked from commit b7d588f341)
This commit is contained in:
Victor Chang
2018-02-28 19:37:36 +00:00
parent 368b5a6b2c
commit d5adf42c5e
15 changed files with 1182 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="time_zone_settings_screen"
android:title="@string/date_time_set_timezone">
<PreferenceCategory
android:key="time_zone_region_preference_category">
<com.android.settingslib.RestrictedPreference
android:key="region"
android:title="@string/date_time_select_region"
android:summary="@string/summary_placeholder" />
<com.android.settingslib.RestrictedPreference
android:key="region_zone"
android:title="@string/date_time_select_zone"
android:summary="@string/summary_placeholder" />
<com.android.settingslib.widget.FooterPreference/>
</PreferenceCategory>
<PreferenceCategory
android:key="time_zone_fixed_offset_preference_category">
<com.android.settingslib.RestrictedPreference
android:key="fixed_offset"
android:title="@string/date_time_select_fixed_offset_time_zones"
android:summary="@string/summary_placeholder"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,51 @@
/*
* 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 android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.google.common.base.Objects;
public abstract class BaseTimeZonePreferenceController extends BasePreferenceController {
private OnPreferenceClickListener mOnClickListener;
protected BaseTimeZonePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (mOnClickListener == null || !Objects.equal(getPreferenceKey(), preference.getKey())) {
return false;
}
mOnClickListener.onClick();
return true;
}
public void setOnClickListener(OnPreferenceClickListener listener) {
mOnClickListener = listener;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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 android.content.Context;
import android.support.v7.preference.Preference;
public class FixedOffsetPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "fixed_offset";
private TimeZoneInfo mTimeZoneInfo;
public FixedOffsetPreferenceController(Context context) {
super(context, PREFERENCE_KEY);
}
@Override
public CharSequence getSummary() {
// This is a Spannable object, which contains TTS span. It shouldn't be converted to String.
return mTimeZoneInfo == null ? "" : mTimeZoneInfo.getGmtOffset();
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
/**
* Callback when a preference is clicked in {@class TimeZoneSettings}
*/
public interface OnPreferenceClickListener {
void onClick();
}

View File

@@ -0,0 +1,49 @@
/*
* 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 android.content.Context;
import android.icu.text.LocaleDisplayNames;
import android.support.v7.preference.Preference;
import java.util.Locale;
public class RegionPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "region";
private final LocaleDisplayNames mLocaleDisplayNames;
private String mRegionId = "";
public RegionPreferenceController(Context context) {
super(context, PREFERENCE_KEY);
Locale locale = context.getResources().getConfiguration().getLocales().get(0);
mLocaleDisplayNames = LocaleDisplayNames.getInstance(locale);
}
@Override
public CharSequence getSummary() {
return mLocaleDisplayNames.regionDisplayName(mRegionId);
}
public void setRegionId(String regionId) {
mRegionId = regionId;
}
public String getRegionId() {
return mRegionId;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.R;
public class RegionZonePreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = "region_zone";
private TimeZoneInfo mTimeZoneInfo;
private boolean mIsClickable;
public RegionZonePreferenceController(Context context) {
super(context, PREFERENCE_KEY);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
preference.setEnabled(isClickable());
}
@Override
public CharSequence getSummary() {
return mTimeZoneInfo == null ? ""
: SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_exemplar_location_and_offset,
mTimeZoneInfo.getExemplarLocation(), mTimeZoneInfo.getGmtOffset());
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
public void setClickable(boolean clickable) {
mIsClickable = clickable;
}
public boolean isClickable() {
return mIsClickable;
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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 android.content.Context;
import android.icu.impl.OlsonTimeZone;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
import android.icu.util.Calendar;
import android.icu.util.TimeZone;
import android.icu.util.TimeZoneTransition;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.widget.FooterPreference;
import java.util.Date;
public class TimeZoneInfoPreferenceController extends BaseTimeZonePreferenceController {
private static final String PREFERENCE_KEY = FooterPreference.KEY_FOOTER;
private TimeZoneInfo mTimeZoneInfo;
private final DateFormat mDateFormat;
private final Date mDate;
public TimeZoneInfoPreferenceController(Context context) {
this(context, new Date());
}
@VisibleForTesting
TimeZoneInfoPreferenceController(Context context, Date date) {
super(context, PREFERENCE_KEY);
mDateFormat = DateFormat.getDateInstance(SimpleDateFormat.LONG);
mDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
mDate = date;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void updateState(Preference preference) {
CharSequence formattedTimeZone = mTimeZoneInfo == null ? "" : formatInfo(mTimeZoneInfo);
preference.setTitle(formattedTimeZone);
preference.setVisible(mTimeZoneInfo != null);
}
public void setTimeZoneInfo(TimeZoneInfo timeZoneInfo) {
mTimeZoneInfo = timeZoneInfo;
}
public TimeZoneInfo getTimeZoneInfo() {
return mTimeZoneInfo;
}
private CharSequence formatOffsetAndName(TimeZoneInfo item) {
String name = item.getGenericName();
if (name == null) {
if (item.getTimeZone().inDaylightTime(mDate)) {
name = item.getDaylightName();
} else {
name = item.getStandardName();
}
}
if (name == null) {
return item.getGmtOffset().toString();
} else {
return SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_offset_and_name, item.getGmtOffset(),
name);
}
}
private CharSequence formatInfo(TimeZoneInfo item) {
final CharSequence offsetAndName = formatOffsetAndName(item);
final TimeZone timeZone = item.getTimeZone();
if (!timeZone.observesDaylightTime()) {
return mContext.getString(R.string.zone_info_footer_no_dst, offsetAndName);
}
final TimeZoneTransition nextDstTransition = findNextDstTransition(timeZone);
if (nextDstTransition == null) {
return null;
}
final boolean toDst = nextDstTransition.getTo().getDSTSavings() != 0;
String timeType = toDst ? item.getDaylightName() : item.getStandardName();
if (timeType == null) {
// Fall back to generic "summer time" and "standard time" if the time zone has no
// specific names.
timeType = toDst ?
mContext.getString(R.string.zone_time_type_dst) :
mContext.getString(R.string.zone_time_type_standard);
}
final Calendar transitionTime = Calendar.getInstance(timeZone);
transitionTime.setTimeInMillis(nextDstTransition.getTime());
final String date = mDateFormat.format(transitionTime);
return SpannableUtil.getResourcesText(mContext.getResources(),
R.string.zone_info_footer, offsetAndName, timeType, date);
}
private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
if (!(timeZone instanceof OlsonTimeZone)) {
return null;
}
final OlsonTimeZone olsonTimeZone = (OlsonTimeZone) timeZone;
TimeZoneTransition transition = olsonTimeZone.getNextTransition(
mDate.getTime(), /* inclusive */ false);
do {
if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
break;
}
transition = olsonTimeZone.getNextTransition(
transition.getTime(), /*inclusive */ false);
} while (transition != null);
return transition;
}
}

View File

@@ -0,0 +1,372 @@
/*
* 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 android.app.Activity;
import android.app.AlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.icu.util.TimeZone;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceCategory;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
import com.android.settings.datetime.timezone.model.TimeZoneData;
import com.android.settings.datetime.timezone.model.TimeZoneDataLoader;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
/**
* The class displays a time zone picker either by regions or fixed offset time zones.
*/
public class TimeZoneSettings extends DashboardFragment {
private static final String TAG = "TimeZoneSettings";
private static final int MENU_BY_REGION = Menu.FIRST;
private static final int MENU_BY_OFFSET = Menu.FIRST + 1;
private static final int REQUEST_CODE_REGION_PICKER = 1;
private static final int REQUEST_CODE_ZONE_PICKER = 2;
private static final int REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER = 3;
private static final String PREF_KEY_REGION = "time_zone_region";
private static final String PREF_KEY_REGION_CATEGORY = "time_zone_region_preference_category";
private static final String PREF_KEY_FIXED_OFFSET_CATEGORY =
"time_zone_fixed_offset_preference_category";
private Locale mLocale;
private boolean mSelectByRegion;
private TimeZoneData mTimeZoneData;
private String mSelectedTimeZoneId;
private TimeZoneInfo.Formatter mTimeZoneInfoFormatter;
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.ZONE_PICKER;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.time_zone_prefs;
}
@Override
protected String getLogTag() {
return TAG;
}
/**
* Called during onAttach
*/
@VisibleForTesting
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mLocale = context.getResources().getConfiguration().getLocales().get(0);
mTimeZoneInfoFormatter = new TimeZoneInfo.Formatter(mLocale, new Date());
final List<AbstractPreferenceController> controllers = new ArrayList<>();
RegionPreferenceController regionPreferenceController =
new RegionPreferenceController(context);
regionPreferenceController.setOnClickListener(this::onRegionPreferenceClicked);
RegionZonePreferenceController regionZonePreferenceController =
new RegionZonePreferenceController(context);
regionZonePreferenceController.setOnClickListener(this::onRegionZonePreferenceClicked);
TimeZoneInfoPreferenceController timeZoneInfoPreferenceController =
new TimeZoneInfoPreferenceController(context);
FixedOffsetPreferenceController fixedOffsetPreferenceController =
new FixedOffsetPreferenceController(context);
fixedOffsetPreferenceController.setOnClickListener(this::onFixedOffsetPreferenceClicked);
controllers.add(regionPreferenceController);
controllers.add(regionZonePreferenceController);
controllers.add(timeZoneInfoPreferenceController);
controllers.add(fixedOffsetPreferenceController);
return controllers;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Hide all interactive preferences
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_REGION_CATEGORY), false);
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_FIXED_OFFSET_CATEGORY), false);
// Start loading TimeZoneData
getLoaderManager().initLoader(0, null, new TimeZoneDataLoader.LoaderCreator(
getContext(), this::onTimeZoneDataReady));
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK || data == null) {
return;
}
switch (requestCode) {
case REQUEST_CODE_REGION_PICKER:
case REQUEST_CODE_ZONE_PICKER: {
String regionId = data.getStringExtra(RegionSearchPicker.EXTRA_RESULT_REGION_ID);
String tzId = data.getStringExtra(RegionZonePicker.EXTRA_RESULT_TIME_ZONE_ID);
// Ignore the result if user didn't change the region or time zone.
if (!Objects.equals(regionId, use(RegionPreferenceController.class).getRegionId())
|| !Objects.equals(tzId, mSelectedTimeZoneId)) {
onRegionZoneChanged(regionId, tzId);
}
break;
}
case REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER: {
String tzId = data.getStringExtra(FixedOffsetPicker.EXTRA_RESULT_TIME_ZONE_ID);
// Ignore the result if user didn't change the time zone.
if (tzId != null && !tzId.equals(mSelectedTimeZoneId)) {
onFixedOffsetZoneChanged(tzId);
}
break;
}
}
}
@VisibleForTesting
void setTimeZoneData(TimeZoneData timeZoneData) {
mTimeZoneData = timeZoneData;
}
private void onTimeZoneDataReady(TimeZoneData timeZoneData) {
if (mTimeZoneData == null && timeZoneData != null) {
mTimeZoneData = timeZoneData;
setupForCurrentTimeZone();
getActivity().invalidateOptionsMenu();
}
}
private void onRegionPreferenceClicked() {
startPickerFragment(RegionSearchPicker.class, new Bundle(), REQUEST_CODE_REGION_PICKER);
}
private void onRegionZonePreferenceClicked() {
final Bundle args = new Bundle();
args.putString(RegionZonePicker.EXTRA_REGION_ID,
use(RegionPreferenceController.class).getRegionId());
startPickerFragment(RegionZonePicker.class, args, REQUEST_CODE_ZONE_PICKER);
}
private void onFixedOffsetPreferenceClicked() {
startPickerFragment(FixedOffsetPicker.class, new Bundle(),
REQUEST_CODE_FIXED_OFFSET_ZONE_PICKER);
}
private void startPickerFragment(Class<? extends BaseTimeZonePicker> fragmentClass, Bundle args,
int resultRequestCode) {
new SubSettingLauncher(getContext())
.setDestination(fragmentClass.getCanonicalName())
.setArguments(args)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, resultRequestCode)
.launch();
}
private void setDisplayedRegion(String regionId) {
use(RegionPreferenceController.class).setRegionId(regionId);
updatePreferenceStates();
}
private void setDisplayedTimeZoneInfo(String regionId, String tzId) {
final TimeZoneInfo tzInfo = tzId == null ? null : mTimeZoneInfoFormatter.format(tzId);
final FilteredCountryTimeZones countryTimeZones =
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.
use(RegionZonePreferenceController.class).setClickable(tzInfo == null ||
(countryTimeZones != null && countryTimeZones.getTimeZoneIds().size() > 1));
use(TimeZoneInfoPreferenceController.class).setTimeZoneInfo(tzInfo);
updatePreferenceStates();
}
private void setDisplayedFixedOffsetTimeZoneInfo(String tzId) {
if (isFixedOffset(tzId)) {
use(FixedOffsetPreferenceController.class).setTimeZoneInfo(
mTimeZoneInfoFormatter.format(tzId));
} else {
use(FixedOffsetPreferenceController.class).setTimeZoneInfo(null);
}
updatePreferenceStates();
}
private void onRegionZoneChanged(String regionId, String tzId) {
FilteredCountryTimeZones countryTimeZones =
mTimeZoneData.lookupCountryTimeZones(regionId);
if (countryTimeZones == null || !countryTimeZones.getTimeZoneIds().contains(tzId)) {
Log.e(TAG, "Unknown time zone id is selected: " + tzId);
return;
}
mSelectedTimeZoneId = tzId;
setDisplayedRegion(regionId);
setDisplayedTimeZoneInfo(regionId, mSelectedTimeZoneId);
saveTimeZone(regionId, mSelectedTimeZoneId);
}
private void onFixedOffsetZoneChanged(String tzId) {
mSelectedTimeZoneId = tzId;
setDisplayedFixedOffsetTimeZoneInfo(tzId);
saveTimeZone(null, mSelectedTimeZoneId);
}
private void saveTimeZone(String regionId, String tzId) {
SharedPreferences.Editor editor = getPreferenceManager().getSharedPreferences().edit();
if (regionId == null) {
editor.remove(PREF_KEY_REGION);
} else {
editor.putString(PREF_KEY_REGION, regionId);
}
editor.apply();
getActivity().getSystemService(AlarmManager.class).setTimeZone(tzId);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_BY_REGION, 0, R.string.zone_menu_by_region);
menu.add(0, MENU_BY_OFFSET, 0, R.string.zone_menu_by_offset);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
// Do not show menu when data is not ready,
menu.findItem(MENU_BY_REGION).setVisible(mTimeZoneData != null && !mSelectByRegion);
menu.findItem(MENU_BY_OFFSET).setVisible(mTimeZoneData != null && mSelectByRegion);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_BY_REGION:
setSelectByRegion(true);
return true;
case MENU_BY_OFFSET:
setSelectByRegion(false);
return true;
default:
return false;
}
}
private void setupForCurrentTimeZone() {
mSelectedTimeZoneId = TimeZone.getDefault().getID();
setSelectByRegion(!isFixedOffset(mSelectedTimeZoneId));
}
private static boolean isFixedOffset(String tzId) {
return tzId.startsWith("Etc/GMT") || tzId.equals("Etc/UTC");
}
/**
* Switch the current view to select region or select fixed offset time zone.
* When showing the selected region, it guess the selected region from time zone id.
* See {@link #findRegionIdForTzId} for more info.
*/
private void setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_REGION_CATEGORY), selectByRegion);
setPreferenceCategoryVisible((PreferenceCategory) findPreference(
PREF_KEY_FIXED_OFFSET_CATEGORY), !selectByRegion);
final String localeRegionId = getLocaleRegionId();
final Set<String> allCountryIsoCodes = mTimeZoneData.getRegionIds();
String displayRegion = allCountryIsoCodes.contains(localeRegionId) ? localeRegionId : null;
setDisplayedRegion(displayRegion);
setDisplayedTimeZoneInfo(displayRegion, null);
if (!mSelectByRegion) {
setDisplayedFixedOffsetTimeZoneInfo(mSelectedTimeZoneId);
return;
}
String regionId = findRegionIdForTzId(mSelectedTimeZoneId);
if (regionId != null) {
setDisplayedRegion(regionId);
setDisplayedTimeZoneInfo(regionId, mSelectedTimeZoneId);
}
}
/**
* Find the a region associated with the specified time zone, based on the time zone data.
* If there are multiple regions associated with the given time zone, the priority will be given
* to the region the user last picked and the country in user's locale.
* @return null if no region associated with the time zone
*/
private String findRegionIdForTzId(String tzId) {
return findRegionIdForTzId(tzId,
getPreferenceManager().getSharedPreferences().getString(PREF_KEY_REGION, null),
getLocaleRegionId());
}
@VisibleForTesting
String findRegionIdForTzId(String tzId, String sharePrefRegionId, String localeRegionId) {
final Set<String> matchedRegions = mTimeZoneData.lookupCountryCodesForZoneId(tzId);
if (matchedRegions.size() == 0) {
return null;
}
if (sharePrefRegionId != null && matchedRegions.contains(sharePrefRegionId)) {
return sharePrefRegionId;
}
if (localeRegionId != null && matchedRegions.contains(localeRegionId)) {
return localeRegionId;
}
return matchedRegions.toArray(new String[matchedRegions.size()])[0];
}
private void setPreferenceCategoryVisible(PreferenceCategory category,
boolean isVisible) {
// Hiding category doesn't hide all the children preference. Set visibility of its children.
// Do not care grandchildren as time_zone_pref.xml has only 2 levels.
category.setVisible(isVisible);
for (int i = 0; i < category.getPreferenceCount(); i++) {
category.getPreference(i).setVisible(isVisible);
}
}
private String getLocaleRegionId() {
return mLocale.getCountry().toUpperCase(Locale.US);
}
}

View File

@@ -24,3 +24,4 @@ com.android.settings.wifi.SavedAccessPointsWifiSettings
com.android.settings.notification.ZenModeEventRuleSettings
com.android.settings.notification.ZenModeScheduleRuleSettings
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.datetime.timezone.TimeZoneSettings

View File

@@ -0,0 +1,99 @@
/*
* 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 android.app.Activity;
import android.content.Context;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class BaseTimeZonePreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void handlePreferenceTreeClick_correctKey_triggerOnClickListener() {
String prefKey = "key1";
TestClickListener clickListener = new TestClickListener();
TestPreference preference = new TestPreference(mActivity, prefKey);
TestPreferenceController controller = new TestPreferenceController(mActivity, prefKey);
controller.setOnClickListener(clickListener);
controller.handlePreferenceTreeClick(preference);
assertThat(clickListener.isClicked()).isTrue();
}
@Test
public void handlePreferenceTreeClick_wrongKey_triggerOnClickListener() {
String prefKey = "key1";
TestClickListener clickListener = new TestClickListener();
TestPreference preference = new TestPreference(mActivity, "wrong_key");
TestPreferenceController controller = new TestPreferenceController(mActivity, prefKey);
controller.setOnClickListener(clickListener);
controller.handlePreferenceTreeClick(preference);
assertThat(clickListener.isClicked()).isFalse();
}
private static class TestPreferenceController extends BaseTimeZonePreferenceController {
private final Preference mTestPreference;
public TestPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mTestPreference = new Preference(context);
mTestPreference.setKey(preferenceKey);
}
}
private static class TestPreference extends Preference {
public TestPreference(Context context, String preferenceKey) {
super(context);
setKey(preferenceKey);
}
}
private static class TestClickListener implements OnPreferenceClickListener {
private boolean isClicked = false;
@Override
public void onClick() {
isClicked = true;
}
public boolean isClicked() {
return isClicked;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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 android.app.Activity;
import android.icu.util.TimeZone;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class FixedOffsetPreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchTimeZoneSummary() {
TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
TimeZone.getFrozenTimeZone("Etc/GMT-8"))
.setExemplarLocation("Los Angeles")
.setGmtOffset("GMT-08:00")
.setItemId(0)
.build();
Preference preference = new Preference(mActivity);
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
controller.setTimeZoneInfo(fixedOffsetZone);
controller.updateState(preference);
assertThat(preference.getSummary()).isEqualTo("GMT-08:00");
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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 android.app.Activity;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class RegionPreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchCountryName() {
Preference preference = new Preference(mActivity);
RegionPreferenceController controller = new RegionPreferenceController(mActivity);
controller.setRegionId("US");
controller.updateState(preference);
assertThat(controller.getSummary()).isEqualTo("United States");
assertThat(preference.getSummary()).isEqualTo("United States");
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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 android.app.Activity;
import android.icu.util.TimeZone;
import android.support.v7.preference.Preference;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
public class RegionZonePreferenceControllerTest {
private Activity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.setupActivity(Activity.class);
}
@Test
public void updateState_matchTimeZoneName() {
TimeZoneInfo tzInfo = new TimeZoneInfo.Builder(
TimeZone.getFrozenTimeZone("America/Los_Angeles"))
.setGenericName("Pacific Time")
.setStandardName("Pacific Standard Time")
.setDaylightName("Pacific Daylight Time")
.setExemplarLocation("Los Angeles")
.setGmtOffset("GMT-08:00")
.setItemId(0)
.build();
Preference preference = new Preference(mActivity);
RegionZonePreferenceController controller = new RegionZonePreferenceController(mActivity);
controller.setTimeZoneInfo(tzInfo);
controller.setClickable(false);
controller.updateState(preference);
String expectedSummary = "Los Angeles (GMT-08:00)";
assertThat(controller.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
assertThat(preference.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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 android.support.v7.preference.Preference;
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.RuntimeEnvironment;
import java.util.Date;
import java.util.Locale;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.spy;
@RunWith(SettingsRobolectricTestRunner.class)
public class TimeZoneInfoPreferenceControllerTest {
@Test
public void updateState_matchExpectedFormattedText() {
Date now = new Date(0L); // 00:00 1/1/1970
Formatter formatter = new Formatter(Locale.US, now);
TimeZoneInfo timeZoneInfo = formatter.format("America/Los_Angeles");
TimeZoneInfoPreferenceController controller =
new TimeZoneInfoPreferenceController(RuntimeEnvironment.application, now);
controller.setTimeZoneInfo(timeZoneInfo);
Preference preference = spy(new Preference(RuntimeEnvironment.application));
controller.updateState(preference);
assertEquals("Uses Pacific Time (GMT-08:00). "
+ "Pacific Daylight Time starts on April 26, 1970.",
preference.getTitle().toString());
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.datetime.timezone.model.TimeZoneData;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class,
})
public class TimeZoneSettingsTest {
@Test
public void findRegionIdForTzId_matchExpectedCountry() {
String tzId = "Unknown/Secret_City";
TimeZoneData timeZoneData = mock(TimeZoneData.class);
when(timeZoneData.lookupCountryCodesForZoneId(tzId))
.thenReturn(new HashSet<>(Arrays.asList("US", "GB")));
TimeZoneSettings settings = new TimeZoneSettings();
settings.setTimeZoneData(timeZoneData);
assertThat(settings.findRegionIdForTzId(tzId, null, "")).matches("US|GB");
assertThat(settings.findRegionIdForTzId(tzId, "GB", "")).isEqualTo("GB");
assertThat(settings.findRegionIdForTzId(tzId, null, "GB")).isEqualTo("GB");
}
@Test
public void createPreferenceControllers_matchExpectedControllers() {
TimeZoneSettings settings = new TimeZoneSettings();
List<AbstractPreferenceController> controllers =
settings.createPreferenceControllers(RuntimeEnvironment.application);
assertThat(controllers).hasSize(4);
assertThat(controllers.get(0)).isInstanceOf(RegionPreferenceController.class);
assertThat(controllers.get(1)).isInstanceOf(RegionZonePreferenceController.class);
assertThat(controllers.get(2)).isInstanceOf(TimeZoneInfoPreferenceController.class);
assertThat(controllers.get(3)).isInstanceOf(FixedOffsetPreferenceController.class);
}
}