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