Combine alarms and jobs into one queue

pull/574/head
Alex Baker 8 years ago
parent c573d03ca4
commit 248588b355

@ -54,7 +54,7 @@ public class ReminderServiceTest extends InjectingTestCase {
private ReminderService service; private ReminderService service;
private Random random; private Random random;
private JobQueue<Reminder> jobs; private JobQueue jobs;
@Before @Before
public void before() { public void before() {
@ -78,14 +78,14 @@ public class ReminderServiceTest extends InjectingTestCase {
public void dontScheduleDueDateReminderWhenFlagNotSet() { public void dontScheduleDueDateReminderWhenFlagNotSet() {
service.scheduleAlarm(null, newTask(with(ID, 1L), with(DUE_TIME, newDateTime()))); service.scheduleAlarm(null, newTask(with(ID, 1L), with(DUE_TIME, newDateTime())));
verify(jobs).cancel(1); verify(jobs).cancelReminder(1);
} }
@Test @Test
public void dontScheduleDueDateReminderWhenTimeNotSet() { public void dontScheduleDueDateReminderWhenTimeNotSet() {
service.scheduleAlarm(null, newTask(with(ID, 1L), with(REMINDERS, NOTIFY_AT_DEADLINE))); service.scheduleAlarm(null, newTask(with(ID, 1L), with(REMINDERS, NOTIFY_AT_DEADLINE)));
verify(jobs).cancel(1); verify(jobs).cancelReminder(1);
} }
@Test @Test
@ -97,7 +97,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@ -110,7 +110,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@ -124,7 +124,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE));
} }
@ -138,7 +138,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
verify(jobs).cancel(1); verify(jobs).cancelReminder(1);
} }
@Test @Test
@ -151,7 +151,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
verify(jobs).cancel(1); verify(jobs).cancelReminder(1);
} }
@Test @Test
@ -165,7 +165,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
verify(jobs).cancel(1); verify(jobs).cancelReminder(1);
} }
@Test @Test
@ -178,7 +178,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@ -197,7 +197,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM)); order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -216,7 +216,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM)); order.verify(jobs).add(new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -235,7 +235,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM)); order.verify(jobs).add(new Reminder(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -252,7 +252,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, task.getDueDate() + 4582800, ReminderService.TYPE_OVERDUE)); order.verify(jobs).add(new Reminder(1L, task.getDueDate() + 4582800, ReminderService.TYPE_OVERDUE));
}}); }});
} }
@ -269,7 +269,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, currentTimeMillis(), ReminderService.TYPE_OVERDUE)); order.verify(jobs).add(new Reminder(1L, currentTimeMillis(), ReminderService.TYPE_OVERDUE));
}}); }});
} }
@ -286,7 +286,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, currentTimeMillis(), ReminderService.TYPE_OVERDUE)); order.verify(jobs).add(new Reminder(1L, currentTimeMillis(), ReminderService.TYPE_OVERDUE));
}}); }});
} }
@ -305,7 +305,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, currentTimeMillis() + 22748400, ReminderService.TYPE_OVERDUE)); order.verify(jobs).add(new Reminder(1L, currentTimeMillis() + 22748400, ReminderService.TYPE_OVERDUE));
}}); }});
} }
@ -323,7 +323,7 @@ public class ReminderServiceTest extends InjectingTestCase {
service.scheduleAlarm(null, task); service.scheduleAlarm(null, task);
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancel(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE)); order.verify(jobs).add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE));
} }

