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