Home | History | Annotate | Download | only in deskclock
      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 
     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
     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                 final AlarmInstance nextAlarm = AlarmStateManager.getNextFiringAlarm(mContext);
    109                 if (nextAlarm == null) {
    110                     final String reason = mContext.getString(R.string.no_scheduled_alarms);
    111                     notifyFailureAndLog(reason, mActivity);
    112                     return;
    113                 }
    114 
    115                 // get time from nextAlarm and see if there are any other alarms matching this time
    116                 final Calendar nextTime = nextAlarm.getAlarmTime();
    117                 final List<Alarm> alarmsFiringAtSameTime = getAlarmsByHourMinutes(
    118                         nextTime.get(Calendar.HOUR_OF_DAY), nextTime.get(Calendar.MINUTE), cr);
    119                 // there might me multiple alarms firing next
    120                 mMatchingAlarms.addAll(alarmsFiringAtSameTime);
    121                 break;
    122             case AlarmClock.ALARM_SEARCH_MODE_ALL:
    123                 mMatchingAlarms.addAll(mAlarms);
    124                 break;
    125             case AlarmClock.ALARM_SEARCH_MODE_LABEL:
    126                 // EXTRA_MESSAGE has to be set in this mode
    127                 final String label = mIntent.getStringExtra(AlarmClock.EXTRA_MESSAGE);
    128                 if (label == null) {
    129                     final String reason = mContext.getString(R.string.no_label_specified);
    130                     notifyFailureAndLog(reason, mActivity);
    131                     return;
    132                 }
    133 
    134                 // there might me multiple alarms with this label
    135                 for (Alarm alarm : mAlarms) {
    136                     if (alarm.label.contains(label)) {
    137                         mMatchingAlarms.add(alarm);
    138                     }
    139                 }
    140 
    141                 if (mMatchingAlarms.isEmpty()) {
    142                     final String reason = mContext.getString(R.string.no_alarms_with_label);
    143                     notifyFailureAndLog(reason, mActivity);
    144                     return;
    145                 }
    146                 break;
    147         }
    148     }
    149 
    150     private List<Alarm> getAlarmsByHourMinutes(int hour24, int minutes, ContentResolver cr) {
    151         // if we want to dismiss we should only add enabled alarms
    152         final String selection = String.format("%s=? AND %s=? AND %s=?",
    153                 Alarm.HOUR, Alarm.MINUTES, Alarm.ENABLED);
    154         final String[] args = { String.valueOf(hour24), String.valueOf(minutes), "1" };
    155         return Alarm.getAlarms(cr, selection, args);
    156     }
    157 
    158     public List<Alarm> getMatchingAlarms() {
    159         return mMatchingAlarms;
    160     }
    161 
    162     private void notifyFailureAndLog(String reason, Activity activity) {
    163         LogUtils.e(reason);
    164         Voice.notifyFailure(activity, reason);
    165     }
    166 }