SettingsActivity.java revision ef0635176bcfc9b64f8790c487f63ee81433d295
1/* 2 * Copyright (C) 2015 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.deskclock.settings; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.res.Resources; 22import android.os.Bundle; 23import android.os.Vibrator; 24import android.provider.Settings; 25import android.support.annotation.NonNull; 26import android.support.v7.preference.ListPreference; 27import android.support.v7.preference.ListPreferenceDialogFragmentCompat; 28import android.support.v7.preference.Preference; 29import android.support.v7.preference.PreferenceDialogFragmentCompat; 30import android.support.v7.preference.PreferenceFragmentCompat; 31import android.support.v7.preference.TwoStatePreference; 32import android.text.format.DateUtils; 33import android.view.Menu; 34import android.view.MenuItem; 35import android.view.View; 36 37import com.android.deskclock.BaseActivity; 38import com.android.deskclock.DropShadowController; 39import com.android.deskclock.LogUtils; 40import com.android.deskclock.R; 41import com.android.deskclock.Utils; 42import com.android.deskclock.actionbarmenu.MenuItemControllerFactory; 43import com.android.deskclock.actionbarmenu.NavUpMenuItemController; 44import com.android.deskclock.actionbarmenu.OptionsMenuManager; 45import com.android.deskclock.data.DataModel; 46import com.android.deskclock.data.Weekdays; 47import com.android.deskclock.ringtone.RingtonePickerActivity; 48 49import java.util.ArrayList; 50import java.util.Collections; 51import java.util.List; 52import java.util.TimeZone; 53 54/** 55 * Settings for the Alarm Clock. 56 */ 57public final class SettingsActivity extends BaseActivity { 58 59 public static final String KEY_ALARM_SNOOZE = "snooze_duration"; 60 public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration"; 61 public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration"; 62 public static final String KEY_TIMER_RINGTONE = "timer_ringtone"; 63 public static final String KEY_TIMER_VIBRATE = "timer_vibrate"; 64 public static final String KEY_AUTO_SILENCE = "auto_silence"; 65 public static final String KEY_CLOCK_STYLE = "clock_style"; 66 public static final String KEY_CLOCK_DISPLAY_SECONDS = "display_clock_seconds"; 67 public static final String KEY_HOME_TZ = "home_time_zone"; 68 public static final String KEY_AUTO_HOME_CLOCK = "automatic_home_clock"; 69 public static final String KEY_DATE_TIME = "date_time"; 70 public static final String KEY_VOLUME_BUTTONS = "volume_button_setting"; 71 public static final String KEY_WEEK_START = "week_start"; 72 73 public static final String DEFAULT_VOLUME_BEHAVIOR = "0"; 74 public static final String VOLUME_BEHAVIOR_SNOOZE = "1"; 75 public static final String VOLUME_BEHAVIOR_DISMISS = "2"; 76 77 public static final String PREFS_FRAGMENT_TAG = "prefs_fragment"; 78 public static final String PREFERENCE_DIALOG_FRAGMENT_TAG = "preference_dialog"; 79 80 private final OptionsMenuManager mOptionsMenuManager = new OptionsMenuManager(); 81 82 /** 83 * The controller that shows the drop shadow when content is not scrolled to the top. 84 */ 85 private DropShadowController mDropShadowController; 86 87 @Override 88 protected void onCreate(Bundle savedInstanceState) { 89 super.onCreate(savedInstanceState); 90 setContentView(R.layout.settings); 91 92 mOptionsMenuManager.addMenuItemController(new NavUpMenuItemController(this)) 93 .addMenuItemController(MenuItemControllerFactory.getInstance() 94 .buildMenuItemControllers(this)); 95 96 // Create the prefs fragment in code to ensure it's created before PreferenceDialogFragment 97 if (savedInstanceState == null) { 98 getSupportFragmentManager().beginTransaction() 99 .replace(R.id.main, new PrefsFragment(), PREFS_FRAGMENT_TAG) 100 .disallowAddToBackStack() 101 .commit(); 102 } 103 } 104 105 @Override 106 protected void onResume() { 107 super.onResume(); 108 109 final View dropShadow = findViewById(R.id.drop_shadow); 110 final PrefsFragment fragment = 111 (PrefsFragment) getSupportFragmentManager().findFragmentById(R.id.main); 112 mDropShadowController = new DropShadowController(dropShadow, fragment.getListView()); 113 } 114 115 @Override 116 protected void onPause() { 117 mDropShadowController.stop(); 118 super.onPause(); 119 } 120 121 @Override 122 public boolean onCreateOptionsMenu(Menu menu) { 123 mOptionsMenuManager.onCreateOptionsMenu(menu); 124 return true; 125 } 126 127 @Override 128 public boolean onPrepareOptionsMenu(Menu menu) { 129 mOptionsMenuManager.onPrepareOptionsMenu(menu); 130 return true; 131 } 132 133 @Override 134 public boolean onOptionsItemSelected(MenuItem item) { 135 return mOptionsMenuManager.onOptionsItemSelected(item) 136 || super.onOptionsItemSelected(item); 137 } 138 139 public static class PrefsFragment extends PreferenceFragmentCompat implements 140 Preference.OnPreferenceChangeListener, 141 Preference.OnPreferenceClickListener { 142 143 @Override 144 public void onCreatePreferences(Bundle bundle, String rootKey) { 145 getPreferenceManager().setStorageDeviceProtected(); 146 addPreferencesFromResource(R.xml.settings); 147 final Preference timerVibrate = findPreference(KEY_TIMER_VIBRATE); 148 final boolean hasVibrator = ((Vibrator) timerVibrate.getContext() 149 .getSystemService(VIBRATOR_SERVICE)).hasVibrator(); 150 timerVibrate.setVisible(hasVibrator); 151 loadTimeZoneList(); 152 } 153 154 @Override 155 public void onActivityCreated(Bundle savedInstanceState) { 156 super.onActivityCreated(savedInstanceState); 157 158 // By default, do not recreate the DeskClock activity 159 getActivity().setResult(RESULT_CANCELED); 160 } 161 162 @Override 163 public void onResume() { 164 super.onResume(); 165 refresh(); 166 } 167 168 @Override 169 public boolean onPreferenceChange(Preference pref, Object newValue) { 170 switch (pref.getKey()) { 171 case KEY_ALARM_CRESCENDO: 172 case KEY_HOME_TZ: 173 case KEY_ALARM_SNOOZE: 174 case KEY_TIMER_CRESCENDO: 175 final ListPreference preference = (ListPreference) pref; 176 final int index = preference.findIndexOfValue((String) newValue); 177 preference.setSummary(preference.getEntries()[index]); 178 break; 179 case KEY_CLOCK_STYLE: 180 case KEY_WEEK_START: 181 case KEY_VOLUME_BUTTONS: 182 final SimpleMenuPreference simpleMenuPreference = (SimpleMenuPreference) pref; 183 final int i = simpleMenuPreference.findIndexOfValue((String) newValue); 184 pref.setSummary(simpleMenuPreference.getEntries()[i]); 185 break; 186 case KEY_CLOCK_DISPLAY_SECONDS: 187 DataModel.getDataModel().setDisplayClockSeconds((boolean) newValue); 188 break; 189 case KEY_AUTO_SILENCE: 190 final String delay = (String) newValue; 191 updateAutoSnoozeSummary((ListPreference) pref, delay); 192 break; 193 case KEY_AUTO_HOME_CLOCK: 194 final boolean autoHomeClockEnabled = ((TwoStatePreference) pref).isChecked(); 195 final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ); 196 homeTimeZonePref.setEnabled(!autoHomeClockEnabled); 197 break; 198 case KEY_TIMER_VIBRATE: 199 final TwoStatePreference timerVibratePref = (TwoStatePreference) pref; 200 DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked()); 201 break; 202 case KEY_TIMER_RINGTONE: 203 pref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle()); 204 break; 205 } 206 // Set result so DeskClock knows to refresh itself 207 getActivity().setResult(RESULT_OK); 208 return true; 209 } 210 211 @Override 212 public boolean onPreferenceClick(Preference pref) { 213 final Context context = getActivity(); 214 if (context == null) { 215 return false; 216 } 217 218 switch (pref.getKey()) { 219 case KEY_DATE_TIME: 220 final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS); 221 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 222 startActivity(dialogIntent); 223 return true; 224 case KEY_TIMER_RINGTONE: 225 startActivity(RingtonePickerActivity.createTimerRingtonePickerIntent(context)); 226 return true; 227 } 228 229 return false; 230 } 231 232 @Override 233 public void onDisplayPreferenceDialog(Preference preference) { 234 // Only single-selection lists are currently supported. 235 final PreferenceDialogFragmentCompat f; 236 if (preference instanceof ListPreference) { 237 f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey()); 238 } else { 239 throw new IllegalArgumentException("Unsupported DialogPreference type"); 240 } 241 showDialog(f); 242 } 243 244 private void showDialog(PreferenceDialogFragmentCompat fragment) { 245 // Don't show dialog if one is already shown. 246 if (getFragmentManager().findFragmentByTag(PREFERENCE_DIALOG_FRAGMENT_TAG) != null) { 247 return; 248 } 249 // Always set the target fragment, this is required by PreferenceDialogFragment 250 // internally. 251 fragment.setTargetFragment(this, 0); 252 // Don't use getChildFragmentManager(), it causes issues on older platforms when the 253 // target fragment is being restored after an orientation change. 254 fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG); 255 } 256 257 /** 258 * Reconstruct the timezone list. 259 */ 260 private void loadTimeZoneList() { 261 final CharSequence[][] timezones = getAllTimezones(); 262 final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ); 263 homeTimezonePref.setEntryValues(timezones[0]); 264 homeTimezonePref.setEntries(timezones[1]); 265 homeTimezonePref.setSummary(homeTimezonePref.getEntry()); 266 homeTimezonePref.setOnPreferenceChangeListener(this); 267 } 268 269 /** 270 * Returns an array of ids/time zones. This returns a double indexed array 271 * of ids and time zones for Calendar. It is an inefficient method and 272 * shouldn't be called often, but can be used for one time generation of 273 * this list. 274 * 275 * @return double array of tz ids and tz names 276 */ 277 public CharSequence[][] getAllTimezones() { 278 final Resources res = getResources(); 279 final String[] ids = res.getStringArray(R.array.timezone_values); 280 final String[] labels = res.getStringArray(R.array.timezone_labels); 281 282 int minLength = ids.length; 283 if (ids.length != labels.length) { 284 minLength = Math.min(minLength, labels.length); 285 LogUtils.e("Timezone ids and labels have different length!"); 286 } 287 288 final long currentTimeMillis = System.currentTimeMillis(); 289 final List<TimeZoneRow> timezones = new ArrayList<>(minLength); 290 for (int i = 0; i < minLength; i++) { 291 timezones.add(new TimeZoneRow(ids[i], labels[i], currentTimeMillis)); 292 } 293 Collections.sort(timezones); 294 295 final CharSequence[][] timeZones = new CharSequence[2][timezones.size()]; 296 int i = 0; 297 for (TimeZoneRow row : timezones) { 298 timeZones[0][i] = row.mId; 299 timeZones[1][i++] = row.mDisplayName; 300 } 301 return timeZones; 302 } 303 304 private void refresh() { 305 final ListPreference autoSilencePref = 306 (ListPreference) findPreference(KEY_AUTO_SILENCE); 307 String delay = autoSilencePref.getValue(); 308 updateAutoSnoozeSummary(autoSilencePref, delay); 309 autoSilencePref.setOnPreferenceChangeListener(this); 310 311 final SimpleMenuPreference clockStylePref = (SimpleMenuPreference) 312 findPreference(KEY_CLOCK_STYLE); 313 clockStylePref.setSummary(clockStylePref.getEntry()); 314 clockStylePref.setOnPreferenceChangeListener(this); 315 316 final SimpleMenuPreference volumeButtonsPref = (SimpleMenuPreference) 317 findPreference(KEY_VOLUME_BUTTONS); 318 volumeButtonsPref.setSummary(volumeButtonsPref.getEntry()); 319 volumeButtonsPref.setOnPreferenceChangeListener(this); 320 321 final Preference clockSecondsPref = findPreference(KEY_CLOCK_DISPLAY_SECONDS); 322 clockSecondsPref.setOnPreferenceChangeListener(this); 323 324 final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK); 325 final boolean autoHomeClockEnabled = 326 ((TwoStatePreference) autoHomeClockPref).isChecked(); 327 autoHomeClockPref.setOnPreferenceChangeListener(this); 328 329 final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ); 330 homeTimezonePref.setEnabled(autoHomeClockEnabled); 331 refreshListPreference(homeTimezonePref); 332 333 refreshListPreference((ListPreference) findPreference(KEY_ALARM_CRESCENDO)); 334 refreshListPreference((ListPreference) findPreference(KEY_TIMER_CRESCENDO)); 335 refreshListPreference((ListPreference) findPreference(KEY_ALARM_SNOOZE)); 336 337 final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME); 338 dateAndTimeSetting.setOnPreferenceClickListener(this); 339 340 final SimpleMenuPreference weekStartPref = (SimpleMenuPreference) 341 findPreference(KEY_WEEK_START); 342 // Set the default value programmatically 343 final Weekdays.Order weekdayOrder = DataModel.getDataModel().getWeekdayOrder(); 344 final Integer firstDay = weekdayOrder.getCalendarDays().get(0); 345 final String value = String.valueOf(firstDay); 346 final int idx = weekStartPref.findIndexOfValue(value); 347 weekStartPref.setValueIndex(idx); 348 weekStartPref.setSummary(weekStartPref.getEntries()[idx]); 349 weekStartPref.setOnPreferenceChangeListener(this); 350 351 final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE); 352 timerRingtonePref.setOnPreferenceClickListener(this); 353 timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle()); 354 } 355 356 private void refreshListPreference(ListPreference preference) { 357 preference.setSummary(preference.getEntry()); 358 preference.setOnPreferenceChangeListener(this); 359 } 360 361 private void updateAutoSnoozeSummary(ListPreference listPref, String delay) { 362 int i = Integer.parseInt(delay); 363 if (i == -1) { 364 listPref.setSummary(R.string.auto_silence_never); 365 } else { 366 listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(), 367 R.plurals.auto_silence_summary, i)); 368 } 369 } 370 371 private static class TimeZoneRow implements Comparable<TimeZoneRow> { 372 373 public final String mId; 374 public final String mDisplayName; 375 public final int mOffset; 376 377 public TimeZoneRow(String id, String name, long currentTimeMillis) { 378 final TimeZone tz = TimeZone.getTimeZone(id); 379 final boolean useDaylightTime = tz.useDaylightTime(); 380 mId = id; 381 mOffset = tz.getOffset(currentTimeMillis); 382 mDisplayName = buildGmtDisplayName(name, useDaylightTime); 383 } 384 385 @Override 386 public int compareTo(@NonNull TimeZoneRow another) { 387 return mOffset - another.mOffset; 388 } 389 390 public String buildGmtDisplayName(String displayName, boolean useDaylightTime) { 391 final int p = Math.abs(mOffset); 392 final StringBuilder name = new StringBuilder("(GMT"); 393 name.append(mOffset < 0 ? '-' : '+'); 394 395 name.append(p / DateUtils.HOUR_IN_MILLIS); 396 name.append(':'); 397 398 int min = p / 60000; 399 min %= 60; 400 401 if (min < 10) { 402 name.append('0'); 403 } 404 name.append(min); 405 name.append(") "); 406 name.append(displayName); 407 return name.toString(); 408 } 409 } 410 } 411}