/* * 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.car.settings.datetime; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneDetector; import android.car.drivingstate.CarUxRestrictions; import android.content.Context; import android.content.Intent; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import com.android.car.settings.common.FragmentController; import com.android.car.settings.common.PreferenceController; import com.android.car.ui.preference.CarUiPreference; import com.android.settingslib.datetime.ZoneGetter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Business logic which will populate the timezone options. */ public class TimeZonePickerScreenPreferenceController extends PreferenceController { private List mZonesList; @VisibleForTesting TimeZoneDetector mTimeZoneDetector; public TimeZonePickerScreenPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions) { super(context, preferenceKey, fragmentController, uxRestrictions); mTimeZoneDetector = getContext().getSystemService(TimeZoneDetector.class); } @Override protected Class getPreferenceType() { return PreferenceGroup.class; } @Override protected void onCreateInternal() { super.onCreateInternal(); setClickableWhileDisabled(getPreference(), /* clickable= */ true, p -> DatetimeUtils.runClickableWhileDisabled(getContext(), getFragmentController())); } @Override protected void updateState(PreferenceGroup preferenceGroup) { if (mZonesList == null) { constructTimeZoneList(); } for (Preference zonePreference : mZonesList) { preferenceGroup.addPreference(zonePreference); } } @Override public int getDefaultAvailabilityStatus() { return DatetimeUtils.getAvailabilityStatus(getContext()); } private void constructTimeZoneList() { // We load all of the time zones on the UI thread. However it shouldn't be very expensive // and also shouldn't take a long time. We can revisit this to setup background work and // paging, if it becomes an issue. List> zones = ZoneGetter.getZonesList(getContext()); setZonesList(zones); } @VisibleForTesting void setZonesList(List> zones) { Collections.sort(zones, new TimeZonesComparator()); mZonesList = new ArrayList<>(); for (Map zone : zones) { mZonesList.add(createTimeZonePreference(zone)); } } /** Construct a time zone preference based on the Map object given by {@link ZoneGetter}. */ private Preference createTimeZonePreference(Map timeZone) { CarUiPreference preference = new CarUiPreference(getContext()); preference.setKey(timeZone.get(ZoneGetter.KEY_ID).toString()); preference.setTitle(timeZone.get(ZoneGetter.KEY_DISPLAY_LABEL).toString()); preference.setSummary(timeZone.get(ZoneGetter.KEY_OFFSET_LABEL).toString()); preference.setOnPreferenceClickListener(pref -> { String tzId = timeZone.get(ZoneGetter.KEY_ID).toString(); ManualTimeZoneSuggestion suggestion = TimeZoneDetector.createManualTimeZoneSuggestion( tzId, "Settings: Set time zone"); mTimeZoneDetector.suggestManualTimeZone(suggestion); getFragmentController().goBack(); // Note: This is intentionally ACTION_TIME_CHANGED, not ACTION_TIMEZONE_CHANGED. // Timezone change is handled by the alarm manager. This broadcast message is used // to update the clock and other time related displays that the time has changed due // to a change in the timezone. getContext().sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED)); return true; }); return preference; } /** Compares the timezone objects returned by {@link ZoneGetter}. */ private static final class TimeZonesComparator implements Comparator> { /** Compares timezones based on 1. offset, 2. display label/name. */ TimeZonesComparator() { } @Override public int compare(Map map1, Map map2) { int timeZoneOffsetCompare = compareWithKey(map1, map2, ZoneGetter.KEY_OFFSET); // If equivalent timezone offset, compare based on display label. if (timeZoneOffsetCompare == 0) { return compareWithKey(map1, map2, ZoneGetter.KEY_DISPLAY_LABEL); } return timeZoneOffsetCompare; } private int compareWithKey(Map map1, Map map2, String comparisonKey) { Object value1 = map1.get(comparisonKey); Object value2 = map2.get(comparisonKey); if (!isComparable(value1) || !isComparable(value2)) { throw new IllegalArgumentException( "Cannot use Map which has values that are not Comparable"); } return ((Comparable) value1).compareTo(value2); } private boolean isComparable(Object value) { return (value != null) && (value instanceof Comparable); } } }