| /* |
| * 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<PreferenceGroup> { |
| |
| private List<Preference> 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<PreferenceGroup> 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<Map<String, Object>> zones = ZoneGetter.getZonesList(getContext()); |
| setZonesList(zones); |
| } |
| |
| @VisibleForTesting |
| void setZonesList(List<Map<String, Object>> zones) { |
| Collections.sort(zones, new TimeZonesComparator()); |
| mZonesList = new ArrayList<>(); |
| for (Map<String, Object> zone : zones) { |
| mZonesList.add(createTimeZonePreference(zone)); |
| } |
| } |
| |
| /** Construct a time zone preference based on the Map object given by {@link ZoneGetter}. */ |
| private Preference createTimeZonePreference(Map<String, Object> 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<Map<String, Object>> { |
| |
| /** Compares timezones based on 1. offset, 2. display label/name. */ |
| TimeZonesComparator() { |
| } |
| |
| @Override |
| public int compare(Map<String, Object> map1, Map<String, Object> 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<String, Object> map1, Map<String, Object> 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); |
| } |
| } |
| } |