/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.deskclock; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Looper; import android.provider.AlarmClock; import com.android.deskclock.alarms.AlarmStateManager; import com.android.deskclock.provider.Alarm; import com.android.deskclock.provider.AlarmInstance; import java.text.DateFormatSymbols; import java.util.ArrayList; import java.util.Calendar; import java.util.List; /** * Returns a list of alarms that are specified by the intent * processed by HandleDeskClockApiCalls * if there are more than 1 matching alarms and the SEARCH_MODE is not ALL * we show a picker UI dialog */ class FetchMatchingAlarmsAction implements Runnable { private final Context mContext; private final List mAlarms; private final Intent mIntent; private final List mMatchingAlarms = new ArrayList<>(); private final Activity mActivity; public FetchMatchingAlarmsAction(Context context, List alarms, Intent intent, Activity activity) { mContext = context; // only enabled alarms are passed mAlarms = alarms; mIntent = intent; mActivity = activity; } @Override public void run() { // only allow on background thread if (Looper.myLooper() == Looper.getMainLooper()) { throw new IllegalStateException("Must be called on a background thread"); } final String searchMode = mIntent.getStringExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE); // if search mode isn't specified show all alarms in the UI picker if (searchMode == null) { mMatchingAlarms.addAll(mAlarms); return; } final ContentResolver cr = mContext.getContentResolver(); switch (searchMode) { case AlarmClock.ALARM_SEARCH_MODE_TIME: // at least one of these has to be specified in this search mode. final int hour = mIntent.getIntExtra(AlarmClock.EXTRA_HOUR, -1); // if minutes weren't specified default to 0 final int minutes = mIntent.getIntExtra(AlarmClock.EXTRA_MINUTES, 0); final Boolean isPm = (Boolean) mIntent.getExtras().get(AlarmClock.EXTRA_IS_PM); boolean badInput = isPm != null && hour > 12 && isPm; badInput |= hour < 0 || hour > 23; badInput |= minutes < 0 || minutes > 59; if (badInput) { final String[] ampm = new DateFormatSymbols().getAmPmStrings(); final String amPm = isPm == null ? "" : (isPm ? ampm[1] : ampm[0]); final String reason = mContext.getString(R.string.invalid_time, hour, minutes, amPm); notifyFailureAndLog(reason, mActivity); return; } final int hour24 = Boolean.TRUE.equals(isPm) && hour < 12 ? (hour + 12) : hour; // there might me multiple alarms at the same time for (Alarm alarm : mAlarms) { if (alarm.hour == hour24 && alarm.minutes == minutes) { mMatchingAlarms.add(alarm); } } if (mMatchingAlarms.isEmpty()) { final String reason = mContext.getString(R.string.no_alarm_at, hour24, minutes); notifyFailureAndLog(reason, mActivity); return; } break; case AlarmClock.ALARM_SEARCH_MODE_NEXT: // Match currently firing alarms before scheduled alarms. for (Alarm alarm : mAlarms) { final AlarmInstance alarmInstance = AlarmInstance.getNextUpcomingInstanceByAlarmId(cr, alarm.id); if (alarmInstance != null && alarmInstance.mAlarmState == AlarmInstance.FIRED_STATE) { mMatchingAlarms.add(alarm); } } if (!mMatchingAlarms.isEmpty()) { // return the matched firing alarms return; } final AlarmInstance nextAlarm = AlarmStateManager.getNextFiringAlarm(mContext); if (nextAlarm == null) { final String reason = mContext.getString(R.string.no_scheduled_alarms); notifyFailureAndLog(reason, mActivity); return; } // get time from nextAlarm and see if there are any other alarms matching this time final Calendar nextTime = nextAlarm.getAlarmTime(); final List alarmsFiringAtSameTime = getAlarmsByHourMinutes( nextTime.get(Calendar.HOUR_OF_DAY), nextTime.get(Calendar.MINUTE), cr); // there might me multiple alarms firing next mMatchingAlarms.addAll(alarmsFiringAtSameTime); break; case AlarmClock.ALARM_SEARCH_MODE_ALL: mMatchingAlarms.addAll(mAlarms); break; case AlarmClock.ALARM_SEARCH_MODE_LABEL: // EXTRA_MESSAGE has to be set in this mode final String label = mIntent.getStringExtra(AlarmClock.EXTRA_MESSAGE); if (label == null) { final String reason = mContext.getString(R.string.no_label_specified); notifyFailureAndLog(reason, mActivity); return; } // there might me multiple alarms with this label for (Alarm alarm : mAlarms) { if (alarm.label.contains(label)) { mMatchingAlarms.add(alarm); } } if (mMatchingAlarms.isEmpty()) { final String reason = mContext.getString(R.string.no_alarms_with_label); notifyFailureAndLog(reason, mActivity); return; } break; } } private List getAlarmsByHourMinutes(int hour24, int minutes, ContentResolver cr) { // if we want to dismiss we should only add enabled alarms final String selection = String.format("%s=? AND %s=? AND %s=?", Alarm.HOUR, Alarm.MINUTES, Alarm.ENABLED); final String[] args = { String.valueOf(hour24), String.valueOf(minutes), "1" }; return Alarm.getAlarms(cr, selection, args); } public List getMatchingAlarms() { return mMatchingAlarms; } private void notifyFailureAndLog(String reason, Activity activity) { LogUtils.e(reason); Voice.notifyFailure(activity, reason); } }