diff --git a/src/androidTest/java/com/todoroo/astrid/reminders/NotifyAtDeadlineTest.java b/src/androidTest/java/com/todoroo/astrid/reminders/NotifyAtDeadlineTest.java deleted file mode 100644 index 21fc0cece..000000000 --- a/src/androidTest/java/com/todoroo/astrid/reminders/NotifyAtDeadlineTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.todoroo.astrid.reminders; - -import android.support.test.runner.AndroidJUnit4; - -import com.todoroo.astrid.data.Task; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.tasks.preferences.Preferences; -import org.tasks.time.DateTime; - -import static android.support.test.InstrumentationRegistry.getTargetContext; -import static com.natpryce.makeiteasy.MakeItEasy.with; -import static com.todoroo.astrid.data.Task.NOTIFY_AT_DEADLINE; -import static com.todoroo.astrid.reminders.ReminderService.NO_ALARM; -import static junit.framework.Assert.assertEquals; -import static org.tasks.makers.TaskMaker.DUE_DATE; -import static org.tasks.makers.TaskMaker.DUE_TIME; -import static org.tasks.makers.TaskMaker.REMINDERS; -import static org.tasks.makers.TaskMaker.REMINDER_LAST; -import static org.tasks.makers.TaskMaker.newTask; - -@RunWith(AndroidJUnit4.class) -public class NotifyAtDeadlineTest { - - private ReminderService reminderService; - - @Before - public void setUp() { - Preferences preferences = new Preferences(getTargetContext(), null); - reminderService = new ReminderService(preferences, null); - } - - @Test - public void testNoReminderWhenNoDueDate() { - Task task = newTask(with(REMINDERS, NOTIFY_AT_DEADLINE)); - assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task)); - } - - @Test - public void testNoReminderWhenNotifyAtDeadlineFlagNotSet() { - Task task = newTask(with(DUE_TIME, new DateTime(2014, 1, 24, 19, 23))); - assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task)); - } - - @Test - public void testScheduleReminderAtDueTime() { - final DateTime dueDate = new DateTime(2014, 1, 24, 19, 23); - Task task = newTask(with(DUE_TIME, dueDate), with(REMINDERS, NOTIFY_AT_DEADLINE)); - assertEquals(dueDate.plusSeconds(1).getMillis(), reminderService.calculateNextDueDateReminder(task)); - } - - @Test - public void testScheduleReminderAtDefaultDueTime() { - final DateTime dueDate = new DateTime(2015, 12, 29, 12, 0); - Task task = newTask(with(DUE_DATE, dueDate), with(REMINDERS, NOTIFY_AT_DEADLINE)); - assertEquals(dueDate.withHourOfDay(18).getMillis(), - reminderService.calculateNextDueDateReminder(task)); - } - - @Test - public void testNoReminderIfAlreadyRemindedPastDueDate() { - final DateTime dueDate = new DateTime(2015, 12, 29, 19, 23); - Task task = newTask( - with(DUE_TIME, dueDate), - with(REMINDER_LAST, dueDate.plusSeconds(1)), - with(REMINDERS, NOTIFY_AT_DEADLINE)); - assertEquals(NO_ALARM, reminderService.calculateNextDueDateReminder(task)); - } -} diff --git a/src/androidTest/java/com/todoroo/astrid/reminders/ReminderAlarmSchedulerTest.java b/src/androidTest/java/com/todoroo/astrid/reminders/ReminderAlarmSchedulerTest.java deleted file mode 100644 index b4ae1867f..000000000 --- a/src/androidTest/java/com/todoroo/astrid/reminders/ReminderAlarmSchedulerTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.todoroo.astrid.reminders; - -import android.support.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.tasks.Freeze; -import org.tasks.Snippet; -import org.tasks.jobs.JobManager; -import org.tasks.jobs.Reminder; -import org.tasks.makers.TaskMaker; -import org.tasks.preferences.Preferences; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static com.natpryce.makeiteasy.MakeItEasy.with; -import static com.todoroo.astrid.reminders.ReminderService.TYPE_DUE; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static junit.framework.Assert.assertEquals; -import static org.mockito.AdditionalAnswers.returnsFirstArg; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.tasks.makers.TaskMaker.newTask; -import static org.tasks.time.DateTimeUtils.currentTimeMillis; - -@RunWith(AndroidJUnit4.class) -public class ReminderAlarmSchedulerTest { - - private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1); - - private JobManager jobManager; - private ReminderAlarmScheduler scheduler; - - @Before - public void before() { - jobManager = mock(JobManager.class); - Preferences preferences = mock(Preferences.class); - when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg()); - scheduler = new ReminderAlarmScheduler(jobManager, preferences); - } - - @After - public void after() { - verifyNoMoreInteractions(jobManager); - } - - @Test - public void scheduleFirstReminder() { - long now = currentTimeMillis(); - - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, 0); - - verify(jobManager).scheduleReminder(now, true); - } - - @Test - public void dontScheduleLaterReminder() { - long now = currentTimeMillis(); - - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, 0); - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now + ONE_MINUTE, 0); - - verify(jobManager).scheduleReminder(now, true); - } - - @Test - public void rescheduleNewerReminder() { - long now = currentTimeMillis(); - - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, 0); - scheduler.createAlarm(newTask(with(TaskMaker.ID, 2L)), now - ONE_MINUTE, 0); - - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleReminder(now, true); - order.verify(jobManager).scheduleReminder(now - ONE_MINUTE, true); - } - - @Test - public void removeLastReminderCancelsJob() { - long now = currentTimeMillis(); - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, 0); - - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), 0, 0); - - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleReminder(now, true); - order.verify(jobManager).cancelReminders(); - } - - @Test - public void removePastRemindersReturnsPastReminder() { - long now = currentTimeMillis(); - - Freeze.freezeAt(now).thawAfter(new Snippet() {{ - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, TYPE_DUE); - - List reminders = scheduler.removePastReminders(); - - verify(jobManager).scheduleReminder(now, true); - - assertEquals(singletonList(new Reminder(1, now, TYPE_DUE)), reminders); - }}); - } - - @Test - public void dontRescheduleForSecondJobAtSameTime() { - long now = currentTimeMillis(); - - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, TYPE_DUE); - scheduler.createAlarm(newTask(with(TaskMaker.ID, 2L)), now, TYPE_DUE); - - verify(jobManager).scheduleReminder(now, true); - } - - @Test - public void removePastRemindersReturnsPastRemindersAtSameTime() { - long now = currentTimeMillis(); - - Freeze.freezeAt(now).thawAfter(new Snippet() {{ - scheduler.createAlarm(newTask(with(TaskMaker.ID, 1L)), now, TYPE_DUE); - scheduler.createAlarm(newTask(with(TaskMaker.ID, 2L)), now, TYPE_DUE); - - List reminders = scheduler.removePastReminders(); - - verify(jobManager).scheduleReminder(now, true); - - assertEquals(asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now, TYPE_DUE)), reminders); - }}); - } -} diff --git a/src/androidTest/java/com/todoroo/astrid/reminders/ReminderServiceTest.java b/src/androidTest/java/com/todoroo/astrid/reminders/ReminderServiceTest.java index 89ea28ef5..d6abdd085 100644 --- a/src/androidTest/java/com/todoroo/astrid/reminders/ReminderServiceTest.java +++ b/src/androidTest/java/com/todoroo/astrid/reminders/ReminderServiceTest.java @@ -1,41 +1,68 @@ -/** - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ package com.todoroo.astrid.reminders; import android.support.test.runner.AndroidJUnit4; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.data.Task; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.tasks.Snippet; import org.tasks.injection.InjectingTestCase; import org.tasks.injection.TestComponent; +import org.tasks.jobs.JobQueue; +import org.tasks.jobs.Reminder; +import org.tasks.preferences.Preferences; +import org.tasks.time.DateTime; + +import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import static com.natpryce.makeiteasy.MakeItEasy.with; +import static com.todoroo.astrid.data.Task.NOTIFY_AFTER_DEADLINE; +import static com.todoroo.astrid.data.Task.NOTIFY_AT_DEADLINE; +import static com.todoroo.astrid.reminders.ReminderService.TYPE_RANDOM; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.tasks.Freeze.freezeClock; -import static org.tasks.Freeze.thaw; import static org.tasks.date.DateTimeUtils.newDateTime; +import static org.tasks.makers.TaskMaker.COMPLETION_TIME; +import static org.tasks.makers.TaskMaker.DELETION_TIME; +import static org.tasks.makers.TaskMaker.DUE_DATE; +import static org.tasks.makers.TaskMaker.DUE_TIME; +import static org.tasks.makers.TaskMaker.ID; +import static org.tasks.makers.TaskMaker.RANDOM_REMINDER_PERIOD; +import static org.tasks.makers.TaskMaker.REMINDERS; +import static org.tasks.makers.TaskMaker.REMINDER_LAST; +import static org.tasks.makers.TaskMaker.SNOOZE_TIME; +import static org.tasks.makers.TaskMaker.newTask; +import static org.tasks.time.DateTimeUtils.currentTimeMillis; @RunWith(AndroidJUnit4.class) public class ReminderServiceTest extends InjectingTestCase { - @Inject TaskDao taskDao; - @Inject ReminderService reminderService; + @Inject Preferences preferences; - @Override - public void setUp() { - super.setUp(); - freezeClock(); + private ReminderService service; + private JobQueue jobs; + + @Before + public void before() { + jobs = mock(JobQueue.class); + service = new ReminderService(preferences, jobs); + } + + @After + public void after() { + verifyNoMoreInteractions(jobs); } @Override @@ -43,235 +70,246 @@ public class ReminderServiceTest extends InjectingTestCase { component.inject(this); } - @After - public void tearDown() { - thaw(); + @Test + public void dontScheduleDueDateReminderWhenFlagNotSet() { + service.scheduleAlarm(null, newTask(with(ID, 1L), with(DUE_TIME, newDateTime()))); + + verify(jobs).cancel(1); } @Test - public void testNoReminders() { - reminderService.setScheduler(new NoAlarmExpected()); - - Task task = new Task(); - task.setTitle("water"); - task.setReminderFlags(0); - task.setReminderPeriod(0L); - taskDao.save(task); - reminderService.scheduleAlarm(taskDao, task); + public void dontScheduleDueDateReminderWhenTimeNotSet() { + service.scheduleAlarm(null, newTask(with(ID, 1L), with(REMINDERS, NOTIFY_AT_DEADLINE))); + + verify(jobs).cancel(1); } @Test - public void testDueDates() { - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertEquals((long) task.getDueDate(), time); - assertEquals(type, ReminderService.TYPE_DUE); - } - }); - - // test due date in the past - final Task task = new Task(); - task.setTitle("water"); - task.setDueDate(DateUtilities.now() - DateUtilities.ONE_DAY); - task.setReminderFlags(Task.NOTIFY_AT_DEADLINE); - taskDao.save(task); - - // test due date in the future - task.setDueDate(DateUtilities.now() + DateUtilities.ONE_DAY); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); + public void schedulePastDueDate() { + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, newDateTime().minusDays(1)), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + service.scheduleAlarm(null, task); + + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); } @Test - public void testRandom() { - // test random - final Task task = new Task(); - task.setTitle("water"); - task.setReminderPeriod(DateUtilities.ONE_WEEK); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now()); - assertTrue(time < DateUtilities.now() + 1.2 * DateUtilities.ONE_WEEK); - assertEquals(type, ReminderService.TYPE_RANDOM); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); + public void scheduleFutureDueDate() { + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, newDateTime().plusDays(1)), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + service.scheduleAlarm(null, task); + + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); } @Test - public void testOverdue() { - // test due date in the future - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > task.getDueDate()); - assertTrue(time < task.getDueDate() + DateUtilities.ONE_DAY); - assertEquals(type, ReminderService.TYPE_OVERDUE); - } - }); - final Task task = new Task(); - task.setTitle("water"); - task.setDueDate(DateUtilities.now() + DateUtilities.ONE_DAY); - task.setReminderFlags(Task.NOTIFY_AFTER_DEADLINE); - taskDao.save(task); - - // test due date in the past - task.setDueDate(DateUtilities.now() - DateUtilities.ONE_DAY); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now() - 1000L); - assertTrue(time < DateUtilities.now() + 2 * DateUtilities.ONE_DAY); - assertEquals(type, ReminderService.TYPE_OVERDUE); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); - - // test due date in the past, but recently notified - task.setReminderLast(DateUtilities.now()); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now() + DateUtilities.ONE_HOUR); - assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); - assertEquals(type, ReminderService.TYPE_OVERDUE); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); + public void scheduleReminderAtDefaultDueTime() { + DateTime now = newDateTime(); + Task task = newTask( + with(ID, 1L), + with(DUE_DATE, now), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + service.scheduleAlarm(null, task); + + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE)); } @Test - public void testMultipleReminders() { - // test due date in the future, enable random - final Task task = new Task(); - task.setTitle("water"); - task.setDueDate(DateUtilities.now() + DateUtilities.ONE_WEEK); - task.setReminderFlags(Task.NOTIFY_AT_DEADLINE); - task.setReminderPeriod(DateUtilities.ONE_HOUR); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now()); - assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); - assertEquals(type, ReminderService.TYPE_RANDOM); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); - - // now set the due date in the past - task.setDueDate(DateUtilities.now() - DateUtilities.ONE_WEEK); - ((AlarmExpected) reminderService.getScheduler()).alarmCreated = false; - reminderService.scheduleAlarm(taskDao, task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); - - // now set the due date before the random - task.setDueDate(DateUtilities.now() + DateUtilities.ONE_HOUR); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertEquals((long) task.getDueDate(), time); - assertEquals(type, ReminderService.TYPE_DUE); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); + public void dontScheduleReminderForCompletedTask() { + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, newDateTime().plusDays(1)), + with(COMPLETION_TIME, newDateTime()), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + + service.scheduleAlarm(null, task); + + verify(jobs).cancel(1); } @Test - public void testSnoozeReminders() { - thaw(); // TODO: get rid of this - - // test due date and snooze in the future - final Task task = new Task(); - task.setTitle("spacemen"); - task.setDueDate(DateUtilities.now() + 5000L); - task.setReminderFlags(Task.NOTIFY_AT_DEADLINE); - task.setReminderSnooze(DateUtilities.now() + DateUtilities.ONE_WEEK); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now() + DateUtilities.ONE_WEEK - 1000L); - assertTrue(time < DateUtilities.now() + DateUtilities.ONE_WEEK + 1000L); - assertEquals(type, ReminderService.TYPE_SNOOZE); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); - - // snooze in the past - task.setReminderSnooze(DateUtilities.now() - DateUtilities.ONE_WEEK); - reminderService.setScheduler(new AlarmExpected() { - @Override - public void createAlarm(Task task, long time, int type) { - if (time == ReminderService.NO_ALARM) - return; - super.createAlarm(task, time, type); - assertTrue(time > DateUtilities.now() - 1000L); - assertTrue(time < DateUtilities.now() + 5000L); - assertEquals(type, ReminderService.TYPE_DUE); - } - }); - taskDao.save(task); - assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); - } + public void dontScheduleReminderForDeletedTask() { + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, newDateTime().plusDays(1)), + with(DELETION_TIME, newDateTime()), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + + service.scheduleAlarm(null, task); - // --- helper classes + verify(jobs).cancel(1); + } - public class NoAlarmExpected implements AlarmScheduler { - @Override - public void createAlarm(Task task, long time, int type) { - if(time == 0 || time == Long.MAX_VALUE) - return; - fail("created alarm, no alarm expected (" + type + ": " + newDateTime(time)); - } + @Test + public void dontScheduleDueDateReminderWhenAlreadyReminded() { + DateTime now = newDateTime(); + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, now), + with(REMINDER_LAST, now.plusSeconds(1)), + with(REMINDERS, NOTIFY_AT_DEADLINE)); - @Override - public void clear() { + service.scheduleAlarm(null, task); - } + verify(jobs).cancel(1); } - public class AlarmExpected implements AlarmScheduler { - public boolean alarmCreated = false; + @Test + public void snoozeOverridesAll() { + DateTime now = newDateTime(); + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, now), + with(SNOOZE_TIME, now.plusMonths(12)), + with(REMINDERS, NOTIFY_AT_DEADLINE | NOTIFY_AFTER_DEADLINE), + with(RANDOM_REMINDER_PERIOD, TimeUnit.HOURS.toMillis(1))); + + service.scheduleAlarm(null, task); + + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE)); + } - @Override - public void createAlarm(Task task, long time, int type) { - alarmCreated = true; - } + @Test + public void ignoreLapsedSnoozeTime() { + Task task = newTask( + with(ID, 1L), + with(DUE_TIME, newDateTime()), + with(SNOOZE_TIME, newDateTime().minusMinutes(5)), + with(REMINDERS, NOTIFY_AT_DEADLINE)); + service.scheduleAlarm(null, task); + + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); - @Override - public void clear() { + } - } + @Test + public void scheduleRandomReminder() { + freezeClock().thawAfter(new Snippet() {{ + Task task = newTask( + with(ID, 1L), + with(RANDOM_REMINDER_PERIOD, TimeUnit.DAYS.toMillis(7))); + service.scheduleAlarm(null, task); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Reminder.class); + InOrder order = inOrder(jobs); + order.verify(jobs).cancel(1); + order.verify(jobs).add(captor.capture()); + + Reminder reminder = captor.getValue(); + assertTrue(reminder.getTime() > currentTimeMillis()); + assertTrue(reminder.getTime() < currentTimeMillis() + 1.2 * TimeUnit.DAYS.toMillis(7)); + assertEquals(TYPE_RANDOM, reminder.getType()); + }}); } + +// @Test +// public void testOverdue() { +// // test due date in the future +// reminderService.setScheduler(new AlarmExpected() { +// @Override +// public void createAlarm(Task task, long time, int type) { +// if (time == ReminderService.NO_ALARM) +// return; +// super.createAlarm(task, time, type); +// assertTrue(time > task.getDueDate()); +// assertTrue(time < task.getDueDate() + DateUtilities.ONE_DAY); +// assertEquals(type, ReminderService.TYPE_OVERDUE); +// } +// }); +// final Task task = new Task(); +// task.setTitle("water"); +// task.setDueDate(DateUtilities.now() + DateUtilities.ONE_DAY); +// task.setReminderFlags(Task.NOTIFY_AFTER_DEADLINE); +// taskDao.save(task); +// +// // test due date in the past +// task.setDueDate(DateUtilities.now() - DateUtilities.ONE_DAY); +// reminderService.setScheduler(new AlarmExpected() { +// @Override +// public void createAlarm(Task task, long time, int type) { +// if (time == ReminderService.NO_ALARM) +// return; +// super.createAlarm(task, time, type); +// assertTrue(time > DateUtilities.now() - 1000L); +// assertTrue(time < DateUtilities.now() + 2 * DateUtilities.ONE_DAY); +// assertEquals(type, ReminderService.TYPE_OVERDUE); +// } +// }); +// taskDao.save(task); +// assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); +// +// // test due date in the past, but recently notified +// task.setReminderLast(DateUtilities.now()); +// reminderService.setScheduler(new AlarmExpected() { +// @Override +// public void createAlarm(Task task, long time, int type) { +// if (time == ReminderService.NO_ALARM) +// return; +// super.createAlarm(task, time, type); +// assertTrue(time > DateUtilities.now() + DateUtilities.ONE_HOUR); +// assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); +// assertEquals(type, ReminderService.TYPE_OVERDUE); +// } +// }); +// taskDao.save(task); +// assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); +// } +// +// @Test +// public void testMultipleReminders() { +// // test due date in the future, enable random +// final Task task = new Task(); +// task.setTitle("water"); +// task.setDueDate(DateUtilities.now() + DateUtilities.ONE_WEEK); +// task.setReminderFlags(Task.NOTIFY_AT_DEADLINE); +// task.setReminderPeriod(DateUtilities.ONE_HOUR); +// reminderService.setScheduler(new AlarmExpected() { +// @Override +// public void createAlarm(Task task, long time, int type) { +// if (time == ReminderService.NO_ALARM) +// return; +// super.createAlarm(task, time, type); +// assertTrue(time > DateUtilities.now()); +// assertTrue(time < DateUtilities.now() + DateUtilities.ONE_DAY); +// assertEquals(type, ReminderService.TYPE_RANDOM); +// } +// }); +// taskDao.save(task); +// assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); +// +// // now set the due date in the past +// task.setDueDate(DateUtilities.now() - DateUtilities.ONE_WEEK); +// ((AlarmExpected) reminderService.getScheduler()).alarmCreated = false; +// reminderService.scheduleAlarm(taskDao, task); +// assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); +// +// // now set the due date before the random +// task.setDueDate(DateUtilities.now() + DateUtilities.ONE_HOUR); +// reminderService.setScheduler(new AlarmExpected() { +// @Override +// public void createAlarm(Task task, long time, int type) { +// if (time == ReminderService.NO_ALARM) +// return; +// super.createAlarm(task, time, type); +// assertEquals((long) task.getDueDate(), time); +// assertEquals(type, ReminderService.TYPE_DUE); +// } +// }); +// taskDao.save(task); +// assertTrue(((AlarmExpected) reminderService.getScheduler()).alarmCreated); +// } } diff --git a/src/androidTest/java/org/tasks/jobs/JobQueueTest.java b/src/androidTest/java/org/tasks/jobs/JobQueueTest.java index 4cb0b3fb2..d21ddb919 100644 --- a/src/androidTest/java/org/tasks/jobs/JobQueueTest.java +++ b/src/androidTest/java/org/tasks/jobs/JobQueueTest.java @@ -2,9 +2,11 @@ package org.tasks.jobs; import android.support.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.tasks.Freeze; import org.tasks.Snippet; import org.tasks.preferences.Preferences; @@ -14,11 +16,12 @@ import java.util.concurrent.TimeUnit; import static com.todoroo.astrid.reminders.ReminderService.TYPE_DUE; import static java.util.Collections.singletonList; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; import static org.mockito.AdditionalAnswers.returnsFirstArg; import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.tasks.time.DateTimeUtils.currentTimeMillis; @@ -26,15 +29,23 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis; public class JobQueueTest { private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1); + private static final String TAG = "test"; private JobQueue queue; + private JobManager jobManager; private Preferences preferences; @Before public void before() { preferences = mock(Preferences.class); when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg()); - queue = new JobQueue<>(preferences); + jobManager = mock(JobManager.class); + queue = new JobQueue<>(preferences, jobManager, TAG); + } + + @After + public void after() { + verifyNoMoreInteractions(jobManager); } @Test @@ -42,33 +53,44 @@ public class JobQueueTest { queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(2, 1, 0)); + verify(jobManager).schedule(TAG, 1, true); + assertEquals(2, queue.size()); } @Test public void rescheduleForFirstJob() { - assertTrue(queue.add(new Reminder(1, 1, 0))); + queue.add(new Reminder(1, 1, 0)); + + verify(jobManager).schedule(TAG, 1, true); } @Test public void dontRescheduleForLaterJobs() { queue.add(new Reminder(1, 1, 0)); + queue.add(new Reminder(2, 2, 0)); - assertFalse(queue.add(new Reminder(2, 2, 0))); + verify(jobManager).schedule(TAG, 1, true); } @Test public void rescheduleForNewerJob() { queue.add(new Reminder(1, 2, 0)); + queue.add(new Reminder(1, 1, 0)); - assertTrue(queue.add(new Reminder(1, 1, 0))); + InOrder order = inOrder(jobManager); + order.verify(jobManager).schedule(TAG, 2, true); + order.verify(jobManager).schedule(TAG, 1, true); } @Test public void rescheduleWhenCancelingOnlyJob() { queue.add(new Reminder(1, 2, 0)); + queue.cancel(1); - assertTrue(queue.cancel(1)); + InOrder order = inOrder(jobManager); + order.verify(jobManager).schedule(TAG, 2, true); + order.verify(jobManager).cancel(TAG); } @Test @@ -76,7 +98,11 @@ public class JobQueueTest { queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(2, 2, 0)); - assertTrue(queue.cancel(1)); + queue.cancel(1); + + InOrder order = inOrder(jobManager); + order.verify(jobManager).schedule(TAG, 1, true); + order.verify(jobManager).schedule(TAG, 2, true); } @Test @@ -84,7 +110,9 @@ public class JobQueueTest { queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(2, 2, 0)); - assertFalse(queue.cancel(2)); + queue.cancel(2); + + verify(jobManager).schedule(TAG, 1, true); } @Test @@ -99,7 +127,7 @@ public class JobQueueTest { when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L); queue.add(new Reminder(1, 1, 1)); - assertEquals(1234, queue.nextScheduledTime()); + verify(jobManager).schedule(TAG, 1234, true); } @Test @@ -109,6 +137,8 @@ public class JobQueueTest { queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); + verify(jobManager).schedule(TAG, now, true); + Freeze.freezeAt(now).thawAfter(new Snippet() {{ assertEquals( singletonList(new Reminder(1, now, TYPE_DUE)), @@ -123,6 +153,8 @@ public class JobQueueTest { queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); + verify(jobManager).schedule(TAG, now, true); + Freeze.freezeAt(now).thawAfter(new Snippet() {{ queue.removeOverdueJobs(); }}); @@ -131,4 +163,26 @@ public class JobQueueTest { singletonList(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)), queue.getJobs()); } + + @Test + public void clearShouldCancelExisting() { + queue.add(new Reminder(1, 1, 0)); + + queue.clear(); + + InOrder order = inOrder(jobManager); + order.verify(jobManager).schedule(TAG, 1, true); + order.verify(jobManager).cancel(TAG); + assertEquals(0, queue.size()); + } + + @Test + public void ignoreInvalidCancel() { + long now = currentTimeMillis(); + + queue.add(new Reminder(1, now, TYPE_DUE)); + queue.cancel(2); + + verify(jobManager).schedule(TAG, now, true); + } } diff --git a/src/androidTest/java/org/tasks/makers/TaskMaker.java b/src/androidTest/java/org/tasks/makers/TaskMaker.java index c43dd49a6..dd9a3e942 100644 --- a/src/androidTest/java/org/tasks/makers/TaskMaker.java +++ b/src/androidTest/java/org/tasks/makers/TaskMaker.java @@ -1,5 +1,6 @@ package org.tasks.makers; +import com.google.common.base.Strings; import com.natpryce.makeiteasy.Instantiator; import com.natpryce.makeiteasy.Property; import com.natpryce.makeiteasy.PropertyValue; @@ -9,15 +10,21 @@ import org.tasks.time.DateTime; import static com.natpryce.makeiteasy.Property.newProperty; import static org.tasks.makers.Maker.make; +import static org.tasks.time.DateTimeUtils.currentTimeMillis; public class TaskMaker { public static Property ID = newProperty(); + public static Property TITLE = newProperty(); public static Property DUE_DATE = newProperty(); public static Property DUE_TIME = newProperty(); public static Property REMINDER_LAST = newProperty(); + public static Property RANDOM_REMINDER_PERIOD = newProperty(); public static Property HIDE_TYPE = newProperty(); public static Property REMINDERS = newProperty(); + public static Property COMPLETION_TIME = newProperty(); + public static Property DELETION_TIME = newProperty(); + public static Property SNOOZE_TIME = newProperty(); @SafeVarargs public static Task newTask(PropertyValue... properties) { @@ -27,6 +34,11 @@ public class TaskMaker { private static final Instantiator instantiator = lookup -> { Task task = new Task(); + String title = lookup.valueOf(TITLE, (String) null); + if (!Strings.isNullOrEmpty(title)) { + task.setTitle(title); + } + long id = lookup.valueOf(ID, Task.NO_ID); if (id != Task.NO_ID) { task.setId(id); @@ -42,6 +54,21 @@ public class TaskMaker { task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueTime.getMillis())); } + DateTime completionTime = lookup.valueOf(COMPLETION_TIME, (DateTime) null); + if (completionTime != null) { + task.setCompletionDate(completionTime.getMillis()); + } + + DateTime deletedTime = lookup.valueOf(DELETION_TIME, (DateTime) null); + if (deletedTime != null) { + task.setDeletionDate(deletedTime.getMillis()); + } + + DateTime snoozeTime = lookup.valueOf(SNOOZE_TIME, (DateTime) null); + if (snoozeTime != null) { + task.setReminderSnooze(snoozeTime.getMillis()); + } + int hideType = lookup.valueOf(HIDE_TYPE, -1); if (hideType >= 0) { task.setHideUntil(task.createHideUntil(hideType, 0)); @@ -57,6 +84,13 @@ public class TaskMaker { task.setReminderLast(reminderLast.getMillis()); } + long randomReminderPeriod = lookup.valueOf(RANDOM_REMINDER_PERIOD, 0L); + if (randomReminderPeriod > 0) { + task.setReminderPeriod(randomReminderPeriod); + } + + task.setCreationDate(currentTimeMillis()); + return task; }; } diff --git a/src/main/java/com/todoroo/astrid/alarms/AlarmService.java b/src/main/java/com/todoroo/astrid/alarms/AlarmService.java index 217f9cc6d..ea477f47d 100644 --- a/src/main/java/com/todoroo/astrid/alarms/AlarmService.java +++ b/src/main/java/com/todoroo/astrid/alarms/AlarmService.java @@ -48,13 +48,11 @@ public class AlarmService { private final JobQueue jobs; private final MetadataDao metadataDao; - private final JobManager jobManager; @Inject public AlarmService(MetadataDao metadataDao, JobManager jobManager, Preferences preferences) { this.metadataDao = metadataDao; - this.jobManager = jobManager; - jobs = new JobQueue<>(preferences); + jobs = JobQueue.newAlarmQueue(preferences, jobManager); } public void getAlarms(long taskId, Callback callback) { @@ -77,7 +75,7 @@ public class AlarmService { metadata.add(item); } - boolean changed = synchronizeMetadata(taskId, metadata, m -> cancelAlarm(m.getId())); + boolean changed = synchronizeMetadata(taskId, metadata, m -> jobs.cancel(m.getId())); if(changed) { scheduleAlarms(taskId); @@ -103,7 +101,6 @@ public class AlarmService { public void clear() { jobs.clear(); - jobManager.cancelAlarms(); } /** @@ -131,17 +128,9 @@ public class AlarmService { Alarm alarm = new Alarm(metadata); long time = alarm.getTime(); if(time == 0 || time == NO_ALARM) { - cancelAlarm(alarm.getId()); + jobs.cancel(alarm.getId()); } else { - if (jobs.add(alarm)) { - scheduleNext(true); - } - } - } - - private void cancelAlarm(Long alarmId) { - if (jobs.cancel(alarmId)) { - scheduleNext(true); + jobs.add(alarm); } } @@ -197,13 +186,7 @@ public class AlarmService { } public void scheduleNextJob() { - scheduleNext(false); - } - - private void scheduleNext(boolean cancelCurrent) { - if (!jobs.isEmpty()) { - jobManager.scheduleAlarm(jobs.nextScheduledTime(), cancelCurrent); - } + jobs.scheduleNext(); } public List removePastDueAlarms() { diff --git a/src/main/java/com/todoroo/astrid/reminders/AlarmScheduler.java b/src/main/java/com/todoroo/astrid/reminders/AlarmScheduler.java deleted file mode 100644 index e7d3ecffc..000000000 --- a/src/main/java/com/todoroo/astrid/reminders/AlarmScheduler.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.todoroo.astrid.reminders; - -import com.todoroo.astrid.data.Task; - -public interface AlarmScheduler { - void createAlarm(Task task, long time, int type); - - void clear(); -} diff --git a/src/main/java/com/todoroo/astrid/reminders/ReminderAlarmScheduler.java b/src/main/java/com/todoroo/astrid/reminders/ReminderAlarmScheduler.java deleted file mode 100644 index ba18649c9..000000000 --- a/src/main/java/com/todoroo/astrid/reminders/ReminderAlarmScheduler.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.todoroo.astrid.reminders; - -import com.todoroo.astrid.data.Task; - -import org.tasks.injection.ApplicationScope; -import org.tasks.jobs.JobManager; -import org.tasks.jobs.JobQueue; -import org.tasks.jobs.Reminder; -import org.tasks.preferences.Preferences; - -import java.util.List; - -import javax.inject.Inject; - -import static com.todoroo.astrid.reminders.ReminderService.NO_ALARM; - -@ApplicationScope -public class ReminderAlarmScheduler implements AlarmScheduler { - - private final JobQueue jobs; - private final JobManager jobManager; - - @Inject - public ReminderAlarmScheduler(JobManager jobManager, Preferences preferences) { - this.jobManager = jobManager; - jobs = new JobQueue<>(preferences); - } - - /** - * Create an alarm for the given task at the given type - */ - @Override - public void createAlarm(Task task, long time, int type) { - long taskId = task.getId(); - if(taskId == Task.NO_ID) { - return; - } - - if (time == 0 || time == NO_ALARM) { - if (jobs.cancel(taskId)) { - scheduleNext(true); - } - } else { - Reminder reminder = new Reminder(taskId, time, type); - if (jobs.add(reminder)) { - scheduleNext(true); - } - } - } - - @Override - public void clear() { - jobs.clear(); - jobManager.cancelReminders(); - } - - public void scheduleNextJob() { - scheduleNext(false); - } - - private void scheduleNext(boolean cancelCurrent) { - if (jobs.isEmpty()) { - if (cancelCurrent) { - jobManager.cancelReminders(); - } - } else { - jobManager.scheduleReminder(jobs.nextScheduledTime(), cancelCurrent); - } - } - - public List removePastReminders() { - return jobs.removeOverdueJobs(); - } -} diff --git a/src/main/java/com/todoroo/astrid/reminders/ReminderPreferences.java b/src/main/java/com/todoroo/astrid/reminders/ReminderPreferences.java index b607f3129..281cdd98f 100644 --- a/src/main/java/com/todoroo/astrid/reminders/ReminderPreferences.java +++ b/src/main/java/com/todoroo/astrid/reminders/ReminderPreferences.java @@ -16,13 +16,10 @@ import android.preference.PreferenceManager; import android.provider.Settings; import android.support.annotation.NonNull; -import com.todoroo.astrid.alarms.AlarmService; - import org.tasks.R; import org.tasks.activities.ColorPickerActivity; import org.tasks.activities.TimePickerActivity; import org.tasks.dialogs.ColorPickerDialog; -import org.tasks.dialogs.DialogBuilder; import org.tasks.injection.ActivityComponent; import org.tasks.injection.InjectingPreferenceActivity; import org.tasks.preferences.ActivityPermissionRequestor; @@ -52,10 +49,8 @@ public class ReminderPreferences extends InjectingPreferenceActivity { @Inject Device device; @Inject ActivityPermissionRequestor permissionRequestor; @Inject PermissionChecker permissionChecker; - @Inject DialogBuilder dialogBuilder; @Inject Preferences preferences; @Inject ThemeCache themeCache; - @Inject AlarmService alarmService; private CheckBoxPreference fieldMissedCalls; diff --git a/src/main/java/com/todoroo/astrid/reminders/ReminderService.java b/src/main/java/com/todoroo/astrid/reminders/ReminderService.java index 41579e264..325bce4f1 100644 --- a/src/main/java/com/todoroo/astrid/reminders/ReminderService.java +++ b/src/main/java/com/todoroo/astrid/reminders/ReminderService.java @@ -15,21 +15,19 @@ import com.todoroo.astrid.data.Task; import org.tasks.R; import org.tasks.injection.ApplicationScope; +import org.tasks.jobs.JobManager; +import org.tasks.jobs.JobQueue; +import org.tasks.jobs.Reminder; import org.tasks.preferences.Preferences; import org.tasks.time.DateTime; +import java.util.List; import java.util.Random; import javax.inject.Inject; import static org.tasks.date.DateTimeUtils.newDateTime; -/** - * Data service for reminders - * - * @author Tim Su - * - */ @ApplicationScope public final class ReminderService { @@ -60,38 +58,36 @@ public final class ReminderService { public static final int TYPE_ALARM = 4; private static final Random random = new Random(); + private static final long NO_ALARM = Long.MAX_VALUE; - // --- instance variables - - private AlarmScheduler scheduler; + private final JobQueue jobs; + private final Preferences preferences; private long now = -1; // For tracking when reminders might be scheduled all at once - private final Preferences preferences; @Inject - ReminderService(Preferences preferences, ReminderAlarmScheduler scheduler) { + ReminderService(Preferences preferences, JobManager jobManager) { + this(preferences, JobQueue.newReminderQueue(preferences, jobManager)); + } + + ReminderService(Preferences preferences, JobQueue jobs) { this.preferences = preferences; - this.scheduler = scheduler; + this.jobs = jobs; } private static final int MILLIS_PER_HOUR = 60 * 60 * 1000; - // --- reminder scheduling logic - - /** - * Schedules all alarms - */ public void scheduleAllAlarms(TaskDao taskDao) { now = DateUtilities.now(); // Before mass scheduling, initialize now variable Query query = Query.select(NOTIFICATION_PROPERTIES).where(Criterion.and( TaskCriteria.isActive(), Criterion.or(Task.REMINDER_FLAGS.gt(0), Task.REMINDER_PERIOD.gt(0)))); - taskDao.forEach(query, task -> scheduleAlarm(task, null)); + taskDao.forEach(query, task -> scheduleAlarm(null, task)); now = -1; // Signal done with now variable } public void clear() { - scheduler.clear(); + jobs.clear(); } private long getNowValue() { @@ -99,29 +95,25 @@ public final class ReminderService { return (now == -1 ? DateUtilities.now() : now); } - public static final long NO_ALARM = Long.MAX_VALUE; - - /** - * Schedules alarms for a single task - */ - public void scheduleAlarm(TaskDao taskDao, Task task) { - scheduleAlarm(task, taskDao); + public List getPastReminders() { + return jobs.removeOverdueJobs(); } - private void clearAllAlarms(Task task) { - scheduler.createAlarm(task, NO_ALARM, 0); + public void scheduleNextJob() { + jobs.scheduleNext(); } - private void scheduleAlarm(Task task, TaskDao taskDao) { + public void scheduleAlarm(TaskDao taskDao, Task task) { if(task == null || !task.isSaved()) { return; } // read data if necessary + long taskId = task.getId(); if(taskDao != null) { for(Property property : NOTIFICATION_PROPERTIES) { if(!task.containsValue(property)) { - task = taskDao.fetch(task.getId(), NOTIFICATION_PROPERTIES); + task = taskDao.fetch(taskId, NOTIFICATION_PROPERTIES); if(task == null) { return; } @@ -132,7 +124,8 @@ public final class ReminderService { // Make sure no alarms are scheduled other than the next one. When that one is shown, it // will schedule the next one after it, and so on and so forth. - clearAllAlarms(task); + jobs.cancel(taskId); + if(task.isCompleted() || task.isDeleted()) { return; } @@ -164,15 +157,13 @@ public final class ReminderService { // snooze trumps all if(whenSnooze != NO_ALARM) { - scheduler.createAlarm(task, whenSnooze, TYPE_SNOOZE); + jobs.add(new Reminder(taskId, whenSnooze, TYPE_SNOOZE)); } else if(whenRandom < whenDueDate && whenRandom < whenOverdue) { - scheduler.createAlarm(task, whenRandom, TYPE_RANDOM); + jobs.add(new Reminder(taskId, whenRandom, TYPE_RANDOM)); } else if(whenDueDate < whenOverdue) { - scheduler.createAlarm(task, whenDueDate, TYPE_DUE); + jobs.add(new Reminder(taskId, whenDueDate, TYPE_DUE)); } else if(whenOverdue != NO_ALARM) { - scheduler.createAlarm(task, whenOverdue, TYPE_OVERDUE); - } else { - scheduler.createAlarm(task, 0, 0); + jobs.add(new Reminder(taskId, whenOverdue, TYPE_OVERDUE)); } } @@ -238,7 +229,7 @@ public final class ReminderService { * If the date was indicated to not have a due time, we read from * preferences and assign a time. */ - long calculateNextDueDateReminder(Task task) { + private long calculateNextDueDateReminder(Task task) { if(task.hasDueDate() && task.isNotifyAtDeadline()) { long dueDate = task.getDueDate(); long lastReminder = task.getReminderLast(); @@ -285,14 +276,4 @@ public final class ReminderService { } return NO_ALARM; } - - // --- alarm manager alarm creation - - public void setScheduler(AlarmScheduler scheduler) { - this.scheduler = scheduler; - } - - public AlarmScheduler getScheduler() { - return scheduler; - } } diff --git a/src/main/java/org/tasks/jobs/JobCreator.java b/src/main/java/org/tasks/jobs/JobCreator.java index 5c5c003e9..498d7d0b5 100644 --- a/src/main/java/org/tasks/jobs/JobCreator.java +++ b/src/main/java/org/tasks/jobs/JobCreator.java @@ -6,7 +6,7 @@ import com.evernote.android.job.Job; import com.todoroo.astrid.alarms.AlarmService; import com.todoroo.astrid.backup.TasksXmlExporter; import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.reminders.ReminderAlarmScheduler; +import com.todoroo.astrid.reminders.ReminderService; import org.tasks.Broadcaster; import org.tasks.Notifier; @@ -29,13 +29,13 @@ public class JobCreator implements com.evernote.android.job.JobCreator { private final RefreshScheduler refreshScheduler; private final AlarmService alarmService; private final TaskDao taskDao; - private final ReminderAlarmScheduler reminderAlarmScheduler; + private final ReminderService reminderService; @Inject public JobCreator(@ForApplication Context context, Notifier notifier, JobManager jobManager, Broadcaster broadcaster, TasksXmlExporter tasksXmlExporter, Preferences preferences, RefreshScheduler refreshScheduler, - AlarmService alarmService, TaskDao taskDao, ReminderAlarmScheduler reminderAlarmScheduler) { + AlarmService alarmService, TaskDao taskDao, ReminderService reminderService) { this.context = context; this.notifier = notifier; this.jobManager = jobManager; @@ -45,14 +45,14 @@ public class JobCreator implements com.evernote.android.job.JobCreator { this.refreshScheduler = refreshScheduler; this.alarmService = alarmService; this.taskDao = taskDao; - this.reminderAlarmScheduler = reminderAlarmScheduler; + this.reminderService = reminderService; } @Override public Job create(String tag) { switch (tag) { case ReminderJob.TAG: - return new ReminderJob(preferences, reminderAlarmScheduler, notifier); + return new ReminderJob(preferences, reminderService, notifier); case AlarmJob.TAG: return new AlarmJob(preferences, alarmService, notifier, taskDao); case RefreshJob.TAG: diff --git a/src/main/java/org/tasks/jobs/JobManager.java b/src/main/java/org/tasks/jobs/JobManager.java index 3551dc8f6..c8777cf9a 100644 --- a/src/main/java/org/tasks/jobs/JobManager.java +++ b/src/main/java/org/tasks/jobs/JobManager.java @@ -28,16 +28,8 @@ public class JobManager { jobManager.addJobCreator(jobCreator); } - public void scheduleAlarm(long time, boolean cancelCurrent) { - new JobRequest.Builder(AlarmJob.TAG) - .setExact(Math.max(time - currentTimeMillis(), 5000)) - .setUpdateCurrent(cancelCurrent) - .build() - .schedule(); - } - - public void scheduleReminder(long time, boolean cancelCurrent) { - new JobRequest.Builder(ReminderJob.TAG) + public void schedule(String tag, long time, boolean cancelCurrent) { + new JobRequest.Builder(tag) .setExact(Math.max(time - currentTimeMillis(), 5000)) .setUpdateCurrent(cancelCurrent) .build() @@ -69,15 +61,11 @@ public class JobManager { .schedule(); } - public void cancelAlarms() { - jobManager.cancelAllForTag(AlarmJob.TAG); + public void cancel(String tag) { + jobManager.cancelAllForTag(tag); } public void cancelRefreshes() { jobManager.cancelAllForTag(RefreshJob.TAG); } - - public void cancelReminders() { - jobManager.cancelAllForTag(ReminderJob.TAG); - } } diff --git a/src/main/java/org/tasks/jobs/JobQueue.java b/src/main/java/org/tasks/jobs/JobQueue.java index 8952b422b..2c54c3bc5 100644 --- a/src/main/java/org/tasks/jobs/JobQueue.java +++ b/src/main/java/org/tasks/jobs/JobQueue.java @@ -17,26 +17,37 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis; public class JobQueue { private final TreeMultimap jobs = TreeMultimap.create(Ordering.natural(), (l, r) -> Longs.compare(l.getId(), r.getId())); private final Preferences preferences; + private final JobManager jobManager; + private final String tag; - public JobQueue(Preferences preferences) { - this.preferences = preferences; + public static JobQueue newReminderQueue(Preferences preferences, JobManager jobManager) { + return new JobQueue<>(preferences, jobManager, ReminderJob.TAG); } - public boolean add(T entry) { - boolean result = jobs.isEmpty() || entry.getTime() < firstTime(); - jobs.put(entry.getTime(), entry); - return result; + public static JobQueue newAlarmQueue(Preferences preferences, JobManager jobManager) { + return new JobQueue<>(preferences, jobManager, AlarmJob.TAG); + } + + JobQueue(Preferences preferences, JobManager jobManager, String tag) { + this.preferences = preferences; + this.jobManager = jobManager; + this.tag = tag; } - public boolean isEmpty() { - return jobs.isEmpty(); + public void add(T entry) { + boolean reschedule = jobs.isEmpty() || entry.getTime() < firstTime(); + jobs.put(entry.getTime(), entry); + if (reschedule) { + scheduleNext(true); + } } public void clear() { jobs.clear(); + jobManager.cancel(tag); } - public boolean cancel(long id) { + public void cancel(long id) { boolean reschedule = false; long firstTime = firstTime(); List existing = newArrayList(filter(jobs.values(), r -> r.getId() == id)); @@ -44,11 +55,9 @@ public class JobQueue { reschedule |= entry.getTime() == firstTime; jobs.remove(entry.getTime(), entry); } - return reschedule; - } - - List getJobs() { - return ImmutableList.copyOf(jobs.values()); + if (reschedule) { + scheduleNext(true); + } } public List removeOverdueJobs() { @@ -60,16 +69,34 @@ public class JobQueue { return result; } - public int size() { - return jobs.size(); + public void scheduleNext() { + scheduleNext(false); + } + + private void scheduleNext(boolean cancelCurrent) { + if (jobs.isEmpty()) { + if (cancelCurrent) { + jobManager.cancel(tag); + } + } else { + jobManager.schedule(tag, nextScheduledTime(), cancelCurrent); + } } private long firstTime() { return jobs.isEmpty() ? 0 : jobs.asMap().firstKey(); } - public long nextScheduledTime() { + long nextScheduledTime() { long next = firstTime(); return next > 0 ? preferences.adjustForQuietHours(next) : 0; } + + int size() { + return jobs.size(); + } + + List getJobs() { + return ImmutableList.copyOf(jobs.values()); + } } diff --git a/src/main/java/org/tasks/jobs/ReminderJob.java b/src/main/java/org/tasks/jobs/ReminderJob.java index 831329233..2e8098a1c 100644 --- a/src/main/java/org/tasks/jobs/ReminderJob.java +++ b/src/main/java/org/tasks/jobs/ReminderJob.java @@ -3,7 +3,7 @@ package org.tasks.jobs; import android.support.annotation.NonNull; import com.evernote.android.job.Job; -import com.todoroo.astrid.reminders.ReminderAlarmScheduler; +import com.todoroo.astrid.reminders.ReminderService; import org.tasks.Notifier; import org.tasks.preferences.Preferences; @@ -13,12 +13,12 @@ public class ReminderJob extends Job { public static final String TAG = "job_reminder"; private final Preferences preferences; - private final ReminderAlarmScheduler reminderAlarmScheduler; + private final ReminderService reminderService; private final Notifier notifier; - public ReminderJob(Preferences preferences, ReminderAlarmScheduler reminderAlarmScheduler, Notifier notifier) { + public ReminderJob(Preferences preferences, ReminderService reminderService, Notifier notifier) { this.preferences = preferences; - this.reminderAlarmScheduler = reminderAlarmScheduler; + this.reminderService = reminderService; this.notifier = notifier; } @@ -27,13 +27,13 @@ public class ReminderJob extends Job { protected Result onRunJob(Params params) { try { if (!preferences.isCurrentlyQuietHours()) { - for (Reminder reminder : reminderAlarmScheduler.removePastReminders()) { + for (Reminder reminder : reminderService.getPastReminders()) { notifier.triggerTaskNotification(reminder.getId(), reminder.getType()); } } return Result.SUCCESS; } finally { - reminderAlarmScheduler.scheduleNextJob(); + reminderService.scheduleNextJob(); } } }