SettingsActivity.java revision 270e0e33f661dc7a726b0c64d4227a54d3068946
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.app.Activity; 20import android.content.Intent; 21import android.content.res.Resources; 22import android.net.Uri; 23import android.os.Bundle; 24import android.provider.Settings; 25import android.support.annotation.NonNull; 26import android.support.v14.preference.PreferenceDialogFragment; 27import android.support.v14.preference.PreferenceFragment; 28import android.support.v7.preference.ListPreference; 29import android.support.v7.preference.Preference; 30import android.support.v7.preference.TwoStatePreference; 31import android.text.format.DateUtils; 32import android.view.Menu; 33import android.view.MenuItem; 34import android.view.View; 35 36import com.android.deskclock.BaseActivity; 37import com.android.deskclock.DropShadowController; 38import com.android.deskclock.LogUtils; 39import com.android.deskclock.R; 40import com.android.deskclock.RingtonePickerDialogFragment; 41import com.android.deskclock.Utils; 42import com.android.deskclock.actionbarmenu.ActionBarMenuManager; 43import com.android.deskclock.actionbarmenu.MenuItemControllerFactory; 44import com.android.deskclock.actionbarmenu.NavUpMenuItemController; 45import com.android.deskclock.data.DataModel; 46 47import java.util.ArrayList; 48import java.util.Collections; 49import java.util.List; 50import java.util.TimeZone; 51 52/** 53 * Settings for the Alarm Clock. 54 */ 55public final class SettingsActivity extends BaseActivity { 56 57 public static final String KEY_ALARM_SNOOZE = "snooze_duration"; 58 public static final String KEY_ALARM_CRESCENDO = "alarm_crescendo_duration"; 59 public static final String KEY_TIMER_CRESCENDO = "timer_crescendo_duration"; 60 public static final String KEY_TIMER_RINGTONE = "timer_ringtone"; 61 public static final String KEY_TIMER_VIBRATE = "timer_vibrate"; 62 public static final String KEY_AUTO_SILENCE = "auto_silence"; 63 public static final String KEY_CLOCK_STYLE = "clock_style"; 64 public static final String KEY_HOME_TZ = "home_time_zone"; 65 public static final String KEY_AUTO_HOME_CLOCK = "automatic_home_clock"; 66 public static final String KEY_DATE_TIME = "date_time"; 67 public static final String KEY_VOLUME_BUTTONS = "volume_button_setting"; 68 public static final String KEY_WEEK_START = "week_start"; 69 70 public static final String DEFAULT_VOLUME_BEHAVIOR = "0"; 71 public static final String VOLUME_BEHAVIOR_SNOOZE = "1"; 72 public static final String VOLUME_BEHAVIOR_DISMISS = "2"; 73 74 public static final String PREFS_FRAGMENT_TAG = "prefs_fragment"; 75 public static final String PREFERENCE_DIALOG_FRAGMENT_TAG = "preference_dialog"; 76 77 private final ActionBarMenuManager mActionBarMenuManager = new ActionBarMenuManager(); 78 79 /** 80 * The controller that shows the drop shadow when content is not scrolled to the top. 81 */ 82 private DropShadowController mDropShadowController; 83 84 @Override 85 protected void onCreate(Bundle savedInstanceState) { 86 super.onCreate(savedInstanceState); 87 setContentView(R.layout.settings); 88 mActionBarMenuManager.addMenuItemController(new NavUpMenuItemController(this)) 89 .addMenuItemController(MenuItemControllerFactory.getInstance() 90 .buildMenuItemControllers(this)); 91 92 // Create the prefs fragment in code to ensure it's created before PreferenceDialogFragment 93 if (savedInstanceState == null) { 94 getFragmentManager().beginTransaction() 95 .replace(R.id.main, new PrefsFragment(), PREFS_FRAGMENT_TAG) 96 .disallowAddToBackStack() 97 .commit(); 98 } 99 } 100 101 @Override 102 protected void onResume() { 103 super.onResume(); 104 105 final View dropShadow = findViewById(R.id.drop_shadow); 106 final PrefsFragment fragment = 107 (PrefsFragment) getFragmentManager().findFragmentById(R.id.main); 108 mDropShadowController = new DropShadowController(dropShadow, fragment.getListView()); 109 } 110 111 @Override 112 protected void onPause() { 113 mDropShadowController.stop(); 114 super.onPause(); 115 } 116 117 @Override 118 public boolean onCreateOptionsMenu(Menu menu) { 119 mActionBarMenuManager.createOptionsMenu(menu, getMenuInflater()); 120 return true; 121 } 122 123 @Override 124 public boolean onPrepareOptionsMenu(Menu menu) { 125 mActionBarMenuManager.prepareShowMenu(menu); 126 return true; 127 } 128 129 @Override 130 public boolean onOptionsItemSelected(MenuItem item) { 131 return mActionBarMenuManager.handleMenuItemClick(item) 132 || super.onOptionsItemSelected(item); 133 } 134 135 public static class PrefsFragment extends PreferenceFragment implements 136 RingtonePickerDialogFragment.OnRingtoneSelectedListener, 137 Preference.OnPreferenceChangeListener, 138 Preference.OnPreferenceClickListener { 139 140 @Override 141 public void onCreatePreferences(Bundle bundle, String rootKey) { 142 addPreferencesFromResource(R.xml.settings); 143 loadTimeZoneList(); 144 } 145 146 @Override 147 public void onActivityCreated(Bundle savedInstanceState) { 148 super.onActivityCreated(savedInstanceState); 149 150 // By default, do not recreate the DeskClock activity 151 getActivity().setResult(RESULT_CANCELED); 152 } 153 154 @Override 155 public void onResume() { 156 super.onResume(); 157 refresh(); 158 } 159 160 @Override 161 public void onDisplayPreferenceDialog(Preference preference) { 162 final String key = preference.getKey(); 163 switch (key) { 164 case KEY_ALARM_SNOOZE: 165 showDialog(SnoozeLengthDialogFragment.newInstance(preference)); 166 break; 167 case KEY_ALARM_CRESCENDO: 168 case KEY_TIMER_CRESCENDO: 169 showDialog(CrescendoLengthDialogFragment.newInstance(preference)); 170 break; 171 default: 172 super.onDisplayPreferenceDialog(preference); 173 } 174 } 175 176 @Override 177 public boolean onPreferenceChange(Preference pref, Object newValue) { 178 final int idx; 179 switch (pref.getKey()) { 180 case KEY_AUTO_SILENCE: 181 String delay = (String) newValue; 182 updateAutoSnoozeSummary((ListPreference) pref, delay); 183 break; 184 case KEY_CLOCK_STYLE: 185 final ListPreference clockStylePref = (ListPreference) pref; 186 idx = clockStylePref.findIndexOfValue((String) newValue); 187 clockStylePref.setSummary(clockStylePref.getEntries()[idx]); 188 break; 189 case KEY_HOME_TZ: 190 final ListPreference homeTimezonePref = (ListPreference) pref; 191 idx = homeTimezonePref.findIndexOfValue((String) newValue); 192 homeTimezonePref.setSummary(homeTimezonePref.getEntries()[idx]); 193 break; 194 case KEY_AUTO_HOME_CLOCK: 195 final boolean autoHomeClockEnabled = ((TwoStatePreference) pref).isChecked(); 196 final Preference homeTimeZonePref = findPreference(KEY_HOME_TZ); 197 homeTimeZonePref.setEnabled(!autoHomeClockEnabled); 198 break; 199 case KEY_VOLUME_BUTTONS: 200 final ListPreference volumeButtonsPref = (ListPreference) pref; 201 final int index = volumeButtonsPref.findIndexOfValue((String) newValue); 202 volumeButtonsPref.setSummary(volumeButtonsPref.getEntries()[index]); 203 break; 204 case KEY_WEEK_START: 205 final ListPreference weekStartPref = (ListPreference) pref; 206 idx = weekStartPref.findIndexOfValue((String) newValue); 207 weekStartPref.setSummary(weekStartPref.getEntries()[idx]); 208 break; 209 case KEY_TIMER_VIBRATE: 210 final TwoStatePreference timerVibratePref = (TwoStatePreference) pref; 211 DataModel.getDataModel().setTimerVibrate(timerVibratePref.isChecked()); 212 break; 213 case KEY_TIMER_RINGTONE: 214 pref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle()); 215 break; 216 } 217 // Set result so DeskClock knows to refresh itself 218 getActivity().setResult(RESULT_OK); 219 return true; 220 } 221 222 @Override 223 public boolean onPreferenceClick(Preference pref) { 224 final Activity activity = getActivity(); 225 if (activity == null) { 226 return false; 227 } 228 229 switch (pref.getKey()) { 230 case KEY_DATE_TIME: 231 final Intent dialogIntent = new Intent(Settings.ACTION_DATE_SETTINGS); 232 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 233 startActivity(dialogIntent); 234 return true; 235 case KEY_TIMER_RINGTONE: 236 new RingtonePickerDialogFragment.Builder() 237 .setTitle(R.string.timer_ringtone_title) 238 .setDefaultRingtoneTitle(R.string.default_timer_ringtone_title) 239 .setDefaultRingtoneUri( 240 DataModel.getDataModel().getDefaultTimerRingtoneUri()) 241 .setExistingRingtoneUri(DataModel.getDataModel().getTimerRingtoneUri()) 242 .show(getChildFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG); 243 default: 244 return false; 245 } 246 } 247 248 @Override 249 public void onRingtoneSelected(String tag, Uri ringtoneUri) { 250 DataModel.getDataModel().setTimerRingtoneUri(ringtoneUri); 251 252 // Manually call onPreferenceChange since PreferenceFragment doesn't listen to 253 // external changes via SharedPreferences. 254 onPreferenceChange(findPreference(KEY_TIMER_RINGTONE), ringtoneUri); 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 ListPreference clockStylePref = (ListPreference) findPreference(KEY_CLOCK_STYLE); 312 clockStylePref.setSummary(clockStylePref.getEntry()); 313 clockStylePref.setOnPreferenceChangeListener(this); 314 315 final Preference autoHomeClockPref = findPreference(KEY_AUTO_HOME_CLOCK); 316 final boolean autoHomeClockEnabled = 317 ((TwoStatePreference) autoHomeClockPref).isChecked(); 318 autoHomeClockPref.setOnPreferenceChangeListener(this); 319 320 final ListPreference homeTimezonePref = (ListPreference) findPreference(KEY_HOME_TZ); 321 homeTimezonePref.setEnabled(autoHomeClockEnabled); 322 homeTimezonePref.setSummary(homeTimezonePref.getEntry()); 323 homeTimezonePref.setOnPreferenceChangeListener(this); 324 325 final ListPreference volumeButtonsPref = 326 (ListPreference) findPreference(KEY_VOLUME_BUTTONS); 327 volumeButtonsPref.setSummary(volumeButtonsPref.getEntry()); 328 volumeButtonsPref.setOnPreferenceChangeListener(this); 329 330 ((SnoozeLengthDialogPreference) findPreference(KEY_ALARM_SNOOZE)).updateSummary(); 331 ((CrescendoLengthDialogPreference) findPreference(KEY_ALARM_CRESCENDO)).updateSummary(); 332 ((CrescendoLengthDialogPreference) findPreference(KEY_TIMER_CRESCENDO)).updateSummary(); 333 334 final Preference dateAndTimeSetting = findPreference(KEY_DATE_TIME); 335 dateAndTimeSetting.setOnPreferenceClickListener(this); 336 337 final ListPreference weekStartPref = (ListPreference) findPreference(KEY_WEEK_START); 338 // Set the default value programmatically 339 final String value = weekStartPref.getValue(); 340 final int idx = weekStartPref.findIndexOfValue( 341 value == null ? String.valueOf(Utils.DEFAULT_WEEK_START) : value); 342 weekStartPref.setValueIndex(idx); 343 weekStartPref.setSummary(weekStartPref.getEntries()[idx]); 344 weekStartPref.setOnPreferenceChangeListener(this); 345 346 final Preference timerRingtonePref = findPreference(KEY_TIMER_RINGTONE); 347 timerRingtonePref.setOnPreferenceClickListener(this); 348 timerRingtonePref.setSummary(DataModel.getDataModel().getTimerRingtoneTitle()); 349 } 350 351 private void updateAutoSnoozeSummary(ListPreference listPref, String delay) { 352 int i = Integer.parseInt(delay); 353 if (i == -1) { 354 listPref.setSummary(R.string.auto_silence_never); 355 } else { 356 listPref.setSummary(Utils.getNumberFormattedQuantityString(getActivity(), 357 R.plurals.auto_silence_summary, i)); 358 } 359 } 360 361 private void showDialog(PreferenceDialogFragment fragment) { 362 // Always set the target fragment, this is required by PreferenceDialogFragment 363 // internally. 364 fragment.setTargetFragment(this, 0); 365 // Don't use getChildFragmentManager(), it causes issues on older platforms when the 366 // target fragment is being restored after an orientation change. 367 fragment.show(getFragmentManager(), PREFERENCE_DIALOG_FRAGMENT_TAG); 368 } 369 370 private static class TimeZoneRow implements Comparable<TimeZoneRow> { 371 372 public final String mId; 373 public final String mDisplayName; 374 public final int mOffset; 375 376 public TimeZoneRow(String id, String name, long currentTimeMillis) { 377 final TimeZone tz = TimeZone.getTimeZone(id); 378 final boolean useDaylightTime = tz.useDaylightTime(); 379 mId = id; 380 mOffset = tz.getOffset(currentTimeMillis); 381 mDisplayName = buildGmtDisplayName(name, useDaylightTime); 382 } 383 384 @Override 385 public int compareTo(@NonNull TimeZoneRow another) { 386 return mOffset - another.mOffset; 387 } 388 389 public String buildGmtDisplayName(String displayName, boolean useDaylightTime) { 390 final int p = Math.abs(mOffset); 391 final StringBuilder name = new StringBuilder("(GMT"); 392 name.append(mOffset < 0 ? '-' : '+'); 393 394 name.append(p / DateUtils.HOUR_IN_MILLIS); 395 name.append(':'); 396 397 int min = p / 60000; 398 min %= 60; 399 400 if (min < 10) { 401 name.append('0'); 402 } 403 name.append(min); 404 name.append(") "); 405 name.append(displayName); 406 return name.toString(); 407 } 408 } 409 } 410} 411