Use android-job for notifications

pull/699/head
Alex Baker 8 years ago
parent 351604f4a5
commit 8f6fb07d7a

@ -137,6 +137,7 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
debugImplementation 'com.android.support:multidex:1.0.3' debugImplementation 'com.android.support:multidex:1.0.3'
implementation 'com.evernote:android-job:1.2.5'
implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.google.code.gson:gson:2.8.2' implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.github.rey5137:material:1.2.4' implementation 'com.github.rey5137:material:1.2.4'

11
app/proguard.pro vendored

@ -43,3 +43,14 @@
-keep class net.fortuna.ical4j.** { *; } # keep all model classes (properties/factories, created at runtime) -keep class net.fortuna.ical4j.** { *; } # keep all model classes (properties/factories, created at runtime)
-keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing) -keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing)
-keep class at.bitfire.** { *; } # all DAVdroid code is required -keep class at.bitfire.** { *; } # all DAVdroid code is required
# https://github.com/evernote/android-job/blob/v1.2.5/library/proguard.cfg
-dontwarn com.evernote.android.job.gcm.**
-dontwarn com.evernote.android.job.GcmAvailableHelper
-keep public class com.evernote.android.job.v21.PlatformJobService
-keep public class com.evernote.android.job.v14.PlatformAlarmService
-keep public class com.evernote.android.job.v14.PlatformAlarmReceiver
-keep public class com.evernote.android.job.JobBootReceiver
-keep public class com.evernote.android.job.JobRescheduleService

@ -20,8 +20,8 @@ import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao; import org.tasks.data.AlarmDao;
import org.tasks.injection.InjectingTestCase; import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent; import org.tasks.injection.TestComponent;
import org.tasks.jobs.AlarmJob; import org.tasks.jobs.AlarmEntry;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.NotificationQueue;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@ -31,11 +31,11 @@ public class AlarmJobServiceTest extends InjectingTestCase {
@Inject TaskDao taskDao; @Inject TaskDao taskDao;
private AlarmService alarmService; private AlarmService alarmService;
private JobQueue jobs; private NotificationQueue jobs;
@Before @Before
public void before() { public void before() {
jobs = mock(JobQueue.class); jobs = mock(NotificationQueue.class);
alarmService = new AlarmService(alarmDao, jobs); alarmService = new AlarmService(alarmDao, jobs);
} }
@ -57,7 +57,7 @@ public class AlarmJobServiceTest extends InjectingTestCase {
alarmService.scheduleAllAlarms(); alarmService.scheduleAllAlarms();
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).add(new AlarmJob(alarm)); order.verify(jobs).add(new AlarmEntry(alarm));
} }
@Test @Test

@ -38,8 +38,8 @@ import org.tasks.R;
import org.tasks.Snippet; import org.tasks.Snippet;
import org.tasks.injection.InjectingTestCase; import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent; import org.tasks.injection.TestComponent;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.NotificationQueue;
import org.tasks.jobs.Reminder; import org.tasks.jobs.ReminderEntry;
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;
@ -52,11 +52,11 @@ public class ReminderServiceTest extends InjectingTestCase {
private ReminderService service; private ReminderService service;
private Random random; private Random random;
private JobQueue jobs; private NotificationQueue jobs;
@Before @Before
public void before() { public void before() {
jobs = mock(JobQueue.class); jobs = mock(NotificationQueue.class);
random = mock(Random.class); random = mock(Random.class);
when(random.nextFloat()).thenReturn(1.0f); when(random.nextFloat()).thenReturn(1.0f);
preferences.reset(); preferences.reset();
@ -97,7 +97,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new ReminderEntry(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@Test @Test
@ -110,7 +110,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new ReminderEntry(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@Test @Test
@ -126,7 +126,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add( order.verify(jobs).add(
new Reminder(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE)); new ReminderEntry(1, now.startOfDay().withHourOfDay(18).getMillis(), ReminderService.TYPE_DUE));
} }
@Test @Test
@ -181,7 +181,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getDueDate(), ReminderService.TYPE_DUE)); order.verify(jobs).add(new ReminderEntry(1, task.getDueDate(), ReminderService.TYPE_DUE));
} }
@Test @Test
@ -197,7 +197,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1, task.getReminderSnooze(), ReminderService.TYPE_SNOOZE)); order.verify(jobs).add(new ReminderEntry(1, task.getReminderSnooze(), ReminderService.TYPE_SNOOZE));
} }
@Test @Test
@ -216,7 +216,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add( order.verify(jobs).add(
new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM)); new ReminderEntry(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -236,7 +236,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add( order.verify(jobs).add(
new Reminder(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM)); new ReminderEntry(1L, now.minusDays(1).getMillis() + 584206592, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -256,7 +256,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs) order.verify(jobs)
.add(new Reminder(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM)); .add(new ReminderEntry(1L, now.getMillis() + 10148400, ReminderService.TYPE_RANDOM));
}}); }});
} }
@ -272,7 +272,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -288,7 +288,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -304,7 +304,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 25, 12, 30, 1, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 25, 12, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -321,7 +321,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 0, 0, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 23, 15, 0, 0, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -337,7 +337,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 24, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -353,7 +353,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs).add(new Reminder(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(), order.verify(jobs).add(new ReminderEntry(1L, new DateTime(2017, 9, 23, 15, 30, 1, 0).getMillis(),
ReminderService.TYPE_OVERDUE)); ReminderService.TYPE_OVERDUE));
} }
@ -372,6 +372,6 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs); InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1); order.verify(jobs).cancelReminder(1);
order.verify(jobs) order.verify(jobs)
.add(new Reminder(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE)); .add(new ReminderEntry(1, now.plusMonths(12).getMillis(), ReminderService.TYPE_SNOOZE));
} }
} }

