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