New manual time zone picker.

This implements a new manual time zone picker that allows selection of a
time zone for a selected country. It also allows selecting a fixed
offset time zone (most importantly Etc/UTC, which is a frequently
requested feature).

The new time zone picker is currently behind a feature flag
(settings_zone_picker_v2), which is disabled by default.

Test: manual
Test: SettingsFunctionalTests
Test: SettingsRobotTests
Bug: 62255208
Change-Id: I89c5a04bcb562b6facf5f31a8aa4ad1cdd51ab10
This commit is contained in:
Joachim Sauer
2017-11-07 13:56:16 +00:00
parent 0bed37a086
commit 0cdbe1897c
9 changed files with 703 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
<!--
Copyright (C) 2017 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/tz_region_spinner_layout"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/colorAccent"
android:gravity="center_vertical"
android:paddingEnd="@dimen/switchbar_subsettings_margin_end"
android:orientation="horizontal">
<Spinner
android:id="@+id/tz_region_spinner"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:paddingStart="64dp"
android:layout_weight="1"
android:background="@drawable/app_filter_spinner_background"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/tz_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -0,0 +1,62 @@
<!--
Copyright (C) 2017 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@+id/tz_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TimeZone name"
android:textAppearance="?android:attr/textAppearanceListItem" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tz_item_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" />
<TextView
android:id="@+id/tz_item_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="viewEnd"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
<TextView
android:id="@+id/tz_item_dst"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>

View File

@@ -752,6 +752,17 @@
<string name="zone_list_menu_sort_alphabetically">Sort alphabetically</string> <string name="zone_list_menu_sort_alphabetically">Sort alphabetically</string>
<!-- Menu item on Select time zone screen --> <!-- Menu item on Select time zone screen -->
<string name="zone_list_menu_sort_by_timezone">Sort by time zone</string> <string name="zone_list_menu_sort_by_timezone">Sort by time zone</string>
<!-- Label describing when a given time zone changes to DST or standard time -->
<string name="zone_change_to_from_dst"><xliff:g id="time_type" example="Pacific Summer Time">%1$s</xliff:g> starts on <xliff:g id="transition_date" example="Mar 11 2018">%2$s</xliff:g>.</string>
<!-- Describes the time type "daylight savings time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
<string name="zone_time_type_dst">Daylight savings time</string>
<!-- Describes the time type "standard time" (used in zone_change_to_from_dst, when no zone specific name is available) -->
<string name="zone_time_type_standard">Standard time</string>
<!-- The menu item to switch to selecting a time zone by region (default) -->
<string name="zone_menu_by_region">Time zone by region</string>
<!-- The menu item to switch to selecting a time zone with a fixed offset (such as UTC or GMT+0200) -->
<string name="zone_menu_by_offset">Fixed offset time zones</string>
<!-- Title string shown above DatePicker, letting a user select system date <!-- Title string shown above DatePicker, letting a user select system date
[CHAR LIMIT=20] --> [CHAR LIMIT=20] -->
<string name="date_picker_title">Date</string> <string name="date_picker_title">Date</string>

View File

@@ -26,4 +26,5 @@ public class FeatureFlags {
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2"; public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list"; public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
public static final String SECURITY_SETTINGS_V2 = "settings_security_settings_v2"; public static final String SECURITY_SETTINGS_V2 = "settings_security_settings_v2";
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
} }

View File

