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