1 /* 2 * Copyright (C) 2010 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.Activity; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.net.Uri; 24 import android.os.AsyncTask; 25 import android.os.Bundle; 26 import android.os.Parcelable; 27 import android.provider.AlarmClock; 28 import android.text.TextUtils; 29 import android.text.format.DateFormat; 30 31 import com.android.deskclock.alarms.AlarmStateManager; 32 import com.android.deskclock.controller.Controller; 33 import com.android.deskclock.data.DataModel; 34 import com.android.deskclock.data.Timer; 35 import com.android.deskclock.data.Weekdays; 36 import com.android.deskclock.events.Events; 37 import com.android.deskclock.provider.Alarm; 38 import com.android.deskclock.provider.AlarmInstance; 39 import com.android.deskclock.timer.TimerFragment; 40 import com.android.deskclock.timer.TimerService; 41 import com.android.deskclock.uidata.UiDataModel; 42 43 import java.util.ArrayList; 44 import java.util.Calendar; 45 import java.util.Date; 46 import java.util.Iterator; 47 import java.util.List; 48 49 import static android.text.format.DateUtils.SECOND_IN_MILLIS; 50 import static com.android.deskclock.AlarmSelectionActivity.ACTION_DISMISS; 51 import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ACTION; 52 import static com.android.deskclock.AlarmSelectionActivity.EXTRA_ALARMS; 53 import static com.android.deskclock.provider.AlarmInstance.FIRED_STATE; 54 import static com.android.deskclock.provider.AlarmInstance.SNOOZE_STATE; 55 import static com.android.deskclock.uidata.UiDataModel.Tab.ALARMS; 56 import static com.android.deskclock.uidata.UiDataModel.Tab.TIMERS; 57 58 /** 59 * This activity is never visible. It processes all public intents defined by {@link AlarmClock} 60 * that apply to alarms and timers. Its definition in AndroidManifest.xml requires callers to hold 61 * the com.android.alarm.permission.SET_ALARM permission to complete the requested action. 62 */ 63 public class HandleApiCalls extends Activity { 64 65 private static final LogUtils.Logger LOGGER = new LogUtils.Logger("HandleApiCalls"); 66 67 static final String ACTION_SHOW_TIMERS = "android.intent.action.SHOW_TIMERS"; 68 69 private Context mAppContext; 70 71 @Override onCreate(Bundle icicle)72 protected void onCreate(Bundle icicle) { 73 super.onCreate(icicle); 74 75 mAppContext = getApplicationContext(); 76 77 try { 78 final Intent intent = getIntent(); 79 final String action = intent == null ? null : intent.getAction(); 80 if (action == null) { 81 return; 82 } 83 LOGGER.i("onCreate: " + intent); 84 85 switch (action) { 86 case AlarmClock.ACTION_SET_ALARM: 87 handleSetAlarm(intent); 88 break; 89 case AlarmClock.ACTION_SHOW_ALARMS: 90 handleShowAlarms(intent); 91 break; 92 case AlarmClock.ACTION_SET_TIMER: 93 handleSetTimer(intent); 94 break; 95 case ACTION_SHOW_TIMERS: 96 handleShowTimers(intent); 97 break; 98 case AlarmClock.ACTION_DISMISS_ALARM: 99 handleDismissAlarm(intent); 100 break; 101 case AlarmClock.ACTION_SNOOZE_ALARM: 102 handleSnoozeAlarm(intent); 103 break; 104 } 105 } catch (Exception e) { 106 LOGGER.wtf(e); 107 } finally { 108 finish(); 109 } 110 } 111 112 handleDismissAlarm(Intent intent)113 private void handleDismissAlarm(Intent intent) { 114 // Change to the alarms tab. 115 UiDataModel.getUiDataModel().setSelectedTab(ALARMS); 116 117 // Open DeskClock which is now positioned on the alarms tab. 118 startActivity(new Intent(mAppContext, DeskClock.class)); 119 120 new DismissAlarmAsync(mAppContext, intent, this).execute(); 121 } 122 dismissAlarm(Alarm alarm, Activity activity)123 public static void dismissAlarm(Alarm alarm, Activity activity) { 124 final Context context = activity.getApplicationContext(); 125 final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( 126 context.getContentResolver(), alarm.id); 127 if (instance == null) { 128 final String reason = context.getString(R.string.no_alarm_scheduled_for_this_time); 129 Controller.getController().notifyVoiceFailure(activity, reason); 130 LOGGER.i("No alarm instance to dismiss"); 131 return; 132 } 133 134 dismissAlarmInstance(instance, activity); 135 } 136 dismissAlarmInstance(AlarmInstance instance, Activity activity)137 public static void dismissAlarmInstance(AlarmInstance instance, Activity activity) { 138 Utils.enforceNotMainLooper(); 139 140 final Context context = activity.getApplicationContext(); 141 final Date alarmTime = instance.getAlarmTime().getTime(); 142 final String time = DateFormat.getTimeFormat(context).format(alarmTime); 143 144 if (instance.mAlarmState == FIRED_STATE || instance.mAlarmState == SNOOZE_STATE) { 145 // Always dismiss alarms that are fired or snoozed. 146 AlarmStateManager.deleteInstanceAndUpdateParent(context, instance); 147 } else if (Utils.isAlarmWithin24Hours(instance)) { 148 // Upcoming alarms are always predismissed. 149 AlarmStateManager.setPreDismissState(context, instance); 150 } else { 151 // Otherwise the alarm cannot be dismissed at this time. 152 final String reason = context.getString( 153 R.string.alarm_cant_be_dismissed_still_more_than_24_hours_away, time); 154 Controller.getController().notifyVoiceFailure(activity, reason); 155 LOGGER.i("Can't dismiss alarm more than 24 hours in advance"); 156 } 157 158 // Log the successful dismissal. 159 final String reason = context.getString(R.string.alarm_is_dismissed, time); 160 Controller.getController().notifyVoiceSuccess(activity, reason); 161 LOGGER.i("Alarm dismissed: " + instance); 162 Events.sendAlarmEvent(R.string.action_dismiss, R.string.label_intent); 163 } 164 165 private static class DismissAlarmAsync extends AsyncTask<Void, Void, Void> { 166 167 private final Context mContext; 168 private final Intent mIntent; 169 private final Activity mActivity; 170 DismissAlarmAsync(Context context, Intent intent, Activity activity)171 public DismissAlarmAsync(Context context, Intent intent, Activity activity) { 172 mContext = context; 173 mIntent = intent; 174 mActivity = activity; 175 } 176 177 @Override doInBackground(Void... parameters)178 protected Void doInBackground(Void... parameters) { 179 final ContentResolver cr = mContext.getContentResolver(); 180 final List<Alarm> alarms = getEnabledAlarms(mContext); 181 if (alarms.isEmpty()) { 182 final String reason = mContext.getString(R.string.no_scheduled_alarms); 183 Controller.getController().notifyVoiceFailure(mActivity, reason); 184 LOGGER.i("No scheduled alarms"); 185 return null; 186 } 187 188 // remove Alarms in MISSED, DISMISSED, and PREDISMISSED states 189 for (Iterator<Alarm> i = alarms.iterator(); i.hasNext();) { 190 final AlarmInstance instance = AlarmInstance.getNextUpcomingInstanceByAlarmId( 191 cr, i.next().id); 192 if (instance == null || instance.mAlarmState > FIRED_STATE) { 193 i.remove(); 194 } 195 } 196 197 final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE); 198 if (searchMode == null && alarms.size() > 1) { 199 // shows the UI where user picks which alarm they want to DISMISS 200 final Intent pickSelectionIntent = new Intent(mContext, 201 AlarmSelectionActivity.class) 202 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 203 .putExtra(EXTRA_ACTION, ACTION_DISMISS) 204 .putExtra(EXTRA_ALARMS, alarms.toArray(new Parcelable[alarms.size()])); 205 mContext.startActivity(pickSelectionIntent); 206 final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); 207 Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); 208 return null; 209 } 210 211 // fetch the alarms that are specified by the intent 212 final FetchMatchingAlarmsAction fmaa = 213 new FetchMatchingAlarmsAction(mContext, alarms, mIntent, mActivity); 214 fmaa.run(); 215 final List<Alarm> matchingAlarms = fmaa.getMatchingAlarms(); 216 217 // If there are multiple matching alarms and it wasn't expected 218 // disambiguate what the user meant 219 if (!AlarmClock.ALARM_SEARCH_MODE_ALL.equals(searchMode) && matchingAlarms.size() > 1) { 220 final Intent pickSelectionIntent = new Intent(mContext, AlarmSelectionActivity.class) 221 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 222 .putExtra(EXTRA_ACTION, ACTION_DISMISS) 223 .putExtra(EXTRA_ALARMS, 224 matchingAlarms.toArray(new Parcelable[matchingAlarms.size()])); 225 mContext.startActivity(pickSelectionIntent); 226 final String voiceMessage = mContext.getString(R.string.pick_alarm_to_dismiss); 227 Controller.getController().notifyVoiceSuccess(mActivity, voiceMessage); 228 return null; 229 } 230 231 // Apply the action to the matching alarms 232 for (Alarm alarm : matchingAlarms) { 233 dismissAlarm(alarm, mActivity); 234 LOGGER.i("Alarm dismissed: " + alarm); 235 } 236 return null; 237 } 238 getEnabledAlarms(Context context)239 private static List<Alarm> getEnabledAlarms(Context context) { 240 final String selection = String.format("%s=?", Alarm.ENABLED); 241 final String[] args = { "1" }; 242 return Alarm.getAlarms(context.getContentResolver(), selection, args); 243 } 244 } 245 handleSnoozeAlarm(Intent intent)246 private void handleSnoozeAlarm(Intent intent) { 247 new SnoozeAlarmAsync(intent, this).execute(); 248 } 249 250 private static class SnoozeAlarmAsync extends AsyncTask<Void, Void, Void> { 251 252 private final Context mContext; 253 private final Intent mIntent; 254 private final Activity mActivity; 255 SnoozeAlarmAsync(Intent intent, Activity activity)256 public SnoozeAlarmAsync(Intent intent, Activity activity) { 257 mContext = activity.getApplicationContext(); 258 mIntent = intent; 259 mActivity = activity; 260 } 261 262 @Override doInBackground(Void... parameters)263 protected Void doInBackground(Void... parameters) { 264 final ContentResolver cr = mContext.getContentResolver(); 265 final List<AlarmInstance> alarmInstances = AlarmInstance.getInstancesByState( 266 cr, FIRED_STATE); 267 if (alarmInstances.isEmpty()) { 268 final String reason = mContext.getString(R.string.no_firing_alarms); 269 Controller.getController().notifyVoiceFailure(mActivity, reason); 270 LOGGER.i("No firing alarms"); 271 return null; 272 } 273 274 for (AlarmInstance firingAlarmInstance : alarmInstances) { 275 snoozeAlarm(firingAlarmInstance, mContext, mActivity); 276 } 277 return null; 278 } 279 } 280 snoozeAlarm(AlarmInstance alarmInstance, Context context, Activity activity)281 static void snoozeAlarm(AlarmInstance alarmInstance, Context context, Activity activity) { 282 Utils.enforceNotMainLooper(); 283 284 final String time = DateFormat.getTimeFormat(context).format( 285 alarmInstance.getAlarmTime().getTime()); 286 final String reason = context.getString(R.string.alarm_is_snoozed, time); 287 AlarmStateManager.setSnoozeState(context, alarmInstance, true); 288 289 Controller.getController().notifyVoiceSuccess(activity, reason); 290 LOGGER.i("Alarm snoozed: " + alarmInstance); 291 Events.sendAlarmEvent(R.string.action_snooze, R.string.label_intent); 292 } 293 294 /*** 295 * Processes the SET_ALARM intent 296 * @param intent Intent passed to the app 297 */ handleSetAlarm(Intent intent)298 private void handleSetAlarm(Intent intent) { 299 // Validate the hour, if one was given. 300 int hour = -1; 301 if (intent.hasExtra(AlarmClock.EXTRA_HOUR)) { 302 hour = intent.getIntExtra(AlarmClock.EXTRA_HOUR, hour); 303 if (hour < 0 || hour > 23) { 304 final int mins = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0); 305 final String voiceMessage = getString(R.string.invalid_time, hour, mins, " "); 306 Controller.getController().notifyVoiceFailure(this, voiceMessage); 307 LOGGER.i("Illegal hour: " + hour); 308 return; 309 } 310 } 311 312 // Validate the minute, if one was given. 313 final int minutes = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0); 314 if (minutes < 0 || minutes > 59) { 315 final String voiceMessage = getString(R.string.invalid_time, hour, minutes, " "); 316 Controller.getController().notifyVoiceFailure(this, voiceMessage); 317 LOGGER.i("Illegal minute: " + minutes); 318 return; 319 } 320 321 final boolean skipUi = intent.getBooleanExtra(AlarmClock.EXTRA_SKIP_UI, false); 322 final ContentResolver cr = getContentResolver(); 323 324 // If time information was not provided an existing alarm cannot be located and a new one 325 // cannot be created so show the UI for creating the alarm from scratch per spec. 326 if (hour == -1) { 327 // Change to the alarms tab. 328 UiDataModel.getUiDataModel().setSelectedTab(ALARMS); 329 330 // Intent has no time or an invalid time, open the alarm creation UI. 331 final Intent createAlarm = Alarm.createIntent(this, DeskClock.class, Alarm.INVALID_ID) 332 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 333 .putExtra(AlarmClockFragment.ALARM_CREATE_NEW_INTENT_EXTRA, true); 334 335 // Open DeskClock which is now positioned on the alarms tab. 336 startActivity(createAlarm); 337 final String voiceMessage = getString(R.string.invalid_time, hour, minutes, " "); 338 Controller.getController().notifyVoiceFailure(this, voiceMessage); 339 LOGGER.i("Missing alarm time; opening UI"); 340 return; 341 } 342 343 final StringBuilder selection = new StringBuilder(); 344 final List<String> argsList = new ArrayList<>(); 345 setSelectionFromIntent(intent, hour, minutes, selection, argsList); 346 347 // Try to locate an existing alarm using the intent data. 348 final String[] args = argsList.toArray(new String[argsList.size()]); 349 final List<Alarm> alarms = Alarm.getAlarms(cr, selection.toString(), args); 350 351 final Alarm alarm; 352 if (!alarms.isEmpty()) { 353 // Enable the first matching alarm. 354 alarm = alarms.get(0); 355 alarm.enabled = true; 356 Alarm.updateAlarm(cr, alarm); 357 358 // Delete all old instances. 359 AlarmStateManager.deleteAllInstances(this, alarm.id); 360 361 Events.sendAlarmEvent(R.string.action_update, R.string.label_intent); 362 LOGGER.i("Updated alarm: " + alarm); 363 } else { 364 // No existing alarm could be located; create one using the intent data. 365 alarm = new Alarm(); 366 updateAlarmFromIntent(alarm, intent); 367 alarm.deleteAfterUse = !alarm.daysOfWeek.isRepeating() && skipUi; 368 369 // Save the new alarm. 370 Alarm.addAlarm(cr, alarm); 371 372 Events.sendAlarmEvent(R.string.action_create, R.string.label_intent); 373 LOGGER.i("Created new alarm: " + alarm); 374 } 375 376 // Schedule the next instance. 377 final Calendar now = DataModel.getDataModel().getCalendar(); 378 final AlarmInstance alarmInstance = alarm.createInstanceAfter(now); 379 setupInstance(alarmInstance, skipUi); 380 381 final String time = DateFormat.getTimeFormat(this) 382 .format(alarmInstance.getAlarmTime().getTime()); 383 Controller.getController().notifyVoiceSuccess(this, getString(R.string.alarm_is_set, time)); 384 } 385 handleShowAlarms(Intent intent)386 private void handleShowAlarms(Intent intent) { 387 Events.sendAlarmEvent(R.string.action_show, R.string.label_intent); 388 389 // Open DeskClock positioned on the alarms tab. 390 UiDataModel.getUiDataModel().setSelectedTab(ALARMS); 391 startActivity(new Intent(this, DeskClock.class)); 392 } 393 handleShowTimers(Intent intent)394 private void handleShowTimers(Intent intent) { 395 Events.sendTimerEvent(R.string.action_show, R.string.label_intent); 396 397 final Intent showTimersIntent = new Intent(this, DeskClock.class); 398 399 final List<Timer> timers = DataModel.getDataModel().getTimers(); 400 if (!timers.isEmpty()) { 401 final Timer newestTimer = timers.get(timers.size() - 1); 402 showTimersIntent.putExtra(TimerService.EXTRA_TIMER_ID, newestTimer.getId()); 403 } 404 405 // Open DeskClock positioned on the timers tab. 406 UiDataModel.getUiDataModel().setSelectedTab(TIMERS); 407 startActivity(showTimersIntent); 408 } 409 handleSetTimer(Intent intent)410 private void handleSetTimer(Intent intent) { 411 // If no length is supplied, show the timer setup view. 412 if (!intent.hasExtra(AlarmClock.EXTRA_LENGTH)) { 413 // Change to the timers tab. 414 UiDataModel.getUiDataModel().setSelectedTab(TIMERS); 415 416 // Open DeskClock which is now positioned on the timers tab and show the timer setup. 417 startActivity(TimerFragment.createTimerSetupIntent(this)); 418 LOGGER.i("Showing timer setup"); 419 return; 420 } 421 422 // Verify that the timer length is between one second and one day. 423 final long lengthMillis = SECOND_IN_MILLIS * intent.getIntExtra(AlarmClock.EXTRA_LENGTH, 0); 424 if (lengthMillis < Timer.MIN_LENGTH) { 425 final String voiceMessage = getString(R.string.invalid_timer_length); 426 Controller.getController().notifyVoiceFailure(this, voiceMessage); 427 LOGGER.i("Invalid timer length requested: " + lengthMillis); 428 return; 429 } 430 431 final String label = getLabelFromIntent(intent, ""); 432 final boolean skipUi = intent.getBooleanExtra(AlarmClock.EXTRA_SKIP_UI, false); 433 434 // Attempt to reuse an existing timer that is Reset with the same length and label. 435 Timer timer = null; 436 for (Timer t : DataModel.getDataModel().getTimers()) { 437 if (!t.isReset()) { continue; } 438 if (t.getLength() != lengthMillis) { continue; } 439 if (!TextUtils.equals(label, t.getLabel())) { continue; } 440 441 timer = t; 442 break; 443 } 444 445 // Create a new timer if one could not be reused. 446 if (timer == null) { 447 timer = DataModel.getDataModel().addTimer(lengthMillis, label, skipUi); 448 Events.sendTimerEvent(R.string.action_create, R.string.label_intent); 449 } 450 451 // Start the selected timer. 452 DataModel.getDataModel().startTimer(timer); 453 Events.sendTimerEvent(R.string.action_start, R.string.label_intent); 454 Controller.getController().notifyVoiceSuccess(this, getString(R.string.timer_created)); 455 456 // If not instructed to skip the UI, display the running timer. 457 if (!skipUi) { 458 // Change to the timers tab. 459 UiDataModel.getUiDataModel().setSelectedTab(TIMERS); 460 461 // Open DeskClock which is now positioned on the timers tab. 462 startActivity(new Intent(this, DeskClock.class) 463 .putExtra(TimerService.EXTRA_TIMER_ID, timer.getId())); 464 } 465 } 466 setupInstance(AlarmInstance instance, boolean skipUi)467 private void setupInstance(AlarmInstance instance, boolean skipUi) { 468 instance = AlarmInstance.addInstance(this.getContentResolver(), instance); 469 AlarmStateManager.registerInstance(this, instance, true); 470 AlarmUtils.popAlarmSetToast(this, instance.getAlarmTime().getTimeInMillis()); 471 if (!skipUi) { 472 // Change to the alarms tab. 473 UiDataModel.getUiDataModel().setSelectedTab(ALARMS); 474 475 // Open DeskClock which is now positioned on the alarms tab. 476 final Intent showAlarm = Alarm.createIntent(this, DeskClock.class, instance.mAlarmId) 477 .putExtra(AlarmClockFragment.SCROLL_TO_ALARM_INTENT_EXTRA, instance.mAlarmId) 478 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 479 startActivity(showAlarm); 480 } 481 } 482 483 /** 484 * @param alarm the alarm to be updated 485 * @param intent the intent containing new alarm field values to merge into the {@code alarm} 486 */ updateAlarmFromIntent(Alarm alarm, Intent intent)487 private static void updateAlarmFromIntent(Alarm alarm, Intent intent) { 488 alarm.enabled = true; 489 alarm.hour = intent.getIntExtra(AlarmClock.EXTRA_HOUR, alarm.hour); 490 alarm.minutes = intent.getIntExtra(AlarmClock.EXTRA_MINUTES, alarm.minutes); 491 alarm.vibrate = intent.getBooleanExtra(AlarmClock.EXTRA_VIBRATE, alarm.vibrate); 492 alarm.alert = getAlertFromIntent(intent, alarm.alert); 493 alarm.label = getLabelFromIntent(intent, alarm.label); 494 alarm.daysOfWeek = getDaysFromIntent(intent, alarm.daysOfWeek); 495 } 496 getLabelFromIntent(Intent intent, String defaultLabel)497 private static String getLabelFromIntent(Intent intent, String defaultLabel) { 498 final String message = intent.getExtras().getString(AlarmClock.EXTRA_MESSAGE, defaultLabel); 499 return message == null ? "" : message; 500 } 501 getDaysFromIntent(Intent intent, Weekdays defaultWeekdays)502 private static Weekdays getDaysFromIntent(Intent intent, Weekdays defaultWeekdays) { 503 if (!intent.hasExtra(AlarmClock.EXTRA_DAYS)) { 504 return defaultWeekdays; 505 } 506 507 final List<Integer> days = intent.getIntegerArrayListExtra(AlarmClock.EXTRA_DAYS); 508 if (days != null) { 509 final int[] daysArray = new int[days.size()]; 510 for (int i = 0; i < days.size(); i++) { 511 daysArray[i] = days.get(i); 512 } 513 return Weekdays.fromCalendarDays(daysArray); 514 } else { 515 // API says to use an ArrayList<Integer> but we allow the user to use a int[] too. 516 final int[] daysArray = intent.getIntArrayExtra(AlarmClock.EXTRA_DAYS); 517 if (daysArray != null) { 518 return Weekdays.fromCalendarDays(daysArray); 519 } 520 } 521 return defaultWeekdays; 522 } 523 getAlertFromIntent(Intent intent, Uri defaultUri)524 private static Uri getAlertFromIntent(Intent intent, Uri defaultUri) { 525 final String alert = intent.getStringExtra(AlarmClock.EXTRA_RINGTONE); 526 if (alert == null) { 527 return defaultUri; 528 } else if (AlarmClock.VALUE_RINGTONE_SILENT.equals(alert) || alert.isEmpty()) { 529 return Alarm.NO_RINGTONE_URI; 530 } 531 532 return Uri.parse(alert); 533 } 534 535 /** 536 * Assemble a database where clause to search for an alarm matching the given {@code hour} and 537 * {@code minutes} as well as all of the optional information within the {@code intent} 538 * including: 539 * 540 * <ul> 541 * <li>alarm message</li> 542 * <li>repeat days</li> 543 * <li>vibration setting</li> 544 * <li>ringtone uri</li> 545 * </ul> 546 * 547 * @param intent contains details of the alarm to be located 548 * @param hour the hour of the day of the alarm 549 * @param minutes the minute of the hour of the alarm 550 * @param selection an out parameter containing a SQL where clause 551 * @param args an out parameter containing the values to substitute into the {@code selection} 552 */ setSelectionFromIntent( Intent intent, int hour, int minutes, StringBuilder selection, List<String> args)553 private void setSelectionFromIntent( 554 Intent intent, 555 int hour, 556 int minutes, 557 StringBuilder selection, 558 List<String> args) { 559 selection.append(Alarm.HOUR).append("=?"); 560 args.add(String.valueOf(hour)); 561 selection.append(" AND ").append(Alarm.MINUTES).append("=?"); 562 args.add(String.valueOf(minutes)); 563 564 if (intent.hasExtra(AlarmClock.EXTRA_MESSAGE)) { 565 selection.append(" AND ").append(Alarm.LABEL).append("=?"); 566 args.add(getLabelFromIntent(intent, "")); 567 } 568 569 // Days is treated differently than other fields because if days is not specified, it 570 // explicitly means "not recurring". 571 selection.append(" AND ").append(Alarm.DAYS_OF_WEEK).append("=?"); 572 args.add(String.valueOf(getDaysFromIntent(intent, Weekdays.NONE).getBits())); 573 574 if (intent.hasExtra(AlarmClock.EXTRA_VIBRATE)) { 575 selection.append(" AND ").append(Alarm.VIBRATE).append("=?"); 576 args.add(intent.getBooleanExtra(AlarmClock.EXTRA_VIBRATE, false) ? "1" : "0"); 577 } 578 579 if (intent.hasExtra(AlarmClock.EXTRA_RINGTONE)) { 580 selection.append(" AND ").append(Alarm.RINGTONE).append("=?"); 581 582 // If the intent explicitly specified a NULL ringtone, treat it as the default ringtone. 583 final Uri defaultRingtone = DataModel.getDataModel().getDefaultAlarmRingtoneUri(); 584 final Uri ringtone = getAlertFromIntent(intent, defaultRingtone); 585 args.add(ringtone.toString()); 586 } 587 } 588 } 589