• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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.os.Looper;
24 import android.provider.AlarmClock;
25 
26 import com.android.deskclock.alarms.AlarmStateManager;
27 import com.android.deskclock.controller.Controller;
28 import com.android.deskclock.provider.Alarm;
29 import com.android.deskclock.provider.AlarmInstance;
30 
31 import java.text.DateFormatSymbols;
32 import java.util.ArrayList;
33 import java.util.Calendar;
34 import java.util.List;
35 
36 /**
37  * Returns a list of alarms that are specified by the intent
38  * processed by HandleDeskClockApiCalls
39  * if there are more than 1 matching alarms and the SEARCH_MODE is not ALL
40  * we show a picker UI dialog
41  */
42 class FetchMatchingAlarmsAction implements Runnable {
43 
44     private final Context mContext;
45     private final List<Alarm> mAlarms;
46     private final Intent mIntent;
47     private final List<Alarm> mMatchingAlarms = new ArrayList<>();
48     private final Activity mActivity;
49 
FetchMatchingAlarmsAction(Context context, List<Alarm> alarms, Intent intent, Activity activity)50     public FetchMatchingAlarmsAction(Context context, List<Alarm> alarms, Intent intent,
51                                      Activity activity) {
52         mContext = context;
53         // only enabled alarms are passed
54         mAlarms = alarms;
55         mIntent = intent;
56         mActivity = activity;
57     }
58 
59     @Override
run()60     public void run() {
61         Utils.enforceNotMainLooper();
62 
63         final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE);
64         // if search mode isn't specified show all alarms in the UI picker
65         if (searchMode == null) {
66             mMatchingAlarms.addAll(mAlarms);
67             return;
68         }
69 
70         final ContentResolver cr = mContext.getContentResolver();
71         switch (searchMode) {
72             case AlarmClock.ALARM_SEARCH_MODE_TIME:
73                 // at least one of these has to be specified in this search mode.
74                 final int hour = mIntent.getIntExtra(AlarmClock.EXTRA_HOUR, -1);
75                 // if minutes weren't specified default to 0
76                 final int minutes = mIntent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0);
77                 final Boolean isPm = (Boolean) mIntent.getExtras().get(AlarmClock.EXTRA_IS_PM);
78                 boolean badInput = isPm != null && hour > 12 && isPm;
79                 badInput |= hour < 0 || hour > 23;
80                 badInput |= minutes < 0 || minutes > 59;
81 
82                 if (badInput) {
83                     final String[] ampm = new DateFormatSymbols().getAmPmStrings();
84                     final String amPm = isPm == null ? "" : (isPm ? ampm[1] : ampm[0]);
85                     final String reason = mContext.getString(R.string.invalid_time, hour, minutes,
86                             amPm);
87                     notifyFailureAndLog(reason, mActivity);
88                     return;
89                 }
90 
91                 final int hour24 = Boolean.TRUE.equals(isPm) && hour < 12 ? (hour + 12) : hour;
92 
93                 // there might me multiple alarms at the same time
94                 for (Alarm alarm : mAlarms) {
95                     if (alarm.hour == hour24 && alarm.minutes == minutes) {
96                         mMatchingAlarms.add(alarm);
97                     }
98                 }
99                 if (mMatchingAlarms.isEmpty()) {
100                     final String reason = mContext.getString(R.string.no_alarm_at, hour24, minutes);
101                     notifyFailureAndLog(reason, mActivity);
102                     return;
103                 }
104                 break;
105             case AlarmClock.ALARM_SEARCH_MODE_NEXT:
106                 // Match currently firing alarms before scheduled alarms.
107                 for (Alarm alarm : mAlarms) {
108                     final AlarmInstance alarmInstance =
109                             AlarmInstance.getNextUpcomingInstanceByAlarmId(cr, alarm.id);
110                     if (alarmInstance != null
111                             && alarmInstance.mAlarmState == AlarmInstance.FIRED_STATE) {
112                         mMatchingAlarms.add(alarm);
113                     }
114                 }
115                 if (!mMatchingAlarms.isEmpty()) {
116                     // return the matched firing alarms
117                     return;
118                 }
119 
120                 final AlarmInstance nextAlarm = AlarmStateManager.getNextFiringAlarm(mContext);
121                 if (nextAlarm == null) {
122                     final String reason = mContext.getString(R.string.no_scheduled_alarms);
123                     notifyFailureAndLog(reason, mActivity);
124                     return;
125                 }
126 
127                 // get time from nextAlarm and see if there are any other alarms matching this time
128                 final Calendar nextTime = nextAlarm.getAlarmTime();
129                 final List<Alarm> alarmsFiringAtSameTime = getAlarmsByHourMinutes(
130                         nextTime.get(Calendar.HOUR_OF_DAY), nextTime.get(Calendar.MINUTE), cr);
131                 // there might me multiple alarms firing next
132                 mMatchingAlarms.addAll(alarmsFiringAtSameTime);
133                 break;
134             case AlarmClock.ALARM_SEARCH_MODE_ALL:
135                 mMatchingAlarms.addAll(mAlarms);
136                 break;
137             case AlarmClock.ALARM_SEARCH_MODE_LABEL:
138                 // EXTRA_MESSAGE has to be set in this mode
139                 final String label = mIntent.getStringExtra(AlarmClock.EXTRA_MESSAGE);
140                 if (label == null) {
141                     final String reason = mContext.getString(R.string.no_label_specified);
142                     notifyFailureAndLog(reason, mActivity);
143                     return;
144                 }
145 
146                 // there might me multiple alarms with this label
147                 for (Alarm alarm : mAlarms) {
148                     if (alarm.label.contains(label)) {
149                         mMatchingAlarms.add(alarm);
150                     }
151                 }
152 
153                 if (mMatchingAlarms.isEmpty()) {
154                     final String reason = mContext.getString(R.string.no_alarms_with_label);
155                     notifyFailureAndLog(reason, mActivity);
156                     return;
157                 }
158                 break;
159         }
160     }
161 
162     private List<Alarm> getAlarmsByHourMinutes(int hour24, int minutes, ContentResolver cr) {
163         // if we want to dismiss we should only add enabled alarms
164         final String selection = String.format("%s=? AND %s=? AND %s=?",
165                 Alarm.HOUR, Alarm.MINUTES, Alarm.ENABLED);
166         final String[] args = { String.valueOf(hour24), String.valueOf(minutes), "1" };
167         return Alarm.getAlarms(cr, selection, args);
168     }
169 
170     public List<Alarm> getMatchingAlarms() {
171         return mMatchingAlarms;
172     }
173 
174     private void notifyFailureAndLog(String reason, Activity activity) {
175         LogUtils.e(reason);
176         Controller.getController().notifyVoiceFailure(activity, reason);
177     }
178 }
179