@ -30,9 +30,9 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class JobQueueTest { public class JobQueueTest {
private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1); private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
private static final String TAG = "test"; private static final String TAG = NotificationJob.TAG;
private JobQueue<Reminder> queue; private JobQueue queue;
private JobManager jobManager; private JobManager jobManager;
private Preferences preferences; private Preferences preferences;
@ -41,7 +41,7 @@ public class JobQueueTest {
preferences = mock(Preferences.class); preferences = mock(Preferences.class);
when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg()); when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg());
jobManager = mock(JobManager.class); jobManager = mock(JobManager.class);
queue = new JobQueue<>(preferences, jobManager, TAG); queue = new JobQueue(preferences, jobManager);
} }
@After @After
@ -49,6 +49,59 @@ public class JobQueueTest {
verifyNoMoreInteractions(jobManager); verifyNoMoreInteractions(jobManager);
} }
@Test
public void alarmAndReminderSameTimeSameID() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Alarm(1, 1, now));
verify(jobManager).schedule(TAG, now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
asList(new Reminder(1, now, TYPE_DUE),
new Alarm(1, 1, now)),
queue.getOverdueJobs());
}});
}
@Test
public void removeAlarmLeaveReminder() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Alarm(1, 1, now));
verify(jobManager).schedule(TAG, now);
queue.remove(new Alarm(1, 1, now));
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)),
queue.getOverdueJobs());
}});
}
@Test
public void removeReminderLeaveAlarm() {
long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE));
queue.add(new Alarm(1, 1, now));
verify(jobManager).schedule(TAG, now);
queue.remove(new Reminder(1, now, TYPE_DUE));
Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals(
singletonList(new Alarm(1, 1, now)),
queue.getOverdueJobs());
}});
}
@Test @Test
public void twoJobsAtSameTime() { public void twoJobsAtSameTime() {
queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(1, 1, 0));
@ -87,7 +140,7 @@ public class JobQueueTest {
@Test @Test
public void rescheduleWhenCancelingOnlyJob() { public void rescheduleWhenCancelingOnlyJob() {
queue.add(new Reminder(1, 2, 0)); queue.add(new Reminder(1, 2, 0));
queue.cancel(1); queue.cancelReminder(1);
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2); order.verify(jobManager).schedule(TAG, 2);
@ -99,7 +152,7 @@ public class JobQueueTest {
queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0)); queue.add(new Reminder(2, 2, 0));
queue.cancel(1); queue.cancelReminder(1);
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1); order.verify(jobManager).schedule(TAG, 1);
@ -111,7 +164,7 @@ public class JobQueueTest {
queue.add(new Reminder(1, 1, 0)); queue.add(new Reminder(1, 1, 0));
queue.add(new Reminder(2, 2, 0)); queue.add(new Reminder(2, 2, 0));
queue.cancel(2); queue.cancelReminder(2);
verify(jobManager).schedule(TAG, 1); verify(jobManager).schedule(TAG, 1);
} }
@ -234,7 +287,7 @@ public class JobQueueTest {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new Reminder(1, now, TYPE_DUE));
queue.cancel(2); queue.cancelReminder(2);
verify(jobManager).schedule(TAG, now); verify(jobManager).schedule(TAG, now);
} }

@ -456,15 +456,9 @@
android:permission="android.permission.BIND_JOB_SERVICE" android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" /> android:exported="false" />
<receiver android:name=".jobs.AlarmJob$Broadcast" /> <receiver android:name=".jobs.NotificationJob$Broadcast" />
<service <service
android:name=".jobs.AlarmJob" android:name=".jobs.NotificationJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<receiver android:name=".jobs.ReminderJob$Broadcast" />
<service
android:name=".jobs.ReminderJob"
android:permission="android.permission.BIND_JOB_SERVICE" android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" /> android:exported="false" />

