From 22973eb262a23e85cdbb56e5b22d8dedfa9d8230 Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Mon, 13 Jun 2016 18:50:43 -0700 Subject: [PATCH] Fixed bug where a recurring alarm dismissed in its upcoming state is rescheduled immediately for its normal ring time --- app/src/main/AndroidManifest.xml | 9 ++++-- .../clock2/PendingAlarmScheduler.java | 32 +++++++++++++++++++ .../clock2/UpcomingAlarmReceiver.java | 2 +- .../clock2/editalarm/EditAlarmActivity.java | 2 +- .../clock2/ringtone/RingtoneActivity.java | 6 ---- .../philliphsu/clock2/util/AlarmUtils.java | 20 ++++++++++-- 6 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44a5356..947f3a1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,9 +20,9 @@ @@ -47,7 +47,6 @@ android:name="android.support.PARENT_ACTIVITY" android:value="com.philliphsu.clock2.MainActivity"/> - + + + \ No newline at end of file diff --git a/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java new file mode 100644 index 0000000..b441467 --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java @@ -0,0 +1,32 @@ +package com.philliphsu.clock2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.philliphsu.clock2.model.AlarmsRepository; +import com.philliphsu.clock2.util.AlarmUtils; + +import static com.philliphsu.clock2.util.Preconditions.checkNotNull; + +/** + * Used to reschedule recurring alarms that were dismissed in their upcoming state, so {@link Alarm#ringsAt()} + * still refers to the time it rings today. This class receives + * your intent at the Alarm instance's normal ring time, so by the time you make a subsequent call + * to {@link Alarm#ringsAt()}, the value returned refers to the next time the alarm will recur. + */ +public class PendingAlarmScheduler extends BroadcastReceiver { + // We include the class name in the string to distinguish this constant from the one defined + // in UpcomingAlarmReceiver. + public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.PendingAlarmScheduler.extra.ALARM_ID"; + + @Override + public void onReceive(Context context, Intent intent) { + long id = intent.getLongExtra(EXTRA_ALARM_ID, -1); + if (id < 0) { + throw new IllegalStateException("No alarm id received"); + } + Alarm alarm = checkNotNull(AlarmsRepository.getInstance(context).getItem(id)); + AlarmUtils.scheduleAlarm(context, alarm, false); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java index d1bc9e5..bfeea5c 100644 --- a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java +++ b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java @@ -37,7 +37,7 @@ public class UpcomingAlarmReceiver extends BroadcastReceiver { } else { Alarm alarm = checkNotNull(AlarmsRepository.getInstance(context).getItem(id)); if (ACTION_DISMISS_NOW.equals(intent.getAction())) { - AlarmUtils.cancelAlarm(context, alarm, true); + AlarmUtils.cancelAlarm(context, alarm, true); // TODO: Cancel only, do not reschedule. } else { // Prepare notification String title; diff --git a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java index 7142208..9b03bfe 100644 --- a/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/editalarm/EditAlarmActivity.java @@ -433,7 +433,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi @Override public void cancelAlarm(Alarm alarm, boolean showToast) { - AlarmUtils.cancelAlarm(this, alarm, showToast); + AlarmUtils.cancelAlarm(this, alarm, showToast); // TODO: Cancel only? if (RingtoneActivity.isAlive()) { Intent intent = new Intent(this, RingtoneActivity.class) .setAction(RingtoneActivity.ACTION_UNBIND); diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java index 26dc55c..a26dde1 100644 --- a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java @@ -158,12 +158,6 @@ public class RingtoneActivity extends AppCompatActivity implements RingtoneServi private void dismiss() { AlarmUtils.cancelAlarm(this, mAlarm, false); - // As of API 19, alarms scheduled with AlarmManager.setRepeating() are inexact. The recommended - // workaround is to schedule one-time exact alarms, and reschedule each time after handling - // an alarm delivery. - if (mAlarm.hasRecurrence()) { - AlarmUtils.scheduleAlarm(this, mAlarm, false /*show toast?*/); - } unbindAndFinish(); } diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java index 451c7e7..2a157c6 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java @@ -10,6 +10,7 @@ import android.util.Log; import android.widget.Toast; import com.philliphsu.clock2.Alarm; +import com.philliphsu.clock2.PendingAlarmScheduler; import com.philliphsu.clock2.R; import com.philliphsu.clock2.UpcomingAlarmReceiver; import com.philliphsu.clock2.model.AlarmsRepository; @@ -84,7 +85,7 @@ public final class AlarmUtils { removeUpcomingAlarmNotification(c, a); // TOneverDO: Place block after making value changes to the alarm. - if (showToast && (a.ringsIn() <= HOURS.toMillis(hoursBeforeUpcoming(c)) || a.isSnoozed())) { + if (showToast && (a.ringsWithinHours(hoursBeforeUpcoming(c)) || a.isSnoozed())) { String time = formatTime(c, a.isSnoozed() ? a.snoozingUntil() : a.ringsAt()); String text = c.getString(R.string.upcoming_alarm_dismissed, time); Toast.makeText(c, text, Toast.LENGTH_LONG).show(); @@ -96,6 +97,19 @@ public final class AlarmUtils { if (!a.hasRecurrence()) { a.setEnabled(false); + } else { + if (a.isEnabled()) { + if (a.ringsWithinHours(hoursBeforeUpcoming(c))) { + // Still upcoming today, so wait until the normal ring time passes before + // rescheduling the alarm. + Intent intent = new Intent(c, PendingAlarmScheduler.class) + .putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id()); + pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT); + am.set(AlarmManager.RTC_WAKEUP, a.ringsAt(), pi); + } else { + scheduleAlarm(c, a, false); + } + } } save(c); // Save any changes @@ -108,8 +122,8 @@ public final class AlarmUtils { } public static void snoozeAlarm(Context c, Alarm a) { - a.snooze(AlarmUtils.snoozeDuration(c)); - AlarmUtils.scheduleAlarm(c, a, true); + a.snooze(snoozeDuration(c)); + scheduleAlarm(c, a, true); save(c); }