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