Use android-job for notifications

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

@ -137,6 +137,7 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
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.google.code.gson:gson:2.8.2'
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 org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing)
-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.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.jobs.AlarmJob;
import org.tasks.jobs.JobQueue;
import org.tasks.jobs.AlarmEntry;
import org.tasks.jobs.NotificationQueue;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
@ -31,11 +31,11 @@ public class AlarmJobServiceTest extends InjectingTestCase {
@Inject TaskDao taskDao;
private AlarmService alarmService;
private JobQueue jobs;
private NotificationQueue jobs;
@Before
public void before() {
jobs = mock(JobQueue.class);
jobs = mock(NotificationQueue.class);
alarmService = new AlarmService(alarmDao, jobs);
}
@ -57,7 +57,7 @@ public class AlarmJobServiceTest extends InjectingTestCase {
alarmService.scheduleAllAlarms();
InOrder order = inOrder(jobs);
order.verify(jobs).add(new AlarmJob(alarm));
order.verify(jobs).add(new AlarmEntry(alarm));
}
@Test

@ -38,8 +38,8 @@ import org.tasks.R;
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.jobs.NotificationQueue;
import org.tasks.jobs.ReminderEntry;
import org.tasks.preferences.Preferences;
import org.tasks.reminders.Random;
import org.tasks.time.DateTime;
@ -52,11 +52,11 @@ public class ReminderServiceTest extends InjectingTestCase {
private ReminderService service;
private Random random;
private JobQueue jobs;
private NotificationQueue jobs;
@Before
public void before() {
jobs = mock(JobQueue.class);
jobs = mock(NotificationQueue.class);
random = mock(Random.class);
when(random.nextFloat()).thenReturn(1.0f);
preferences.reset();
@ -97,7 +97,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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
@ -110,7 +110,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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
@ -126,7 +126,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
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
@ -181,7 +181,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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
@ -197,7 +197,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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
@ -216,7 +216,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
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);
order.verify(jobs).cancelReminder(1);
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);
order.verify(jobs).cancelReminder(1);
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);
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));
}
@ -288,7 +288,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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));
}
@ -304,7 +304,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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));
}
@ -321,7 +321,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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));
}
@ -337,7 +337,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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));
}
@ -353,7 +353,7 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
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));
}
@ -372,6 +372,6 @@ public class ReminderServiceTest extends InjectingTestCase {
InOrder order = inOrder(jobs);
order.verify(jobs).cancelReminder(1);
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
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());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
@ -133,7 +133,7 @@ public class BackupServiceTests extends InjectingTestCase {
// backup
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());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);

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

@ -91,6 +91,7 @@
<application
tools:ignore="GoogleAppIndexingWarning"
android:icon="@mipmap/ic_launcher"
android:fullBackupContent="@xml/backup_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:theme="@style/Tasks"
@ -429,12 +430,6 @@
android:permission="android.permission.BIND_JOB_SERVICE"
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" />
<service
android:name=".location.GeofenceTransitionsIntentService"

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

