platform-packages-apps-Settings / src / com / android / settings / datetime / timezone / RegionZonePicker.java
RegionZonePicker.java
Raw
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.datetime.timezone;

import android.content.Intent;
import android.icu.text.Collator;
import android.icu.text.LocaleDisplayNames;
import android.icu.util.TimeZone;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.datetime.timezone.model.FilteredCountryTimeZones;
import com.android.settings.datetime.timezone.model.TimeZoneData;

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.TreeSet;

/**
 * Given a region, render a list of time zone {@class TimeZoneInfo} into a list view.
 */
public class RegionZonePicker extends BaseTimeZoneInfoPicker {

    public static final String EXTRA_REGION_ID =
            "com.android.settings.datetime.timezone.region_id";

    private @Nullable String mRegionName;

    public RegionZonePicker() {
        super(R.string.date_time_set_timezone_title, R.string.search_settings, true, false);
    }

    @Override
    public int getMetricsCategory() {
        return MetricsProto.MetricsEvent.SETTINGS_ZONE_PICKER_TIME_ZONE;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final LocaleDisplayNames localeDisplayNames = LocaleDisplayNames.getInstance(getLocale());
        final String regionId =
                getArguments() == null ? null : getArguments().getString(EXTRA_REGION_ID);
        mRegionName = regionId == null ? null : localeDisplayNames.regionDisplayName(regionId);
    }

    @Override
    protected @Nullable CharSequence getHeaderText() {
        return mRegionName;
    }

    /**
     * Add the extra region id into the result.
     */
    @Override
    protected Intent prepareResultData(TimeZoneInfo selectedTimeZoneInfo) {
        final Intent intent = super.prepareResultData(selectedTimeZoneInfo);
        intent.putExtra(EXTRA_RESULT_REGION_ID, getArguments().getString(EXTRA_REGION_ID));
        return intent;
    }

    @Override
    public List<TimeZoneInfo> getAllTimeZoneInfos(TimeZoneData timeZoneData) {
        if (getArguments() == null) {
            Log.e(TAG, "getArguments() == null");
            getActivity().finish();
            return Collections.emptyList();
        }
        String regionId = getArguments().getString(EXTRA_REGION_ID);

        FilteredCountryTimeZones filteredCountryTimeZones = timeZoneData.lookupCountryTimeZones(
                regionId);
        if (filteredCountryTimeZones == null) {
            Log.e(TAG, "region id is not valid: " + regionId);
            getActivity().finish();
            return Collections.emptyList();
        }

        // It could be a timely operations if there are many time zones. A region in time zone data
        // contains a maximum of 29 time zones currently. It may change in the future, but it's
        // unlikely to be changed drastically.
        return getRegionTimeZoneInfo(filteredCountryTimeZones.getTimeZoneIds());
    }

    /**
     * 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> getRegionTimeZoneInfo(Collection<String> timeZoneIds) {
        final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(),
                new Date());
        final TreeSet<TimeZoneInfo> timeZoneInfos =
                new TreeSet<>(new TimeZoneInfoComparator(Collator.getInstance(getLocale()),
                        new Date()));

        for (final String timeZoneId : timeZoneIds) {
            final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
            // Skip time zone ICU isn't aware.
            if (timeZone.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
                continue;
            }
            timeZoneInfos.add(formatter.format(timeZone));
        }
        return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
    }

    @VisibleForTesting
    static class TimeZoneInfoComparator implements Comparator<TimeZoneInfo> {
        private Collator mCollator;
        private final Date mNow;

        @VisibleForTesting
        TimeZoneInfoComparator(Collator collator, Date now) {
            mCollator = collator;
            mNow = now;
        }

        @Override
        public int compare(TimeZoneInfo tzi1, TimeZoneInfo tzi2) {
            int result = Integer.compare(tzi1.getTimeZone().getOffset(mNow.getTime()),
                    tzi2.getTimeZone().getOffset(mNow.getTime()));
            if (result == 0) {
                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;
        }
    }
}