@ -94,7 +94,7 @@ public class BackupServiceTests extends InjectingTestCase {
// create a backup // create a backup
BackupJob service = new BackupJob(getTargetContext(), BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences); new JobManager(getTargetContext(), mock(AlarmManager.class), null), xmlExporter, preferences);
service.startBackup(getTargetContext()); service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME); AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
@ -133,7 +133,7 @@ public class BackupServiceTests extends InjectingTestCase {
// backup // backup
BackupJob service = new BackupJob(getTargetContext(), BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences); new JobManager(getTargetContext(), mock(AlarmManager.class), null), xmlExporter, preferences);
service.startBackup(getTargetContext()); service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME); AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);

@ -29,12 +29,11 @@ import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class JobQueueTest { public class NotificationQueueTest {
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 = NotificationJob.TAG;
private JobQueue queue; private NotificationQueue queue;
private JobManager jobManager; private JobManager jobManager;
private Preferences preferences; private Preferences preferences;
@ -43,7 +42,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); queue = new NotificationQueue(preferences, jobManager);
} }
@After @After
@ -55,15 +54,15 @@ public class JobQueueTest {
public void alarmAndReminderSameTimeSameID() { public void alarmAndReminderSameTimeSameID() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now)); queue.add(new AlarmEntry(1, 1, now));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals( assertEquals(
newHashSet(new AlarmJob(1, 1, now), newHashSet(new AlarmEntry(1, 1, now),
new Reminder(1, now, TYPE_DUE)), new ReminderEntry(1, now, TYPE_DUE)),
newHashSet(queue.getOverdueJobs())); newHashSet(queue.getOverdueJobs()));
}}); }});
} }
@ -72,16 +71,16 @@ public class JobQueueTest {
public void removeAlarmLeaveReminder() { public void removeAlarmLeaveReminder() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now)); queue.add(new AlarmEntry(1, 1, now));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
queue.remove(singletonList(new AlarmJob(1, 1, now))); queue.remove(singletonList(new AlarmEntry(1, 1, now)));
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals( assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)), singletonList(new ReminderEntry(1, now, TYPE_DUE)),
queue.getOverdueJobs()); queue.getOverdueJobs());
}}); }});
} }
@ -90,85 +89,85 @@ public class JobQueueTest {
public void removeReminderLeaveAlarm() { public void removeReminderLeaveAlarm() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new AlarmJob(1, 1, now)); queue.add(new AlarmEntry(1, 1, now));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
queue.remove(singletonList(new Reminder(1, now, TYPE_DUE))); queue.remove(singletonList(new ReminderEntry(1, now, TYPE_DUE)));
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals( assertEquals(
singletonList(new AlarmJob(1, 1, now)), singletonList(new AlarmEntry(1, 1, now)),
queue.getOverdueJobs()); queue.getOverdueJobs());
}}); }});
} }
@Test @Test
public void twoJobsAtSameTime() { public void twoJobsAtSameTime() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
queue.add(new Reminder(2, 1, 0)); queue.add(new ReminderEntry(2, 1, 0));
verify(jobManager).schedule(TAG, 1); verify(jobManager).scheduleNotification(1);
assertEquals(2, queue.size()); assertEquals(2, queue.size());
} }
@Test @Test
public void rescheduleForFirstJob() { public void rescheduleForFirstJob() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
verify(jobManager).schedule(TAG, 1); verify(jobManager).scheduleNotification(1);
} }
@Test @Test
public void dontRescheduleForLaterJobs() { public void dontRescheduleForLaterJobs() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
queue.add(new Reminder(2, 2, 0)); queue.add(new ReminderEntry(2, 2, 0));
verify(jobManager).schedule(TAG, 1); verify(jobManager).scheduleNotification(1);
} }
@Test @Test
public void rescheduleForNewerJob() { public void rescheduleForNewerJob() {
queue.add(new Reminder(1, 2, 0)); queue.add(new ReminderEntry(1, 2, 0));
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2); order.verify(jobManager).scheduleNotification(2);
order.verify(jobManager).schedule(TAG, 1); order.verify(jobManager).scheduleNotification(1);
} }
@Test @Test
public void rescheduleWhenCancelingOnlyJob() { public void rescheduleWhenCancelingOnlyJob() {
queue.add(new Reminder(1, 2, 0)); queue.add(new ReminderEntry(1, 2, 0));
queue.cancelReminder(1); queue.cancelReminder(1);
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 2); order.verify(jobManager).scheduleNotification(2);
order.verify(jobManager).cancel(TAG); order.verify(jobManager).cancelNotifications();
} }
@Test @Test
public void rescheduleWhenCancelingFirstJob() { public void rescheduleWhenCancelingFirstJob() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
queue.add(new Reminder(2, 2, 0)); queue.add(new ReminderEntry(2, 2, 0));
queue.cancelReminder(1); queue.cancelReminder(1);
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1); order.verify(jobManager).scheduleNotification(1);
order.verify(jobManager).schedule(TAG, 2); order.verify(jobManager).scheduleNotification(2);
} }
@Test @Test
public void dontRescheduleWhenCancelingLaterJob() { public void dontRescheduleWhenCancelingLaterJob() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
queue.add(new Reminder(2, 2, 0)); queue.add(new ReminderEntry(2, 2, 0));
queue.cancelReminder(2); queue.cancelReminder(2);
verify(jobManager).schedule(TAG, 1); verify(jobManager).scheduleNotification(1);
} }
@Test @Test
@ -181,23 +180,23 @@ public class JobQueueTest {
@Test @Test
public void adjustNextScheduledTimeForQuietHours() { public void adjustNextScheduledTimeForQuietHours() {
when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L); when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L);
queue.add(new Reminder(1, 1, 1)); queue.add(new ReminderEntry(1, 1, 1));
verify(jobManager).schedule(TAG, 1234); verify(jobManager).scheduleNotification(1234);
} }
@Test @Test
public void overdueJobsAreReturned() { public void overdueJobsAreReturned() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals( assertEquals(
singletonList(new Reminder(1, now, TYPE_DUE)), singletonList(new ReminderEntry(1, now, TYPE_DUE)),
queue.getOverdueJobs()); queue.getOverdueJobs());
}}); }});
} }
@ -206,14 +205,14 @@ public class JobQueueTest {
public void twoOverdueJobsAtSameTimeReturned() { public void twoOverdueJobsAtSameTimeReturned() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new Reminder(2, now, TYPE_DUE)); queue.add(new ReminderEntry(2, now, TYPE_DUE));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
assertEquals( assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now, TYPE_DUE)), asList(new ReminderEntry(1, now, TYPE_DUE), new ReminderEntry(2, now, TYPE_DUE)),
queue.getOverdueJobs()); queue.getOverdueJobs());
}}); }});
} }
@ -222,14 +221,14 @@ public class JobQueueTest {
public void twoOverdueJobsAtDifferentTimes() { public void twoOverdueJobsAtDifferentTimes() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now + 2 * ONE_MINUTE).thawAfter(new Snippet() {{ Freeze.freezeAt(now + 2 * ONE_MINUTE).thawAfter(new Snippet() {{
assertEquals( assertEquals(
asList(new Reminder(1, now, TYPE_DUE), new Reminder(2, now + ONE_MINUTE, TYPE_DUE)), asList(new ReminderEntry(1, now, TYPE_DUE), new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getOverdueJobs()); queue.getOverdueJobs());
}}); }});
} }
@ -238,17 +237,17 @@ public class JobQueueTest {
public void overdueJobsAreRemoved() { public void overdueJobsAreRemoved() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs()); queue.remove(queue.getOverdueJobs());
}}); }});
assertEquals( assertEquals(
singletonList(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)), singletonList(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)),
queue.getJobs()); queue.getJobs());
} }
@ -256,30 +255,30 @@ public class JobQueueTest {
public void multipleOverduePeriodsLapsed() { public void multipleOverduePeriodsLapsed() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.add(new Reminder(2, now + ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE));
queue.add(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(3, now + 2 * ONE_MINUTE, TYPE_DUE));
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
Freeze.freezeAt(now + ONE_MINUTE).thawAfter(new Snippet() {{ Freeze.freezeAt(now + ONE_MINUTE).thawAfter(new Snippet() {{
queue.remove(queue.getOverdueJobs()); queue.remove(queue.getOverdueJobs());
}}); }});
assertEquals( assertEquals(
singletonList(new Reminder(3, now + 2 * ONE_MINUTE, TYPE_DUE)), singletonList(new ReminderEntry(3, now + 2 * ONE_MINUTE, TYPE_DUE)),
queue.getJobs()); queue.getJobs());
} }
@Test @Test
public void clearShouldCancelExisting() { public void clearShouldCancelExisting() {
queue.add(new Reminder(1, 1, 0)); queue.add(new ReminderEntry(1, 1, 0));
queue.clear(); queue.clear();
InOrder order = inOrder(jobManager); InOrder order = inOrder(jobManager);
order.verify(jobManager).schedule(TAG, 1); order.verify(jobManager).scheduleNotification(1);
order.verify(jobManager).cancel(TAG); order.verify(jobManager).cancelNotifications();
assertEquals(0, queue.size()); assertEquals(0, queue.size());
} }
@ -287,10 +286,10 @@ public class JobQueueTest {
public void ignoreInvalidCancel() { public void ignoreInvalidCancel() {
long now = currentTimeMillis(); long now = currentTimeMillis();
queue.add(new Reminder(1, now, TYPE_DUE)); queue.add(new ReminderEntry(1, now, TYPE_DUE));
queue.cancelReminder(2); queue.cancelReminder(2);
verify(jobManager).schedule(TAG, now); verify(jobManager).scheduleNotification(now);
} }
@Test @Test
@ -299,21 +298,21 @@ public class JobQueueTest {
DateTime due = new DateTime(2017, 9, 3, 0, 14, 0, 0); DateTime due = new DateTime(2017, 9, 3, 0, 14, 0, 0);
DateTime snooze = new DateTime(2017, 9, 3, 0, 14, 59, 999); DateTime snooze = new DateTime(2017, 9, 3, 0, 14, 59, 999);
queue.add(new Reminder(1, due.getMillis(), TYPE_DUE)); queue.add(new ReminderEntry(1, due.getMillis(), TYPE_DUE));
queue.add(new Reminder(2, snooze.getMillis(), TYPE_SNOOZE)); queue.add(new ReminderEntry(2, snooze.getMillis(), TYPE_SNOOZE));
queue.add(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE)); queue.add(new ReminderEntry(3, due.plusMinutes(1).getMillis(), TYPE_DUE));
verify(jobManager).schedule(TAG, due.getMillis()); verify(jobManager).scheduleNotification(due.getMillis());
Freeze.freezeAt(now).thawAfter(new Snippet() {{ Freeze.freezeAt(now).thawAfter(new Snippet() {{
List<? extends JobQueueEntry> overdueJobs = queue.getOverdueJobs(); List<? extends NotificationQueueEntry> overdueJobs = queue.getOverdueJobs();
assertEquals( assertEquals(
asList(new Reminder(1, due.getMillis(), TYPE_DUE), asList(new ReminderEntry(1, due.getMillis(), TYPE_DUE),
new Reminder(2, snooze.getMillis(), TYPE_SNOOZE)), new ReminderEntry(2, snooze.getMillis(), TYPE_SNOOZE)),
overdueJobs); overdueJobs);
queue.remove(overdueJobs); queue.remove(overdueJobs);
assertEquals( assertEquals(
singletonList(new Reminder(3, due.plusMinutes(1).getMillis(), TYPE_DUE)), singletonList(new ReminderEntry(3, due.plusMinutes(1).getMillis(), TYPE_DUE)),
queue.getJobs()); queue.getJobs());
}}); }});
} }