@ -12,8 +12,8 @@ import com.todoroo.astrid.data.Task;
import java.util.List;
import javax.inject.Inject;
import org.tasks.injection.ApplicationScope;
import org.tasks.jobs.JobQueue;
import org.tasks.jobs.Reminder;
import org.tasks.jobs.NotificationQueue;
import org.tasks.jobs.ReminderEntry;
import org.tasks.preferences.Preferences;
import org.tasks.reminders.Random;
import org.tasks.time.DateTime;
@ -44,17 +44,17 @@ public final class ReminderService {
private static final long NO_ALARM = Long.MAX_VALUE;
private final JobQueue jobs;
private final NotificationQueue jobs;
private final Random random;
private final TaskDao taskDao;
private final Preferences preferences;
@Inject
ReminderService(Preferences preferences, JobQueue jobQueue, TaskDao taskDao) {
this(preferences, jobQueue, new Random(), taskDao);
ReminderService(Preferences preferences, NotificationQueue notificationQueue, TaskDao 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.jobs = jobs;
this.random = random;
@ -107,13 +107,13 @@ public final class ReminderService {
// snooze trumps all
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) {
jobs.add(new Reminder(taskId, whenRandom, TYPE_RANDOM));
jobs.add(new ReminderEntry(taskId, whenRandom, TYPE_RANDOM));
} 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) {
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 javax.inject.Inject;
import org.tasks.injection.ForApplication;
import org.tasks.jobs.JobQueueEntry;
import org.tasks.jobs.NotificationQueueEntry;
import org.tasks.notifications.AudioManager;
import org.tasks.notifications.NotificationManager;
import org.tasks.notifications.TelephonyManager;
@ -111,8 +111,8 @@ public class Notifier {
triggerNotifications(Collections.singletonList(notification), true);
}
public void triggerTaskNotifications(List<? extends JobQueueEntry> entries) {
triggerNotifications(transform(entries, JobQueueEntry::toNotification), true);
public void triggerTaskNotifications(List<? extends NotificationQueueEntry> entries) {
triggerNotifications(transform(entries, NotificationQueueEntry::toNotification), true);
}
private void triggerNotifications(List<org.tasks.notifications.Notification> entries,

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

@ -2,6 +2,7 @@ package org.tasks.injection;
import android.arch.persistence.room.Room;
import android.content.Context;
import com.evernote.android.job.JobManager;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.provider.Astrid2TaskProvider;
@ -143,4 +144,10 @@ public class ApplicationModule {
taskDao.initialize(context);
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.BackupJob;
import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.RefreshJob;
import org.tasks.locale.receiver.TaskerIntentService;
import org.tasks.location.GeofenceTransitionsIntentService;
@ -24,8 +23,6 @@ public interface IntentServiceComponent {
void inject(NotificationSchedulerIntentService notificationSchedulerIntentService);
void inject(NotificationJob notificationJob);
void inject(BackupJob backupJob);
void inject(MidnightRefreshJob midnightRefreshJob);

@ -3,20 +3,19 @@ package org.tasks.jobs;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import com.todoroo.astrid.reminders.ReminderService;
import org.tasks.data.Alarm;
import org.tasks.notifications.Notification;
public class AlarmJob implements JobQueueEntry {
public class AlarmEntry implements NotificationQueueEntry {
private final long alarmId;
private final long taskId;
private final long time;
public AlarmJob(Alarm alarm) {
public AlarmEntry(org.tasks.data.Alarm alarm) {
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.taskId = taskId;
this.time = time;
@ -50,15 +49,15 @@ public class AlarmJob implements JobQueueEntry {
return false;
}
AlarmJob alarmJob = (AlarmJob) o;
AlarmEntry alarmEntry = (AlarmEntry) o;
if (alarmId != alarmJob.alarmId) {
if (alarmId != alarmEntry.alarmId) {
return false;
}
if (taskId != alarmJob.taskId) {
if (taskId != alarmEntry.taskId) {
return false;
}
return time == alarmJob.time;
return time == alarmEntry.time;
}
@Override
@ -71,7 +70,7 @@ public class AlarmJob implements JobQueueEntry {
@Override
public String toString() {
return "AlarmJob{" +
return "AlarmEntry{" +
"alarmId=" + alarmId +
", taskId=" + taskId +
", 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.content.Context;
import android.content.Intent;
import com.evernote.android.job.JobRequest;
import javax.inject.Inject;
import org.tasks.injection.ApplicationScope;
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_TASKER = 11;
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_BACKUP = 7;
private final Context context;
private final AlarmManager alarmManager;
private final com.evernote.android.job.JobManager jobManager;
@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.alarmManager = alarmManager;
this.jobManager = jobManager;
}
@SuppressWarnings("WeakerAccess")
public void schedule(String tag, long time) {
Timber.d("%s: %s", tag, printTimestamp(time));
alarmManager.wakeup(adjust(time), getPendingIntent(tag));
public void scheduleNotification(long time) {
Timber.d("schedule notification: %s", printTimestamp(time));
new JobRequest.Builder(NotificationJob.TAG)
.setExact(calculateDelay(time))
.build()
.schedule();
}
public void scheduleRefresh(long time) {
@ -59,9 +64,18 @@ public class JobManager {
alarmManager.wakeup(adjust(time), getPendingBroadcast(BackupJob.Broadcast.class));
}
public void cancel(String tag) {
Timber.d("CXL %s", tag);
alarmManager.cancel(getPendingIntent(tag));
public void cancelNotifications() {
Timber.d("cancelNotifications");
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) {
@ -70,8 +84,6 @@ public class JobManager {
private PendingIntent getPendingIntent(String tag) {
switch (tag) {
case NotificationJob.TAG:
return getPendingBroadcast(NotificationJob.Broadcast.class);
case RefreshJob.TAG:
return getPendingBroadcast(RefreshJob.Broadcast.class);
default:

@ -1,51 +1,37 @@
package org.tasks.jobs;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import android.support.annotation.NonNull;
import java.util.List;
import javax.inject.Inject;
import org.tasks.BuildConfig;
import org.tasks.Notifier;
import org.tasks.injection.IntentServiceComponent;
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";
@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
protected void run() {
protected Result onRunJob(@NonNull Params params) {
if (!preferences.isCurrentlyQuietHours()) {
List<? extends JobQueueEntry> overdueJobs = jobQueue.getOverdueJobs();
List<? extends NotificationQueueEntry> overdueJobs = notificationQueue.getOverdueJobs();
notifier.triggerTaskNotifications(overdueJobs);
boolean success = jobQueue.remove(overdueJobs);
boolean success = notificationQueue.remove(overdueJobs);
if (BuildConfig.DEBUG && !success) {
throw new RuntimeException("Failed to remove jobs from queue");
}
}
}
@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);
}
notificationQueue.scheduleNext();
return Result.SUCCESS;
}
}

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

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

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

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

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