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