@ -91,6 +91,7 @@
<application <application
tools:ignore="GoogleAppIndexingWarning" tools:ignore="GoogleAppIndexingWarning"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:fullBackupContent="@xml/backup_config"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Tasks" android:theme="@style/Tasks"
@ -429,12 +430,6 @@
android:permission="android.permission.BIND_JOB_SERVICE" android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" /> android:exported="false" />
<receiver android:name=".jobs.NotificationJob$Broadcast" />
<service
android:name=".jobs.NotificationJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<receiver android:name=".location.GeofenceTransitionsIntentService$Broadcast" /> <receiver android:name=".location.GeofenceTransitionsIntentService$Broadcast" />
<service <service
android:name=".location.GeofenceTransitionsIntentService" android:name=".location.GeofenceTransitionsIntentService"

@ -13,8 +13,8 @@ import javax.inject.Inject;
import org.tasks.data.Alarm; import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao; import org.tasks.data.AlarmDao;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.AlarmJob; import org.tasks.jobs.AlarmEntry;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.NotificationQueue;
/** /**
* Provides operations for working with alerts * Provides operations for working with alerts
@ -26,13 +26,13 @@ public class AlarmService {
private static final long NO_ALARM = Long.MAX_VALUE; private static final long NO_ALARM = Long.MAX_VALUE;
private final JobQueue jobs; private final NotificationQueue jobs;
private final AlarmDao alarmDao; private final AlarmDao alarmDao;
@Inject @Inject
public AlarmService(AlarmDao alarmDao, JobQueue jobQueue) { public AlarmService(AlarmDao alarmDao, NotificationQueue notificationQueue) {
this.alarmDao = alarmDao; this.alarmDao = alarmDao;
jobs = jobQueue; jobs = notificationQueue;
} }
public void rescheduleAlarms(long taskId, long oldDueDate, long newDueDate) { public void rescheduleAlarms(long taskId, long oldDueDate, long newDueDate) {
@ -49,7 +49,7 @@ public class AlarmService {
} }
} }
public List<org.tasks.data.Alarm> getAlarms(long taskId) { public List<Alarm> getAlarms(long taskId) {
return alarmDao.getAlarms(taskId); return alarmDao.getAlarms(taskId);
} }
@ -83,11 +83,11 @@ public class AlarmService {
// --- alarm scheduling // --- alarm scheduling
private List<org.tasks.data.Alarm> getActiveAlarms() { private List<Alarm> getActiveAlarms() {
return alarmDao.getActiveAlarms(); return alarmDao.getActiveAlarms();
} }
private List<org.tasks.data.Alarm> getActiveAlarmsForTask(long taskId) { private List<Alarm> getActiveAlarmsForTask(long taskId) {
return alarmDao.getActiveAlarms(taskId); return alarmDao.getActiveAlarms(taskId);
} }
@ -117,12 +117,12 @@ public class AlarmService {
return; return;
} }
AlarmJob alarmJob = new AlarmJob(alarm); AlarmEntry alarmEntry = new AlarmEntry(alarm);
long time = alarmJob.getTime(); long time = alarmEntry.getTime();
if (time == 0 || time == NO_ALARM) { if (time == 0 || time == NO_ALARM) {
jobs.cancelAlarm(alarmJob.getId()); jobs.cancelAlarm(alarmEntry.getId());
} else { } else {
jobs.add(alarmJob); jobs.add(alarmEntry);
} }
} }
} }

@ -12,8 +12,8 @@ import com.todoroo.astrid.data.Task;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.NotificationQueue;
import org.tasks.jobs.Reminder; import org.tasks.jobs.ReminderEntry;
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;
@ -44,17 +44,17 @@ 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 jobs; private final NotificationQueue jobs;
private final Random random; private final Random random;
private final TaskDao taskDao; private final TaskDao taskDao;
private final Preferences preferences; private final Preferences preferences;
@Inject @Inject
ReminderService(Preferences preferences, JobQueue jobQueue, TaskDao taskDao) { ReminderService(Preferences preferences, NotificationQueue notificationQueue, TaskDao taskDao) {
this(preferences, jobQueue, new Random(), taskDao); this(preferences, notificationQueue, new Random(), taskDao);
} }
ReminderService(Preferences preferences, JobQueue jobs, Random random, TaskDao taskDao) { ReminderService(Preferences preferences, NotificationQueue jobs, Random random, TaskDao taskDao) {
this.preferences = preferences; this.preferences = preferences;
this.jobs = jobs; this.jobs = jobs;
this.random = random; this.random = random;
@ -107,13 +107,13 @@ public final class ReminderService {
// snooze trumps all // snooze trumps all
if (whenSnooze != NO_ALARM) { if (whenSnooze != NO_ALARM) {
jobs.add(new Reminder(taskId, whenSnooze, TYPE_SNOOZE)); jobs.add(new ReminderEntry(taskId, whenSnooze, TYPE_SNOOZE));
} else if (whenRandom < whenDueDate && whenRandom < whenOverdue) { } else if (whenRandom < whenDueDate && whenRandom < whenOverdue) {
jobs.add(new Reminder(taskId, whenRandom, TYPE_RANDOM)); jobs.add(new ReminderEntry(taskId, whenRandom, TYPE_RANDOM));
} else if (whenDueDate < whenOverdue) { } else if (whenDueDate < whenOverdue) {
jobs.add(new Reminder(taskId, whenDueDate, TYPE_DUE)); jobs.add(new ReminderEntry(taskId, whenDueDate, TYPE_DUE));
} else if (whenOverdue != NO_ALARM) { } else if (whenOverdue != NO_ALARM) {
jobs.add(new Reminder(taskId, whenOverdue, TYPE_OVERDUE)); jobs.add(new ReminderEntry(taskId, whenOverdue, TYPE_OVERDUE));
} }
} }

@ -21,7 +21,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
import org.tasks.jobs.JobQueueEntry; import org.tasks.jobs.NotificationQueueEntry;
import org.tasks.notifications.AudioManager; import org.tasks.notifications.AudioManager;
import org.tasks.notifications.NotificationManager; import org.tasks.notifications.NotificationManager;
import org.tasks.notifications.TelephonyManager; import org.tasks.notifications.TelephonyManager;
@ -111,8 +111,8 @@ public class Notifier {
triggerNotifications(Collections.singletonList(notification), true); triggerNotifications(Collections.singletonList(notification), true);
} }
public void triggerTaskNotifications(List<? extends JobQueueEntry> entries) { public void triggerTaskNotifications(List<? extends NotificationQueueEntry> entries) {
triggerNotifications(transform(entries, JobQueueEntry::toNotification), true); triggerNotifications(transform(entries, NotificationQueueEntry::toNotification), true);
} }
private void triggerNotifications(List<org.tasks.notifications.Notification> entries, private void triggerNotifications(List<org.tasks.notifications.Notification> entries,

@ -1,11 +1,13 @@
package org.tasks; package org.tasks;
import com.evernote.android.job.JobManager;
import com.jakewharton.threetenabp.AndroidThreeTen; import com.jakewharton.threetenabp.AndroidThreeTen;
import com.todoroo.astrid.service.StartupService; import com.todoroo.astrid.service.StartupService;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
import org.tasks.injection.ApplicationComponent; import org.tasks.injection.ApplicationComponent;
import org.tasks.injection.InjectingApplication; import org.tasks.injection.InjectingApplication;
import org.tasks.jobs.JobCreator;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.receivers.Badger; import org.tasks.receivers.Badger;
import org.tasks.themes.ThemeCache; import org.tasks.themes.ThemeCache;
@ -19,6 +21,8 @@ public class Tasks extends InjectingApplication {
@Inject BuildSetup buildSetup; @Inject BuildSetup buildSetup;
@Inject ThemeCache themeCache; @Inject ThemeCache themeCache;
@Inject Badger badger; @Inject Badger badger;
@Inject JobManager jobManager;
@Inject JobCreator jobCreator;
@Override @Override
public void onCreate() { public void onCreate() {
@ -39,6 +43,8 @@ public class Tasks extends InjectingApplication {
themeCache.getThemeBase(preferences.getInt(R.string.p_theme, 0)).setDefaultNightMode(); themeCache.getThemeBase(preferences.getInt(R.string.p_theme, 0)).setDefaultNightMode();
startupService.onStartupApplication(); startupService.onStartupApplication();
jobManager.addJobCreator(jobCreator);
} }
@Override @Override

@ -2,6 +2,7 @@ package org.tasks.injection;
import android.arch.persistence.room.Room; import android.arch.persistence.room.Room;
import android.content.Context; import android.content.Context;
import com.evernote.android.job.JobManager;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.provider.Astrid2TaskProvider; import com.todoroo.astrid.provider.Astrid2TaskProvider;
@ -143,4 +144,10 @@ public class ApplicationModule {
taskDao.initialize(context); taskDao.initialize(context);
return taskDao; return taskDao;
} }
@Provides
@ApplicationScope
public JobManager getJobManager() {
return JobManager.create(context);
}
} }

@ -4,7 +4,6 @@ import dagger.Subcomponent;
import org.tasks.jobs.AfterSaveIntentService; import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.jobs.BackupJob; import org.tasks.jobs.BackupJob;
import org.tasks.jobs.MidnightRefreshJob; import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.RefreshJob; import org.tasks.jobs.RefreshJob;
import org.tasks.locale.receiver.TaskerIntentService; import org.tasks.locale.receiver.TaskerIntentService;
import org.tasks.location.GeofenceTransitionsIntentService; import org.tasks.location.GeofenceTransitionsIntentService;
@ -24,8 +23,6 @@ public interface IntentServiceComponent {
void inject(NotificationSchedulerIntentService notificationSchedulerIntentService); void inject(NotificationSchedulerIntentService notificationSchedulerIntentService);
void inject(NotificationJob notificationJob);
void inject(BackupJob backupJob); void inject(BackupJob backupJob);
void inject(MidnightRefreshJob midnightRefreshJob); void inject(MidnightRefreshJob midnightRefreshJob);

@ -3,20 +3,19 @@ package org.tasks.jobs;
import static org.tasks.time.DateTimeUtils.currentTimeMillis; import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.reminders.ReminderService;
import org.tasks.data.Alarm;
import org.tasks.notifications.Notification; import org.tasks.notifications.Notification;
public class AlarmJob implements JobQueueEntry { public class AlarmEntry implements NotificationQueueEntry {
private final long alarmId; private final long alarmId;
private final long taskId; private final long taskId;
private final long time; private final long time;
public AlarmJob(Alarm alarm) { public AlarmEntry(org.tasks.data.Alarm alarm) {
this(alarm.getId(), alarm.getTask(), alarm.getTime()); this(alarm.getId(), alarm.getTask(), alarm.getTime());
} }
public AlarmJob(long alarmId, long taskId, Long time) { public AlarmEntry(long alarmId, long taskId, Long time) {
this.alarmId = alarmId; this.alarmId = alarmId;
this.taskId = taskId; this.taskId = taskId;
this.time = time; this.time = time;
@ -50,15 +49,15 @@ public class AlarmJob implements JobQueueEntry {
return false; return false;
} }
AlarmJob alarmJob = (AlarmJob) o; AlarmEntry alarmEntry = (AlarmEntry) o;
if (alarmId != alarmJob.alarmId) { if (alarmId != alarmEntry.alarmId) {
return false; return false;
} }
if (taskId != alarmJob.taskId) { if (taskId != alarmEntry.taskId) {
return false; return false;
} }
return time == alarmJob.time; return time == alarmEntry.time;
} }
@Override @Override
@ -71,7 +70,7 @@ public class AlarmJob implements JobQueueEntry {
@Override @Override
public String toString() { public String toString() {
return "AlarmJob{" + return "AlarmEntry{" +
"alarmId=" + alarmId + "alarmId=" + alarmId +
", taskId=" + taskId + ", taskId=" + taskId +
", time=" + time + ", time=" + time +

@ -0,0 +1,35 @@
package org.tasks.jobs;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.evernote.android.job.Job;
import javax.inject.Inject;
import org.tasks.Notifier;
import org.tasks.injection.ApplicationScope;
import org.tasks.preferences.Preferences;
@ApplicationScope
public class JobCreator implements com.evernote.android.job.JobCreator {
private final Preferences preferences;
private final Notifier notifier;
private final NotificationQueue notificationQueue;
@Inject
public JobCreator(Preferences preferences, Notifier notifier, NotificationQueue notificationQueue) {
this.preferences = preferences;
this.notifier = notifier;
this.notificationQueue = notificationQueue;
}
@Nullable
@Override
public Job create(@NonNull String tag) {
switch (tag) {
case NotificationJob.TAG:
return new NotificationJob(preferences, notifier, notificationQueue);
default:
throw new IllegalArgumentException("Unhandled tag: " + tag);
}
}
}

@ -7,6 +7,7 @@ import static org.tasks.time.DateTimeUtils.printTimestamp;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import com.evernote.android.job.JobRequest;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication; import org.tasks.injection.ForApplication;
@ -24,22 +25,26 @@ public class JobManager {
public static final int JOB_ID_CALENDAR_NOTIFICATION = 10; public static final int JOB_ID_CALENDAR_NOTIFICATION = 10;
public static final int JOB_ID_TASKER = 11; public static final int JOB_ID_TASKER = 11;
static final int JOB_ID_REFRESH = 1; static final int JOB_ID_REFRESH = 1;
static final int JOB_ID_NOTIFICATION = 3;
static final int JOB_ID_MIDNIGHT_REFRESH = 6; static final int JOB_ID_MIDNIGHT_REFRESH = 6;
static final int JOB_ID_BACKUP = 7; static final int JOB_ID_BACKUP = 7;
private final Context context; private final Context context;
private final AlarmManager alarmManager; private final AlarmManager alarmManager;
private final com.evernote.android.job.JobManager jobManager;
@Inject @Inject
public JobManager(@ForApplication Context context, AlarmManager alarmManager) { public JobManager(@ForApplication Context context, AlarmManager alarmManager,
com.evernote.android.job.JobManager jobManager) {
this.context = context; this.context = context;
this.alarmManager = alarmManager; this.alarmManager = alarmManager;
this.jobManager = jobManager;
} }
@SuppressWarnings("WeakerAccess") public void scheduleNotification(long time) {
public void schedule(String tag, long time) { Timber.d("schedule notification: %s", printTimestamp(time));
Timber.d("%s: %s", tag, printTimestamp(time)); new JobRequest.Builder(NotificationJob.TAG)
alarmManager.wakeup(adjust(time), getPendingIntent(tag)); .setExact(calculateDelay(time))
.build()
.schedule();
} }
public void scheduleRefresh(long time) { public void scheduleRefresh(long time) {
@ -59,9 +64,18 @@ public class JobManager {
alarmManager.wakeup(adjust(time), getPendingBroadcast(BackupJob.Broadcast.class)); alarmManager.wakeup(adjust(time), getPendingBroadcast(BackupJob.Broadcast.class));
} }
public void cancel(String tag) { public void cancelNotifications() {
Timber.d("CXL %s", tag); Timber.d("cancelNotifications");
alarmManager.cancel(getPendingIntent(tag)); jobManager.cancelAllForTag(NotificationJob.TAG);
}
public void cancelRefresh() {
Timber.d("cancelRefresh");
alarmManager.cancel(getPendingIntent(RefreshJob.TAG));
}
private long calculateDelay(long time) {
return Math.max(5000, time - currentTimeMillis());
} }
private long adjust(long time) { private long adjust(long time) {
@ -70,8 +84,6 @@ public class JobManager {
private PendingIntent getPendingIntent(String tag) { private PendingIntent getPendingIntent(String tag) {
switch (tag) { switch (tag) {
case NotificationJob.TAG:
return getPendingBroadcast(NotificationJob.Broadcast.class);
case RefreshJob.TAG: case RefreshJob.TAG:
return getPendingBroadcast(RefreshJob.Broadcast.class); return getPendingBroadcast(RefreshJob.Broadcast.class);
default: default:

@ -1,51 +1,37 @@
package org.tasks.jobs; package org.tasks.jobs;
import android.content.BroadcastReceiver; import android.support.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import java.util.List; import java.util.List;
import javax.inject.Inject;
import org.tasks.BuildConfig; import org.tasks.BuildConfig;
import org.tasks.Notifier; import org.tasks.Notifier;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
public class NotificationJob extends Job { public class NotificationJob extends com.evernote.android.job.Job {
public static final String TAG = "job_notification"; public static final String TAG = "job_notification";
@Inject Preferences preferences;
@Inject Notifier notifier;
@Inject JobQueue jobQueue;
private final Preferences preferences;
private final Notifier notifier;
private final NotificationQueue notificationQueue;
public NotificationJob(Preferences preferences, Notifier notifier, NotificationQueue notificationQueue) {
this.preferences = preferences;
this.notifier = notifier;
this.notificationQueue = notificationQueue;
}
@NonNull
@Override @Override
protected void run() { protected Result onRunJob(@NonNull Params params) {
if (!preferences.isCurrentlyQuietHours()) { if (!preferences.isCurrentlyQuietHours()) {
List<? extends JobQueueEntry> overdueJobs = jobQueue.getOverdueJobs(); List<? extends NotificationQueueEntry> overdueJobs = notificationQueue.getOverdueJobs();
notifier.triggerTaskNotifications(overdueJobs); notifier.triggerTaskNotifications(overdueJobs);
boolean success = jobQueue.remove(overdueJobs); boolean success = notificationQueue.remove(overdueJobs);
if (BuildConfig.DEBUG && !success) { if (BuildConfig.DEBUG && !success) {
throw new RuntimeException("Failed to remove jobs from queue"); throw new RuntimeException("Failed to remove jobs from queue");
} }
} }
} notificationQueue.scheduleNext();
return Result.SUCCESS;
@Override
protected void scheduleNext() {
jobQueue.scheduleNext();
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService
.enqueueWork(context, NotificationJob.class, JobManager.JOB_ID_NOTIFICATION, intent);
}
} }
} }

@ -14,20 +14,20 @@ import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@ApplicationScope @ApplicationScope
public class JobQueue { public class NotificationQueue {
private final TreeMultimap<Long, JobQueueEntry> jobs = TreeMultimap private final TreeMultimap<Long, NotificationQueueEntry> jobs = TreeMultimap
.create(Ordering.natural(), (l, r) -> Ints.compare(l.hashCode(), r.hashCode())); .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;
@Inject @Inject
public JobQueue(Preferences preferences, JobManager jobManager) { public NotificationQueue(Preferences preferences, JobManager jobManager) {
this.preferences = preferences; this.preferences = preferences;
this.jobManager = jobManager; this.jobManager = jobManager;
} }
public synchronized <T extends JobQueueEntry> void add(T entry) { public synchronized <T extends NotificationQueueEntry> 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) {
@ -37,23 +37,23 @@ public class JobQueue {
public synchronized void clear() { public synchronized void clear() {
jobs.clear(); jobs.clear();
jobManager.cancel(NotificationJob.TAG); jobManager.cancelNotifications();
} }
public synchronized void cancelAlarm(long alarmId) { public synchronized void cancelAlarm(long alarmId) {
cancel(AlarmJob.class, alarmId); cancel(AlarmEntry.class, alarmId);
} }
public synchronized void cancelReminder(long taskId) { public synchronized void cancelReminder(long taskId) {
cancel(Reminder.class, taskId); cancel(ReminderEntry.class, taskId);
} }
private synchronized void cancel(Class<? extends JobQueueEntry> c, long id) { private synchronized void cancel(Class<? extends NotificationQueueEntry> c, long id) {
boolean reschedule = false; boolean reschedule = false;
long firstTime = firstTime(); long firstTime = firstTime();
List<JobQueueEntry> existing = newArrayList( List<NotificationQueueEntry> existing = newArrayList(
filter(jobs.values(), r -> r.getClass().equals(c) && r.getId() == id)); filter(jobs.values(), r -> r.getClass().equals(c) && r.getId() == id));
for (JobQueueEntry entry : existing) { for (NotificationQueueEntry entry : existing) {
reschedule |= entry.getTime() == firstTime; reschedule |= entry.getTime() == firstTime;
jobs.remove(entry.getTime(), entry); jobs.remove(entry.getTime(), entry);
} }
@ -62,8 +62,8 @@ public class JobQueue {
} }
} }
synchronized List<? extends JobQueueEntry> getOverdueJobs() { synchronized List<? extends NotificationQueueEntry> getOverdueJobs() {
List<JobQueueEntry> result = newArrayList(); List<NotificationQueueEntry> result = newArrayList();
long cutoff = new DateTime().startOfMinute().plusMinutes(1).getMillis(); long cutoff = new DateTime().startOfMinute().plusMinutes(1).getMillis();
for (Long key : jobs.keySet().headSet(cutoff)) { for (Long key : jobs.keySet().headSet(cutoff)) {
result.addAll(jobs.get(key)); result.addAll(jobs.get(key));
@ -78,10 +78,10 @@ public class JobQueue {
private void scheduleNext(boolean cancelCurrent) { private void scheduleNext(boolean cancelCurrent) {
if (jobs.isEmpty()) { if (jobs.isEmpty()) {
if (cancelCurrent) { if (cancelCurrent) {
jobManager.cancel(NotificationJob.TAG); jobManager.cancelNotifications();
} }
} else { } else {
jobManager.schedule(NotificationJob.TAG, nextScheduledTime()); jobManager.scheduleNotification(nextScheduledTime());
} }
} }
@ -98,13 +98,13 @@ public class JobQueue {
return jobs.size(); return jobs.size();
} }
List<JobQueueEntry> getJobs() { List<NotificationQueueEntry> getJobs() {
return ImmutableList.copyOf(jobs.values()); return ImmutableList.copyOf(jobs.values());
} }
public synchronized boolean remove(List<? extends JobQueueEntry> entries) { public synchronized boolean remove(List<? extends NotificationQueueEntry> entries) {
boolean success = true; boolean success = true;
for (JobQueueEntry entry : entries) { for (NotificationQueueEntry entry : entries) {
success &= !jobs.containsEntry(entry.getTime(), entry) || success &= !jobs.containsEntry(entry.getTime(), entry) ||
jobs.remove(entry.getTime(), entry); jobs.remove(entry.getTime(), entry);
} }

@ -2,7 +2,7 @@ package org.tasks.jobs;
import org.tasks.notifications.Notification; import org.tasks.notifications.Notification;
public interface JobQueueEntry { public interface NotificationQueueEntry {
long getId(); long getId();

@ -4,13 +4,13 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import org.tasks.notifications.Notification; import org.tasks.notifications.Notification;
public class Reminder implements JobQueueEntry { public class ReminderEntry implements NotificationQueueEntry {
private final long taskId; private final long taskId;
private final long time; private final long time;
private final int type; private final int type;
public Reminder(long taskId, long time, int type) { public ReminderEntry(long taskId, long time, int type) {
this.taskId = taskId; this.taskId = taskId;
this.time = time; this.time = time;
this.type = type; this.type = type;
@ -44,15 +44,15 @@ public class Reminder implements JobQueueEntry {
return false; return false;
} }
Reminder reminder = (Reminder) o; ReminderEntry reminderEntry = (ReminderEntry) o;
if (taskId != reminder.taskId) { if (taskId != reminderEntry.taskId) {
return false; return false;
} }
if (time != reminder.time) { if (time != reminderEntry.time) {
return false; return false;
} }
return type == reminder.type; return type == reminderEntry.type;
} }
@ -66,7 +66,7 @@ public class Reminder implements JobQueueEntry {
@Override @Override
public String toString() { public String toString() {
return "Reminder{" + return "ReminderEntry{" +
"taskId=" + taskId + "taskId=" + taskId +
", time=" + time + ", time=" + time +
", type=" + type + ", type=" + type +

@ -9,7 +9,7 @@ import javax.inject.Inject;
import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.IntentServiceComponent; import org.tasks.injection.IntentServiceComponent;
import org.tasks.jobs.JobManager; import org.tasks.jobs.JobManager;
import org.tasks.jobs.JobQueue; import org.tasks.jobs.NotificationQueue;
import org.tasks.notifications.NotificationManager; import org.tasks.notifications.NotificationManager;
import timber.log.Timber; import timber.log.Timber;
@ -18,7 +18,7 @@ public class NotificationSchedulerIntentService extends InjectingJobIntentServic
private static final String EXTRA_CANCEL_EXISTING_NOTIFICATIONS = "extra_cancel_existing_notifications"; private static final String EXTRA_CANCEL_EXISTING_NOTIFICATIONS = "extra_cancel_existing_notifications";
@Inject AlarmService alarmService; @Inject AlarmService alarmService;
@Inject ReminderService reminderService; @Inject ReminderService reminderService;
@Inject JobQueue jobQueue; @Inject NotificationQueue notificationQueue;
@Inject NotificationManager notificationManager; @Inject NotificationManager notificationManager;
public static void enqueueWork(Context context, boolean cancelNotifications) { public static void enqueueWork(Context context, boolean cancelNotifications) {
@ -34,7 +34,7 @@ public class NotificationSchedulerIntentService extends InjectingJobIntentServic
Timber.d("onHandleWork(%s)", intent); Timber.d("onHandleWork(%s)", intent);
jobQueue.clear(); notificationQueue.clear();
boolean cancelExistingNotifications = intent boolean cancelExistingNotifications = intent
.getBooleanExtra(EXTRA_CANCEL_EXISTING_NOTIFICATIONS, false); .getBooleanExtra(EXTRA_CANCEL_EXISTING_NOTIFICATIONS, false);

@ -10,7 +10,6 @@ import java.util.TreeSet;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.injection.ApplicationScope; import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.JobManager; import org.tasks.jobs.JobManager;
import org.tasks.jobs.RefreshJob;
@ApplicationScope @ApplicationScope
public class RefreshScheduler { public class RefreshScheduler {
@ -25,7 +24,7 @@ public class RefreshScheduler {
public void clear() { public void clear() {
jobs.clear(); jobs.clear();
jobManager.cancel(RefreshJob.TAG); jobManager.cancelRefresh();
} }
public void scheduleRefresh(Task task) { public void scheduleRefresh(Task task) {

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="sharedpref" path="evernote_jobs.xml" />
<exclude domain="database" path="evernote_jobs.db" />
</full-backup-content>
Loading…
Cancel
Save