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