@ -22,9 +22,8 @@ import com.todoroo.astrid.service.SynchronizeMetadataCallback;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.Alarm; import org.tasks.jobs.Alarm;
import org.tasks.jobs.JobManager;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.JobQueue;
import org.tasks.preferences.Preferences; import org.tasks.jobs.JobQueueEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@ -45,14 +44,14 @@ public class AlarmService {
private static final long NO_ALARM = Long.MAX_VALUE; private static final long NO_ALARM = Long.MAX_VALUE;
private final JobQueue<Alarm> jobs; private final JobQueue jobs;
private final MetadataDao metadataDao; private final MetadataDao metadataDao;
@Inject @Inject
public AlarmService(MetadataDao metadataDao, JobManager jobManager, Preferences preferences) { public AlarmService(MetadataDao metadataDao, JobQueue jobQueue) {
this.metadataDao = metadataDao; this.metadataDao = metadataDao;
jobs = JobQueue.newAlarmQueue(preferences, jobManager); jobs = jobQueue;
} }
public void getAlarms(long taskId, Callback<Metadata> callback) { public void getAlarms(long taskId, Callback<Metadata> callback) {
@ -75,7 +74,7 @@ public class AlarmService {
metadata.add(item); metadata.add(item);
} }
boolean changed = synchronizeMetadata(taskId, metadata, m -> jobs.cancel(m.getId())); boolean changed = synchronizeMetadata(taskId, metadata, m -> jobs.cancelAlarm(m.getId()));
if(changed) { if(changed) {
scheduleAlarms(taskId); scheduleAlarms(taskId);
@ -99,10 +98,6 @@ public class AlarmService {
MetadataCriteria.byTaskAndwithKey(taskId, AlarmFields.METADATA_KEY)))); MetadataCriteria.byTaskAndwithKey(taskId, AlarmFields.METADATA_KEY))));
} }
public void clear() {
jobs.clear();
}
/** /**
* Schedules all alarms * Schedules all alarms
*/ */
@ -128,7 +123,7 @@ public class AlarmService {
Alarm alarm = new Alarm(metadata); Alarm alarm = new Alarm(metadata);
long time = alarm.getTime(); long time = alarm.getTime();
if(time == 0 || time == NO_ALARM) { if(time == 0 || time == NO_ALARM) {
jobs.cancel(alarm.getId()); jobs.cancelAlarm(alarm.getId());
} else { } else {
jobs.add(alarm); jobs.add(alarm);
} }
@ -184,16 +179,4 @@ public class AlarmService {
return dirty[0]; return dirty[0];
} }
public void scheduleNextJob() {
jobs.scheduleNext();
}
public List<Alarm> getOverdueAlarms() {
return jobs.getOverdueJobs();
}
public boolean remove(Alarm alarm) {
return jobs.remove(alarm);
}
} }

