/** * Used by dismissed and missed states, to update parent alarm. This will either disable, delete * or reschedule parent alarm. * * @param context application context * @param instance to update parent for */ private static void updateParentAlarm(Context context, AlarmInstance instance) { ContentResolver cr = context.getContentResolver(); Alarm alarm = Alarm.getAlarm(cr, instance.mAlarmId); if (alarm == null) { LogUtils.e("Parent has been deleted with instance: " + instance.toString()); return; } if (!alarm.daysOfWeek.isRepeating()) { if (alarm.deleteAfterUse) { LogUtils.i("Deleting parent alarm: " + alarm.id); Alarm.deleteAlarm(cr, alarm.id); } else { LogUtils.i("Disabling parent alarm: " + alarm.id); alarm.enabled = false; Alarm.updateAlarm(cr, alarm); } } else { // Schedule the next repeating instance after the current time AlarmInstance nextRepeatedInstance = alarm.createInstanceAfter(getCurrentTime()); LogUtils.i( "Creating new instance for repeating alarm " + alarm.id + " at " + AlarmUtils.getFormattedTime(context, nextRepeatedInstance.getAlarmTime())); AlarmInstance.addInstance(cr, nextRepeatedInstance); registerInstance(context, nextRepeatedInstance, true); } }
/** * This registers the AlarmInstance to the state manager. This will look at the instance and * choose the most appropriate state to put it in. This is primarily used by new alarms, but it * can also be called when the system time changes. * * <p>Most state changes are handled by the states themselves, but during major time changes we * have to correct the alarm instance state. This means we have to handle special cases as * describe below: * * <ul> * <li>Make sure all dismissed alarms are never re-activated * <li>Make sure pre-dismissed alarms stay predismissed * <li>Make sure firing alarms stayed fired unless they should be auto-silenced * <li>Missed instance that have parents should be re-enabled if we went back in time * <li>If alarm was SNOOZED, then show the notification but don't update time * <li>If low priority notification was hidden, then make sure it stays hidden * </ul> * * If none of these special case are found, then we just check the time and see what is the proper * state for the instance. * * @param context application context * @param instance to register */ public static void registerInstance( Context context, AlarmInstance instance, boolean updateNextAlarm) { final ContentResolver cr = context.getContentResolver(); final Alarm alarm = Alarm.getAlarm(cr, instance.mAlarmId); final Calendar currentTime = getCurrentTime(); final Calendar alarmTime = instance.getAlarmTime(); final Calendar timeoutTime = instance.getTimeout(context); final Calendar lowNotificationTime = instance.getLowNotificationTime(); final Calendar highNotificationTime = instance.getHighNotificationTime(); final Calendar missedTTL = instance.getMissedTimeToLive(); // Handle special use cases here if (instance.mAlarmState == AlarmInstance.DISMISSED_STATE) { // This should never happen, but add a quick check here LogUtils.e("Alarm Instance is dismissed, but never deleted"); setDismissState(context, instance); return; } else if (instance.mAlarmState == AlarmInstance.FIRED_STATE) { // Keep alarm firing, unless it should be timed out boolean hasTimeout = timeoutTime != null && currentTime.after(timeoutTime); if (!hasTimeout) { setFiredState(context, instance); return; } } else if (instance.mAlarmState == AlarmInstance.MISSED_STATE) { if (currentTime.before(alarmTime)) { if (instance.mAlarmId == null) { // This instance parent got deleted (ie. deleteAfterUse), so // we should not re-activate it.- setDismissState(context, instance); return; } // TODO: This will re-activate missed snoozed alarms, but will // use our normal notifications. This is not ideal, but very rare use-case. // We should look into fixing this in the future. // Make sure we re-enable the parent alarm of the instance // because it will get activated by by the below code alarm.enabled = true; Alarm.updateAlarm(cr, alarm); } } else if (instance.mAlarmState == AlarmInstance.PREDISMISSED_STATE) { if (currentTime.before(alarmTime)) { setPreDismissState(context, instance); } else { setDismissState(context, instance); } return; } // Fix states that are time sensitive if (currentTime.after(missedTTL)) { // Alarm is so old, just dismiss it setDismissState(context, instance); } else if (currentTime.after(alarmTime)) { // There is a chance that the TIME_SET occurred right when the alarm should go off, so // we need to add a check to see if we should fire the alarm instead of marking it // missed. Calendar alarmBuffer = Calendar.getInstance(); alarmBuffer.setTime(alarmTime.getTime()); alarmBuffer.add(Calendar.SECOND, ALARM_FIRE_BUFFER); if (currentTime.before(alarmBuffer)) { setFiredState(context, instance); } else { setMissedState(context, instance); } } else if (instance.mAlarmState == AlarmInstance.SNOOZE_STATE) { // We only want to display snooze notification and not update the time, // so handle showing the notification directly AlarmNotifications.showSnoozeNotification(context, instance); scheduleInstanceStateChange( context, instance.getAlarmTime(), instance, AlarmInstance.FIRED_STATE); } else if (currentTime.after(highNotificationTime)) { setHighNotificationState(context, instance); } else if (currentTime.after(lowNotificationTime)) { // Only show low notification if it wasn't hidden in the past if (instance.mAlarmState == AlarmInstance.HIDE_NOTIFICATION_STATE) { setHideNotificationState(context, instance); } else { setLowNotificationState(context, instance); } } else { // Alarm is still active, so initialize as a silent alarm setSilentState(context, instance); } // The caller prefers to handle updateNextAlarm for optimization if (updateNextAlarm) { updateNextAlarm(context); } }