• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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