DateTimeSettings.java revision 797d620084df296b1d1d3e3c03d519fdf916f2d3
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
122        // Prevents duplicated values on date format selector.
123        mDummyDate.set(mDummyDate.get(Calendar.YEAR), mDummyDate.DECEMBER, 31, 13, 0, 0);
124
125        for (int i = 0; i < formattedDates.length; i++) {
126            String formatted =
127                    DateFormat.getDateFormatForSetting(getActivity(), dateFormats[i])
128                    .format(mDummyDate.getTime());
129
130            if (dateFormats[i].length() == 0) {
131                formattedDates[i] = getResources().
132                    getString(R.string.normal_date_format, formatted);
133            } else {
134                formattedDates[i] = formatted;
135            }
136        }
137
138        mDateFormat.setEntries(formattedDates);
139        mDateFormat.setEntryValues(R.array.date_format_values);
140        mDateFormat.setValue(currentFormat);
141
142        mTimePref.setEnabled(!autoTimeEnabled);
143        mDatePref.setEnabled(!autoTimeEnabled);
144        mTimeZone.setEnabled(!autoTimeZoneEnabled);
145    }
146
147    @Override
148    public void onResume() {
149        super.onResume();
150
151        getPreferenceScreen().getSharedPreferences()
152                .registerOnSharedPreferenceChangeListener(this);
153
154        ((CheckBoxPreference)mTime24Pref).setChecked(is24Hour());
155
156        // Register for time ticks and other reasons for time change
157        IntentFilter filter = new IntentFilter();
158        filter.addAction(Intent.ACTION_TIME_TICK);
159        filter.addAction(Intent.ACTION_TIME_CHANGED);
160        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
161        getActivity().registerReceiver(mIntentReceiver, filter, null, null);
162
163        updateTimeAndDateDisplay(getActivity());
164    }
165
166    @Override
167    public void onPause() {
168        super.onPause();
169        getActivity().unregisterReceiver(mIntentReceiver);
170        getPreferenceScreen().getSharedPreferences()
171                .unregisterOnSharedPreferenceChangeListener(this);
172    }
173
174    public void updateTimeAndDateDisplay(Context context) {
175        java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(context);
176        final Calendar now = Calendar.getInstance();
177        mDummyDate.setTimeZone(now.getTimeZone());
178        // We use December 31st because it's unambiguous when demonstrating the date format.
179        // We use 13:00 so we can demonstrate the 12/24 hour options.
180        mDummyDate.set(now.get(Calendar.YEAR), 11, 31, 13, 0, 0);
181        Date dummyDate = mDummyDate.getTime();
182        mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime()));
183        mTimeZone.setSummary(getTimeZoneText(now.getTimeZone()));
184        mDatePref.setSummary(shortDateFormat.format(now.getTime()));
185        mDateFormat.setSummary(shortDateFormat.format(dummyDate));
186        mTime24Pref.setSummary(DateFormat.getTimeFormat(getActivity()).format(dummyDate));
187    }
188
189    @Override
190    public void onDateSet(DatePicker view, int year, int month, int day) {
191        setDate(year, month, day);
192        final Activity activity = getActivity();
193        if (activity != null) {
194            updateTimeAndDateDisplay(activity);
195        }
196    }
197
198    @Override
199    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
200        setTime(hourOfDay, minute);
201        final Activity activity = getActivity();
202        if (activity != null) {
203            updateTimeAndDateDisplay(activity);
204        }
205
206        // We don't need to call timeUpdated() here because the TIME_CHANGED
207        // broadcast is sent by the AlarmManager as a side effect of setting the
208        // SystemClock time.
209    }
210
211    @Override
212    public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
213        if (key.equals(KEY_DATE_FORMAT)) {
214            String format = preferences.getString(key,
215                    getResources().getString(R.string.default_date_format));
216            Settings.System.putString(getContentResolver(),
217                    Settings.System.DATE_FORMAT, format);
218            updateTimeAndDateDisplay(getActivity());
219        } else if (key.equals(KEY_AUTO_TIME)) {
220            boolean autoEnabled = preferences.getBoolean(key, true);
221            Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME,
222                    autoEnabled ? 1 : 0);
223            mTimePref.setEnabled(!autoEnabled);
224            mDatePref.setEnabled(!autoEnabled);
225        } else if (key.equals(KEY_AUTO_TIME_ZONE)) {
226            boolean autoZoneEnabled = preferences.getBoolean(key, true);
227            Settings.Global.putInt(
228                    getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);
229            mTimeZone.setEnabled(!autoZoneEnabled);
230        }
231    }
232
233    @Override
234    public Dialog onCreateDialog(int id) {
235        Dialog d;
236
237        switch (id) {
238        case DIALOG_DATEPICKER: {
239            final Calendar calendar = Calendar.getInstance();
240            d = new DatePickerDialog(
241                getActivity(),
242                this,
243                calendar.get(Calendar.YEAR),
244                calendar.get(Calendar.MONTH),
245                calendar.get(Calendar.DAY_OF_MONTH));
246            break;
247        }
248        case DIALOG_TIMEPICKER: {
249            final Calendar calendar = Calendar.getInstance();
250            d = new TimePickerDialog(
251                    getActivity(),
252                    this,
253                    calendar.get(Calendar.HOUR_OF_DAY),
254                    calendar.get(Calendar.MINUTE),
255                    DateFormat.is24HourFormat(getActivity()));
256            break;
257        }
258        default:
259            d = null;
260            break;
261        }
262
263        return d;
264    }
265
266    /*
267    @Override
268    public void onPrepareDialog(int id, Dialog d) {
269        switch (id) {
270        case DIALOG_DATEPICKER: {
271            DatePickerDialog datePicker = (DatePickerDialog)d;
272            final Calendar calendar = Calendar.getInstance();
273            datePicker.updateDate(
274                    calendar.get(Calendar.YEAR),
275                    calendar.get(Calendar.MONTH),
276                    calendar.get(Calendar.DAY_OF_MONTH));
277            break;
278        }
279        case DIALOG_TIMEPICKER: {
280            TimePickerDialog timePicker = (TimePickerDialog)d;
281            final Calendar calendar = Calendar.getInstance();
282            timePicker.updateTime(
283                    calendar.get(Calendar.HOUR_OF_DAY),
284                    calendar.get(Calendar.MINUTE));
285            break;
286        }
287        default:
288            break;
289        }
290    }
291    */
292    @Override
293    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
294        if (preference == mDatePref) {
295            showDialog(DIALOG_DATEPICKER);
296        } else if (preference == mTimePref) {
297            // The 24-hour mode may have changed, so recreate the dialog
298            removeDialog(DIALOG_TIMEPICKER);
299            showDialog(DIALOG_TIMEPICKER);
300        } else if (preference == mTime24Pref) {
301            set24Hour(((CheckBoxPreference)mTime24Pref).isChecked());
302            updateTimeAndDateDisplay(getActivity());
303            timeUpdated();
304        }
305        return super.onPreferenceTreeClick(preferenceScreen, preference);
306    }
307
308    @Override
309    public void onActivityResult(int requestCode, int resultCode,
310            Intent data) {
311        updateTimeAndDateDisplay(getActivity());
312    }
313
314    private void timeUpdated() {
315        Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
316        getActivity().sendBroadcast(timeChanged);
317    }
318
319    /*  Get & Set values from the system settings  */
320
321    private boolean is24Hour() {
322        return DateFormat.is24HourFormat(getActivity());
323    }
324
325    private void set24Hour(boolean is24Hour) {
326        Settings.System.putString(getContentResolver(),
327                Settings.System.TIME_12_24,
328                is24Hour? HOURS_24 : HOURS_12);
329    }
330
331    private String getDateFormat() {
332        return Settings.System.getString(getContentResolver(),
333                Settings.System.DATE_FORMAT);
334    }
335
336    private boolean getAutoState(String name) {
337        try {
338            return Settings.Global.getInt(getContentResolver(), name) > 0;
339        } catch (SettingNotFoundException snfe) {
340            return false;
341        }
342    }
343
344    /* package */ static void setDate(int year, int month, int day) {
345        Calendar c = Calendar.getInstance();
346
347        c.set(Calendar.YEAR, year);
348        c.set(Calendar.MONTH, month);
349        c.set(Calendar.DAY_OF_MONTH, day);
350        long when = c.getTimeInMillis();
351
352        if (when / 1000 < Integer.MAX_VALUE) {
353            SystemClock.setCurrentTimeMillis(when);
354        }
355    }
356
357    /* package */ static void setTime(int hourOfDay, int minute) {
358        Calendar c = Calendar.getInstance();
359
360        c.set(Calendar.HOUR_OF_DAY, hourOfDay);
361        c.set(Calendar.MINUTE, minute);
362        c.set(Calendar.SECOND, 0);
363        c.set(Calendar.MILLISECOND, 0);
364        long when = c.getTimeInMillis();
365
366        if (when / 1000 < Integer.MAX_VALUE) {
367            SystemClock.setCurrentTimeMillis(when);
368        }
369    }
370
371    /*  Helper routines to format timezone */
372
373    /* package */ static String getTimeZoneText(TimeZone tz) {
374        // Similar to new SimpleDateFormat("'GMT'Z, zzzz").format(new Date()), but
375        // we want "GMT-03:00" rather than "GMT-0300".
376        Date now = new Date();
377        return formatOffset(new StringBuilder(), tz, now).
378            append(", ").
379            append(tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG)).toString();
380    }
381
382    private static StringBuilder formatOffset(StringBuilder sb, TimeZone tz, Date d) {
383        int off = tz.getOffset(d.getTime()) / 1000 / 60;
384
385        sb.append("GMT");
386        if (off < 0) {
387            sb.append('-');
388            off = -off;
389        } else {
390            sb.append('+');
391        }
392
393        int hours = off / 60;
394        int minutes = off % 60;
395
396        sb.append((char) ('0' + hours / 10));
397        sb.append((char) ('0' + hours % 10));
398
399        sb.append(':');
400
401        sb.append((char) ('0' + minutes / 10));
402        sb.append((char) ('0' + minutes % 10));
403
404        return sb;
405    }
406
407    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
408        @Override
409        public void onReceive(Context context, Intent intent) {
410            final Activity activity = getActivity();
411            if (activity != null) {
412                updateTimeAndDateDisplay(activity);
413            }
414        }
415    };
416}
417