1 /* 2 * Copyright (C) 2007 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.calendar; 18 19 import android.app.Activity; 20 import android.app.FragmentManager; 21 import android.app.backup.BackupManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.SharedPreferences; 25 import android.content.SharedPreferences.Editor; 26 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 27 import android.media.Ringtone; 28 import android.media.RingtoneManager; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.Vibrator; 32 import android.preference.CheckBoxPreference; 33 import android.preference.ListPreference; 34 import android.preference.Preference; 35 import android.preference.Preference.OnPreferenceChangeListener; 36 import android.preference.Preference.OnPreferenceClickListener; 37 import android.preference.PreferenceCategory; 38 import android.preference.PreferenceFragment; 39 import android.preference.PreferenceManager; 40 import android.preference.PreferenceScreen; 41 import android.preference.RingtonePreference; 42 import android.provider.CalendarContract; 43 import android.provider.CalendarContract.CalendarCache; 44 import android.provider.SearchRecentSuggestions; 45 import android.text.TextUtils; 46 import android.text.format.Time; 47 import android.widget.Toast; 48 49 import com.android.calendar.alerts.AlertReceiver; 50 import com.android.timezonepicker.TimeZoneInfo; 51 import com.android.timezonepicker.TimeZonePickerDialog; 52 import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener; 53 import com.android.timezonepicker.TimeZonePickerUtils; 54 55 public class GeneralPreferences extends PreferenceFragment implements 56 OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener { 57 // The name of the shared preferences file. This name must be maintained for historical 58 // reasons, as it's what PreferenceManager assigned the first time the file was created. 59 static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; 60 static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"; 61 62 private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"; 63 64 // Preference keys 65 public static final String KEY_HIDE_DECLINED = "preferences_hide_declined"; 66 public static final String KEY_WEEK_START_DAY = "preferences_week_start_day"; 67 public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num"; 68 public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week"; 69 public static final String KEY_SKIP_SETUP = "preferences_skip_setup"; 70 71 public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"; 72 73 public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category"; 74 public static final String KEY_ALERTS = "preferences_alerts"; 75 public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"; 76 public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"; 77 public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup"; 78 79 public static final String KEY_SHOW_CONTROLS = "preferences_show_controls"; 80 81 public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder"; 82 public static final int NO_REMINDER = -1; 83 public static final String NO_REMINDER_STRING = "-1"; 84 public static final int REMINDER_DEFAULT_TIME = 10; // in minutes 85 86 public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"; 87 public static final String KEY_VERSION = "preferences_version"; 88 89 /** Key to SharePreference for default view (CalendarController.ViewType) */ 90 public static final String KEY_START_VIEW = "preferred_startView"; 91 /** 92 * Key to SharePreference for default detail view (CalendarController.ViewType) 93 * Typically used by widget 94 */ 95 public static final String KEY_DETAILED_VIEW = "preferred_detailedView"; 96 public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"; 97 98 // These must be in sync with the array preferences_week_start_day_values 99 public static final String WEEK_START_DEFAULT = "-1"; 100 public static final String WEEK_START_SATURDAY = "7"; 101 public static final String WEEK_START_SUNDAY = "1"; 102 public static final String WEEK_START_MONDAY = "2"; 103 104 // These keys are kept to enable migrating users from previous versions 105 private static final String KEY_ALERTS_TYPE = "preferences_alerts_type"; 106 private static final String ALERT_TYPE_ALERTS = "0"; 107 private static final String ALERT_TYPE_STATUS_BAR = "1"; 108 private static final String ALERT_TYPE_OFF = "2"; 109 static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; 110 static final String KEY_HOME_TZ = "preferences_home_tz"; 111 112 // Default preference values 113 public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK; 114 public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY; 115 public static final boolean DEFAULT_SHOW_WEEK_NUM = false; 116 // This should match the XML file. 117 public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; 118 119 CheckBoxPreference mAlert; 120 CheckBoxPreference mVibrate; 121 RingtonePreference mRingtone; 122 CheckBoxPreference mPopup; 123 CheckBoxPreference mUseHomeTZ; 124 CheckBoxPreference mHideDeclined; 125 Preference mHomeTZ; 126 TimeZonePickerUtils mTzPickerUtils; 127 ListPreference mWeekStart; 128 ListPreference mDefaultReminder; 129 130 private String mTimeZoneId; 131 132 /** Return a properly configured SharedPreferences instance */ 133 public static SharedPreferences getSharedPreferences(Context context) { 134 return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); 135 } 136 137 /** Set the default shared preferences in the proper context */ 138 public static void setDefaultValues(Context context) { 139 PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, 140 R.xml.general_preferences, false); 141 } 142 143 @Override 144 public void onCreate(Bundle icicle) { 145 super.onCreate(icicle); 146 147 final Activity activity = getActivity(); 148 149 // Make sure to always use the same preferences file regardless of the package name 150 // we're running under 151 final PreferenceManager preferenceManager = getPreferenceManager(); 152 final SharedPreferences sharedPreferences = getSharedPreferences(activity); 153 preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME); 154 155 // Load the preferences from an XML resource 156 addPreferencesFromResource(R.xml.general_preferences); 157 158 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 159 mAlert = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS); 160 mVibrate = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_VIBRATE); 161 Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); 162 if (vibrator == null || !vibrator.hasVibrator()) { 163 PreferenceCategory mAlertGroup = (PreferenceCategory) preferenceScreen 164 .findPreference(KEY_ALERTS_CATEGORY); 165 mAlertGroup.removePreference(mVibrate); 166 } 167 168 mRingtone = (RingtonePreference) preferenceScreen.findPreference(KEY_ALERTS_RINGTONE); 169 String ringToneUri = Utils.getRingTonePreference(activity); 170 171 // Set the ringToneUri to the backup-able shared pref only so that 172 // the Ringtone dialog will open up with the correct value. 173 final Editor editor = preferenceScreen.getEditor(); 174 editor.putString(GeneralPreferences.KEY_ALERTS_RINGTONE, ringToneUri).apply(); 175 176 String ringtoneDisplayString = getRingtoneTitleFromUri(activity, ringToneUri); 177 mRingtone.setSummary(ringtoneDisplayString == null ? "" : ringtoneDisplayString); 178 179 mPopup = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_POPUP); 180 mUseHomeTZ = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED); 181 mHideDeclined = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HIDE_DECLINED); 182 mWeekStart = (ListPreference) preferenceScreen.findPreference(KEY_WEEK_START_DAY); 183 mDefaultReminder = (ListPreference) preferenceScreen.findPreference(KEY_DEFAULT_REMINDER); 184 mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ); 185 mWeekStart.setSummary(mWeekStart.getEntry()); 186 mDefaultReminder.setSummary(mDefaultReminder.getEntry()); 187 188 // This triggers an asynchronous call to the provider to refresh the data in shared pref 189 mTimeZoneId = Utils.getTimeZone(activity, null); 190 191 SharedPreferences prefs = CalendarUtils.getSharedPreferences(activity, 192 Utils.SHARED_PREFS_NAME); 193 194 // Utils.getTimeZone will return the currentTimeZone instead of the one 195 // in the shared_pref if home time zone is disabled. So if home tz is 196 // off, we will explicitly read it. 197 if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) { 198 mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); 199 } 200 201 mHomeTZ.setOnPreferenceClickListener(new OnPreferenceClickListener() { 202 @Override 203 public boolean onPreferenceClick(Preference preference) { 204 showTimezoneDialog(); 205 return true; 206 } 207 }); 208 209 if (mTzPickerUtils == null) { 210 mTzPickerUtils = new TimeZonePickerUtils(getActivity()); 211 } 212 CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName(getActivity(), mTimeZoneId, 213 System.currentTimeMillis(), false); 214 mHomeTZ.setSummary(timezoneName != null ? timezoneName : mTimeZoneId); 215 216 TimeZonePickerDialog tzpd = (TimeZonePickerDialog) activity.getFragmentManager() 217 .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); 218 if (tzpd != null) { 219 tzpd.setOnTimeZoneSetListener(this); 220 } 221 222 migrateOldPreferences(sharedPreferences); 223 224 updateChildPreferences(); 225 } 226 227 private void showTimezoneDialog() { 228 final Activity activity = getActivity(); 229 if (activity == null) { 230 return; 231 } 232 233 Bundle b = new Bundle(); 234 b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()); 235 b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)); 236 237 FragmentManager fm = getActivity().getFragmentManager(); 238 TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm 239 .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); 240 if (tzpd != null) { 241 tzpd.dismiss(); 242 } 243 tzpd = new TimeZonePickerDialog(); 244 tzpd.setArguments(b); 245 tzpd.setOnTimeZoneSetListener(this); 246 tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER); 247 } 248 249 @Override 250 public void onStart() { 251 super.onStart(); 252 getPreferenceScreen().getSharedPreferences() 253 .registerOnSharedPreferenceChangeListener(this); 254 setPreferenceListeners(this); 255 } 256 257 /** 258 * Sets up all the preference change listeners to use the specified 259 * listener. 260 */ 261 private void setPreferenceListeners(OnPreferenceChangeListener listener) { 262 mUseHomeTZ.setOnPreferenceChangeListener(listener); 263 mHomeTZ.setOnPreferenceChangeListener(listener); 264 mWeekStart.setOnPreferenceChangeListener(listener); 265 mDefaultReminder.setOnPreferenceChangeListener(listener); 266 mRingtone.setOnPreferenceChangeListener(listener); 267 mHideDeclined.setOnPreferenceChangeListener(listener); 268 mVibrate.setOnPreferenceChangeListener(listener); 269 } 270 271 @Override 272 public void onStop() { 273 getPreferenceScreen().getSharedPreferences() 274 .unregisterOnSharedPreferenceChangeListener(this); 275 setPreferenceListeners(null); 276 super.onStop(); 277 } 278 279 @Override 280 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 281 Activity a = getActivity(); 282 if (key.equals(KEY_ALERTS)) { 283 updateChildPreferences(); 284 if (a != null) { 285 Intent intent = new Intent(); 286 intent.setClass(a, AlertReceiver.class); 287 if (mAlert.isChecked()) { 288 intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS); 289 } else { 290 intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION); 291 } 292 a.sendBroadcast(intent); 293 } 294 } 295 if (a != null) { 296 BackupManager.dataChanged(a.getPackageName()); 297 } 298 } 299 300 /** 301 * Handles time zone preference changes 302 */ 303 @Override 304 public boolean onPreferenceChange(Preference preference, Object newValue) { 305 String tz; 306 final Activity activity = getActivity(); 307 if (preference == mUseHomeTZ) { 308 if ((Boolean)newValue) { 309 tz = mTimeZoneId; 310 } else { 311 tz = CalendarCache.TIMEZONE_TYPE_AUTO; 312 } 313 Utils.setTimeZone(activity, tz); 314 return true; 315 } else if (preference == mHideDeclined) { 316 mHideDeclined.setChecked((Boolean) newValue); 317 Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(activity)); 318 intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); 319 activity.sendBroadcast(intent); 320 return true; 321 } else if (preference == mWeekStart) { 322 mWeekStart.setValue((String) newValue); 323 mWeekStart.setSummary(mWeekStart.getEntry()); 324 } else if (preference == mDefaultReminder) { 325 mDefaultReminder.setValue((String) newValue); 326 mDefaultReminder.setSummary(mDefaultReminder.getEntry()); 327 } else if (preference == mRingtone) { 328 if (newValue instanceof String) { 329 Utils.setRingTonePreference(activity, (String) newValue); 330 String ringtone = getRingtoneTitleFromUri(activity, (String) newValue); 331 mRingtone.setSummary(ringtone == null ? "" : ringtone); 332 } 333 return true; 334 } else if (preference == mVibrate) { 335 mVibrate.setChecked((Boolean) newValue); 336 return true; 337 } else { 338 return true; 339 } 340 return false; 341 } 342 343 public String getRingtoneTitleFromUri(Context context, String uri) { 344 if (TextUtils.isEmpty(uri)) { 345 return null; 346 } 347 348 Ringtone ring = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); 349 if (ring != null) { 350 return ring.getTitle(context); 351 } 352 return null; 353 } 354 355 /** 356 * If necessary, upgrades previous versions of preferences to the current 357 * set of keys and values. 358 * @param prefs the preferences to upgrade 359 */ 360 private void migrateOldPreferences(SharedPreferences prefs) { 361 // If needed, migrate vibration setting from a previous version 362 363 mVibrate.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)); 364 365 // If needed, migrate the old alerts type settin 366 if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { 367 String type = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR); 368 if (type.equals(ALERT_TYPE_OFF)) { 369 mAlert.setChecked(false); 370 mPopup.setChecked(false); 371 mPopup.setEnabled(false); 372 } else if (type.equals(ALERT_TYPE_STATUS_BAR)) { 373 mAlert.setChecked(true); 374 mPopup.setChecked(false); 375 mPopup.setEnabled(true); 376 } else if (type.equals(ALERT_TYPE_ALERTS)) { 377 mAlert.setChecked(true); 378 mPopup.setChecked(true); 379 mPopup.setEnabled(true); 380 } 381 // clear out the old setting 382 prefs.edit().remove(KEY_ALERTS_TYPE).commit(); 383 } 384 } 385 386 /** 387 * Keeps the dependent settings in sync with the parent preference, so for 388 * example, when notifications are turned off, we disable the preferences 389 * for configuring the exact notification behavior. 390 */ 391 private void updateChildPreferences() { 392 if (mAlert.isChecked()) { 393 mVibrate.setEnabled(true); 394 mRingtone.setEnabled(true); 395 mPopup.setEnabled(true); 396 } else { 397 mVibrate.setEnabled(false); 398 mRingtone.setEnabled(false); 399 mPopup.setEnabled(false); 400 } 401 } 402 403 404 @Override 405 public boolean onPreferenceTreeClick( 406 PreferenceScreen preferenceScreen, Preference preference) { 407 final String key = preference.getKey(); 408 if (KEY_CLEAR_SEARCH_HISTORY.equals(key)) { 409 SearchRecentSuggestions suggestions = new SearchRecentSuggestions(getActivity(), 410 Utils.getSearchAuthority(getActivity()), 411 CalendarRecentSuggestionsProvider.MODE); 412 suggestions.clearHistory(); 413 Toast.makeText(getActivity(), R.string.search_history_cleared, 414 Toast.LENGTH_SHORT).show(); 415 return true; 416 } else { 417 return super.onPreferenceTreeClick(preferenceScreen, preference); 418 } 419 } 420 421 @Override 422 public void onTimeZoneSet(TimeZoneInfo tzi) { 423 if (mTzPickerUtils == null) { 424 mTzPickerUtils = new TimeZonePickerUtils(getActivity()); 425 } 426 427 final CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName( 428 getActivity(), tzi.mTzId, System.currentTimeMillis(), false); 429 mHomeTZ.setSummary(timezoneName); 430 Utils.setTimeZone(getActivity(), tzi.mTzId); 431 } 432 } 433