SetAlarm.java revision 748e9ada696912c262a097d4211fde528f1cc0b8
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 17package com.android.deskclock; 18 19import android.app.ActionBar; 20import android.app.AlertDialog; 21import android.app.TimePickerDialog; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.DialogInterface.OnCancelListener; 25import android.content.Intent; 26import android.os.Bundle; 27import android.os.Handler; 28import android.os.Vibrator; 29import android.preference.CheckBoxPreference; 30import android.preference.Preference; 31import android.preference.PreferenceActivity; 32import android.preference.PreferenceScreen; 33import android.text.format.DateFormat; 34import android.view.LayoutInflater; 35import android.view.Menu; 36import android.view.MenuItem; 37import android.view.View; 38import android.view.View.OnClickListener; 39import android.widget.Button; 40import android.widget.EditText; 41import android.widget.ListView; 42import android.widget.TimePicker; 43import android.widget.Toast; 44 45/** 46 * Manages each alarm 47 */ 48public class SetAlarm extends PreferenceActivity implements Preference.OnPreferenceChangeListener, 49 TimePickerDialog.OnTimeSetListener, OnCancelListener { 50 51 private static final String KEY_CURRENT_ALARM = "currentAlarm"; 52 private static final String KEY_ORIGINAL_ALARM = "originalAlarm"; 53 private static final String KEY_TIME_PICKER_BUNDLE = "timePickerBundle"; 54 55 private EditText mLabel; 56 private CheckBoxPreference mEnabledPref; 57 private Preference mTimePref; 58 private AlarmPreference mAlarmPref; 59 private CheckBoxPreference mVibratePref; 60 private RepeatPreference mRepeatPref; 61 62 private int mId; 63 private int mHour; 64 private int mMinute; 65 private TimePickerDialog mTimePickerDialog; 66 private Alarm mOriginalAlarm; 67 68 @Override 69 protected void onCreate(Bundle icicle) { 70 super.onCreate(icicle); 71 72 // Override the default content view. 73 setContentView(R.layout.set_alarm); 74 75 EditText label = (EditText) getLayoutInflater().inflate(R.layout.alarm_label, null); 76 ListView list = (ListView) findViewById(android.R.id.list); 77 list.addFooterView(label); 78 79 // TODO Stop using preferences for this view. Save on done, not after 80 // each change. 81 addPreferencesFromResource(R.xml.alarm_prefs); 82 83 // Get each preference so we can retrieve the value later. 84 mLabel = label; 85 mEnabledPref = (CheckBoxPreference) findPreference("enabled"); 86 mEnabledPref.setOnPreferenceChangeListener(this); 87 mTimePref = findPreference("time"); 88 mAlarmPref = (AlarmPreference) findPreference("alarm"); 89 mAlarmPref.setOnPreferenceChangeListener(this); 90 mVibratePref = (CheckBoxPreference) findPreference("vibrate"); 91 mVibratePref.setOnPreferenceChangeListener(this); 92 Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); 93 if (!v.hasVibrator()) { 94 getPreferenceScreen().removePreference(mVibratePref); 95 } 96 mRepeatPref = (RepeatPreference) findPreference("setRepeat"); 97 mRepeatPref.setOnPreferenceChangeListener(this); 98 99 Intent i = getIntent(); 100 Alarm alarm = i.getParcelableExtra(Alarms.ALARM_INTENT_EXTRA); 101 102 if (alarm == null) { 103 // No alarm means create a new alarm. 104 alarm = new Alarm(); 105 } 106 mOriginalAlarm = alarm; 107 108 // Populate the prefs with the original alarm data. updatePrefs also 109 // sets mId so it must be called before checking mId below. 110 updatePrefs(mOriginalAlarm); 111 112 // We have to do this to get the save/cancel buttons to highlight on 113 // their own. 114 getListView().setItemsCanFocus(true); 115 116 ActionBar actionBar = getActionBar(); 117 if (actionBar != null) { 118 actionBar.setDisplayOptions( 119 0, ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); 120 LayoutInflater inflater = (LayoutInflater) getSystemService 121 (Context.LAYOUT_INFLATER_SERVICE); 122 View customActionBarView = inflater.inflate(R.layout.set_alarm_action_bar, null); 123 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 124 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | 125 ActionBar.DISPLAY_SHOW_TITLE); 126 actionBar.setCustomView(customActionBarView); 127 View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item); 128 saveMenuItem.setOnClickListener(new OnClickListener() { 129 @Override 130 public void onClick(View v) { 131 saveAndExit(); 132 } 133 }); 134 } 135 } 136 137 @Override 138 public boolean onOptionsItemSelected(MenuItem item) { 139 if (item.getItemId() == R.id.menu_delete) { 140 deleteAlarm(); 141 return true; 142 } 143 return super.onOptionsItemSelected(item); 144 } 145 146 @Override 147 public boolean onCreateOptionsMenu(Menu menu) { 148 getMenuInflater().inflate(R.menu.set_alarm_context, menu); 149 return true; 150 } 151 152 @Override 153 protected void onSaveInstanceState(Bundle outState) { 154 super.onSaveInstanceState(outState); 155 outState.putParcelable(KEY_ORIGINAL_ALARM, mOriginalAlarm); 156 outState.putParcelable(KEY_CURRENT_ALARM, buildAlarmFromUi()); 157 if (mTimePickerDialog != null) { 158 if (mTimePickerDialog.isShowing()) { 159 outState.putParcelable(KEY_TIME_PICKER_BUNDLE, mTimePickerDialog 160 .onSaveInstanceState()); 161 mTimePickerDialog.dismiss(); 162 } 163 mTimePickerDialog = null; 164 } 165 } 166 167 @Override 168 protected void onRestoreInstanceState(Bundle state) { 169 super.onRestoreInstanceState(state); 170 171 Alarm alarmFromBundle = state.getParcelable(KEY_ORIGINAL_ALARM); 172 if (alarmFromBundle != null) { 173 mOriginalAlarm = alarmFromBundle; 174 } 175 176 alarmFromBundle = state.getParcelable(KEY_CURRENT_ALARM); 177 if (alarmFromBundle != null) { 178 updatePrefs(alarmFromBundle); 179 } 180 181 Bundle b = state.getParcelable(KEY_TIME_PICKER_BUNDLE); 182 if (b != null) { 183 showTimePicker(); 184 mTimePickerDialog.onRestoreInstanceState(b); 185 } 186 } 187 188 // Used to post runnables asynchronously. 189 private static final Handler sHandler = new Handler(); 190 191 public boolean onPreferenceChange(final Preference p, Object newValue) { 192 // Asynchronously save the alarm since this method is called _before_ 193 // the value of the preference has changed. 194 sHandler.post(new Runnable() { 195 public void run() { 196 // Editing any preference (except enable) enables the alarm. 197 if (p != mEnabledPref) { 198 mEnabledPref.setChecked(true); 199 } 200 saveAlarm(null); 201 } 202 }); 203 return true; 204 } 205 206 private void updatePrefs(Alarm alarm) { 207 mId = alarm.id; 208 mEnabledPref.setChecked(alarm.enabled); 209 mLabel.setText(alarm.label); 210 mHour = alarm.hour; 211 mMinute = alarm.minutes; 212 mRepeatPref.setDaysOfWeek(alarm.daysOfWeek); 213 mVibratePref.setChecked(alarm.vibrate); 214 // Give the alert uri to the preference. 215 mAlarmPref.setAlert(alarm.alert); 216 updateTime(); 217 } 218 219 @Override 220 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, 221 Preference preference) { 222 if (preference == mTimePref) { 223 showTimePicker(); 224 } 225 226 return super.onPreferenceTreeClick(preferenceScreen, preference); 227 } 228 229 @Override 230 public void onBackPressed() { 231 saveAndExit(); 232 } 233 234 private void showTimePicker() { 235 if (mTimePickerDialog != null) { 236 if (mTimePickerDialog.isShowing()) { 237 Log.e("mTimePickerDialog is already showing."); 238 mTimePickerDialog.dismiss(); 239 } else { 240 Log.e("mTimePickerDialog is not null"); 241 } 242 mTimePickerDialog = null; 243 } 244 245 mTimePickerDialog = new TimePickerDialog(this, this, mHour, mMinute, 246 DateFormat.is24HourFormat(this)); 247 mTimePickerDialog.setOnCancelListener(this); 248 mTimePickerDialog.show(); 249 } 250 251 public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 252 // onTimeSet is called when the user clicks "Set" 253 mTimePickerDialog = null; 254 mHour = hourOfDay; 255 mMinute = minute; 256 updateTime(); 257 // If the time has been changed, enable the alarm. 258 mEnabledPref.setChecked(true); 259 saveAlarm(null); 260 } 261 262 @Override 263 public void onCancel(DialogInterface dialog) { 264 mTimePickerDialog = null; 265 } 266 267 private void updateTime() { 268 mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinute, 269 mRepeatPref.getDaysOfWeek())); 270 } 271 272 private long saveAlarm(Alarm alarm) { 273 if (alarm == null) { 274 alarm = buildAlarmFromUi(); 275 } 276 277 long time; 278 if (alarm.id == -1) { 279 time = Alarms.addAlarm(this, alarm); 280 // addAlarm populates the alarm with the new id. Update mId so that 281 // changes to other preferences update the new alarm. 282 mId = alarm.id; 283 } else { 284 time = Alarms.setAlarm(this, alarm); 285 } 286 return time; 287 } 288 289 private Alarm buildAlarmFromUi() { 290 Alarm alarm = new Alarm(); 291 alarm.id = mId; 292 alarm.enabled = mEnabledPref.isChecked(); 293 alarm.hour = mHour; 294 alarm.minutes = mMinute; 295 alarm.daysOfWeek = mRepeatPref.getDaysOfWeek(); 296 alarm.vibrate = mVibratePref.isChecked(); 297 alarm.label = mLabel.getText().toString(); 298 alarm.alert = mAlarmPref.getAlert(); 299 return alarm; 300 } 301 302 private void deleteAlarm() { 303 if (mId == -1) { 304 // Unedited, newly created alarms don't require confirmation 305 finish(); 306 } else { 307 new AlertDialog.Builder(this) 308 .setTitle(getString(R.string.delete_alarm)) 309 .setMessage(getString(R.string.delete_alarm_confirm)) 310 .setPositiveButton(android.R.string.ok, 311 new DialogInterface.OnClickListener() { 312 public void onClick(DialogInterface d, int w) { 313 Alarms.deleteAlarm(SetAlarm.this, mId); 314 finish(); 315 } 316 }) 317 .setNegativeButton(android.R.string.cancel, null) 318 .show(); 319 } 320 } 321 322 /** 323 * Store any changes to the alarm and exit the activity. 324 * Show a toast if the alarm is enabled with the time remaining until alarm 325 */ 326 private void saveAndExit() { 327 long time = saveAlarm(null); 328 if(mEnabledPref.isChecked()) { 329 popAlarmSetToast(SetAlarm.this, time); 330 } 331 finish(); 332 } 333 334 /** 335 * Display a toast that tells the user how long until the alarm 336 * goes off. This helps prevent "am/pm" mistakes. 337 */ 338 static void popAlarmSetToast(Context context, int hour, int minute, 339 Alarm.DaysOfWeek daysOfWeek) { 340 popAlarmSetToast(context, 341 Alarms.calculateAlarm(hour, minute, daysOfWeek) 342 .getTimeInMillis()); 343 } 344 345 static void popAlarmSetToast(Context context, long timeInMillis) { 346 String toastText = formatToast(context, timeInMillis); 347 Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG); 348 ToastMaster.setToast(toast); 349 toast.show(); 350 } 351 352 /** 353 * format "Alarm set for 2 days 7 hours and 53 minutes from 354 * now" 355 */ 356 static String formatToast(Context context, long timeInMillis) { 357 long delta = timeInMillis - System.currentTimeMillis(); 358 long hours = delta / (1000 * 60 * 60); 359 long minutes = delta / (1000 * 60) % 60; 360 long days = hours / 24; 361 hours = hours % 24; 362 363 String daySeq = (days == 0) ? "" : 364 (days == 1) ? context.getString(R.string.day) : 365 context.getString(R.string.days, Long.toString(days)); 366 367 String minSeq = (minutes == 0) ? "" : 368 (minutes == 1) ? context.getString(R.string.minute) : 369 context.getString(R.string.minutes, Long.toString(minutes)); 370 371 String hourSeq = (hours == 0) ? "" : 372 (hours == 1) ? context.getString(R.string.hour) : 373 context.getString(R.string.hours, Long.toString(hours)); 374 375 boolean dispDays = days > 0; 376 boolean dispHour = hours > 0; 377 boolean dispMinute = minutes > 0; 378 379 int index = (dispDays ? 1 : 0) | 380 (dispHour ? 2 : 0) | 381 (dispMinute ? 4 : 0); 382 383 String[] formats = context.getResources().getStringArray(R.array.alarm_set); 384 return String.format(formats[index], daySeq, hourSeq, minSeq); 385 } 386} 387