DateTimeSettings.java revision f1c085899dd3d9142392b9ab7841c0e9502526a3
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.DatePickerDialog;
21import android.app.Dialog;
22import android.app.TimePickerDialog;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.SharedPreferences;
28import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
29import android.os.Bundle;
30import android.os.SystemClock;
31import android.preference.CheckBoxPreference;
32import android.preference.ListPreference;
33import android.preference.Preference;
34import android.preference.PreferenceScreen;
35import android.provider.Settings;
36import android.provider.Settings.SettingNotFoundException;
37import android.text.format.DateFormat;
38import android.widget.DatePicker;
39import android.widget.TimePicker;
40
41import java.util.Calendar;
42import java.util.Date;
43import java.util.TimeZone;
44
45public class DateTimeSettings extends SettingsPreferenceFragment
46        implements OnSharedPreferenceChangeListener,
47                TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
48
49    private static final String HOURS_12 = "12";
50    private static final String HOURS_24 = "24";
51
52    // Used for showing the current date format, which looks like "12/31/2010", "2010/12/13", etc.
53    // The date value is dummy (independent of actual date).
54    private Calendar mDummyDate;
55
56    private static final String KEY_DATE_FORMAT = "date_format";
57    private static final String KEY_AUTO_TIME = "auto_time";
58    private static final String KEY_AUTO_TIME_ZONE = "auto_zone";
59
60    private static final int DIALOG_DATEPICKER = 0;
61    private static final int DIALOG_TIMEPICKER = 1;
62
63    // have we been launched from the setup wizard?
64    protected static final String EXTRA_IS_FIRST_RUN = "firstRun";
65
66    private CheckBoxPreference mAutoTimePref;
67    private Preference mTimePref;
68    private Preference mTime24Pref;
69    private CheckBoxPreference mAutoTimeZonePref;
70    private Preference mTimeZone;
71    private Preference mDatePref;
72    private ListPreference mDateFormat;
73
74    @Override
75    public void onCreate(Bundle icicle) {
76        super.onCreate(icicle);
77
78        addPreferencesFromResource(R.xml.date_time_prefs);
79
80        initUI();
81    }
82
83    private void initUI() {
84        boolean autoTimeEnabled = getAutoState(Settings.Global.AUTO_TIME);
85        boolean autoTimeZoneEnabled = getAutoState(Settings.Global.AUTO_TIME_ZONE);
86
87        Intent intent = getActivity().getIntent();
88        boolean isFirstRun = intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
89
90        mDummyDate = Calendar.getInstance();
91
92        mAutoTimePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME);
93        mAutoTimePref.setChecked(autoTimeEnabled);
94        mAutoTimeZonePref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME_ZONE);
95        // Override auto-timezone if it's a wifi-only device or if we're still in setup wizard.
96        // TODO: Remove the wifiOnly test when auto-timezone is implemented based on wifi-location.
97        if (Utils.isWifiOnly(getActivity()) || isFirstRun) {
98            getPreferenceScreen().removePreference(mAutoTimeZonePref);
99            autoTimeZoneEnabled = false;
100        }
101        mAutoTimeZonePref.setChecked(autoTimeZoneEnabled);
102
103        mTimePref = findPreference("time");
104        mTime24Pref = findPreference("24 hour");
105        mTimeZone = findPreference("timezone");
106        mDatePref = findPreference("date");
107        mDateFormat = (ListPreference) findPreference(KEY_DATE_FORMAT);
108        if (isFirstRun) {
109            getPreferenceScreen().removePreference(mTime24Pref);
110            getPreferenceScreen().removePreference(mDateFormat);
111        }
112
113        String [] dateFormats = getResources().getStringArray(R.array.date_format_values);
114        String [] formattedDates = new String[dateFormats.length];
115        String currentFormat = getDateFormat();
116        // Initialize if DATE_FORMAT is not set in the system settings
117        // This can happen after a factory reset (or data wipe)
118        if (currentFormat == null) {
119            currentFormat = "";
120        }
121        for (int i = 0; i < formattedDates.length; i++) {
122            String formatted =
123                    DateFormat.getDateFormatForSetting(getActivity(), dateFormats[i])
124                    .format(mDummyDate.getTime());
125
126            if (dateFormats[i].length() == 0) {
127                formattedDates[i] = getResources().
128                    getString(R.string.normal_date_format, formatted);
129            } else {
130                formattedDates[i] = formatted;
131            }
132        }
133
134        mDateFormat.setEntries(formattedDates);
135        mDateFormat.setEntryValues(R.array.date_format_values);
136        mDateFormat.setValue(currentFormat);
137
138        mTimePref.setEnabled(!autoTimeEnabled);
139        mDatePref.setEnabled(!autoTimeEnabled);
140        mTimeZone.setEnabled(!autoTimeZoneEnabled);
141    }
142
143    @Override
144    public void onResume() {
145        super.onResume();
146
147        getPreferenceScreen().getSharedPreferences()
148                .registerOnSharedPreferenceChangeListener(this);
149
150        ((CheckBoxPreference)mTime24Pref).setChecked(is24Hour());
151
152        // Register for time ticks and other reasons for time change
153        IntentFilter filter = new IntentFilter();
154        filter.addAction(Intent.ACTION_TIME_TICK);
155        filter.addAction(Intent.ACTION_TIME_CHANGED);
156        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
157        getActivity().registerReceiver(mIntentReceiver, filter, null, null);
158
159        updateTimeAndDateDisplay(getActivity());
160    }
161
162    @Override
163    public void onPause() {
164        super.onPause();
165        getActivity().unregisterReceiver(mIntentReceiver);
166        getPreferenceScreen().getSharedPreferences()
167                .unregisterOnSharedPreferenceChangeListener(this);
168    }
169
170    public void updateTimeAndDateDisplay(Context context) {
171        java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(context);
172        final Calendar now = Calendar.getInstance();
173        mDummyDate.setTimeZone(now.getTimeZone());
174        // We use December 31st because it's unambiguous when demonstrating the date format.
175        // We use 13:00 so we can demonstrate the 12/24 hour options.
176        mDummyDate.set(now.get(Calendar.YEAR), 11, 31, 13, 0, 0);
177        Date dummyDate = mDummyDate.getTime();
178        mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime()));
179        mTimeZone.setSummary(getTimeZoneText(now.getTimeZone()));
180        mDatePref.setSummary(shortDateFormat.format(now.getTime()));
181        mDateFormat.setSummary(shortDateFormat.format(dummyDate));
182        mTime24Pref.setSummary(DateFormat.getTimeFormat(getActivity()).format(dummyDate));
183    }
184
185    @Override
186    public void onDateSet(DatePicker view, int year, int month, int day) {
187        setDate(year, month, day);
188        final Activity activity = getActivity();
189        if (activity != null) {
190            updateTimeAndDateDisplay(activity);
191        }
192    }
193
194    @Override
195    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
196        setTime(hourOfDay, minute);
197        final Activity activity = getActivity();
198        if (activity != null) {
199            updateTimeAndDateDisplay(activity);
200        }
201
202        // We don't need to call timeUpdated() here because the TIME_CHANGED
203        // broadcast is sent by the AlarmManager as a side effect of setting the
204        // SystemClock time.
205    }
206
207    @Override
208    public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
209        if (key.equals(KEY_DATE_FORMAT)) {
210            String format = preferences.getString(key,
211                    getResources().getString(R.string.default_date_format));
212            Settings.System.putString(getContentResolver(),
213                    Settings.System.DATE_FORMAT, format);
214            updateTimeAndDateDisplay(getActivity());
215        } else if (key.equals(KEY_AUTO_TIME)) {
216            boolean autoEnabled = preferences.getBoolean(key, true);
217            Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME,
218                    autoEnabled ? 1 : 0);
219            mTimePref.setEnabled(!autoEnabled);
220            mDatePref.setEnabled(!autoEnabled);
221        } else if (key.equals(KEY_AUTO_TIME_ZONE)) {
222            boolean autoZoneEnabled = preferences.getBoolean(key, true);
223            Settings.Global.putInt(
224                    getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
225            mTimeZone.setEnabled(!autoZoneEnabled);
226        }
227    }
228
229    @Override
230    public Dialog onCreateDialog(int id) {
231        Dialog d;
232
233        switch (id) {
234        case DIALOG_DATEPICKER: {
235            final Calendar calendar = Calendar.getInstance();
236            d = new DatePickerDialog(
237                getActivity(),
238                this,
239                calendar.get(Calendar.YEAR),
240                calendar.get(Calendar.MONTH),
241                calendar.get(Calendar.DAY_OF_MONTH));
242            break;
243        }
244        case DIALOG_TIMEPICKER: {
245            final Calendar calendar = Calendar.getInstance();
246            d = new TimePickerDialog(
247                    getActivity(),
248                    this,
249                    calendar.get(Calendar.HOUR_OF_DAY),
250                    calendar.get(Calendar.MINUTE),
251                    DateFormat.is24HourFormat(getActivity()));
252            break;
253        }
254        default:
255            d = null;
256            break;
257        }
258
259        return d;
260    }
261
262    /*
263    @Override
264    public void onPrepareDialog(int id, Dialog d) {
265        switch (id) {
266        case DIALOG_DATEPICKER: {
267            DatePickerDialog datePicker = (DatePickerDialog)d;
268            final Calendar calendar = Calendar.getInstance();
269            datePicker.updateDate(
270                    calendar.get(Calendar.YEAR),
271                    calendar.get(Calendar.MONTH),
272                    calendar.get(Calendar.DAY_OF_MONTH));
273            break;
274        }
275        case DIALOG_TIMEPICKER: {
276            TimePickerDialog timePicker = (TimePickerDialog)d;
277            final Calendar calendar = Calendar.getInstance();
278            timePicker.updateTime(
279                    calendar.get(Calendar.HOUR_OF_DAY),
280                    calendar.get(Calendar.MINUTE));
281            break;
282        }
283        default:
284            break;
285        }
286    }
287    */
288    @Override
289    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
290        if (preference == mDatePref) {
291            showDialog(DIALOG_DATEPICKER);
292        } else if (preference == mTimePref) {
293            // The 24-hour mode may have changed, so recreate the dialog
294            removeDialog(DIALOG_TIMEPICKER);
295            showDialog(DIALOG_TIMEPICKER);
296        } else if (preference == mTime24Pref) {
297            set24Hour(((CheckBoxPreference)mTime24Pref).isChecked());
298            updateTimeAndDateDisplay(getActivity());
299            timeUpdated();
300        }
301        return super.onPreferenceTreeClick(preferenceScreen, preference);
302    }
303
304    @Override
305    public void onActivityResult(int requestCode, int resultCode,
306            Intent data) {
307        updateTimeAndDateDisplay(getActivity());
308    }
309
310    private void timeUpdated() {
311        Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
312        getActivity().sendBroadcast(timeChanged);
313    }
314
315    /*  Get & Set values from the system settings  */
316
317    private boolean is24Hour() {
318        return DateFormat.is24HourFormat(getActivity());
319    }
320
321    private void set24Hour(boolean is24Hour) {
322        Settings.System.putString(getContentResolver(),
323                Settings.System.TIME_12_24,
324                is24Hour? HOURS_24 : HOURS_12);
325    }
326
327    private String getDateFormat() {
328        return Settings.System.getString(getContentResolver(),
329                Settings.System.DATE_FORMAT);
330    }
331
332    private boolean getAutoState(String name) {
333        try {
334            return Settings.Global.getInt(getContentResolver(), name) > 0;
335        } catch (SettingNotFoundException snfe) {
336            return false;
337        }
338    }
339
340    /* package */ static void setDate(int year, int month, int day) {
341        Calendar c = Calendar.getInstance();
342
343        c.set(Calendar.YEAR, year);
344        c.set(Calendar.MONTH, month);
345        c.set(Calendar.DAY_OF_MONTH, day);
346        long when = c.getTimeInMillis();
347
348        if (when / 1000 < Integer.MAX_VALUE) {
349            SystemClock.setCurrentTimeMillis(when);
350        }
351    }
352
353    /* package */ static void setTime(int hourOfDay, int minute) {
354        Calendar c = Calendar.getInstance();
355
356        c.set(Calendar.HOUR_OF_DAY, hourOfDay);
357        c.set(Calendar.MINUTE, minute);
358        c.set(Calendar.SECOND, 0);
359        c.set(Calendar.MILLISECOND, 0);
360        long when = c.getTimeInMillis();
361
362        if (when / 1000 < Integer.MAX_VALUE) {
363            SystemClock.setCurrentTimeMillis(when);
364        }
365    }
366
367    /*  Helper routines to format timezone */
368
369    /* package */ static String getTimeZoneText(TimeZone tz) {
370        // Similar to new SimpleDateFormat("'GMT'Z, zzzz").format(new Date()), but
371        // we want "GMT-03:00" rather than "GMT-0300".
372        Date now = new Date();
373        return formatOffset(new StringBuilder(), tz, now).
374            append(", ").
375            append(tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG)).toString();
376    }
377
378    private static StringBuilder formatOffset(StringBuilder sb, TimeZone tz, Date d) {
379        int off = tz.getOffset(d.getTime()) / 1000 / 60;
380
381        sb.append("GMT");
382        if (off < 0) {
383            sb.append('-');
384            off = -off;
385        } else {
386            sb.append('+');
387        }
388
389        int hours = off / 60;
390        int minutes = off % 60;
391
392        sb.append((char) ('0' + hours / 10));
393        sb.append((char) ('0' + hours % 10));
394
395        sb.append(':');
396
397        sb.append((char) ('0' + minutes / 10));
398        sb.append((char) ('0' + minutes % 10));
399
400        return sb;
401    }
402
403    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
404        @Override
405        public void onReceive(Context context, Intent intent) {
406            final Activity activity = getActivity();
407            if (activity != null) {
408                updateTimeAndDateDisplay(activity);
409            }
410        }
411    };
412}
413