Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4da3a7b5 authored by Victor Chang's avatar Victor Chang
Browse files

Remove dead code in time zone picker

The codes are unused due to the UX changes in b/73952488

Test: m RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.datetime.timezone
Bug: 72376227
Change-Id: I9562e97fe13a3c00f8c142f30af7ca350af10757
parent 6366a3cc
Loading
Loading
Loading
Loading

res/layout/time_zone_list.xml

deleted100644 → 0
+0 −44
Original line number Diff line number Diff line
<!--
    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>
+0 −205
Original line number Diff line number Diff line
/*
 * 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());
        }
    }
}
+0 −60
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+0 −213
Original line number Diff line number Diff line
/*
 * 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();
    }
}
+0 −18
Original line number Diff line number Diff line
@@ -37,8 +37,6 @@ public class TimeZoneInfo {
    private final String mDaylightName;
    private final String mExemplarLocation;
    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) {
        mTimeZone = builder.mTimeZone;
@@ -48,7 +46,6 @@ public class TimeZoneInfo {
        mDaylightName = builder.mDaylightName;
        mExemplarLocation = builder.mExemplarLocation;
        mGmtOffset = builder.mGmtOffset;
        mItemId = builder.mItemId;
    }

    public String getId() {
@@ -79,10 +76,6 @@ public class TimeZoneInfo {
        return mGmtOffset;
    }

    public long getItemId() {
        return mItemId;
    }

    public static class Builder {
        private final TimeZone mTimeZone;
        private String mGenericName;
@@ -90,7 +83,6 @@ public class TimeZoneInfo {
        private String mDaylightName;
        private String mExemplarLocation;
        private CharSequence mGmtOffset;
        private long mItemId = -1;

        public Builder(TimeZone timeZone) {
            if (timeZone == null) {
@@ -124,18 +116,10 @@ public class TimeZoneInfo {
            return this;
        }

        public Builder setItemId(long itemId) {
            mItemId = itemId;
            return this;
        }

        public TimeZoneInfo build() {
            if (TextUtils.isEmpty(mGmtOffset)) {
                throw new IllegalStateException("gmtOffset must not be empty!");
            }
            if (mItemId == -1) {
                throw new IllegalStateException("ItemId not set!");
            }
            return new TimeZoneInfo(this);
        }
    }
@@ -179,8 +163,6 @@ public class TimeZoneInfo {
                            TimeZoneNames.NameType.LONG_DAYLIGHT, mNow.getTime()))
                    .setExemplarLocation(timeZoneNames.getExemplarLocationName(id))
                    .setGmtOffset(gmtOffset)
                    // TODO: move Item id to TimeZoneInfoAdapter
                    .setItemId(0)
                    .build();
        }
    }
Loading