@ -15,15 +15,12 @@ import com.todoroo.astrid.data.Task;
import org.tasks.R; import org.tasks.R;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.JobManager;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.JobQueue;
import org.tasks.jobs.Reminder; import org.tasks.jobs.Reminder;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.reminders.Random; import org.tasks.reminders.Random;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.date.DateTimeUtils.newDateTime;
@ -59,18 +56,18 @@ public final class ReminderService {
private static final long NO_ALARM = Long.MAX_VALUE; private static final long NO_ALARM = Long.MAX_VALUE;
private final JobQueue<Reminder> jobs; private final JobQueue jobs;
private final Random random; private final Random random;
private final Preferences preferences; private final Preferences preferences;
private long now = -1; // For tracking when reminders might be scheduled all at once private long now = -1; // For tracking when reminders might be scheduled all at once
@Inject @Inject
ReminderService(Preferences preferences, JobManager jobManager) { ReminderService(Preferences preferences, JobQueue jobQueue) {
this(preferences, JobQueue.newReminderQueue(preferences, jobManager), new Random()); this(preferences, jobQueue, new Random());
} }
ReminderService(Preferences preferences, JobQueue<Reminder> jobs, Random random) { ReminderService(Preferences preferences, JobQueue jobs, Random random) {
this.preferences = preferences; this.preferences = preferences;
this.jobs = jobs; this.jobs = jobs;
this.random = random; this.random = random;
@ -87,27 +84,11 @@ public final class ReminderService {
now = -1; // Signal done with now variable now = -1; // Signal done with now variable
} }
public void clear() {
jobs.clear();
}
private long getNowValue() { private long getNowValue() {
// If we're in the midst of mass scheduling, use the prestored now var // If we're in the midst of mass scheduling, use the prestored now var
return (now == -1 ? DateUtilities.now() : now); return (now == -1 ? DateUtilities.now() : now);
} }
public List<Reminder> getPastReminders() {
return jobs.getOverdueJobs();
}
public boolean remove(Reminder reminder) {
return jobs.remove(reminder);
}
public void scheduleNextJob() {
jobs.scheduleNext();
}
public void scheduleAlarm(TaskDao taskDao, Task task) { public void scheduleAlarm(TaskDao taskDao, Task task) {
if(task == null || !task.isSaved()) { if(task == null || !task.isSaved()) {
return; return;
@ -129,7 +110,7 @@ public final class ReminderService {
// Make sure no alarms are scheduled other than the next one. When that one is shown, it // 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. // will schedule the next one after it, and so on and so forth.
jobs.cancel(taskId); jobs.cancelReminder(taskId);
if(task.isCompleted() || task.isDeleted()) { if(task.isCompleted() || task.isDeleted()) {
return; return;

@ -1,10 +1,9 @@
package org.tasks.injection; package org.tasks.injection;
import org.tasks.jobs.AlarmJob; import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.BackupJob; import org.tasks.jobs.BackupJob;
import org.tasks.jobs.MidnightRefreshJob; import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.RefreshJob; import org.tasks.jobs.RefreshJob;
import org.tasks.jobs.ReminderJob;
import org.tasks.location.GeofenceTransitionsIntentService; import org.tasks.location.GeofenceTransitionsIntentService;
import org.tasks.scheduling.CalendarNotificationIntentService; import org.tasks.scheduling.CalendarNotificationIntentService;
import org.tasks.scheduling.GeofenceSchedulingIntentService; import org.tasks.scheduling.GeofenceSchedulingIntentService;
@ -25,13 +24,11 @@ public interface IntentServiceComponent {
void inject(NotificationSchedulerIntentService notificationSchedulerIntentService); void inject(NotificationSchedulerIntentService notificationSchedulerIntentService);
void inject(AlarmJob alarmJob); void inject(NotificationJob notificationJob);
void inject(BackupJob backupJob); void inject(BackupJob backupJob);
void inject(MidnightRefreshJob midnightRefreshJob); void inject(MidnightRefreshJob midnightRefreshJob);
void inject(RefreshJob refreshJob); void inject(RefreshJob refreshJob);
void inject(ReminderJob reminderJob);
} }

@ -39,12 +39,17 @@ public class Alarm implements JobQueueEntry {
Alarm alarm = (Alarm) o; Alarm alarm = (Alarm) o;
return alarmId == alarm.alarmId; if (alarmId != alarm.alarmId) return false;
if (taskId != alarm.taskId) return false;
return time == alarm.time;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return (int) (alarmId ^ (alarmId >>> 32)); int result = (int) (alarmId ^ (alarmId >>> 32));
result = 31 * result + (int) (taskId ^ (taskId >>> 32));
result = 31 * result + (int) (time ^ (time >>> 32));
return result;
} }
@Override @Override

@ -20,8 +20,7 @@ import static org.tasks.time.DateTimeUtils.printTimestamp;
public class JobManager { public class JobManager {
static final int JOB_ID_REFRESH = 1; static final int JOB_ID_REFRESH = 1;
static final int JOB_ID_REMINDER = 2; static final int JOB_ID_NOTIFICATION = 3;
static final int JOB_ID_ALARM = 3;
public static final int JOB_ID_GEOFENCE_TRANSITION = 4; public static final int JOB_ID_GEOFENCE_TRANSITION = 4;
public static final int JOB_ID_GEOFENCE_SCHEDULING = 5; public static final int JOB_ID_GEOFENCE_SCHEDULING = 5;
static final int JOB_ID_MIDNIGHT_REFRESH = 6; static final int JOB_ID_MIDNIGHT_REFRESH = 6;
@ -72,10 +71,8 @@ public class JobManager {
private PendingIntent getPendingIntent(String tag) { private PendingIntent getPendingIntent(String tag) {
switch (tag) { switch (tag) {
case ReminderJob.TAG: case NotificationJob.TAG:
return getPendingBroadcast(ReminderJob.Broadcast.class); return getPendingBroadcast(NotificationJob.Broadcast.class);
case AlarmJob.TAG:
return getPendingBroadcast(AlarmJob.Broadcast.class);
case RefreshJob.TAG: case RefreshJob.TAG:
return getPendingBroadcast(RefreshJob.Broadcast.class); return getPendingBroadcast(RefreshJob.Broadcast.class);
default: default:

@ -1,42 +1,35 @@
package org.tasks.jobs; package org.tasks.jobs;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultimap; import com.google.common.collect.TreeMultimap;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import org.tasks.injection.ApplicationScope;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import java.util.List; import java.util.List;
import java.util.NavigableSet;
import java.util.SortedSet; import javax.inject.Inject;
import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static org.tasks.time.DateTimeUtils.currentTimeMillis; import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class JobQueue<T extends JobQueueEntry> { @ApplicationScope
private final TreeMultimap<Long, T> jobs = TreeMultimap.create(Ordering.natural(), (l, r) -> Longs.compare(l.getId(), r.getId())); public class JobQueue {
private final TreeMultimap<Long, JobQueueEntry> jobs = TreeMultimap.create(Ordering.natural(), (l, r) -> Ints.compare(l.hashCode(), r.hashCode()));
private final Preferences preferences; private final Preferences preferences;
private final JobManager jobManager; private final JobManager jobManager;
private final String tag;
public static JobQueue<Reminder> newReminderQueue(Preferences preferences, JobManager jobManager) {
return new JobQueue<>(preferences, jobManager, ReminderJob.TAG);
}
public static JobQueue<Alarm> newAlarmQueue(Preferences preferences, JobManager jobManager) {
return new JobQueue<>(preferences, jobManager, AlarmJob.TAG);
}
JobQueue(Preferences preferences, JobManager jobManager, String tag) { @Inject
public JobQueue(Preferences preferences, JobManager jobManager) {
this.preferences = preferences; this.preferences = preferences;
this.jobManager = jobManager; this.jobManager = jobManager;
this.tag = tag;
} }
public synchronized void add(T entry) { public synchronized <T extends JobQueueEntry> void add(T entry) {
boolean reschedule = jobs.isEmpty() || entry.getTime() < firstTime(); boolean reschedule = jobs.isEmpty() || entry.getTime() < firstTime();
jobs.put(entry.getTime(), entry); jobs.put(entry.getTime(), entry);
if (reschedule) { if (reschedule) {
@ -46,14 +39,23 @@ public class JobQueue<T extends JobQueueEntry> {
public synchronized void clear() { public synchronized void clear() {
jobs.clear(); jobs.clear();
jobManager.cancel(tag); jobManager.cancel(NotificationJob.TAG);
}
public synchronized void cancelAlarm(long alarmId) {
cancel(Alarm.class, alarmId);
}
public synchronized void cancelReminder(long taskId) {
cancel(Reminder.class, taskId);
} }
public synchronized void cancel(long id) { private synchronized void cancel(Class<? extends JobQueueEntry> c, long id) {
boolean reschedule = false; boolean reschedule = false;
long firstTime = firstTime(); long firstTime = firstTime();
List<T> existing = newArrayList(filter(jobs.values(), r -> r.getId() == id)); List<JobQueueEntry> existing = newArrayList(
for (T entry : existing) { filter(jobs.values(), r -> r.getClass().equals(c) && r.getId() == id));
for (JobQueueEntry entry : existing) {
reschedule |= entry.getTime() == firstTime; reschedule |= entry.getTime() == firstTime;
jobs.remove(entry.getTime(), entry); jobs.remove(entry.getTime(), entry);
} }
@ -62,29 +64,29 @@ public class JobQueue<T extends JobQueueEntry> {
} }
} }
public synchronized List<T> getOverdueJobs() { synchronized List<? extends JobQueueEntry> getOverdueJobs() {
List<T> result = newArrayList(); List<JobQueueEntry> result = newArrayList();
for (Long key : jobs.keySet().headSet(currentTimeMillis() + 1)) { for (Long key : jobs.keySet().headSet(currentTimeMillis() + 1)) {
result.addAll(jobs.get(key)); result.addAll(jobs.get(key));
} }
return result; return result;
} }
public synchronized boolean remove(T entry) { public synchronized boolean remove(JobQueueEntry entry) {
return jobs.remove(entry.getTime(), entry); return jobs.remove(entry.getTime(), entry);
} }
public synchronized void scheduleNext() { synchronized void scheduleNext() {
scheduleNext(false); scheduleNext(false);
} }
private void scheduleNext(boolean cancelCurrent) { private void scheduleNext(boolean cancelCurrent) {
if (jobs.isEmpty()) { if (jobs.isEmpty()) {
if (cancelCurrent) { if (cancelCurrent) {
jobManager.cancel(tag); jobManager.cancel(NotificationJob.TAG);
} }
} else { } else {
jobManager.schedule(tag, nextScheduledTime()); jobManager.schedule(NotificationJob.TAG, nextScheduledTime());
} }
} }
@ -101,7 +103,7 @@ public class JobQueue<T extends JobQueueEntry> {
return jobs.size(); return jobs.size();
} }
List<T> getJobs() { List<JobQueueEntry> getJobs() {
return ImmutableList.copyOf(jobs.values()); return ImmutableList.copyOf(jobs.values());
} }
} }

@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.v4.app.JobIntentService; import android.support.v4.app.JobIntentService;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.reminders.ReminderService;
@ -16,38 +15,44 @@ import org.tasks.preferences.Preferences;
import javax.inject.Inject; import javax.inject.Inject;
public class AlarmJob extends Job { public class NotificationJob extends Job {
public static class Broadcast extends BroadcastReceiver { public static class Broadcast extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(context, AlarmJob.class, JobManager.JOB_ID_ALARM, intent); JobIntentService.enqueueWork(context, NotificationJob.class, JobManager.JOB_ID_NOTIFICATION, intent);
} }
} }
public static final String TAG = "job_alarm"; public static final String TAG = "job_notification";
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject AlarmService alarmService;
@Inject Notifier notifier; @Inject Notifier notifier;
@Inject TaskDao taskDao; @Inject TaskDao taskDao;
@Inject JobQueue jobQueue;
@Override @Override
protected void run() { protected void run() {
if (!preferences.isCurrentlyQuietHours()) { if (!preferences.isCurrentlyQuietHours()) {
for (Alarm alarm : alarmService.getOverdueAlarms()) { for (JobQueueEntry entry : jobQueue.getOverdueJobs()) {
Task task = taskDao.fetch(alarm.getTaskId(), Task.REMINDER_LAST); if (entry instanceof Alarm) {
if (task != null && task.getReminderLast() < alarm.getTime()) { Alarm alarm = (Alarm) entry;
notifier.triggerTaskNotification(alarm.getTaskId(), ReminderService.TYPE_ALARM); Task task = taskDao.fetch(alarm.getTaskId(), Task.REMINDER_LAST);
if (task != null && task.getReminderLast() < alarm.getTime()) {
notifier.triggerTaskNotification(alarm.getTaskId(), ReminderService.TYPE_ALARM);
}
} else if (entry instanceof Reminder) {
Reminder reminder = (Reminder) entry;
notifier.triggerTaskNotification(reminder.getId(), reminder.getType());
} }
alarmService.remove(alarm); jobQueue.remove(entry);
} }
} }
} }
@Override @Override
protected void scheduleNext() { protected void scheduleNext() {
alarmService.scheduleNextJob(); jobQueue.scheduleNext();
} }
@Override @Override

@ -1,50 +0,0 @@
package org.tasks.jobs;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import com.todoroo.astrid.reminders.ReminderService;
import org.tasks.Notifier;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
public class ReminderJob extends Job {
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(context, ReminderJob.class, JobManager.JOB_ID_REMINDER, intent);
}
}
public static final String TAG = "job_reminder";
@Inject Preferences preferences;
@Inject ReminderService reminderService;
@Inject Notifier notifier;
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
@Override
protected void run() {
if (!preferences.isCurrentlyQuietHours()) {
for (Reminder reminder : reminderService.getPastReminders()) {
notifier.triggerTaskNotification(reminder.getId(), reminder.getType());
reminderService.remove(reminder);
}
}
}
@Override
protected void scheduleNext() {
reminderService.scheduleNextJob();
}
}

@ -8,6 +8,7 @@ import com.todoroo.astrid.reminders.ReminderService;
import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.IntentServiceComponent; import org.tasks.injection.IntentServiceComponent;
import org.tasks.jobs.JobQueue;
import javax.inject.Inject; import javax.inject.Inject;
@ -18,6 +19,7 @@ public class NotificationSchedulerIntentService extends InjectingJobIntentServic
@Inject AlarmService alarmService; @Inject AlarmService alarmService;
@Inject ReminderService reminderService; @Inject ReminderService reminderService;
@Inject TaskDao taskDao; @Inject TaskDao taskDao;
@Inject JobQueue jobQueue;
@Override @Override
protected void onHandleWork(Intent intent) { protected void onHandleWork(Intent intent) {
@ -25,8 +27,7 @@ public class NotificationSchedulerIntentService extends InjectingJobIntentServic
Timber.d("onHandleIntent(%s)", intent); Timber.d("onHandleIntent(%s)", intent);
reminderService.clear(); jobQueue.clear();
alarmService.clear();
reminderService.scheduleAllAlarms(taskDao); reminderService.scheduleAllAlarms(taskDao);
alarmService.scheduleAllAlarms(); alarmService.scheduleAllAlarms();

Loading…
Cancel
Save