Merge "Remove dead code in time zone picker" into pi-dev
This commit is contained in:
@@ -1,44 +0,0 @@
|
|||||||
<!--
|
|
||||||
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>
|
|
@@ -1,205 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.graphics.Paint;
|
|
||||||
import android.icu.text.Collator;
|
|
||||||
import android.icu.text.LocaleDisplayNames;
|
|
||||||
import android.icu.text.TimeZoneFormat;
|
|
||||||
import android.icu.text.TimeZoneNames;
|
|
||||||
import android.icu.text.TimeZoneNames.NameType;
|
|
||||||
import android.icu.util.Region;
|
|
||||||
import android.icu.util.Region.RegionType;
|
|
||||||
import android.icu.util.TimeZone;
|
|
||||||
import android.icu.util.TimeZone.SystemTimeZoneType;
|
|
||||||
import com.android.settingslib.datetime.ZoneGetter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides data for manual selection of time zones based associated to regions. This class makes no
|
|
||||||
* attempt to avoid IO and processing intensive actions. This means it should not be called from the
|
|
||||||
* UI thread.
|
|
||||||
*/
|
|
||||||
public class DataLoader {
|
|
||||||
|
|
||||||
private static final int MIN_HOURS_OFFSET = -14;
|
|
||||||
private static final int MAX_HOURS_OFFSET = +12;
|
|
||||||
|
|
||||||
private final Locale mLocale;
|
|
||||||
|
|
||||||
private final Collator mCollator;
|
|
||||||
private final LocaleDisplayNames mLocaleDisplayNames;
|
|
||||||
private final TimeZoneFormat mTimeZoneFormat;
|
|
||||||
private final Paint mPaint;
|
|
||||||
private final AtomicLong nextItemId = new AtomicLong(1);
|
|
||||||
private final long mNow = System.currentTimeMillis();
|
|
||||||
|
|
||||||
public DataLoader(Locale locale) {
|
|
||||||
mLocale = locale;
|
|
||||||
mCollator = Collator.getInstance(locale);
|
|
||||||
mLocaleDisplayNames = LocaleDisplayNames.getInstance(locale);
|
|
||||||
mTimeZoneFormat = TimeZoneFormat.getInstance(locale);
|
|
||||||
mPaint = new Paint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link RegionInfo} object for each region that has selectable time zones. The
|
|
||||||
* returned list will be sorted properly for display in the locale.
|
|
||||||
*/
|
|
||||||
public List<RegionInfo> loadRegionInfos() {
|
|
||||||
final Set<Region> regions = Region.getAvailable(RegionType.TERRITORY);
|
|
||||||
final TreeSet<RegionInfo> regionInfos = new TreeSet<>(new RegionInfoComparator());
|
|
||||||
for (final Region region : regions) {
|
|
||||||
final String regionId = region.toString();
|
|
||||||
final Set<String> timeZoneIds = getTimeZoneIds(regionId);
|
|
||||||
if (timeZoneIds.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String name = mLocaleDisplayNames.regionDisplayName(regionId);
|
|
||||||
final String regionalIndicator = createRegionalIndicator(regionId);
|
|
||||||
|
|
||||||
regionInfos.add(new RegionInfo(regionId, name, regionalIndicator, timeZoneIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(new ArrayList<>(regionInfos));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for
|
|
||||||
* display in the locale.It may be smaller than the input collection, if equivalent IDs are
|
|
||||||
* passed in.
|
|
||||||
*
|
|
||||||
* @param timeZoneIds a list of Olson IDs.
|
|
||||||
*/
|
|
||||||
public List<TimeZoneInfo> loadTimeZoneInfos(Collection<String> timeZoneIds) {
|
|
||||||
final TreeSet<TimeZoneInfo> timeZoneInfos = new TreeSet<>(new TimeZoneInfoComparator());
|
|
||||||
outer:
|
|
||||||
for (final String timeZoneId : timeZoneIds) {
|
|
||||||
final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
|
|
||||||
for (final TimeZoneInfo other : timeZoneInfos) {
|
|
||||||
if (other.getTimeZone().hasSameRules(timeZone)) {
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeZoneInfos.add(createTimeZoneInfo(timeZone));
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link TimeZoneInfo} for each fixed offset time zone, such as UTC or GMT+4. The
|
|
||||||
* returned list will be sorted in a reasonable way for display.
|
|
||||||
*/
|
|
||||||
public List<TimeZoneInfo> loadFixedOffsets() {
|
|
||||||
final List<TimeZoneInfo> timeZoneInfos = new ArrayList<>();
|
|
||||||
timeZoneInfos.add(createTimeZoneInfo(TimeZone.getFrozenTimeZone("Etc/UTC")));
|
|
||||||
for (int hoursOffset = MAX_HOURS_OFFSET; hoursOffset >= MIN_HOURS_OFFSET; --hoursOffset) {
|
|
||||||
if (hoursOffset == 0) {
|
|
||||||
// UTC is handled above, so don't add GMT +/-0 again.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String id = String.format("Etc/GMT%+d", hoursOffset);
|
|
||||||
timeZoneInfos.add(createTimeZoneInfo(TimeZone.getFrozenTimeZone(id)));
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(timeZoneInfos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the set of ids for relevant TimeZones in the given region.
|
|
||||||
*/
|
|
||||||
private Set<String> getTimeZoneIds(String regionId) {
|
|
||||||
return TimeZone.getAvailableIDs(
|
|
||||||
SystemTimeZoneType.CANONICAL_LOCATION, regionId, /* rawOffset */ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TimeZoneInfo createTimeZoneInfo(TimeZone timeZone) {
|
|
||||||
// Every timezone we handle must be an OlsonTimeZone.
|
|
||||||
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, new Date(mNow));
|
|
||||||
return new TimeZoneInfo.Builder(timeZone)
|
|
||||||
.setGenericName(timeZoneNames.getDisplayName(id, NameType.LONG_GENERIC, mNow))
|
|
||||||
.setStandardName(timeZoneNames.getDisplayName(id, NameType.LONG_STANDARD, mNow))
|
|
||||||
.setDaylightName(timeZoneNames.getDisplayName(id, NameType.LONG_DAYLIGHT, mNow))
|
|
||||||
.setExemplarLocation(timeZoneNames.getExemplarLocationName(id))
|
|
||||||
.setGmtOffset(gmtOffset)
|
|
||||||
.setItemId(nextItemId.getAndIncrement())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Unicode Region Indicator Symbol for a given region id (a.k.a flag emoji). If the
|
|
||||||
* system can't render a flag for this region or the input is not a region id, this returns
|
|
||||||
* {@code null}.
|
|
||||||
*
|
|
||||||
* @param id the two-character region id.
|
|
||||||
* @return a String representing the flag of the region or {@code null}.
|
|
||||||
*/
|
|
||||||
private String createRegionalIndicator(String id) {
|
|
||||||
if (id.length() != 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final char c1 = id.charAt(0);
|
|
||||||
final char c2 = id.charAt(1);
|
|
||||||
if ('A' > c1 || c1 > 'Z' || 'A' > c2 || c2 > 'Z') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Regional Indicator A is U+1F1E6 which is 0xD83C 0xDDE6 in UTF-16.
|
|
||||||
final String regionalIndicator = new String(
|
|
||||||
new char[]{0xd83c, (char) (0xdde6 - 'A' + c1), 0xd83c, (char) (0xdde6 - 'A' + c2)});
|
|
||||||
if (!mPaint.hasGlyph(regionalIndicator)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return regionalIndicator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimeZoneInfoComparator implements Comparator<TimeZoneInfo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(TimeZoneInfo tzi1, TimeZoneInfo tzi2) {
|
|
||||||
int result =
|
|
||||||
Integer
|
|
||||||
.compare(tzi1.getTimeZone().getRawOffset(), tzi2.getTimeZone().getRawOffset());
|
|
||||||
if (result == 0) {
|
|
||||||
result = mCollator.compare(tzi1.getExemplarLocation(), tzi2.getExemplarLocation());
|
|
||||||
}
|
|
||||||
if (result == 0 && tzi1.getGenericName() != null && tzi2.getGenericName() != null) {
|
|
||||||
result = mCollator.compare(tzi1.getGenericName(), tzi2.getGenericName());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RegionInfoComparator implements Comparator<RegionInfo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(RegionInfo r1, RegionInfo r2) {
|
|
||||||
return mCollator.compare(r1.getName(), r2.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data object describing a geographical region.
|
|
||||||
*
|
|
||||||
* Regions are roughly equivalent to countries, but not every region is a country (for example "U.S.
|
|
||||||
* overseas territories" is treated as a country).
|
|
||||||
*/
|
|
||||||
public class RegionInfo {
|
|
||||||
|
|
||||||
private final String mId;
|
|
||||||
private final String mName;
|
|
||||||
private final String mRegionalIndicator;
|
|
||||||
private final Collection<String> mTimeZoneIds;
|
|
||||||
|
|
||||||
public RegionInfo(String id, String name, String regionalIndicator,
|
|
||||||
Collection<String> timeZoneIds) {
|
|
||||||
mId = id;
|
|
||||||
mName = name;
|
|
||||||
mRegionalIndicator = regionalIndicator;
|
|
||||||
mTimeZoneIds = Collections.unmodifiableList(new ArrayList<>(timeZoneIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return mName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<String> getTimeZoneIds() {
|
|
||||||
return mTimeZoneIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return mRegionalIndicator != null ? mRegionalIndicator + " " + mName : mName;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,213 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
// Use android.text.format.DateFormat to observe 24-hour settings and find the best pattern
|
|
||||||
// using ICU with skeleton.
|
|
||||||
mTimeFormat = new SimpleDateFormat(
|
|
||||||
android.text.format.DateFormat.getTimeFormatString(context),
|
|
||||||
Locale.getDefault());
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -37,8 +37,6 @@ public class TimeZoneInfo {
|
|||||||
private final String mDaylightName;
|
private final String mDaylightName;
|
||||||
private final String mExemplarLocation;
|
private final String mExemplarLocation;
|
||||||
private final CharSequence mGmtOffset;
|
private final CharSequence mGmtOffset;
|
||||||
// Arbitrary id that's unique within all TimeZoneInfo objects created by a given DataLoader instance.
|
|
||||||
private final long mItemId;
|
|
||||||
|
|
||||||
public TimeZoneInfo(Builder builder) {
|
public TimeZoneInfo(Builder builder) {
|
||||||
mTimeZone = builder.mTimeZone;
|
mTimeZone = builder.mTimeZone;
|
||||||
@@ -48,7 +46,6 @@ public class TimeZoneInfo {
|
|||||||
mDaylightName = builder.mDaylightName;
|
mDaylightName = builder.mDaylightName;
|
||||||
mExemplarLocation = builder.mExemplarLocation;
|
mExemplarLocation = builder.mExemplarLocation;
|
||||||
mGmtOffset = builder.mGmtOffset;
|
mGmtOffset = builder.mGmtOffset;
|
||||||
mItemId = builder.mItemId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@@ -79,10 +76,6 @@ public class TimeZoneInfo {
|
|||||||
return mGmtOffset;
|
return mGmtOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getItemId() {
|
|
||||||
return mItemId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final TimeZone mTimeZone;
|
private final TimeZone mTimeZone;
|
||||||
private String mGenericName;
|
private String mGenericName;
|
||||||
@@ -90,7 +83,6 @@ public class TimeZoneInfo {
|
|||||||
private String mDaylightName;
|
private String mDaylightName;
|
||||||
private String mExemplarLocation;
|
private String mExemplarLocation;
|
||||||
private CharSequence mGmtOffset;
|
private CharSequence mGmtOffset;
|
||||||
private long mItemId = -1;
|
|
||||||
|
|
||||||
public Builder(TimeZone timeZone) {
|
public Builder(TimeZone timeZone) {
|
||||||
if (timeZone == null) {
|
if (timeZone == null) {
|
||||||
@@ -124,18 +116,10 @@ public class TimeZoneInfo {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setItemId(long itemId) {
|
|
||||||
mItemId = itemId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimeZoneInfo build() {
|
public TimeZoneInfo build() {
|
||||||
if (TextUtils.isEmpty(mGmtOffset)) {
|
if (TextUtils.isEmpty(mGmtOffset)) {
|
||||||
throw new IllegalStateException("gmtOffset must not be empty!");
|
throw new IllegalStateException("gmtOffset must not be empty!");
|
||||||
}
|
}
|
||||||
if (mItemId == -1) {
|
|
||||||
throw new IllegalStateException("ItemId not set!");
|
|
||||||
}
|
|
||||||
return new TimeZoneInfo(this);
|
return new TimeZoneInfo(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,8 +163,6 @@ public class TimeZoneInfo {
|
|||||||
TimeZoneNames.NameType.LONG_DAYLIGHT, mNow.getTime()))
|
TimeZoneNames.NameType.LONG_DAYLIGHT, mNow.getTime()))
|
||||||
.setExemplarLocation(timeZoneNames.getExemplarLocationName(id))
|
.setExemplarLocation(timeZoneNames.getExemplarLocationName(id))
|
||||||
.setGmtOffset(gmtOffset)
|
.setGmtOffset(gmtOffset)
|
||||||
// TODO: move Item id to TimeZoneInfoAdapter
|
|
||||||
.setItemId(0)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.LinearLayout;
|
|
||||||
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 String mSelectedTimeZone;
|
|
||||||
private boolean mSelectByRegion;
|
|
||||||
private DataLoader mDataLoader;
|
|
||||||
private TimeZoneAdapter mTimeZoneAdapter;
|
|
||||||
|
|
||||||
private RecyclerView mRecyclerView;
|
|
||||||
private LinearLayout mRegionSpinnerLayout;
|
|
||||||
private Spinner mRegionSpinner;
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout);
|
|
||||||
mRegionSpinner = view.findViewById(R.id.tz_region_spinner);
|
|
||||||
mRegionSpinner.setAdapter(regionAdapter);
|
|
||||||
mRegionSpinner.setOnItemSelectedListener(this);
|
|
||||||
setupForCurrentTimeZone();
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupForCurrentTimeZone() {
|
|
||||||
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())) {
|
|
||||||
mRegionSpinner.setSelection(regionIndex);
|
|
||||||
}
|
|
||||||
if (!fixedOffset) {
|
|
||||||
for (String timeZoneId: region.getTimeZoneIds()) {
|
|
||||||
if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
|
|
||||||
mRegionSpinner.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;
|
|
||||||
mRegionSpinnerLayout.setVisibility(
|
|
||||||
mSelectByRegion ? View.VISIBLE : View.GONE);
|
|
||||||
List<TimeZoneInfo> tzInfos;
|
|
||||||
if (selectByRegion) {
|
|
||||||
int selectedRegion = mRegionSpinner.getSelectedItemPosition();
|
|
||||||
if (selectedRegion == -1) {
|
|
||||||
// Arbitrarily pick the first item if no region was selected above.
|
|
||||||
selectedRegion = 0;
|
|
||||||
mRegionSpinner.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -89,7 +89,6 @@ public class BaseTimeZoneInfoPickerTest {
|
|||||||
.setDaylightName("Pacific Daylight Time")
|
.setDaylightName("Pacific Daylight Time")
|
||||||
.setExemplarLocation("Los Angeles")
|
.setExemplarLocation("Los Angeles")
|
||||||
.setGmtOffset("GMT-08:00")
|
.setGmtOffset("GMT-08:00")
|
||||||
.setItemId(0)
|
|
||||||
.build();
|
.build();
|
||||||
TimeZoneInfo zone2 = new TimeZoneInfo.Builder(
|
TimeZoneInfo zone2 = new TimeZoneInfo.Builder(
|
||||||
TimeZone.getFrozenTimeZone("America/New_York"))
|
TimeZone.getFrozenTimeZone("America/New_York"))
|
||||||
@@ -98,7 +97,6 @@ public class BaseTimeZoneInfoPickerTest {
|
|||||||
.setDaylightName("Eastern Daylight Time")
|
.setDaylightName("Eastern Daylight Time")
|
||||||
.setExemplarLocation("New York")
|
.setExemplarLocation("New York")
|
||||||
.setGmtOffset("GMT-05:00")
|
.setGmtOffset("GMT-05:00")
|
||||||
.setItemId(1)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return Arrays.asList(zone1, zone2);
|
return Arrays.asList(zone1, zone2);
|
||||||
|
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
public class DataLoaderTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHasData() {
|
|
||||||
List<RegionInfo> regions = new DataLoader(Locale.US).loadRegionInfos();
|
|
||||||
// Sanity check. Real size is closer to 200.
|
|
||||||
assertNotNull(regions);
|
|
||||||
assertTrue(regions.size() > 100);
|
|
||||||
assertEquals("Afghanistan", regions.get(0).getName());
|
|
||||||
assertEquals("Zimbabwe", regions.get(regions.size() - 1).getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRegionsWithTimeZone() {
|
|
||||||
List<RegionInfo> regions = new DataLoader(Locale.US).loadRegionInfos();
|
|
||||||
checkRegionHasTimeZone(regions, "AT", "Europe/Vienna");
|
|
||||||
checkRegionHasTimeZone(regions, "US", "America/Los_Angeles");
|
|
||||||
checkRegionHasTimeZone(regions, "CN", "Asia/Shanghai");
|
|
||||||
checkRegionHasTimeZone(regions, "AU", "Australia/Sydney");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFixedOffsetTimeZones() {
|
|
||||||
List<TimeZoneInfo> timeZones = new DataLoader(Locale.US).loadFixedOffsets();
|
|
||||||
// Etc/GMT would be equivalent to Etc/UTC, except for how it is labelled. Users have
|
|
||||||
// explicitly asked for UTC to be supported, so make sure we label it as such.
|
|
||||||
checkHasTimeZone(timeZones, "Etc/UTC");
|
|
||||||
checkHasTimeZone(timeZones, "Etc/GMT-1");
|
|
||||||
checkHasTimeZone(timeZones, "Etc/GMT-14");
|
|
||||||
checkHasTimeZone(timeZones, "Etc/GMT+1");
|
|
||||||
checkHasTimeZone(timeZones, "Etc/GMT+12");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkRegionHasTimeZone(List<RegionInfo> regions, String regionId, String tzId) {
|
|
||||||
RegionInfo ri = findRegion(regions, regionId);
|
|
||||||
assertTrue("Region " + regionId + " does not have time zone " + tzId,
|
|
||||||
ri.getTimeZoneIds().contains(tzId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkHasTimeZone(List<TimeZoneInfo> timeZoneInfos, String tzId) {
|
|
||||||
for (TimeZoneInfo tz : timeZoneInfos) {
|
|
||||||
if (tz.getId().equals(tzId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fail("Fixed offset time zones do not contain " + tzId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegionInfo findRegion(List<RegionInfo> regions, String regionId) {
|
|
||||||
for (RegionInfo region : regions) {
|
|
||||||
if (region.getId().equals(regionId)) {
|
|
||||||
assertNotNull(region.getName());
|
|
||||||
return region;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
fail("No region with id " + regionId + " found.");
|
|
||||||
return null; // can't reach.
|
|
||||||
}
|
|
||||||
}
|
|
@@ -44,7 +44,6 @@ public class FixedOffsetPreferenceControllerTest {
|
|||||||
TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
|
TimeZoneInfo fixedOffsetZone = new TimeZoneInfo.Builder(
|
||||||
TimeZone.getFrozenTimeZone("Etc/GMT-8"))
|
TimeZone.getFrozenTimeZone("Etc/GMT-8"))
|
||||||
.setGmtOffset("GMT-08:00")
|
.setGmtOffset("GMT-08:00")
|
||||||
.setItemId(0)
|
|
||||||
.build();
|
.build();
|
||||||
Preference preference = new Preference(mActivity);
|
Preference preference = new Preference(mActivity);
|
||||||
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
|
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
|
||||||
@@ -59,7 +58,6 @@ public class FixedOffsetPreferenceControllerTest {
|
|||||||
TimeZone.getFrozenTimeZone("Etc/UTC"))
|
TimeZone.getFrozenTimeZone("Etc/UTC"))
|
||||||
.setStandardName("Coordinated Universal Time")
|
.setStandardName("Coordinated Universal Time")
|
||||||
.setGmtOffset("GMT+00:00")
|
.setGmtOffset("GMT+00:00")
|
||||||
.setItemId(0)
|
|
||||||
.build();
|
.build();
|
||||||
Preference preference = new Preference(mActivity);
|
Preference preference = new Preference(mActivity);
|
||||||
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
|
FixedOffsetPreferenceController controller = new FixedOffsetPreferenceController(mActivity);
|
||||||
|
@@ -48,7 +48,6 @@ public class RegionZonePreferenceControllerTest {
|
|||||||
.setDaylightName("Pacific Daylight Time")
|
.setDaylightName("Pacific Daylight Time")
|
||||||
.setExemplarLocation("Los Angeles")
|
.setExemplarLocation("Los Angeles")
|
||||||
.setGmtOffset("GMT-08:00")
|
.setGmtOffset("GMT-08:00")
|
||||||
.setItemId(0)
|
|
||||||
.build();
|
.build();
|
||||||
Preference preference = new Preference(mActivity);
|
Preference preference = new Preference(mActivity);
|
||||||
RegionZonePreferenceController controller = new RegionZonePreferenceController(mActivity);
|
RegionZonePreferenceController controller = new RegionZonePreferenceController(mActivity);
|
||||||
|
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.icu.util.TimeZone;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
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 org.robolectric.annotation.Implementation;
|
|
||||||
import org.robolectric.annotation.Implements;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
|
||||||
@Config(shadows = TimeZoneAdapterTest.ShadowDataFormat.class)
|
|
||||||
public class TimeZoneAdapterTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private View.OnClickListener mOnClickListener;
|
|
||||||
|
|
||||||
private TimeZoneAdapter mTimeZoneAdapter;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private Locale mDefaultLocale;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mContext = RuntimeEnvironment.application;
|
|
||||||
mTimeZoneAdapter = new TimeZoneAdapter(mOnClickListener, mContext);
|
|
||||||
mDefaultLocale = Locale.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
Locale.setDefault(mDefaultLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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).isNotNull();
|
|
||||||
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).isNotNull();
|
|
||||||
assertThat(viewHolder.mDstView.getVisibility()).isEqualTo(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void bindViewHolder_on24Hour() {
|
|
||||||
Locale.setDefault(Locale.US);
|
|
||||||
ShadowDataFormat.mTimeFormatString = "HH:mm";
|
|
||||||
mTimeZoneAdapter = new TimeZoneAdapter(mOnClickListener, mContext);
|
|
||||||
|
|
||||||
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.mTimeView).isNotNull();
|
|
||||||
assertThat(viewHolder.mTimeView.getText().toString()).hasLength(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void bindViewHolder_on12Hour() {
|
|
||||||
Locale.setDefault(Locale.US);
|
|
||||||
ShadowDataFormat.mTimeFormatString = "hh:mm a";
|
|
||||||
mTimeZoneAdapter = new TimeZoneAdapter(mOnClickListener, mContext);
|
|
||||||
|
|
||||||
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.mTimeView).isNotNull();
|
|
||||||
assertThat(viewHolder.mTimeView.getText().toString()).hasLength(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Implements(android.text.format.DateFormat.class)
|
|
||||||
public static class ShadowDataFormat {
|
|
||||||
|
|
||||||
private static String mTimeFormatString = "";
|
|
||||||
|
|
||||||
@Implementation
|
|
||||||
public static String getTimeFormatString(Context context) {
|
|
||||||
return mTimeFormatString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user