• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings.datetime;
18 
19 import android.app.AlarmManager;
20 import android.car.drivingstate.CarUxRestrictions;
21 import android.content.Context;
22 import android.content.Intent;
23 
24 import androidx.annotation.VisibleForTesting;
25 import androidx.preference.Preference;
26 import androidx.preference.PreferenceGroup;
27 
28 import com.android.car.settings.common.FragmentController;
29 import com.android.car.settings.common.PreferenceController;
30 import com.android.settingslib.datetime.ZoneGetter;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.List;
36 import java.util.Map;
37 
38 /**
39  * Business logic which will populate the timezone options.
40  */
41 public class TimeZonePickerScreenPreferenceController extends
42         PreferenceController<PreferenceGroup> {
43 
44     private List<Preference> mZonesList;
45     @VisibleForTesting
46     AlarmManager mAlarmManager;
47 
TimeZonePickerScreenPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)48     public TimeZonePickerScreenPreferenceController(Context context, String preferenceKey,
49             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
50         super(context, preferenceKey, fragmentController, uxRestrictions);
51         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
52     }
53 
54     @Override
getPreferenceType()55     protected Class<PreferenceGroup> getPreferenceType() {
56         return PreferenceGroup.class;
57     }
58 
59     @Override
updateState(PreferenceGroup preferenceGroup)60     protected void updateState(PreferenceGroup preferenceGroup) {
61         if (mZonesList == null) {
62             constructTimeZoneList();
63         }
64         for (Preference zonePreference : mZonesList) {
65             preferenceGroup.addPreference(zonePreference);
66         }
67     }
68 
constructTimeZoneList()69     private void constructTimeZoneList() {
70         // We load all of the time zones on the UI thread. However it shouldn't be very expensive
71         // and also shouldn't take a long time. We can revisit this to setup background work and
72         // paging, if it becomes an issue.
73         List<Map<String, Object>> zones = ZoneGetter.getZonesList(getContext());
74         setZonesList(zones);
75     }
76 
77     @VisibleForTesting
setZonesList(List<Map<String, Object>> zones)78     void setZonesList(List<Map<String, Object>> zones) {
79         Collections.sort(zones, new TimeZonesComparator());
80         mZonesList = new ArrayList<>();
81         for (Map<String, Object> zone : zones) {
82             mZonesList.add(createTimeZonePreference(zone));
83         }
84     }
85 
86     /** Construct a time zone preference based on the Map object given by {@link ZoneGetter}. */
createTimeZonePreference(Map<String, Object> timeZone)87     private Preference createTimeZonePreference(Map<String, Object> timeZone) {
88         Preference preference = new Preference(getContext());
89         preference.setKey(timeZone.get(ZoneGetter.KEY_ID).toString());
90         preference.setTitle(timeZone.get(ZoneGetter.KEY_DISPLAY_LABEL).toString());
91         preference.setSummary(timeZone.get(ZoneGetter.KEY_OFFSET_LABEL).toString());
92         preference.setOnPreferenceClickListener(pref -> {
93             mAlarmManager.setTimeZone(timeZone.get(ZoneGetter.KEY_ID).toString());
94             getFragmentController().goBack();
95 
96             // Note: This is intentionally ACTION_TIME_CHANGED, not ACTION_TIMEZONE_CHANGED.
97             // Timezone change is handled by the alarm manager. This broadcast message is used
98             // to update the clock and other time related displays that the time has changed due
99             // to a change in the timezone.
100             getContext().sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED));
101             return true;
102         });
103         return preference;
104     }
105 
106     /** Compares the timezone objects returned by {@link ZoneGetter}. */
107     private static final class TimeZonesComparator implements Comparator<Map<String, Object>> {
108 
109         /** Compares timezones based on 1. offset, 2. display label/name. */
TimeZonesComparator()110         TimeZonesComparator() {
111         }
112 
113         @Override
compare(Map<String, Object> map1, Map<String, Object> map2)114         public int compare(Map<String, Object> map1, Map<String, Object> map2) {
115             int timeZoneOffsetCompare = compareWithKey(map1, map2, ZoneGetter.KEY_OFFSET);
116 
117             // If equivalent timezone offset, compare based on display label.
118             if (timeZoneOffsetCompare == 0) {
119                 return compareWithKey(map1, map2, ZoneGetter.KEY_DISPLAY_LABEL);
120             }
121 
122             return timeZoneOffsetCompare;
123         }
124 
compareWithKey(Map<String, Object> map1, Map<String, Object> map2, String comparisonKey)125         private int compareWithKey(Map<String, Object> map1, Map<String, Object> map2,
126                 String comparisonKey) {
127             Object value1 = map1.get(comparisonKey);
128             Object value2 = map2.get(comparisonKey);
129             if (!isComparable(value1) || !isComparable(value2)) {
130                 throw new IllegalArgumentException(
131                         "Cannot use Map which has values that are not Comparable");
132             }
133             return ((Comparable) value1).compareTo(value2);
134         }
135 
isComparable(Object value)136         private boolean isComparable(Object value) {
137             return (value != null) && (value instanceof Comparable);
138         }
139     }
140 }
141