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.DatePickerDialog.OnDateSetListener; 23 import android.app.Dialog; 24 import android.app.TimePickerDialog; 25 import android.app.TimePickerDialog.OnTimeSetListener; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.os.Bundle; 31 import android.os.UserManager; 32 import android.provider.SearchIndexableResource; 33 import android.provider.Settings; 34 import android.provider.Settings.SettingNotFoundException; 35 import android.support.v14.preference.SwitchPreference; 36 import android.support.v7.preference.Preference; 37 import android.support.v7.preference.Preference.OnPreferenceChangeListener; 38 import android.text.format.DateFormat; 39 import android.widget.DatePicker; 40 import android.widget.TimePicker; 41 import com.android.internal.logging.MetricsProto.MetricsEvent; 42 import com.android.settings.dashboard.SummaryLoader; 43 import com.android.settings.search.BaseSearchIndexProvider; 44 import com.android.settings.search.Indexable; 45 import com.android.settingslib.RestrictedLockUtils; 46 import com.android.settingslib.RestrictedSwitchPreference; 47 import com.android.settingslib.datetime.ZoneGetter; 48 49 import java.util.ArrayList; 50 import java.util.Calendar; 51 import java.util.Date; 52 import java.util.List; 53 54 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 55 56 public class DateTimeSettings extends SettingsPreferenceFragment 57 implements OnTimeSetListener, OnDateSetListener, OnPreferenceChangeListener, Indexable { 58 59 private static final String HOURS_12 = "12"; 60 private static final String HOURS_24 = "24"; 61 62 // Used for showing the current date format, which looks like "12/31/2010", "2010/12/13", etc. 63 // The date value is dummy (independent of actual date). 64 private Calendar mDummyDate; 65 66 private static final String KEY_AUTO_TIME = "auto_time"; 67 private static final String KEY_AUTO_TIME_ZONE = "auto_zone"; 68 69 private static final int DIALOG_DATEPICKER = 0; 70 private static final int DIALOG_TIMEPICKER = 1; 71 72 // have we been launched from the setup wizard? 73 protected static final String EXTRA_IS_FIRST_RUN = "firstRun"; 74 75 // Minimum time is Nov 5, 2007, 0:00. 76 private static final long MIN_DATE = 1194220800000L; 77 78 private RestrictedSwitchPreference mAutoTimePref; 79 private Preference mTimePref; 80 private Preference mTime24Pref; 81 private SwitchPreference mAutoTimeZonePref; 82 private Preference mTimeZone; 83 private Preference mDatePref; 84 85 @Override 86 protected int getMetricsCategory() { 87 return MetricsEvent.DATE_TIME; 88 } 89 90 @Override 91 public void onCreate(Bundle icicle) { 92 super.onCreate(icicle); 93 94 addPreferencesFromResource(R.xml.date_time_prefs); 95 96 initUI(); 97 } 98 99 private void initUI() { 100 boolean autoTimeEnabled = getAutoState(Settings.Global.AUTO_TIME); 101 boolean autoTimeZoneEnabled = getAutoState(Settings.Global.AUTO_TIME_ZONE); 102 103 mAutoTimePref = (RestrictedSwitchPreference) findPreference(KEY_AUTO_TIME); 104 mAutoTimePref.setOnPreferenceChangeListener(this); 105 EnforcedAdmin admin = RestrictedLockUtils.checkIfAutoTimeRequired(getActivity()); 106 mAutoTimePref.setDisabledByAdmin(admin); 107 108 Intent intent = getActivity().getIntent(); 109 boolean isFirstRun = intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false); 110 111 mDummyDate = Calendar.getInstance(); 112 113 // If device admin requires auto time device policy manager will set 114 // Settings.Global.AUTO_TIME to true. Note that this app listens to that change. 115 mAutoTimePref.setChecked(autoTimeEnabled); 116 mAutoTimeZonePref = (SwitchPreference) findPreference(KEY_AUTO_TIME_ZONE); 117 mAutoTimeZonePref.setOnPreferenceChangeListener(this); 118 // Override auto-timezone if it's a wifi-only device or if we're still in setup wizard. 119 // TODO: Remove the wifiOnly test when auto-timezone is implemented based on wifi-location. 120 if (Utils.isWifiOnly(getActivity()) || isFirstRun) { 121 getPreferenceScreen().removePreference(mAutoTimeZonePref); 122 autoTimeZoneEnabled = false; 123 } 124 mAutoTimeZonePref.setChecked(autoTimeZoneEnabled); 125 126 mTimePref = findPreference("time"); 127 mTime24Pref = findPreference("24 hour"); 128 mTimeZone = findPreference("timezone"); 129 mDatePref = findPreference("date"); 130 if (isFirstRun) { 131 getPreferenceScreen().removePreference(mTime24Pref); 132 } 133 134 mTimePref.setEnabled(!autoTimeEnabled); 135 mDatePref.setEnabled(!autoTimeEnabled); 136 mTimeZone.setEnabled(!autoTimeZoneEnabled); 137 } 138 139 @Override 140 public void onResume() { 141 super.onResume(); 142 143 ((SwitchPreference)mTime24Pref).setChecked(is24Hour()); 144 145 // Register for time ticks and other reasons for time change 146 IntentFilter filter = new IntentFilter(); 147 filter.addAction(Intent.ACTION_TIME_TICK); 148 filter.addAction(Intent.ACTION_TIME_CHANGED); 149 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 150 getActivity().registerReceiver(mIntentReceiver, filter, null, null); 151 152 updateTimeAndDateDisplay(getActivity()); 153 } 154 155 @Override 156 public void onPause() { 157 super.onPause(); 158 getActivity().unregisterReceiver(mIntentReceiver); 159 } 160 161 public void updateTimeAndDateDisplay(Context context) { 162 final Calendar now = Calendar.getInstance(); 163 mDummyDate.setTimeZone(now.getTimeZone()); 164 // We use December 31st because it's unambiguous when demonstrating the date format. 165 // We use 13:00 so we can demonstrate the 12/24 hour options. 166 mDummyDate.set(now.get(Calendar.YEAR), 11, 31, 13, 0, 0); 167 Date dummyDate = mDummyDate.getTime(); 168 mDatePref.setSummary(DateFormat.getLongDateFormat(context).format(now.getTime())); 169 mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime())); 170 mTimeZone.setSummary(ZoneGetter.getTimeZoneOffsetAndName(now.getTimeZone(), now.getTime())); 171 mTime24Pref.setSummary(DateFormat.getTimeFormat(getActivity()).format(dummyDate)); 172 } 173 174 @Override 175 public void onDateSet(DatePicker view, int year, int month, int day) { 176 final Activity activity = getActivity(); 177 if (activity != null) { 178 setDate(activity, year, month, day); 179 updateTimeAndDateDisplay(activity); 180 } 181 } 182 183 @Override 184 public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 185 final Activity activity = getActivity(); 186 if (activity != null) { 187 setTime(activity, hourOfDay, minute); 188 updateTimeAndDateDisplay(activity); 189 } 190 191 // We don't need to call timeUpdated() here because the TIME_CHANGED 192 // broadcast is sent by the AlarmManager as a side effect of setting the 193 // SystemClock time. 194 } 195 196 @Override 197 public boolean onPreferenceChange(Preference preference, Object newValue) { 198 if (preference.getKey().equals(KEY_AUTO_TIME)) { 199 boolean autoEnabled = (Boolean) newValue; 200 Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, 201 autoEnabled ? 1 : 0); 202 mTimePref.setEnabled(!autoEnabled); 203 mDatePref.setEnabled(!autoEnabled); 204 } else if (preference.getKey().equals(KEY_AUTO_TIME_ZONE)) { 205 boolean autoZoneEnabled = (Boolean) newValue; 206 Settings.Global.putInt( 207 getContentResolver(), Settings.Global.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0); 208 mTimeZone.setEnabled(!autoZoneEnabled); 209 } 210 return true; 211 } 212 213 @Override 214 public Dialog onCreateDialog(int id) { 215 final Calendar calendar = Calendar.getInstance(); 216 switch (id) { 217 case DIALOG_DATEPICKER: 218 DatePickerDialog d = new DatePickerDialog( 219 getActivity(), 220 this, 221 calendar.get(Calendar.YEAR), 222 calendar.get(Calendar.MONTH), 223 calendar.get(Calendar.DAY_OF_MONTH)); 224 configureDatePicker(d.getDatePicker()); 225 return d; 226 case DIALOG_TIMEPICKER: 227 return new TimePickerDialog( 228 getActivity(), 229 this, 230 calendar.get(Calendar.HOUR_OF_DAY), 231 calendar.get(Calendar.MINUTE), 232 DateFormat.is24HourFormat(getActivity())); 233 default: 234 throw new IllegalArgumentException(); 235 } 236 } 237 238 static void configureDatePicker(DatePicker datePicker) { 239 // The system clock can't represent dates outside this range. 240 Calendar t = Calendar.getInstance(); 241 t.clear(); 242 t.set(1970, Calendar.JANUARY, 1); 243 datePicker.setMinDate(t.getTimeInMillis()); 244 t.clear(); 245 t.set(2037, Calendar.DECEMBER, 31); 246 datePicker.setMaxDate(t.getTimeInMillis()); 247 } 248 249 /* 250 @Override 251 public void onPrepareDialog(int id, Dialog d) { 252 switch (id) { 253 case DIALOG_DATEPICKER: { 254 DatePickerDialog datePicker = (DatePickerDialog)d; 255 final Calendar calendar = Calendar.getInstance(); 256 datePicker.updateDate( 257 calendar.get(Calendar.YEAR), 258 calendar.get(Calendar.MONTH), 259 calendar.get(Calendar.DAY_OF_MONTH)); 260 break; 261 } 262 case DIALOG_TIMEPICKER: { 263 TimePickerDialog timePicker = (TimePickerDialog)d; 264 final Calendar calendar = Calendar.getInstance(); 265 timePicker.updateTime( 266 calendar.get(Calendar.HOUR_OF_DAY), 267 calendar.get(Calendar.MINUTE)); 268 break; 269 } 270 default: 271 break; 272 } 273 } 274 */ 275 @Override 276 public boolean onPreferenceTreeClick(Preference preference) { 277 if (preference == mDatePref) { 278 showDialog(DIALOG_DATEPICKER); 279 } else if (preference == mTimePref) { 280 // The 24-hour mode may have changed, so recreate the dialog 281 removeDialog(DIALOG_TIMEPICKER); 282 showDialog(DIALOG_TIMEPICKER); 283 } else if (preference == mTime24Pref) { 284 final boolean is24Hour = ((SwitchPreference)mTime24Pref).isChecked(); 285 set24Hour(is24Hour); 286 updateTimeAndDateDisplay(getActivity()); 287 timeUpdated(is24Hour); 288 } 289 return super.onPreferenceTreeClick(preference); 290 } 291 292 @Override 293 public void onActivityResult(int requestCode, int resultCode, 294 Intent data) { 295 updateTimeAndDateDisplay(getActivity()); 296 } 297 298 private void timeUpdated(boolean is24Hour) { 299 Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED); 300 timeChanged.putExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, is24Hour); 301 getActivity().sendBroadcast(timeChanged); 302 } 303 304 /* Get & Set values from the system settings */ 305 306 private boolean is24Hour() { 307 return DateFormat.is24HourFormat(getActivity()); 308 } 309 310 private void set24Hour(boolean is24Hour) { 311 Settings.System.putString(getContentResolver(), 312 Settings.System.TIME_12_24, 313 is24Hour? HOURS_24 : HOURS_12); 314 } 315 316 private boolean getAutoState(String name) { 317 try { 318 return Settings.Global.getInt(getContentResolver(), name) > 0; 319 } catch (SettingNotFoundException snfe) { 320 return false; 321 } 322 } 323 324 /* package */ static void setDate(Context context, int year, int month, int day) { 325 Calendar c = Calendar.getInstance(); 326 327 c.set(Calendar.YEAR, year); 328 c.set(Calendar.MONTH, month); 329 c.set(Calendar.DAY_OF_MONTH, day); 330 long when = Math.max(c.getTimeInMillis(), MIN_DATE); 331 332 if (when / 1000 < Integer.MAX_VALUE) { 333 ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when); 334 } 335 } 336 337 /* package */ static void setTime(Context context, int hourOfDay, int minute) { 338 Calendar c = Calendar.getInstance(); 339 340 c.set(Calendar.HOUR_OF_DAY, hourOfDay); 341 c.set(Calendar.MINUTE, minute); 342 c.set(Calendar.SECOND, 0); 343 c.set(Calendar.MILLISECOND, 0); 344 long when = Math.max(c.getTimeInMillis(), MIN_DATE); 345 346 if (when / 1000 < Integer.MAX_VALUE) { 347 ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).setTime(when); 348 } 349 } 350 351 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 352 @Override 353 public void onReceive(Context context, Intent intent) { 354 final Activity activity = getActivity(); 355 if (activity != null) { 356 updateTimeAndDateDisplay(activity); 357 } 358 } 359 }; 360 361 private static class SummaryProvider implements SummaryLoader.SummaryProvider { 362 363 private final Context mContext; 364 private final SummaryLoader mSummaryLoader; 365 366 public SummaryProvider(Context context, SummaryLoader summaryLoader) { 367 mContext = context; 368 mSummaryLoader = summaryLoader; 369 } 370 371 @Override 372 public void setListening(boolean listening) { 373 if (listening) { 374 final Calendar now = Calendar.getInstance(); 375 mSummaryLoader.setSummary(this, ZoneGetter.getTimeZoneOffsetAndName( 376 now.getTimeZone(), now.getTime())); 377 } 378 } 379 } 380 381 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 382 = new SummaryLoader.SummaryProviderFactory() { 383 @Override 384 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity, 385 SummaryLoader summaryLoader) { 386 return new SummaryProvider(activity, summaryLoader); 387 } 388 }; 389 390 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 391 new DateTimeSearchIndexProvider(); 392 393 private static class DateTimeSearchIndexProvider extends BaseSearchIndexProvider { 394 395 @Override 396 public List<SearchIndexableResource> getXmlResourcesToIndex( 397 Context context, boolean enabled) { 398 List<SearchIndexableResource> result = new ArrayList<>(); 399 // Remove data/time settings from search in demo mode 400 if (UserManager.isDeviceInDemoMode(context)) { 401 return result; 402 } 403 404 SearchIndexableResource sir = new SearchIndexableResource(context); 405 sir.xmlResId = R.xml.date_time_prefs; 406 result.add(sir); 407 408 return result; 409 } 410 } 411 } 412