1 /*
2  * Copyright (C) 2008 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.settings;
18 
19 import android.app.Activity;
20 import android.app.AlarmManager;
21 import android.app.DatePickerDialog;
22 import android.app.DatePickerDialog.OnDateSetListener;
23 import android.app.Dialog;
24 import android.app.TimePickerDialog;
25 import android.app.TimePickerDialog.OnTimeSetListener;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.os.Bundle;
31 import android.provider.Settings;
32 import android.provider.Settings.SettingNotFoundException;
33 import android.support.v14.preference.SwitchPreference;
34 import android.support.v7.preference.Preference;
35 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
36 import android.text.format.DateFormat;
37 import android.widget.DatePicker;
38 import android.widget.TimePicker;
39 import com.android.internal.logging.MetricsProto.MetricsEvent;
40 import com.android.settings.dashboard.SummaryLoader;
41 import com.android.settingslib.RestrictedLockUtils;
42 import com.android.settingslib.RestrictedSwitchPreference;
43 import com.android.settingslib.datetime.ZoneGetter;
44 
45 import java.util.Calendar;
46 import java.util.Date;
47 
48 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
49 
50 public class DateTimeSettings extends SettingsPreferenceFragment
51         implements OnTimeSetListener, OnDateSetListener, OnPreferenceChangeListener {
52 
53     private static final String HOURS_12 = "12";
54     private static final String HOURS_24 = "24";
55 
56     // Used for showing the current date format, which looks like "12/31/2010", "2010/12/13", etc.
57     // The date value is dummy (independent of actual date).
58     private Calendar mDummyDate;
59 
60     private static final String KEY_AUTO_TIME = "auto_time";
61     private static final String KEY_AUTO_TIME_ZONE = "auto_zone";
62 
63     private static final int DIALOG_DATEPICKER = 0;
64     private static final int DIALOG_TIMEPICKER = 1;
65 
66     // have we been launched from the setup wizard?
67     protected static final String EXTRA_IS_FIRST_RUN = "firstRun";
68 
69     // Minimum time is Nov 5, 2007, 0:00.
70     private static final long MIN_DATE = 1194220800000L;
71 
72     private RestrictedSwitchPreference mAutoTimePref;
73     private Preference mTimePref;
74     private Preference mTime24Pref;
75     private SwitchPreference mAutoTimeZonePref;
76     private Preference mTimeZone;
77     private Preference mDatePref;
78 
79     @Override
getMetricsCategory()80     protected int getMetricsCategory() {
81         return MetricsEvent.DATE_TIME;
82     }
83 
84     @Override
onCreate(Bundle icicle)85     public void onCreate(Bundle icicle) {
86         super.onCreate(icicle);
87 
88         addPreferencesFromResource(R.xml.date_time_prefs);
89 
90         initUI();
91     }
92 
initUI()93     private void initUI() {
94         boolean autoTimeEnabled = getAutoState(Settings.Global.AUTO_TIME);
95         boolean autoTimeZoneEnabled = getAutoState(Settings.Global.AUTO_TIME_ZONE);
96 
97         mAutoTimePref = (RestrictedSwitchPreference) findPreference(KEY_AUTO_TIME);
98         mAutoTimePref.setOnPreferenceChangeListener(this);
99         EnforcedAdmin admin = RestrictedLockUtils.checkIfAutoTimeRequired(getActivity());
100         mAutoTimePref.setDisabledByAdmin(admin);
101 
102         Intent intent = getActivity().getIntent();
103         boolean isFirstRun = intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
104 
105         mDummyDate = Calendar.getInstance();
106 
107         // If device admin requires auto time device policy manager will set
108         // Settings.Global.AUTO_TIME to true. Note that this app listens to that change.
109         mAutoTimePref.setChecked(autoTimeEnabled);
110         mAutoTimeZonePref = (SwitchPreference) findPreference(KEY_AUTO_TIME_ZONE);
111         mAutoTimeZonePref.setOnPreferenceChangeListener(this);
112         // Override auto-timezone if it's a wifi-only device or if we're still in setup wizard.
113         // TODO: Remove the wifiOnly test when auto-timezone is implemented based on wifi-location.
114         if (Utils.isWifiOnly(getActivity()) || isFirstRun) {
115             getPreferenceScreen().removePreference(mAutoTimeZonePref);
116             autoTimeZoneEnabled = false;
117         }
118         mAutoTimeZonePref.setChecked(autoTimeZoneEnabled);
119 
120         mTimePref = findPreference("time");
121         mTime24Pref = findPreference("24 hour");
122         mTimeZone = findPreference("timezone");
123         mDatePref = findPreference("date");
124         if (isFirstRun) {
125             getPreferenceScreen().removePreference(mTime24Pref);
126         }
127 
128         mTimePref.setEnabled(!autoTimeEnabled);
129         mDatePref.setEnabled(!autoTimeEnabled);
130         mTimeZone.setEnabled(!autoTimeZoneEnabled);
131     }
132 
133     @Override
onResume()134     public void onResume() {
135         super.onResume();
136 
137         ((SwitchPreference)mTime24Pref).setChecked(is24Hour());
138 
139         // Register for time ticks and other reasons for time change
140         IntentFilter filter = new IntentFilter();
141         filter.addAction(Intent.ACTION_TIME_TICK);
142         filter.addAction(Intent.ACTION_TIME_CHANGED);
143         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
144         getActivity().registerReceiver(mIntentReceiver, filter, null, null);
145 
146         updateTimeAndDateDisplay(getActivity());
147     }
148 
149     @Override
onPause()150     public void onPause() {
151         super.onPause();
152         getActivity().unregisterReceiver(mIntentReceiver);
153     }
154 
updateTimeAndDateDisplay(Context context)155     public void updateTimeAndDateDisplay(Context context) {
156         final Calendar now = Calendar.getInstance();
157         mDummyDate.setTimeZone(now.getTimeZone());
158         // We use December 31st because it's unambiguous when demonstrating the date format.
159         // We use 13:00 so we can demonstrate the 12/24 hour options.
160         mDummyDate.set(now.get(Calendar.YEAR), 11, 31, 13, 0, 0);
161         Date dummyDate = mDummyDate.getTime();
162         mDatePref.setSummary(DateFormat.getLongDateFormat(context).format(now.getTime()));
163         mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime()));
164         mTimeZone.setSummary(ZoneGetter.getTimeZoneOffsetAndName(now.getTimeZone(), now.getTime()));
165         mTime24Pref.setSummary(DateFormat.getTimeFormat(getActivity()).format(dummyDate));
166     }
167 
168     @Override
onDateSet(DatePicker view, int year, int month, int day)169     public void onDateSet(DatePicker view, int year, int month, int day) {
170         final Activity activity = getActivity();
171         if (activity != null) {
172             setDate(activity, year, month, day);
173             updateTimeAndDateDisplay(activity);
174         }
175     }
176 
177     @Override
onTimeSet(TimePicker view, int hourOfDay, int minute)178     public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
179         final Activity activity = getActivity();
180         if (activity != null) {
181             setTime(activity, hourOfDay, minute);
182             updateTimeAndDateDisplay(activity);
183         }
184 
185         // We don't need to call timeUpdated() here because the TIME_CHANGED
186         // broadcast is sent by the AlarmManager as a side effect of setting the
187         // SystemClock time.
188     }
189 
190     @Override
onPreferenceChange(Preference preference, Object newValue)191     public boolean onPreferenceChange(Preference preference, Object newValue) {
192         if (preference.getKey().equals(KEY_AUTO_TIME)) {
193             boolean autoEnabled = (Boolean) newValue;
194             Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME,
195                     autoEnabled ? 1 : 0);
196             mTimePref.setEnabled(!autoEnabled);
197             mDatePref.setEnabled(!autoEnabled);
198         } else if (preference.getKey().equals(KEY_AUTO_TIME_ZONE)) {
199             boolean autoZoneEnabled = (Boolean) newValue;
200             Settings.Global.putInt(
201                     getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
202             mTimeZone.setEnabled(!autoZoneEnabled);
203         }
204         return true;
205     }
206 
207     @Override
onCreateDialog(int id)208     public Dialog onCreateDialog(int id) {
209         final Calendar calendar = Calendar.getInstance();
210         switch (id) {
211         case DIALOG_DATEPICKER:
212             DatePickerDialog d = new DatePickerDialog(
213                     getActivity(),
214                     this,
215                     calendar.get(Calendar.YEAR),
216                     calendar.get(Calendar.MONTH),
217                     calendar.get(Calendar.DAY_OF_MONTH));
218             configureDatePicker(d.getDatePicker());
219             return d;
220         case DIALOG_TIMEPICKER:
221             return new TimePickerDialog(
222                     getActivity(),
223                     this,
224                     calendar.get(Calendar.HOUR_OF_DAY),
225                     calendar.get(Calendar.MINUTE),
226                     DateFormat.is24HourFormat(getActivity()));
227         default:
228             throw new IllegalArgumentException();
229         }
230     }
231 
configureDatePicker(DatePicker datePicker)232     static void configureDatePicker(DatePicker datePicker) {
233         // The system clock can't represent dates outside this range.
234         Calendar t = Calendar.getInstance();
235         t.clear();
236         t.set(1970, Calendar.JANUARY, 1);
237         datePicker.setMinDate(t.getTimeInMillis());
238         t.clear();
239         t.set(2037, Calendar.DECEMBER, 31);
240         datePicker.setMaxDate(t.getTimeInMillis());
241     }
242 
243     /*
244     @Override
245     public void onPrepareDialog(int id, Dialog d) {
246         switch (id) {
247         case DIALOG_DATEPICKER: {
248             DatePickerDialog datePicker = (DatePickerDialog)d;
249             final Calendar calendar = Calendar.getInstance();
250             datePicker.updateDate(
251                     calendar.get(Calendar.YEAR),
252                     calendar.get(Calendar.MONTH),
253                     calendar.get(Calendar.DAY_OF_MONTH));
254             break;
255         }
256         case DIALOG_TIMEPICKER: {
257             TimePickerDialog timePicker = (TimePickerDialog)d;
258             final Calendar calendar = Calendar.getInstance();
259             timePicker.updateTime(
260                     calendar.get(Calendar.HOUR_OF_DAY),
261                     calendar.get(Calendar.MINUTE));
262             break;
263         }
264         default:
265             break;
266         }
267     }
268     */
269     @Override
onPreferenceTreeClick(Preference preference)270     public boolean onPreferenceTreeClick(Preference preference) {
271         if (preference == mDatePref) {
272             showDialog(DIALOG_DATEPICKER);
273         } else if (preference == mTimePref) {
274             // The 24-hour mode may have changed, so recreate the dialog
275             removeDialog(DIALOG_TIMEPICKER);
276             showDialog(DIALOG_TIMEPICKER);
277         } else if (preference == mTime24Pref) {
278             final boolean is24Hour = ((SwitchPreference)mTime24Pref).isChecked();
279             set24Hour(is24Hour);
280             updateTimeAndDateDisplay(getActivity());
281             timeUpdated(is24Hour);
282         }
283         return super.onPreferenceTreeClick(preference);
284     }
285 
286     @Override
onActivityResult(int requestCode, int resultCode, Intent data)287     public void onActivityResult(int requestCode, int resultCode,
288             Intent data) {
289         updateTimeAndDateDisplay(getActivity());
290     }
291 
timeUpdated(boolean is24Hour)292     private void timeUpdated(boolean is24Hour) {
293         Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
294         timeChanged.putExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, is24Hour);
295         getActivity().sendBroadcast(timeChanged);
296     }
297 
298     /*  Get & Set values from the system settings  */
299 
is24Hour()300     private boolean is24Hour() {
301         return DateFormat.is24HourFormat(getActivity());
302     }
303 
set24Hour(boolean is24Hour)304     private void set24Hour(boolean is24Hour) {
305         Settings.System.putString(getContentResolver(),
306                 Settings.System.TIME_12_24,
307                 is24Hour? HOURS_24 : HOURS_12);
308     }
309 
getAutoState(String name)310     private boolean getAutoState(String name) {
311         try {
312             return Settings.Global.getInt(getContentResolver(), name) > 0;
313         } catch (SettingNotFoundException snfe) {
314             return false;
315         }
316     }
317 
setDate(Context context, int year, int month, int day)318     /* package */ static void setDate(Context context, int year, int month, int day) {
319         Calendar c = Calendar.getInstance();
320 
321         c.set(Calendar.YEAR, year);
322         c.set(Calendar.MONTH, month);
323         c.set(Calendar.DAY_OF_MONTH, day);
324         long when = Math.max(c.getTimeInMillis(), MIN_DATE);
325 
326         if (when / 1000 < Integer.MAX_VALUE) {
327             ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
328         }
329     }
330 
setTime(Context context, int hourOfDay, int minute)331     /* package */ static void setTime(Context context, int hourOfDay, int minute) {
332         Calendar c = Calendar.getInstance();
333 
334         c.set(Calendar.HOUR_OF_DAY, hourOfDay);
335         c.set(Calendar.MINUTE, minute);
336         c.set(Calendar.SECOND, 0);
337         c.set(Calendar.MILLISECOND, 0);
338         long when = Math.max(c.getTimeInMillis(), MIN_DATE);
339 
340         if (when / 1000 < Integer.MAX_VALUE) {
341             ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when);
342         }
343     }
344 
345     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
346         @Override
347         public void onReceive(Context context, Intent intent) {
348             final Activity activity = getActivity();
349             if (activity != null) {
350                 updateTimeAndDateDisplay(activity);
351             }
352         }
353     };
354 
355     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
356 
357         private final Context mContext;
358         private final SummaryLoader mSummaryLoader;
359 
SummaryProvider(Context context, SummaryLoader summaryLoader)360         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
361             mContext = context;
362             mSummaryLoader = summaryLoader;
363         }
364 
365         @Override
setListening(boolean listening)366         public void setListening(boolean listening) {
367             if (listening) {
368                 final Calendar now = Calendar.getInstance();
369                 mSummaryLoader.setSummary(this, ZoneGetter.getTimeZoneOffsetAndName(
370                         now.getTimeZone(), now.getTime()));
371             }
372         }
373     }
374 
375     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
376             = new SummaryLoader.SummaryProviderFactory() {
377         @Override
378         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
379                                                                    SummaryLoader summaryLoader) {
380             return new SummaryProvider(activity, summaryLoader);
381         }
382     };
383 }
384