@@ -20,7 +20,10 @@ import android.content.Context;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.util.FeatureFlagUtils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.datetime.timezone.ZonePicker;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.datetime.ZoneGetter; import com.android.settingslib.datetime.ZoneGetter;
@@ -33,11 +36,13 @@ public class TimeZonePreferenceController extends AbstractPreferenceController
private static final String KEY_TIMEZONE = "timezone"; private static final String KEY_TIMEZONE = "timezone";
private final AutoTimeZonePreferenceController mAutoTimeZonePreferenceController; private final AutoTimeZonePreferenceController mAutoTimeZonePreferenceController;
private final boolean mZonePickerV2;
public TimeZonePreferenceController(Context context, public TimeZonePreferenceController(Context context,
AutoTimeZonePreferenceController autoTimeZonePreferenceController) { AutoTimeZonePreferenceController autoTimeZonePreferenceController) {
super(context); super(context);
mAutoTimeZonePreferenceController = autoTimeZonePreferenceController; mAutoTimeZonePreferenceController = autoTimeZonePreferenceController;
mZonePickerV2 = FeatureFlagUtils.isEnabled(mContext, FeatureFlags.ZONE_PICKER_V2);
} }
@Override @Override
@@ -45,6 +50,9 @@ public class TimeZonePreferenceController extends AbstractPreferenceController
if (!(preference instanceof RestrictedPreference)) { if (!(preference instanceof RestrictedPreference)) {
return; return;
} }
if (mZonePickerV2) {
preference.setFragment(ZonePicker.class.getName());
}
preference.setSummary(getTimeZoneOffsetAndName()); preference.setSummary(getTimeZoneOffsetAndName());
if( !((RestrictedPreference) preference).isDisabledByAdmin()) { if( !((RestrictedPreference) preference).isDisabledByAdmin()) {
preference.setEnabled(!mAutoTimeZonePreferenceController.isEnabled()); preference.setEnabled(!mAutoTimeZonePreferenceController.isEnabled());

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2017 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.graphics.Typeface;
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.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.R;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Adapter for showing {@link TimeZoneInfo} objects in a recycler view.
*/
class TimeZoneAdapter extends RecyclerView.Adapter {
static final int VIEW_TYPE_NORMAL = 1;
static final int VIEW_TYPE_SELECTED = 2;
private final DateFormat mTimeFormat;
private final DateFormat mDateFormat;
private final View.OnClickListener mOnClickListener;
private final Context mContext;
private final String mCurrentTimeZone;
private List<TimeZoneInfo> mTimeZoneInfos;
TimeZoneAdapter(View.OnClickListener onClickListener, Context context) {
mOnClickListener = onClickListener;
mContext = context;
mTimeFormat = DateFormat.getTimeInstance(SimpleDateFormat.SHORT);
mDateFormat = DateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
mDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
mCurrentTimeZone = TimeZone.getDefault().getID();
setHasStableIds(true);
}
@Override
public long getItemId(int position) {
return getItem(position).getItemId();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.time_zone_list_item, parent, false);
view.setOnClickListener(mOnClickListener);
final ViewHolder viewHolder = new ViewHolder(view);
if (viewType == VIEW_TYPE_SELECTED) {
viewHolder.mNameView.setTypeface(
viewHolder.mNameView.getTypeface(), Typeface.BOLD);
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
final TimeZoneInfo item = getItem(position);
final ViewHolder tzHolder = (ViewHolder) holder;
tzHolder.mNameView.setText(formatName(item));
tzHolder.mDetailsView.setText(formatDetails(item));
tzHolder.mTimeView.setText(formatTime(item));
String dstText = formatDstText(item);
tzHolder.mDstView.setText(dstText);
// Hide DST TextView when it has no content.
tzHolder.mDstView.setVisibility(dstText != null ? View.VISIBLE : View.GONE);
}
@Override
public int getItemCount() {
return getTimeZones().size();
}
@Override
public int getItemViewType(int position) {
final TimeZoneInfo tz = getItem(position);
if (tz.getId().equals(mCurrentTimeZone)) {
return VIEW_TYPE_SELECTED;
} else {
return VIEW_TYPE_NORMAL;
}
}
public TimeZoneInfo getItem(int position) {
return getTimeZones().get(position);
}
private CharSequence formatName(TimeZoneInfo item) {
CharSequence name = item.getExemplarLocation();
if (name == null) {
name = item.getGenericName();
}
if (name == null && item.getTimeZone().inDaylightTime(new Date())) {
name = item.getDaylightName();
}
if (name == null) {
name = item.getStandardName();
}
if (name == null) {
name = item.getGmtOffset();
}
return name;
}
private CharSequence formatDetails(TimeZoneInfo item) {
String name = item.getGenericName();
if (name == null) {
if (item.getTimeZone().inDaylightTime(new Date())) {
name = item.getDaylightName();
} else {
name = item.getStandardName();
}
}
if (name == null) {
return item.getGmtOffset();
} else {
return TextUtils.concat(item.getGmtOffset(), " ", name);
}
}
private String formatDstText(TimeZoneInfo item) {
final TimeZone timeZone = item.getTimeZone();
if (!timeZone.observesDaylightTime()) {
return null;
}
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 mContext.getString(R.string.zone_change_to_from_dst, timeType, date);
}
private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
if (!(timeZone instanceof OlsonTimeZone)) {
return null;
}
final OlsonTimeZone olsonTimeZone = (OlsonTimeZone) timeZone;
TimeZoneTransition transition = olsonTimeZone.getNextTransition(
System.currentTimeMillis(), /* inclusive */ false);
do {
if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
break;
}
transition = olsonTimeZone.getNextTransition(
transition.getTime(), /*inclusive */ false);
} while (transition != null);
return transition;
}
private String formatTime(TimeZoneInfo item) {
return mTimeFormat.format(Calendar.getInstance(item.getTimeZone()));
}
private List<TimeZoneInfo> getTimeZones() {
if (mTimeZoneInfos == null) {
return Collections.emptyList();
}
return mTimeZoneInfos;
}
void setTimeZoneInfos(List<TimeZoneInfo> timeZoneInfos) {
mTimeZoneInfos = timeZoneInfos;
notifyDataSetChanged();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2017 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.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.android.settings.R;
/**
* View holder for a time zone list item.
*/
class ViewHolder extends RecyclerView.ViewHolder {
final TextView mNameView;
final TextView mDstView;
final TextView mDetailsView;
final TextView mTimeView;
public ViewHolder(View itemView) {
super(itemView);
mNameView = itemView.findViewById(R.id.tz_item_name);
mDstView = itemView.findViewById(R.id.tz_item_dst);
mDetailsView = itemView.findViewById(R.id.tz_item_details);
mTimeView = itemView.findViewById(R.id.tz_item_time);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2017 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.icu.util.TimeZone;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* The class displaying a region list and a list of time zones for the selected region.
* Choosing an item from the list will set the time zone. Pressing Back without choosing from the
* list will not result in a change in the time zone setting.
*/
public class ZonePicker extends InstrumentedFragment
implements AdapterView.OnItemSelectedListener, View.OnClickListener {
private static final int MENU_BY_REGION = Menu.FIRST;
private static final int MENU_BY_OFFSET = Menu.FIRST + 1;
private Locale mLocale;
private List<RegionInfo> mRegions;
private Map<String, List<TimeZoneInfo>> mZoneInfos;
private List<TimeZoneInfo> mFixedOffsetTimeZones;
private TimeZoneAdapter mTimeZoneAdapter;
private String mSelectedTimeZone;
private boolean mSelectByRegion;
private DataLoader mDataLoader;
private RecyclerView mRecyclerView;
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.ZONE_PICKER;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.time_zone_list, container, false);
mLocale = getContext().getResources().getConfiguration().locale;
mDataLoader = new DataLoader(mLocale);
// TOOD: move this off the UI thread.
mRegions = mDataLoader.loadRegionInfos();
mZoneInfos = new HashMap<>();
mSelectByRegion = true;
mSelectedTimeZone = TimeZone.getDefault().getID();
mTimeZoneAdapter = new TimeZoneAdapter(this, getContext());
mRecyclerView = view.findViewById(R.id.tz_list);
mRecyclerView.setAdapter(mTimeZoneAdapter);
mRecyclerView.setLayoutManager(
new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, /* reverseLayout */ false));
final ArrayAdapter<RegionInfo> regionAdapter = new ArrayAdapter<>(getContext(),
R.layout.filter_spinner_item, mRegions);
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
final Spinner spinner = view.findViewById(R.id.tz_region_spinner);
spinner.setAdapter(regionAdapter);
spinner.setOnItemSelectedListener(this);
setupForCurrentTimeZone(spinner);
setHasOptionsMenu(true);
return view;
}
private void setupForCurrentTimeZone(Spinner spinner) {
final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT);
final String currentTimeZone = TimeZone.getDefault().getID();
boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") ||
currentTimeZone.equals("Etc/UTC");
for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) {
final RegionInfo region = mRegions.get(regionIndex);
if (localeRegionId.equals(region.getId())) {
spinner.setSelection(regionIndex);
}
if (!fixedOffset) {
for (String timeZoneId: region.getTimeZoneIds()) {
if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
spinner.setSelection(regionIndex);
return;
}
}
}
}
if (fixedOffset) {
setSelectByRegion(false);
}
}
@Override
public void onClick(View view) {
// Ignore extra clicks
if (!isResumed()) {
return;
}
final int position = mRecyclerView.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION) {
return;
}
final TimeZoneInfo timeZoneInfo = mTimeZoneAdapter.getItem(position);
// Update the system timezone value
final Activity activity = getActivity();
final AlarmManager alarm = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
alarm.setTimeZone(timeZoneInfo.getId());
activity.onBackPressed();
}
@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) {
if (mSelectByRegion) {
menu.findItem(MENU_BY_REGION).setVisible(false);
menu.findItem(MENU_BY_OFFSET).setVisible(true);
} else {
menu.findItem(MENU_BY_REGION).setVisible(true);
menu.findItem(MENU_BY_OFFSET).setVisible(false);
}
}
@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 setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
mSelectByRegion ? View.VISIBLE : View.GONE);
List<TimeZoneInfo> tzInfos;
if (selectByRegion) {
Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner);
int selectedRegion = regionSpinner.getSelectedItemPosition();
if (selectedRegion == -1) {
// Arbitrarily pick the first item if no region was selected above.
selectedRegion = 0;
regionSpinner.setSelection(selectedRegion);
}
tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion));
} else {
if (mFixedOffsetTimeZones == null) {
mFixedOffsetTimeZones = mDataLoader.loadFixedOffsets();
}
tzInfos = mFixedOffsetTimeZones;
}
mTimeZoneAdapter.setTimeZoneInfos(tzInfos);
}
private List<TimeZoneInfo> getTimeZoneInfos(RegionInfo regionInfo) {
List<TimeZoneInfo> tzInfos = mZoneInfos.get(regionInfo.getId());
if (tzInfos == null) {
// TODO: move this off the UI thread.
Collection<String> tzIds = regionInfo.getTimeZoneIds();
tzInfos = mDataLoader.loadTimeZoneInfos(tzIds);
mZoneInfos.put(regionInfo.getId(), tzInfos);
}
return tzInfos;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mTimeZoneAdapter.setTimeZoneInfos(getTimeZoneInfos(mRegions.get(position)));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
mTimeZoneAdapter.setTimeZoneInfos(null);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2017 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.icu.util.TimeZone;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Collections;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
shadows = {
SettingsShadowResources.class,
SettingsShadowResources.SettingsShadowTheme.class})
public class TimeZoneAdapterTest {
@Mock
private View.OnClickListener mOnClickListener;
private TimeZoneAdapter mTimeZoneAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTimeZoneAdapter = new TimeZoneAdapter(mOnClickListener, RuntimeEnvironment.application);
}
@Test
public void getItemViewType_onDefaultTimeZone_returnsTypeSelected() {
final TimeZoneInfo tzi = dummyTimeZoneInfo(TimeZone.getDefault());
mTimeZoneAdapter.setTimeZoneInfos(Collections.singletonList(tzi));
assertThat(mTimeZoneAdapter.getItemViewType(0)).isEqualTo(TimeZoneAdapter.VIEW_TYPE_SELECTED);
}
@Test
public void getItemViewType_onNonDefaultTimeZone_returnsTypeNormal() {
final TimeZoneInfo tzi = dummyTimeZoneInfo(getNonDefaultTimeZone());
mTimeZoneAdapter.setTimeZoneInfos(Collections.singletonList(tzi));
assertThat(mTimeZoneAdapter.getItemViewType(0)).isEqualTo(TimeZoneAdapter.VIEW_TYPE_NORMAL);
}
@Test
public void bindViewHolder_onDstTimeZone_showsDstLabel() {
final TimeZoneInfo tzi = dummyTimeZoneInfo(TimeZone.getTimeZone("America/Los_Angeles"));
mTimeZoneAdapter.setTimeZoneInfos(Collections.singletonList(tzi));
final FrameLayout parent = new FrameLayout(RuntimeEnvironment.application);
final ViewHolder viewHolder = (ViewHolder) mTimeZoneAdapter.createViewHolder(parent, TimeZoneAdapter.VIEW_TYPE_NORMAL);
mTimeZoneAdapter.bindViewHolder(viewHolder, 0);
assertThat(viewHolder.mDstView.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void bindViewHolder_onNonDstTimeZone_hidesDstLabel() {
final TimeZoneInfo tzi = dummyTimeZoneInfo(TimeZone.getTimeZone("Etc/UTC"));
mTimeZoneAdapter.setTimeZoneInfos(Collections.singletonList(tzi));
final FrameLayout parent = new FrameLayout(RuntimeEnvironment.application);
final ViewHolder viewHolder = (ViewHolder) mTimeZoneAdapter.createViewHolder(parent, TimeZoneAdapter.VIEW_TYPE_NORMAL);
mTimeZoneAdapter.bindViewHolder(viewHolder, 0);
assertThat(viewHolder.mDstView.getVisibility()).isEqualTo(View.GONE);
}
// Pick an arbitrary time zone that's not the current default.
private static TimeZone getNonDefaultTimeZone() {
final String[] availableIDs = TimeZone.getAvailableIDs();
int index = 0;
if (TextUtils.equals(availableIDs[index], TimeZone.getDefault().getID())) {
index++;
}
return TimeZone.getTimeZone(availableIDs[index]);
}
private TimeZoneInfo dummyTimeZoneInfo(TimeZone timeZone) {
return new TimeZoneInfo.Builder(timeZone).setGmtOffset("GMT+0").setItemId(1).build();
}
}