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 17 package com.android.settings; 18 19 import android.app.Activity; 20 import android.app.AlarmManager; 21 import android.app.DatePickerDialog; 22 import android.app.Dialog; 23 import android.app.TimePickerDialog; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.SharedPreferences; 29 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 30 import android.os.Bundle; 31 import android.os.SystemClock; 32 import android.preference.CheckBoxPreference; 33 import android.preference.ListPreference; 34 import android.preference.Preference; 35 import android.preference.PreferenceScreen; 36 import android.provider.Settings; 37 import android.provider.Settings.SettingNotFoundException; 38 import android.text.format.DateFormat; 39 import android.widget.DatePicker; 40 import android.widget.TimePicker; 41 42 import java.util.Calendar; 43 import java.util.Date; 44 import java.util.TimeZone; 45 46 public 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 // The system clock can't represent dates outside this range. 248 DatePickerDialog datePicker = (DatePickerDialog)d; 249 Calendar t = Calendar.getInstance(); 250 t.clear(); 251 t.set(1970, Calendar.JANUARY, 1); 252 datePicker.getDatePicker().setMinDate(t.getTimeInMillis()); 253 t.clear(); 254 t.set(2037, Calendar.DECEMBER, 31); 255 datePicker.getDatePicker().setMaxDate(t.getTimeInMillis()); 256 break; 257 } 258 case DIALOG_TIMEPICKER: { 259 final Calendar calendar = Calendar.getInstance(); 260 d = new TimePickerDialog( 261 getActivity(), 262 this, 263 calendar.get(Calendar.HOUR_OF_DAY), 264 calendar.get(Calendar.MINUTE), 265 DateFormat.is24HourFormat(getActivity())); 266 break; 267 } 268 default: 269 d = null; 270 break; 271 } 272 273 return d; 274 } 275 276 /* 277 @Override 278 public void onPrepareDialog(int id, Dialog d) { 279 switch (id) { 280 case DIALOG_DATEPICKER: { 281 DatePickerDialog datePicker = (DatePickerDialog)d; 282 final Calendar calendar = Calendar.getInstance(); 283 datePicker.updateDate( 284 calendar.get(Calendar.YEAR), 285 calendar.get(Calendar.MONTH), 286 calendar.get(Calendar.DAY_OF_MONTH)); 287 break; 288 } 289 case DIALOG_TIMEPICKER: { 290 TimePickerDialog timePicker = (TimePickerDialog)d; 291 final Calendar calendar = Calendar.getInstance(); 292 timePicker.updateTime( 293 calendar.get(Calendar.HOUR_OF_DAY), 294 calendar.get(Calendar.MINUTE)); 295 break; 296 } 297 default: 298 break; 299 } 300 } 301 */ 302 @Override 303 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 304 if (preference == mDatePref) { 305 showDialog(DIALOG_DATEPICKER); 306 } else if (preference == mTimePref) { 307 // The 24-hour mode may have changed, so recreate the dialog 308 removeDialog(DIALOG_TIMEPICKER); 309 showDialog(DIALOG_TIMEPICKER); 310 } else if (preference == mTime24Pref) { 311 set24Hour(((CheckBoxPreference)mTime24Pref).isChecked()); 312 updateTimeAndDateDisplay(getActivity()); 313 timeUpdated(); 314 } 315 return super.onPreferenceTreeClick(preferenceScreen, preference); 316 } 317 318 @Override 319 public void onActivityResult(int requestCode, int resultCode, 320 Intent data) { 321 updateTimeAndDateDisplay(getActivity()); 322 } 323 324 private void timeUpdated() { 325 Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED); 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 /* Helper routines to format timezone */ 382 383 /* package */ static String getTimeZoneText(TimeZone tz) { 384 // Similar to new SimpleDateFormat("'GMT'Z, zzzz").format(new Date()), but 385 // we want "GMT-03:00" rather than "GMT-0300". 386 Date now = new Date(); 387 return formatOffset(new StringBuilder(), tz, now). 388 append(", "). 389 append(tz.getDisplayName(tz.inDaylightTime(now), TimeZone.LONG)).toString(); 390 } 391 392 private static StringBuilder formatOffset(StringBuilder sb, TimeZone tz, Date d) { 393 int off = tz.getOffset(d.getTime()) / 1000 / 60; 394 395 sb.append("GMT"); 396 if (off < 0) { 397 sb.append('-'); 398 off = -off; 399 } else { 400 sb.append('+'); 401 } 402 403 int hours = off / 60; 404 int minutes = off % 60; 405 406 sb.append((char) ('0' + hours / 10)); 407 sb.append((char) ('0' + hours % 10)); 408 409 sb.append(':'); 410 411 sb.append((char) ('0' + minutes / 10)); 412 sb.append((char) ('0' + minutes % 10)); 413 414 return sb; 415 } 416 417 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 418 @Override 419 public void onReceive(Context context, Intent intent) { 420 final Activity activity = getActivity(); 421 if (activity != null) { 422 updateTimeAndDateDisplay(activity); 423 } 424 } 425 }; 426 } 427