From ba6724a9b0c4ea4b754aeccbc6361e06655a05eb Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 2 Aug 2018 10:09:08 -0500 Subject: [PATCH] Replace android-job with WorkManager --- app/build.gradle | 4 +- .../com/todoroo/astrid/dao/TaskDaoTests.java | 8 + .../java/org/tasks/injection/TestModule.java | 9 +- .../org/tasks/jobs/BackupServiceTests.java | 4 +- ...BackupJobTest.java => BackupWorkTest.java} | 12 +- .../org/tasks/jobs/NotificationQueueTest.java | 62 +++---- .../astrid/gtasks/GtasksListServiceTest.java | 3 + .../GeofenceTransitionsIntentService.java | 3 +- .../todoroo/astrid/service/TaskDeleter.java | 22 +-- app/src/main/java/org/tasks/Tasks.java | 17 +- .../tasks/injection/ApplicationComponent.java | 2 +- .../tasks/injection/ApplicationModule.java | 7 - .../injection/InjectingJobIntentService.java | 8 + ...InjectingJob.java => InjectingWorker.java} | 8 +- .../org/tasks/injection/JobComponent.java | 25 +-- .../{JobModule.java => WorkModule.java} | 2 +- .../tasks/jobs/AfterSaveIntentService.java | 2 +- .../jobs/{BackupJob.java => BackupWork.java} | 29 +-- .../{CleanupJob.java => CleanupWork.java} | 12 +- .../main/java/org/tasks/jobs/DailyWork.java | 31 ++++ .../main/java/org/tasks/jobs/JobCreator.java | 45 ----- .../main/java/org/tasks/jobs/JobManager.java | 142 --------------- .../org/tasks/jobs/MidnightRefreshWork.java | 27 +++ .../org/tasks/jobs/NotificationQueue.java | 12 +- ...ficationJob.java => NotificationWork.java} | 9 +- .../{RefreshJob.java => RefreshWork.java} | 17 +- .../jobs/{SyncJob.java => SyncWork.java} | 10 +- .../main/java/org/tasks/jobs/WorkManager.java | 169 ++++++++++++++++++ .../tasks/locale/receiver/FireReceiver.java | 4 +- .../org/tasks/receivers/PushReceiver.java | 10 +- .../tasks/scheduling/BackgroundScheduler.java | 6 +- .../CalendarNotificationIntentService.java | 4 +- .../GeofenceSchedulingIntentService.java | 3 +- .../NotificationSchedulerIntentService.java | 3 +- .../tasks/scheduling/RefreshScheduler.java | 11 +- .../java/org/tasks/sync/SyncAdapters.java | 10 +- .../sync/SynchronizationPreferences.java | 12 +- 37 files changed, 402 insertions(+), 362 deletions(-) rename app/src/androidTest/java/org/tasks/jobs/{BackupJobTest.java => BackupWorkTest.java} (74%) rename app/src/main/java/org/tasks/injection/{InjectingJob.java => InjectingWorker.java} (52%) rename app/src/main/java/org/tasks/injection/{JobModule.java => WorkModule.java} (69%) rename app/src/main/java/org/tasks/jobs/{BackupJob.java => BackupWork.java} (87%) rename app/src/main/java/org/tasks/jobs/{CleanupJob.java => CleanupWork.java} (76%) create mode 100644 app/src/main/java/org/tasks/jobs/DailyWork.java delete mode 100644 app/src/main/java/org/tasks/jobs/JobCreator.java delete mode 100644 app/src/main/java/org/tasks/jobs/JobManager.java create mode 100644 app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java rename app/src/main/java/org/tasks/jobs/{NotificationJob.java => NotificationWork.java} (84%) rename app/src/main/java/org/tasks/jobs/{RefreshJob.java => RefreshWork.java} (57%) rename app/src/main/java/org/tasks/jobs/{SyncJob.java => SyncWork.java} (85%) create mode 100644 app/src/main/java/org/tasks/jobs/WorkManager.java diff --git a/app/build.gradle b/app/build.gradle index 0dd1e82ca..d1e86cfe0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -112,6 +112,7 @@ final SUPPORT_VERSION = '28.0.0-beta01' final ROOM_VERSION = '1.1.1' final STETHO_VERSION = '1.5.0' final TESTING_SUPPORT_VERSION = '1.0.0' +final WORK_VERSION = '1.0.0-alpha06' dependencies { implementation ":dav4android:" @@ -139,7 +140,6 @@ 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.6' implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.github.rey5137:material:1.2.5' @@ -169,7 +169,9 @@ dependencies { implementation 'com.google.apis:google-api-services-tasks:v1-rev52-1.23.0' implementation 'com.google.api-client:google-api-client-android:1.23.0' implementation 'com.android.billingclient:billing:1.1' + implementation "android.arch.work:work-runtime:${WORK_VERSION}" + googleplayImplementation "android.arch.work:work-firebase:${WORK_VERSION}" googleplayImplementation 'com.crashlytics.sdk.android:crashlytics:2.9.4' googleplayImplementation "com.google.firebase:firebase-core:16.0.1" googleplayImplementation "com.google.android.gms:play-services-location:15.0.1" diff --git a/app/src/androidTest/java/com/todoroo/astrid/dao/TaskDaoTests.java b/app/src/androidTest/java/com/todoroo/astrid/dao/TaskDaoTests.java index 756dcf640..427baa7ec 100644 --- a/app/src/androidTest/java/com/todoroo/astrid/dao/TaskDaoTests.java +++ b/app/src/androidTest/java/com/todoroo/astrid/dao/TaskDaoTests.java @@ -19,12 +19,20 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.tasks.injection.InjectingTestCase; import org.tasks.injection.TestComponent; +import org.tasks.jobs.WorkManager; @RunWith(AndroidJUnit4.class) public class TaskDaoTests extends InjectingTestCase { @Inject TaskDao taskDao; @Inject TaskDeleter taskDeleter; + @Inject WorkManager workManager; + + @Override + public void setUp() { + super.setUp(); + workManager.init(); + } /** Test basic task creation, fetch, and save */ @Test diff --git a/app/src/androidTest/java/org/tasks/injection/TestModule.java b/app/src/androidTest/java/org/tasks/injection/TestModule.java index 41f75726b..119d4a96f 100644 --- a/app/src/androidTest/java/org/tasks/injection/TestModule.java +++ b/app/src/androidTest/java/org/tasks/injection/TestModule.java @@ -2,7 +2,6 @@ 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 dagger.Module; @@ -28,7 +27,7 @@ public class TestModule { private final Context context; - public TestModule(Context context) { + TestModule(Context context) { this.context = context; } @@ -123,10 +122,4 @@ public class TestModule { public PermissionChecker getPermissionChecker() { return new PermissivePermissionChecker(context); } - - @ApplicationScope - @Provides - public JobManager getJobManager() { - return JobManager.create(context); - } } diff --git a/app/src/androidTest/java/org/tasks/jobs/BackupServiceTests.java b/app/src/androidTest/java/org/tasks/jobs/BackupServiceTests.java index aaf88fc9b..94bd0e2ae 100644 --- a/app/src/androidTest/java/org/tasks/jobs/BackupServiceTests.java +++ b/app/src/androidTest/java/org/tasks/jobs/BackupServiceTests.java @@ -81,7 +81,7 @@ public class BackupServiceTests extends InjectingTestCase { assertEquals(0, temporaryDirectory.list().length); // create a backup - BackupJob service = new BackupJob(getTargetContext(), jsonExporter, preferences); + BackupWork service = new BackupWork(getTargetContext(), jsonExporter, preferences); service.startBackup(getTargetContext()); AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME); @@ -89,6 +89,6 @@ public class BackupServiceTests extends InjectingTestCase { // assert file created File[] files = temporaryDirectory.listFiles(); assertEquals(1, files.length); - assertTrue(files[0].getName().matches(BackupJob.BACKUP_FILE_NAME_REGEX)); + assertTrue(files[0].getName().matches(BackupWork.BACKUP_FILE_NAME_REGEX)); } } diff --git a/app/src/androidTest/java/org/tasks/jobs/BackupJobTest.java b/app/src/androidTest/java/org/tasks/jobs/BackupWorkTest.java similarity index 74% rename from app/src/androidTest/java/org/tasks/jobs/BackupJobTest.java rename to app/src/androidTest/java/org/tasks/jobs/BackupWorkTest.java index dccb3fec8..4f5f999aa 100644 --- a/app/src/androidTest/java/org/tasks/jobs/BackupJobTest.java +++ b/app/src/androidTest/java/org/tasks/jobs/BackupWorkTest.java @@ -16,7 +16,7 @@ import org.junit.runner.RunWith; import org.tasks.time.DateTime; @RunWith(AndroidJUnit4.class) -public class BackupJobTest { +public class BackupWorkTest { private static File newFile(DateTime lastModified) { File result = mock(File.class); stub(result.lastModified()).toReturn(lastModified.getMillis()); @@ -25,12 +25,12 @@ public class BackupJobTest { @Test public void filterExcludesXmlFiles() { - assertFalse(BackupJob.FILE_FILTER.accept(new File("/a/b/c/d/auto.180329-0001.xml"))); + assertFalse(BackupWork.FILE_FILTER.accept(new File("/a/b/c/d/auto.180329-0001.xml"))); } @Test public void filterIncludesJsonFiles() { - assertTrue(BackupJob.FILE_FILTER.accept(new File("/a/b/c/d/auto.180329-0001.json"))); + assertTrue(BackupWork.FILE_FILTER.accept(new File("/a/b/c/d/auto.180329-0001.json"))); } @Test @@ -39,12 +39,12 @@ public class BackupJobTest { File file2 = newFile(newDate(2018, 3, 28)); File file3 = newFile(newDate(2018, 3, 29)); - assertEquals(emptyList(), BackupJob.getDeleteList(new File[] {file2, file1, file3}, 7)); + assertEquals(emptyList(), BackupWork.getDeleteList(new File[] {file2, file1, file3}, 7)); } @Test public void getDeleteFromNullFileList() { - assertEquals(emptyList(), BackupJob.getDeleteList(null, 2)); + assertEquals(emptyList(), BackupWork.getDeleteList(null, 2)); } @Test @@ -54,6 +54,6 @@ public class BackupJobTest { File file3 = newFile(newDate(2018, 3, 29)); assertEquals( - singletonList(file1), BackupJob.getDeleteList(new File[] {file2, file1, file3}, 2)); + singletonList(file1), BackupWork.getDeleteList(new File[] {file2, file1, file3}, 2)); } } diff --git a/app/src/androidTest/java/org/tasks/jobs/NotificationQueueTest.java b/app/src/androidTest/java/org/tasks/jobs/NotificationQueueTest.java index 72a55cddf..016b6ca5b 100644 --- a/app/src/androidTest/java/org/tasks/jobs/NotificationQueueTest.java +++ b/app/src/androidTest/java/org/tasks/jobs/NotificationQueueTest.java @@ -34,20 +34,20 @@ public class NotificationQueueTest { private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1); private NotificationQueue queue; - private JobManager jobManager; + private WorkManager workManager; private Preferences preferences; @Before public void before() { preferences = mock(Preferences.class); when(preferences.adjustForQuietHours(anyLong())).then(returnsFirstArg()); - jobManager = mock(JobManager.class); - queue = new NotificationQueue(preferences, jobManager); + workManager = mock(WorkManager.class); + queue = new NotificationQueue(preferences, workManager); } @After public void after() { - verifyNoMoreInteractions(jobManager); + verifyNoMoreInteractions(workManager); } @Test @@ -57,7 +57,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new AlarmEntry(1, 1, now)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now) .thawAfter( @@ -77,7 +77,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new AlarmEntry(1, 1, now)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); queue.remove(singletonList(new AlarmEntry(1, 1, now))); @@ -98,7 +98,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new AlarmEntry(1, 1, now)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); queue.remove(singletonList(new ReminderEntry(1, now, TYPE_DUE))); @@ -116,7 +116,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, 1, 0)); queue.add(new ReminderEntry(2, 1, 0)); - verify(jobManager).scheduleNotification(1); + verify(workManager).scheduleNotification(1, true); assertEquals(2, queue.size()); } @@ -125,7 +125,7 @@ public class NotificationQueueTest { public void rescheduleForFirstJob() { queue.add(new ReminderEntry(1, 1, 0)); - verify(jobManager).scheduleNotification(1); + verify(workManager).scheduleNotification(1, true); } @Test @@ -133,7 +133,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, 1, 0)); queue.add(new ReminderEntry(2, 2, 0)); - verify(jobManager).scheduleNotification(1); + verify(workManager).scheduleNotification(1, true); } @Test @@ -141,9 +141,9 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, 2, 0)); queue.add(new ReminderEntry(1, 1, 0)); - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleNotification(2); - order.verify(jobManager).scheduleNotification(1); + InOrder order = inOrder(workManager); + order.verify(workManager).scheduleNotification(2, true); + order.verify(workManager).scheduleNotification(1, true); } @Test @@ -151,9 +151,9 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, 2, 0)); queue.cancelReminder(1); - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleNotification(2); - order.verify(jobManager).cancelNotifications(); + InOrder order = inOrder(workManager); + order.verify(workManager).scheduleNotification(2, true); + order.verify(workManager).cancelNotifications(); } @Test @@ -163,9 +163,9 @@ public class NotificationQueueTest { queue.cancelReminder(1); - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleNotification(1); - order.verify(jobManager).scheduleNotification(2); + InOrder order = inOrder(workManager); + order.verify(workManager).scheduleNotification(1, true); + order.verify(workManager).scheduleNotification(2, true); } @Test @@ -175,7 +175,7 @@ public class NotificationQueueTest { queue.cancelReminder(2); - verify(jobManager).scheduleNotification(1); + verify(workManager).scheduleNotification(1, true); } @Test @@ -190,7 +190,7 @@ public class NotificationQueueTest { when(preferences.adjustForQuietHours(anyLong())).thenReturn(1234L); queue.add(new ReminderEntry(1, 1, 1)); - verify(jobManager).scheduleNotification(1234); + verify(workManager).scheduleNotification(1234, true); } @Test @@ -200,7 +200,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now) .thawAfter( @@ -219,7 +219,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new ReminderEntry(2, now, TYPE_DUE)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now) .thawAfter( @@ -240,7 +240,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now + 2 * ONE_MINUTE) .thawAfter( @@ -262,7 +262,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now) .thawAfter( @@ -283,7 +283,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(2, now + ONE_MINUTE, TYPE_DUE)); queue.add(new ReminderEntry(3, now + 2 * ONE_MINUTE, TYPE_DUE)); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); Freeze.freezeAt(now + ONE_MINUTE) .thawAfter( @@ -303,9 +303,9 @@ public class NotificationQueueTest { queue.clear(); - InOrder order = inOrder(jobManager); - order.verify(jobManager).scheduleNotification(1); - order.verify(jobManager).cancelNotifications(); + InOrder order = inOrder(workManager); + order.verify(workManager).scheduleNotification(1, true); + order.verify(workManager).cancelNotifications(); assertEquals(0, queue.size()); } @@ -316,7 +316,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(1, now, TYPE_DUE)); queue.cancelReminder(2); - verify(jobManager).scheduleNotification(now); + verify(workManager).scheduleNotification(now, true); } @Test @@ -329,7 +329,7 @@ public class NotificationQueueTest { queue.add(new ReminderEntry(2, snooze.getMillis(), TYPE_SNOOZE)); queue.add(new ReminderEntry(3, due.plusMinutes(1).getMillis(), TYPE_DUE)); - verify(jobManager).scheduleNotification(due.getMillis()); + verify(workManager).scheduleNotification(due.getMillis(), true); Freeze.freezeAt(now) .thawAfter( diff --git a/app/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.java b/app/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.java index c7d67ff04..a902cee37 100644 --- a/app/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.java +++ b/app/src/androidTestGoogleplay/java/com/todoroo/astrid/gtasks/GtasksListServiceTest.java @@ -28,6 +28,7 @@ import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskListDao; import org.tasks.injection.InjectingTestCase; import org.tasks.injection.TestComponent; +import org.tasks.jobs.WorkManager; import org.tasks.makers.RemoteGtaskListMaker; @RunWith(AndroidJUnit4.class) @@ -37,6 +38,7 @@ public class GtasksListServiceTest extends InjectingTestCase { @Inject LocalBroadcastManager localBroadcastManager; @Inject GoogleTaskDao googleTaskDao; @Inject TaskDao taskDao; + @Inject WorkManager workManager; @Inject GoogleTaskListDao googleTaskListDao; private GtasksListService gtasksListService; @@ -44,6 +46,7 @@ public class GtasksListServiceTest extends InjectingTestCase { @Override public void setUp() { super.setUp(); + workManager.init(); gtasksListService = new GtasksListService( googleTaskListDao, taskDeleter, localBroadcastManager, googleTaskDao, taskDao); diff --git a/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java b/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java index 1400d2900..07da39520 100644 --- a/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java +++ b/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java @@ -13,7 +13,6 @@ import org.tasks.data.Location; import org.tasks.data.LocationDao; import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; -import org.tasks.jobs.JobManager; import timber.log.Timber; public class GeofenceTransitionsIntentService extends InjectingJobIntentService { @@ -65,7 +64,7 @@ public class GeofenceTransitionsIntentService extends InjectingJobIntentService JobIntentService.enqueueWork( context, GeofenceTransitionsIntentService.class, - JobManager.JOB_ID_GEOFENCE_TRANSITION, + InjectingJobIntentService.JOB_ID_GEOFENCE_TRANSITION, intent); } } diff --git a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java index 7c85aeec1..df0ba532c 100644 --- a/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java +++ b/app/src/main/java/com/todoroo/astrid/service/TaskDeleter.java @@ -17,11 +17,11 @@ import org.tasks.data.CaldavCalendar; import org.tasks.data.DeletionDao; import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskList; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; public class TaskDeleter { - private final JobManager jobManager; + private final WorkManager workManager; private final TaskDao taskDao; private final LocalBroadcastManager localBroadcastManager; private final DeletionDao deletionDao; @@ -29,11 +29,11 @@ public class TaskDeleter { @Inject public TaskDeleter( DeletionDao deletionDao, - JobManager jobManager, + WorkManager workManager, TaskDao taskDao, LocalBroadcastManager localBroadcastManager) { this.deletionDao = deletionDao; - this.jobManager = jobManager; + this.workManager = workManager; this.taskDao = taskDao; this.localBroadcastManager = localBroadcastManager; } @@ -50,8 +50,8 @@ public class TaskDeleter { public List markDeleted(List taskIds) { deletionDao.markDeleted(taskIds); - jobManager.cleanup(taskIds); - jobManager.syncNow(); + workManager.cleanup(taskIds); + workManager.syncNow(); localBroadcastManager.broadcastRefresh(); return taskDao.fetch(taskIds); } @@ -62,7 +62,7 @@ public class TaskDeleter { public void delete(List tasks) { deletionDao.delete(tasks); - jobManager.cleanup(tasks); + workManager.cleanup(tasks); localBroadcastManager.broadcastRefresh(); } @@ -84,28 +84,28 @@ public class TaskDeleter { public void delete(GoogleTaskList googleTaskList) { List ids = deletionDao.delete(googleTaskList); - jobManager.cleanup(ids); + workManager.cleanup(ids); localBroadcastManager.broadcastRefresh(); localBroadcastManager.broadcastRefreshList(); } public void delete(GoogleTaskAccount googleTaskAccount) { List ids = deletionDao.delete(googleTaskAccount); - jobManager.cleanup(ids); + workManager.cleanup(ids); localBroadcastManager.broadcastRefresh(); localBroadcastManager.broadcastRefreshList(); } public void delete(CaldavCalendar caldavCalendar) { List ids = deletionDao.delete(caldavCalendar); - jobManager.cleanup(ids); + workManager.cleanup(ids); localBroadcastManager.broadcastRefresh(); localBroadcastManager.broadcastRefreshList(); } public void delete(CaldavAccount caldavAccount) { List ids = deletionDao.delete(caldavAccount); - jobManager.cleanup(ids); + workManager.cleanup(ids); localBroadcastManager.broadcastRefresh(); localBroadcastManager.broadcastRefreshList(); } diff --git a/app/src/main/java/org/tasks/Tasks.java b/app/src/main/java/org/tasks/Tasks.java index faeede4e0..43c993d30 100644 --- a/app/src/main/java/org/tasks/Tasks.java +++ b/app/src/main/java/org/tasks/Tasks.java @@ -1,12 +1,12 @@ package org.tasks; +import com.jakewharton.processphoenix.ProcessPhoenix; import com.jakewharton.threetenabp.AndroidThreeTen; import com.todoroo.astrid.service.StartupService; import javax.inject.Inject; import org.tasks.injection.ApplicationComponent; import org.tasks.injection.InjectingApplication; -import org.tasks.jobs.JobCreator; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; import org.tasks.preferences.Preferences; import org.tasks.receivers.Badger; import org.tasks.themes.ThemeCache; @@ -19,23 +19,22 @@ public class Tasks extends InjectingApplication { @Inject BuildSetup buildSetup; @Inject ThemeCache themeCache; @Inject Badger badger; - @Inject JobManager jobManager; - @Inject JobCreator jobCreator; + @Inject WorkManager workManager; @Override public void onCreate() { super.onCreate(); - if (!buildSetup.setup()) { + if (!buildSetup.setup() || ProcessPhoenix.isPhoenixProcess(this)) { return; } + workManager.init(); + AndroidThreeTen.init(this); preferences.setSyncOngoing(false); - jobManager.addJobCreator(jobCreator); - flavorSetup.setup(); badger.setEnabled(preferences.getBoolean(R.string.p_badges_enabled, true)); @@ -44,9 +43,7 @@ public class Tasks extends InjectingApplication { startupService.onStartupApplication(); - jobManager.updateBackgroundSync(); - jobManager.scheduleMidnightRefresh(); - jobManager.scheduleBackup(); + workManager.onStartup(); } @Override diff --git a/app/src/main/java/org/tasks/injection/ApplicationComponent.java b/app/src/main/java/org/tasks/injection/ApplicationComponent.java index f5cf858e5..3cfdac48f 100644 --- a/app/src/main/java/org/tasks/injection/ApplicationComponent.java +++ b/app/src/main/java/org/tasks/injection/ApplicationComponent.java @@ -21,5 +21,5 @@ public interface ApplicationComponent { IntentServiceComponent plus(IntentServiceModule module); - JobComponent plus(JobModule module); + JobComponent plus(WorkModule module); } diff --git a/app/src/main/java/org/tasks/injection/ApplicationModule.java b/app/src/main/java/org/tasks/injection/ApplicationModule.java index 9fb72f337..3e28e165b 100644 --- a/app/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/app/src/main/java/org/tasks/injection/ApplicationModule.java @@ -4,7 +4,6 @@ import static com.todoroo.andlib.utility.AndroidUtilities.atLeastMarshmallow; 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; @@ -146,12 +145,6 @@ public class ApplicationModule { return database.getDeletionDao(); } - @Provides - @ApplicationScope - public JobManager getJobManager() { - return JobManager.create(context); - } - @Provides @ApplicationScope public Encryption getEncryption() { diff --git a/app/src/main/java/org/tasks/injection/InjectingJobIntentService.java b/app/src/main/java/org/tasks/injection/InjectingJobIntentService.java index 5b0f7375c..fc91a55a9 100644 --- a/app/src/main/java/org/tasks/injection/InjectingJobIntentService.java +++ b/app/src/main/java/org/tasks/injection/InjectingJobIntentService.java @@ -8,6 +8,14 @@ import timber.log.Timber; public abstract class InjectingJobIntentService extends JobIntentService { + public static final int JOB_ID_BACKGROUND_SCHEDULER = 2; + public static final int JOB_ID_GEOFENCE_TRANSITION = 4; + public static final int JOB_ID_GEOFENCE_SCHEDULING = 5; + public static final int JOB_ID_TASK_STATUS_CHANGE = 8; + public static final int JOB_ID_NOTIFICATION_SCHEDULER = 9; + public static final int JOB_ID_CALENDAR_NOTIFICATION = 10; + public static final int JOB_ID_TASKER = 11; + @Override protected final void onHandleWork(@NonNull Intent intent) { inject( diff --git a/app/src/main/java/org/tasks/injection/InjectingJob.java b/app/src/main/java/org/tasks/injection/InjectingWorker.java similarity index 52% rename from app/src/main/java/org/tasks/injection/InjectingJob.java rename to app/src/main/java/org/tasks/injection/InjectingWorker.java index eaa02511c..f7a3666c8 100644 --- a/app/src/main/java/org/tasks/injection/InjectingJob.java +++ b/app/src/main/java/org/tasks/injection/InjectingWorker.java @@ -1,15 +1,15 @@ package org.tasks.injection; import android.support.annotation.NonNull; -import com.evernote.android.job.Job; +import androidx.work.Worker; -public abstract class InjectingJob extends Job { +public abstract class InjectingWorker extends Worker { @NonNull @Override - protected Result onRunJob(@NonNull Params params) { + public Result doWork() { JobComponent component = - Dagger.get(getContext()).getApplicationComponent().plus(new JobModule()); + ((InjectingApplication) getApplicationContext()).getComponent().plus(new WorkModule()); inject(component); return Result.SUCCESS; } diff --git a/app/src/main/java/org/tasks/injection/JobComponent.java b/app/src/main/java/org/tasks/injection/JobComponent.java index 6858eaf84..f153da701 100644 --- a/app/src/main/java/org/tasks/injection/JobComponent.java +++ b/app/src/main/java/org/tasks/injection/JobComponent.java @@ -1,22 +1,25 @@ package org.tasks.injection; import dagger.Subcomponent; -import org.tasks.jobs.BackupJob; -import org.tasks.jobs.CleanupJob; -import org.tasks.jobs.NotificationJob; -import org.tasks.jobs.RefreshJob; -import org.tasks.jobs.SyncJob; +import org.tasks.jobs.BackupWork; +import org.tasks.jobs.CleanupWork; +import org.tasks.jobs.MidnightRefreshWork; +import org.tasks.jobs.NotificationWork; +import org.tasks.jobs.RefreshWork; +import org.tasks.jobs.SyncWork; -@Subcomponent(modules = JobModule.class) +@Subcomponent(modules = WorkModule.class) public interface JobComponent { - void inject(SyncJob syncJob); + void inject(SyncWork syncWork); - void inject(NotificationJob notificationJob); + void inject(NotificationWork notificationWork); - void inject(BackupJob backupJob); + void inject(BackupWork backupWork); - void inject(RefreshJob refreshJob); + void inject(RefreshWork refreshWork); - void inject(CleanupJob cleanupJob); + void inject(CleanupWork cleanupWork); + + void inject(MidnightRefreshWork midnightRefreshWork); } diff --git a/app/src/main/java/org/tasks/injection/JobModule.java b/app/src/main/java/org/tasks/injection/WorkModule.java similarity index 69% rename from app/src/main/java/org/tasks/injection/JobModule.java rename to app/src/main/java/org/tasks/injection/WorkModule.java index e5bd2798b..724f562b9 100644 --- a/app/src/main/java/org/tasks/injection/JobModule.java +++ b/app/src/main/java/org/tasks/injection/WorkModule.java @@ -3,4 +3,4 @@ package org.tasks.injection; import dagger.Module; @Module -public class JobModule {} +public class WorkModule {} diff --git a/app/src/main/java/org/tasks/jobs/AfterSaveIntentService.java b/app/src/main/java/org/tasks/jobs/AfterSaveIntentService.java index afae8a720..ae6cfb4c2 100644 --- a/app/src/main/java/org/tasks/jobs/AfterSaveIntentService.java +++ b/app/src/main/java/org/tasks/jobs/AfterSaveIntentService.java @@ -45,7 +45,7 @@ public class AfterSaveIntentService extends InjectingJobIntentService { intent.putExtra(EXTRA_CURRENT, current); intent.putExtra(EXTRA_ORIGINAL, original); AfterSaveIntentService.enqueueWork( - context, AfterSaveIntentService.class, JobManager.JOB_ID_TASK_STATUS_CHANGE, intent); + context, AfterSaveIntentService.class, InjectingJobIntentService.JOB_ID_TASK_STATUS_CHANGE, intent); } @Override diff --git a/app/src/main/java/org/tasks/jobs/BackupJob.java b/app/src/main/java/org/tasks/jobs/BackupWork.java similarity index 87% rename from app/src/main/java/org/tasks/jobs/BackupJob.java rename to app/src/main/java/org/tasks/jobs/BackupWork.java index 83b649a82..c446ab343 100644 --- a/app/src/main/java/org/tasks/jobs/BackupJob.java +++ b/app/src/main/java/org/tasks/jobs/BackupWork.java @@ -5,7 +5,6 @@ import static com.google.common.collect.Lists.newArrayList; import static java.util.Collections.emptyList; import android.content.Context; -import android.support.annotation.NonNull; import java.io.File; import java.io.FileFilter; import java.util.Arrays; @@ -15,12 +14,11 @@ import java.util.List; import javax.inject.Inject; import org.tasks.backup.TasksJsonExporter; import org.tasks.injection.ForApplication; -import org.tasks.injection.InjectingJob; import org.tasks.injection.JobComponent; import org.tasks.preferences.Preferences; import timber.log.Timber; -public class BackupJob extends InjectingJob { +public class BackupWork extends DailyWork { static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.json"; static final FileFilter FILE_FILTER = f -> f.getName().matches(BACKUP_FILE_NAME_REGEX); @@ -31,15 +29,27 @@ public class BackupJob extends InjectingJob { @Inject @ForApplication Context context; @Inject TasksJsonExporter tasksJsonExporter; @Inject Preferences preferences; + @Inject WorkManager workManager; - public BackupJob() {} + public BackupWork() {} - BackupJob(Context context, TasksJsonExporter tasksJsonExporter, Preferences preferences) { + BackupWork(Context context, TasksJsonExporter tasksJsonExporter, Preferences preferences) { this.context = context; this.tasksJsonExporter = tasksJsonExporter; this.preferences = preferences; } + @Override + protected Result doDailyWork() { + startBackup(context); + return Result.SUCCESS; + } + + @Override + protected void scheduleNext() { + workManager.scheduleBackup(); + } + static List getDeleteList(File[] fileArray, int keepNewest) { if (fileArray == null) { return emptyList(); @@ -50,15 +60,6 @@ public class BackupJob extends InjectingJob { return newArrayList(skip(files, keepNewest)); } - @NonNull - @Override - protected Result onRunJob(@NonNull Params params) { - super.onRunJob(params); - - startBackup(context); - return Result.SUCCESS; - } - @Override protected void inject(JobComponent component) { component.inject(this); diff --git a/app/src/main/java/org/tasks/jobs/CleanupJob.java b/app/src/main/java/org/tasks/jobs/CleanupWork.java similarity index 76% rename from app/src/main/java/org/tasks/jobs/CleanupJob.java rename to app/src/main/java/org/tasks/jobs/CleanupWork.java index 2b5e77a59..758b4b28e 100644 --- a/app/src/main/java/org/tasks/jobs/CleanupJob.java +++ b/app/src/main/java/org/tasks/jobs/CleanupWork.java @@ -1,18 +1,17 @@ package org.tasks.jobs; import android.support.annotation.NonNull; -import com.evernote.android.job.util.support.PersistableBundleCompat; import com.todoroo.astrid.alarms.AlarmService; import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.timers.TimerPlugin; import javax.inject.Inject; -import org.tasks.injection.InjectingJob; +import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.location.GeofenceService; import org.tasks.notifications.NotificationManager; import timber.log.Timber; -public class CleanupJob extends InjectingJob { +public class CleanupWork extends InjectingWorker { static final String EXTRA_TASK_IDS = "extra_task_ids"; @@ -24,11 +23,10 @@ public class CleanupJob extends InjectingJob { @NonNull @Override - protected Result onRunJob(@NonNull Params params) { - super.onRunJob(params); + public Result doWork() { + super.doWork(); - PersistableBundleCompat extras = params.getExtras(); - long[] tasks = extras.getLongArray(EXTRA_TASK_IDS); + long[] tasks = getInputData().getLongArray(EXTRA_TASK_IDS); if (tasks == null) { Timber.e("No task ids provided"); return Result.FAILURE; diff --git a/app/src/main/java/org/tasks/jobs/DailyWork.java b/app/src/main/java/org/tasks/jobs/DailyWork.java new file mode 100644 index 000000000..41b6b04df --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/DailyWork.java @@ -0,0 +1,31 @@ +package org.tasks.jobs; + +import android.support.annotation.NonNull; +import javax.inject.Inject; +import org.tasks.analytics.Tracker; +import org.tasks.injection.InjectingWorker; + +public abstract class DailyWork extends InjectingWorker { + + @Inject Tracker tracker; + + @NonNull + @Override + public final Result doWork() { + super.doWork(); + Result result; + try { + result = doDailyWork(); + } catch (Exception e) { + tracker.reportException(e); + result = Result.FAILURE; + } + scheduleNext(); + + return result; + } + + protected abstract Result doDailyWork(); + + protected abstract void scheduleNext(); +} diff --git a/app/src/main/java/org/tasks/jobs/JobCreator.java b/app/src/main/java/org/tasks/jobs/JobCreator.java deleted file mode 100644 index 7e852bf26..000000000 --- a/app/src/main/java/org/tasks/jobs/JobCreator.java +++ /dev/null @@ -1,45 +0,0 @@ -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.injection.ApplicationScope; -import timber.log.Timber; - -@ApplicationScope -public class JobCreator implements com.evernote.android.job.JobCreator { - - static final String TAG_BACKUP = "tag_backup"; - static final String TAG_REFRESH = "tag_refresh"; - static final String TAG_MIDNIGHT_REFRESH = "tag_midnight_refresh"; - static final String TAG_NOTIFICATION = "tag_notification"; - static final String TAG_BACKGROUND_SYNC = "tag_background_sync"; - static final String TAG_SYNC = "tag_sync"; - static final String TAG_CLEANUP = "tag_cleanup"; - - @Inject - public JobCreator() {} - - @Nullable - @Override - public Job create(@NonNull String tag) { - switch (tag) { - case TAG_NOTIFICATION: - return new NotificationJob(); - case TAG_SYNC: - case TAG_BACKGROUND_SYNC: - return new SyncJob(); - case TAG_BACKUP: - return new BackupJob(); - case TAG_MIDNIGHT_REFRESH: - case TAG_REFRESH: - return new RefreshJob(); - case TAG_CLEANUP: - return new CleanupJob(); - default: - Timber.e("Unhandled tag: %s", tag); - return null; - } - } -} diff --git a/app/src/main/java/org/tasks/jobs/JobManager.java b/app/src/main/java/org/tasks/jobs/JobManager.java deleted file mode 100644 index aff422880..000000000 --- a/app/src/main/java/org/tasks/jobs/JobManager.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.tasks.jobs; - -import static org.tasks.jobs.CleanupJob.EXTRA_TASK_IDS; -import static org.tasks.time.DateTimeUtils.currentTimeMillis; -import static org.tasks.time.DateTimeUtils.printTimestamp; - -import com.evernote.android.job.DailyJob; -import com.evernote.android.job.JobRequest; -import com.evernote.android.job.JobRequest.Builder; -import com.evernote.android.job.JobRequest.NetworkType; -import com.evernote.android.job.util.support.PersistableBundleCompat; -import com.google.common.primitives.Longs; -import java.util.List; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; -import javax.inject.Inject; -import org.tasks.R; -import org.tasks.data.CaldavDao; -import org.tasks.data.GoogleTaskListDao; -import org.tasks.injection.ApplicationScope; -import org.tasks.preferences.Preferences; -import timber.log.Timber; - -@ApplicationScope -public class JobManager { - - public static final int JOB_ID_BACKGROUND_SCHEDULER = 2; - public static final int JOB_ID_GEOFENCE_TRANSITION = 4; - public static final int JOB_ID_GEOFENCE_SCHEDULING = 5; - public static final int JOB_ID_TASK_STATUS_CHANGE = 8; - public static final int JOB_ID_NOTIFICATION_SCHEDULER = 9; - public static final int JOB_ID_CALENDAR_NOTIFICATION = 10; - public static final int JOB_ID_TASKER = 11; - - private final com.evernote.android.job.JobManager jobManager; - private final Preferences preferences; - private final CaldavDao caldavDao; - private final GoogleTaskListDao googleTaskListDao; - - @Inject - public JobManager( - com.evernote.android.job.JobManager jobManager, - Preferences preferences, - CaldavDao caldavDao, - GoogleTaskListDao googleTaskListDao) { - this.jobManager = jobManager; - this.preferences = preferences; - this.caldavDao = caldavDao; - this.googleTaskListDao = googleTaskListDao; - } - - public void cleanup(List ids) { - PersistableBundleCompat extras = new PersistableBundleCompat(); - extras.putLongArray(EXTRA_TASK_IDS, Longs.toArray(ids)); - new JobRequest.Builder(JobCreator.TAG_CLEANUP).setExtras(extras).startNow().build().schedule(); - } - - public void scheduleNotification(long time) { - Timber.d("schedule notification: %s", printTimestamp(time)); - new JobRequest.Builder(JobCreator.TAG_NOTIFICATION) - .setExact(calculateDelay(time)) - .setUpdateCurrent(true) - .build() - .schedule(); - } - - public void scheduleRefresh(long time) { - Timber.d("schedule refresh: %s", printTimestamp(time)); - new JobRequest.Builder(JobCreator.TAG_REFRESH) - .setExact(calculateDelay(time)) - .setUpdateCurrent(true) - .build() - .schedule(); - } - - public void scheduleMidnightRefresh() { - DailyJob.schedule(new Builder(JobCreator.TAG_MIDNIGHT_REFRESH), 0, 0); - } - - public void scheduleBackup() { - DailyJob.schedule(new Builder(JobCreator.TAG_BACKUP), 0, TimeUnit.HOURS.toMillis(24) - 1); - } - - public void updateBackgroundSync() { - updateBackgroundSync(null, null, null); - } - - public void updateBackgroundSync( - @Nullable Boolean forceAccountPresent, - @Nullable Boolean forceBackgroundEnabled, - @Nullable Boolean forceOnlyOnUnmetered) { - boolean backgroundEnabled = - forceBackgroundEnabled == null - ? preferences.getBoolean(R.string.p_background_sync, true) - : forceBackgroundEnabled; - boolean accountsPresent = - forceAccountPresent == null - ? (googleTaskListDao.getAccounts().size() > 0 || caldavDao.getAccounts().size() > 0) - : forceAccountPresent; - boolean onlyOnWifi = - forceOnlyOnUnmetered == null - ? preferences.getBoolean(R.string.p_background_sync_unmetered_only, false) - : forceOnlyOnUnmetered; - scheduleBackgroundSynchronization(backgroundEnabled && accountsPresent, onlyOnWifi); - } - - private void scheduleBackgroundSynchronization(boolean enabled, boolean onlyOnUnmetered) { - Timber.d("background sync enabled: %s, onlyOnUnmetered: %s", enabled, onlyOnUnmetered); - if (enabled) { - new JobRequest.Builder(JobCreator.TAG_BACKGROUND_SYNC) - .setPeriodic(TimeUnit.HOURS.toMillis(1)) - .setRequiredNetworkType(onlyOnUnmetered ? NetworkType.UNMETERED : NetworkType.CONNECTED) - .setRequirementsEnforced(true) - .setUpdateCurrent(true) - .build() - .schedule(); - } else { - jobManager.cancelAllForTag(JobCreator.TAG_BACKGROUND_SYNC); - } - } - - public void syncNow() { - new JobRequest.Builder(JobCreator.TAG_SYNC) - .setUpdateCurrent(true) - .startNow() - .build() - .schedule(); - } - - public void cancelNotifications() { - Timber.d("cancelNotifications"); - jobManager.cancelAllForTag(JobCreator.TAG_NOTIFICATION); - } - - private long calculateDelay(long time) { - return Math.max(5000, time - currentTimeMillis()); - } - - public void addJobCreator(JobCreator jobCreator) { - jobManager.addJobCreator(jobCreator); - } -} diff --git a/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java b/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java new file mode 100644 index 000000000..ac7cb14dc --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java @@ -0,0 +1,27 @@ +package org.tasks.jobs; + +import javax.inject.Inject; +import org.tasks.LocalBroadcastManager; +import org.tasks.injection.JobComponent; + +public class MidnightRefreshWork extends DailyWork { + + @Inject WorkManager workManager; + @Inject LocalBroadcastManager localBroadcastManager; + + @Override + protected Result doDailyWork() { + localBroadcastManager.broadcastRefresh(); + return Result.SUCCESS; + } + + @Override + protected void scheduleNext() { + workManager.scheduleMidnightRefresh(); + } + + @Override + protected void inject(JobComponent component) { + component.inject(this); + } +} diff --git a/app/src/main/java/org/tasks/jobs/NotificationQueue.java b/app/src/main/java/org/tasks/jobs/NotificationQueue.java index 8b7ebf35e..2325aa9e0 100644 --- a/app/src/main/java/org/tasks/jobs/NotificationQueue.java +++ b/app/src/main/java/org/tasks/jobs/NotificationQueue.java @@ -19,12 +19,12 @@ public class NotificationQueue { private final TreeMultimap jobs = TreeMultimap.create(Ordering.natural(), (l, r) -> Ints.compare(l.hashCode(), r.hashCode())); private final Preferences preferences; - private final JobManager jobManager; + private final WorkManager workManager; @Inject - public NotificationQueue(Preferences preferences, JobManager jobManager) { + public NotificationQueue(Preferences preferences, WorkManager workManager) { this.preferences = preferences; - this.jobManager = jobManager; + this.workManager = workManager; } public synchronized void add(T entry) { @@ -37,7 +37,7 @@ public class NotificationQueue { public synchronized void clear() { jobs.clear(); - jobManager.cancelNotifications(); + workManager.cancelNotifications(); } public synchronized void cancelAlarm(long alarmId) { @@ -78,10 +78,10 @@ public class NotificationQueue { private void scheduleNext(boolean cancelCurrent) { if (jobs.isEmpty()) { if (cancelCurrent) { - jobManager.cancelNotifications(); + workManager.cancelNotifications(); } } else { - jobManager.scheduleNotification(nextScheduledTime()); + workManager.scheduleNotification(nextScheduledTime(), cancelCurrent); } } diff --git a/app/src/main/java/org/tasks/jobs/NotificationJob.java b/app/src/main/java/org/tasks/jobs/NotificationWork.java similarity index 84% rename from app/src/main/java/org/tasks/jobs/NotificationJob.java rename to app/src/main/java/org/tasks/jobs/NotificationWork.java index dc817b708..30506cb20 100644 --- a/app/src/main/java/org/tasks/jobs/NotificationJob.java +++ b/app/src/main/java/org/tasks/jobs/NotificationWork.java @@ -5,11 +5,11 @@ import java.util.List; import javax.inject.Inject; import org.tasks.BuildConfig; import org.tasks.Notifier; -import org.tasks.injection.InjectingJob; +import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.preferences.Preferences; -public class NotificationJob extends InjectingJob { +public class NotificationWork extends InjectingWorker { @Inject Preferences preferences; @Inject Notifier notifier; @@ -17,9 +17,8 @@ public class NotificationJob extends InjectingJob { @NonNull @Override - protected Result onRunJob(@NonNull Params params) { - super.onRunJob(params); - + public Result doWork() { + super.doWork(); if (!preferences.isCurrentlyQuietHours()) { List overdueJobs = notificationQueue.getOverdueJobs(); notifier.triggerTaskNotifications(overdueJobs); diff --git a/app/src/main/java/org/tasks/jobs/RefreshJob.java b/app/src/main/java/org/tasks/jobs/RefreshWork.java similarity index 57% rename from app/src/main/java/org/tasks/jobs/RefreshJob.java rename to app/src/main/java/org/tasks/jobs/RefreshWork.java index 32ce806a4..a32188a32 100644 --- a/app/src/main/java/org/tasks/jobs/RefreshJob.java +++ b/app/src/main/java/org/tasks/jobs/RefreshWork.java @@ -3,25 +3,22 @@ package org.tasks.jobs; import android.support.annotation.NonNull; import javax.inject.Inject; import org.tasks.LocalBroadcastManager; -import org.tasks.injection.InjectingJob; +import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.scheduling.RefreshScheduler; -public class RefreshJob extends InjectingJob { - - public static final String TAG = "job_refresh"; +public class RefreshWork extends InjectingWorker { @Inject RefreshScheduler refreshScheduler; @Inject LocalBroadcastManager localBroadcastManager; @NonNull @Override - protected Result onRunJob(@NonNull Params params) { - super.onRunJob(params); - - localBroadcastManager.broadcastRefresh(); - refreshScheduler.scheduleNext(); - return Result.SUCCESS; + public Result doWork() { + super.doWork(); + localBroadcastManager.broadcastRefresh(); + refreshScheduler.scheduleNext(); + return Result.SUCCESS; } @Override diff --git a/app/src/main/java/org/tasks/jobs/SyncJob.java b/app/src/main/java/org/tasks/jobs/SyncWork.java similarity index 85% rename from app/src/main/java/org/tasks/jobs/SyncJob.java rename to app/src/main/java/org/tasks/jobs/SyncWork.java index 568bc4599..b0d1805dc 100644 --- a/app/src/main/java/org/tasks/jobs/SyncJob.java +++ b/app/src/main/java/org/tasks/jobs/SyncWork.java @@ -5,12 +5,12 @@ import javax.inject.Inject; import org.tasks.LocalBroadcastManager; import org.tasks.caldav.CaldavSynchronizer; import org.tasks.gtasks.GoogleTaskSynchronizer; -import org.tasks.injection.InjectingJob; +import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.preferences.Preferences; import timber.log.Timber; -public class SyncJob extends InjectingJob { +public class SyncWork extends InjectingWorker { private static final Object LOCK = new Object(); @@ -21,12 +21,12 @@ public class SyncJob extends InjectingJob { @NonNull @Override - protected Result onRunJob(@NonNull Params params) { - super.onRunJob(params); + public Result doWork() { + super.doWork(); synchronized (LOCK) { if (preferences.isSyncOngoing()) { - return Result.RESCHEDULE; + return Result.RETRY; } } diff --git a/app/src/main/java/org/tasks/jobs/WorkManager.java b/app/src/main/java/org/tasks/jobs/WorkManager.java new file mode 100644 index 000000000..95f5ee83a --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/WorkManager.java @@ -0,0 +1,169 @@ +package org.tasks.jobs; + +import static org.tasks.time.DateTimeUtils.currentTimeMillis; +import static org.tasks.time.DateTimeUtils.printTimestamp; + +import androidx.work.BackoffPolicy; +import androidx.work.Constraints; +import androidx.work.Data; +import androidx.work.NetworkType; +import androidx.work.OneTimeWorkRequest; +import androidx.work.PeriodicWorkRequest; +import androidx.work.Worker; +import com.google.common.primitives.Longs; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; +import javax.inject.Inject; +import org.tasks.R; +import org.tasks.data.CaldavDao; +import org.tasks.data.GoogleTaskListDao; +import org.tasks.injection.ApplicationScope; +import org.tasks.preferences.Preferences; +import org.tasks.time.DateTime; +import timber.log.Timber; + +@ApplicationScope +public class WorkManager { + + private static final String TAG_BACKUP = "tag_backup"; + private static final String TAG_REFRESH = "tag_refresh"; + private static final String TAG_MIDNIGHT_REFRESH = "tag_midnight_refresh"; + private static final String TAG_NOTIFICATION = "tag_notification"; + private static final String TAG_SYNC = "tag_sync"; + private static final String TAG_BACKGROUND_SYNC = "tag_background_sync"; + + private final Preferences preferences; + private final GoogleTaskListDao googleTaskListDao; + private final CaldavDao caldavDao; + private androidx.work.WorkManager workManager; + + @Inject + public WorkManager( + Preferences preferences, GoogleTaskListDao googleTaskListDao, CaldavDao caldavDao) { + this.preferences = preferences; + this.googleTaskListDao = googleTaskListDao; + this.caldavDao = caldavDao; + } + + public void init() { + workManager = androidx.work.WorkManager.getInstance(); + } + + public void cleanup(List ids) { + workManager.enqueue( + new OneTimeWorkRequest.Builder(CleanupWork.class) + .setInputData( + new Data.Builder() + .putLongArray(CleanupWork.EXTRA_TASK_IDS, Longs.toArray(ids)) + .build()) + .build()); + } + + public void syncNow() { + workManager.enqueue( + new OneTimeWorkRequest.Builder(SyncWork.class) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) + .addTag(TAG_SYNC) + .build()); + } + + public void updateBackgroundSync() { + updateBackgroundSync(null, null, null); + } + + public void updateBackgroundSync( + @Nullable Boolean forceAccountPresent, + @Nullable Boolean forceBackgroundEnabled, + @Nullable Boolean forceOnlyOnUnmetered) { + boolean backgroundEnabled = + forceBackgroundEnabled == null + ? preferences.getBoolean(R.string.p_background_sync, true) + : forceBackgroundEnabled; + boolean accountsPresent = + forceAccountPresent == null + ? (googleTaskListDao.getAccounts().size() > 0 || caldavDao.getAccounts().size() > 0) + : forceAccountPresent; + boolean onlyOnWifi = + forceOnlyOnUnmetered == null + ? preferences.getBoolean(R.string.p_background_sync_unmetered_only, false) + : forceOnlyOnUnmetered; + scheduleBackgroundSynchronization(backgroundEnabled && accountsPresent, onlyOnWifi); + } + + private void scheduleBackgroundSynchronization(boolean enabled, boolean onlyOnUnmetered) { + cancelAllForTag(TAG_BACKGROUND_SYNC); + Timber.d("background sync enabled: %s, onlyOnUnmetered: %s", enabled, onlyOnUnmetered); + if (enabled) { + workManager.enqueue( + new PeriodicWorkRequest.Builder(SyncWork.class, 1, TimeUnit.HOURS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) + .setConstraints( + new Constraints.Builder() + .setRequiredNetworkType( + onlyOnUnmetered ? NetworkType.UNMETERED : NetworkType.CONNECTED) + .build()) + .build()); + } + } + + public void scheduleRefresh(long time) { + enqueue(RefreshWork.class, time, TAG_REFRESH); + } + + void scheduleMidnightRefresh() { + enqueue( + MidnightRefreshWork.class, + new DateTime(currentTimeMillis()).plusDays(1).startOfDay().getMillis(), + TAG_MIDNIGHT_REFRESH); + } + + void scheduleNotification(long time, boolean cancelCurrent) { + if (cancelCurrent) { + cancelNotifications(); + } + enqueue(NotificationWork.class, time, TAG_NOTIFICATION); + } + + void scheduleBackup() { + enqueue( + BackupWork.class, + new DateTime(currentTimeMillis()).plusDays(1).startOfDay().getMillis(), + TAG_BACKUP); + } + + private void enqueue(Class c, long time, String tag) { + long delay = calculateDelay(time); + Timber.d("enqueue %s: %s (%sms)", tag, printTimestamp(time), delay); + workManager.enqueue( + new OneTimeWorkRequest.Builder(c) + .setInitialDelay(delay, TimeUnit.MILLISECONDS) + .addTag(tag) + .build()); + } + + public void cancelRefresh() { + cancelAllForTag(TAG_REFRESH); + } + + void cancelNotifications() { + cancelAllForTag(TAG_NOTIFICATION); + } + + private void cancelAllForTag(String tag) { + Timber.d("cancelAllWorkByTag(%s)", tag); + workManager.cancelAllWorkByTag(tag); + } + + private long calculateDelay(long time) { + return Math.max(5000, time - currentTimeMillis()); + } + + public void onStartup() { + updateBackgroundSync(); + cancelAllForTag(TAG_MIDNIGHT_REFRESH); + scheduleMidnightRefresh(); + cancelAllForTag(TAG_BACKUP); + scheduleBackup(); + } +} diff --git a/app/src/main/java/org/tasks/locale/receiver/FireReceiver.java b/app/src/main/java/org/tasks/locale/receiver/FireReceiver.java index ea704f8c5..41130ad85 100755 --- a/app/src/main/java/org/tasks/locale/receiver/FireReceiver.java +++ b/app/src/main/java/org/tasks/locale/receiver/FireReceiver.java @@ -5,7 +5,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.support.v4.app.JobIntentService; -import org.tasks.jobs.JobManager; +import org.tasks.injection.InjectingJobIntentService; import timber.log.Timber; public final class FireReceiver extends BroadcastReceiver { @@ -42,6 +42,6 @@ public final class FireReceiver extends BroadcastReceiver { } JobIntentService.enqueueWork( - context, TaskerIntentService.class, JobManager.JOB_ID_TASKER, intent); + context, TaskerIntentService.class, InjectingJobIntentService.JOB_ID_TASKER, intent); } } diff --git a/app/src/main/java/org/tasks/receivers/PushReceiver.java b/app/src/main/java/org/tasks/receivers/PushReceiver.java index 121e870e5..79220af85 100644 --- a/app/src/main/java/org/tasks/receivers/PushReceiver.java +++ b/app/src/main/java/org/tasks/receivers/PushReceiver.java @@ -3,19 +3,19 @@ package org.tasks.receivers; import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.Task; import javax.inject.Inject; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; import org.tasks.sync.SyncAdapters; import timber.log.Timber; public class PushReceiver { private final SyncAdapters syncAdapters; - private final JobManager jobManager; + private final WorkManager workManager; @Inject - public PushReceiver(SyncAdapters syncAdapters, JobManager jobManager) { + public PushReceiver(SyncAdapters syncAdapters, WorkManager workManager) { this.syncAdapters = syncAdapters; - this.jobManager = jobManager; + this.workManager = workManager; } public void push(Task task, Task original) { @@ -31,7 +31,7 @@ public class PushReceiver { if (task.checkAndClearTransitory(SyncFlags.FORCE_SYNC) || (googleTaskSyncEnabled && !task.googleTaskUpToDate(original)) || (caldavSyncEnabled && !task.caldavUpToDate(original))) { - jobManager.syncNow(); + workManager.syncNow(); } } } diff --git a/app/src/main/java/org/tasks/scheduling/BackgroundScheduler.java b/app/src/main/java/org/tasks/scheduling/BackgroundScheduler.java index b31a20a85..14480301f 100644 --- a/app/src/main/java/org/tasks/scheduling/BackgroundScheduler.java +++ b/app/src/main/java/org/tasks/scheduling/BackgroundScheduler.java @@ -9,19 +9,19 @@ import javax.inject.Inject; import org.tasks.injection.ForApplication; import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; import timber.log.Timber; public class BackgroundScheduler extends InjectingJobIntentService { @Inject @ForApplication Context context; @Inject TaskDao taskDao; - @Inject JobManager jobManager; + @Inject WorkManager jobManager; @Inject RefreshScheduler refreshScheduler; public static void enqueueWork(Context context) { BackgroundScheduler.enqueueWork( - context, BackgroundScheduler.class, JobManager.JOB_ID_BACKGROUND_SCHEDULER, new Intent()); + context, BackgroundScheduler.class, InjectingJobIntentService.JOB_ID_BACKGROUND_SCHEDULER, new Intent()); } @Override diff --git a/app/src/main/java/org/tasks/scheduling/CalendarNotificationIntentService.java b/app/src/main/java/org/tasks/scheduling/CalendarNotificationIntentService.java index 98a9802d3..2b2da3ee0 100644 --- a/app/src/main/java/org/tasks/scheduling/CalendarNotificationIntentService.java +++ b/app/src/main/java/org/tasks/scheduling/CalendarNotificationIntentService.java @@ -14,8 +14,8 @@ import org.tasks.R; import org.tasks.calendars.AndroidCalendarEvent; import org.tasks.calendars.CalendarEventProvider; import org.tasks.injection.ForApplication; +import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; -import org.tasks.jobs.JobManager; import org.tasks.preferences.Preferences; import timber.log.Timber; @@ -33,7 +33,7 @@ public class CalendarNotificationIntentService extends RecurringIntervalIntentSe JobIntentService.enqueueWork( context, CalendarNotificationIntentService.class, - JobManager.JOB_ID_CALENDAR_NOTIFICATION, + InjectingJobIntentService.JOB_ID_CALENDAR_NOTIFICATION, new Intent()); } diff --git a/app/src/main/java/org/tasks/scheduling/GeofenceSchedulingIntentService.java b/app/src/main/java/org/tasks/scheduling/GeofenceSchedulingIntentService.java index e5567525d..d964daf41 100644 --- a/app/src/main/java/org/tasks/scheduling/GeofenceSchedulingIntentService.java +++ b/app/src/main/java/org/tasks/scheduling/GeofenceSchedulingIntentService.java @@ -6,7 +6,6 @@ import android.support.v4.app.JobIntentService; import javax.inject.Inject; import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; -import org.tasks.jobs.JobManager; import org.tasks.location.GeofenceService; import timber.log.Timber; @@ -18,7 +17,7 @@ public class GeofenceSchedulingIntentService extends InjectingJobIntentService { JobIntentService.enqueueWork( context, GeofenceSchedulingIntentService.class, - JobManager.JOB_ID_GEOFENCE_SCHEDULING, + InjectingJobIntentService.JOB_ID_GEOFENCE_SCHEDULING, new Intent()); } diff --git a/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.java b/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.java index 3e84d16a2..40d722fcf 100644 --- a/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.java +++ b/app/src/main/java/org/tasks/scheduling/NotificationSchedulerIntentService.java @@ -8,7 +8,6 @@ import com.todoroo.astrid.reminders.ReminderService; import javax.inject.Inject; import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; -import org.tasks.jobs.JobManager; import org.tasks.jobs.NotificationQueue; import org.tasks.notifications.NotificationManager; import timber.log.Timber; @@ -28,7 +27,7 @@ public class NotificationSchedulerIntentService extends InjectingJobIntentServic JobIntentService.enqueueWork( context, NotificationSchedulerIntentService.class, - JobManager.JOB_ID_NOTIFICATION_SCHEDULER, + InjectingJobIntentService.JOB_ID_NOTIFICATION_SCHEDULER, intent); } diff --git a/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java b/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java index 167b1094d..53a9a2f3e 100644 --- a/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java +++ b/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java @@ -9,17 +9,17 @@ import java.util.SortedSet; import java.util.TreeSet; import javax.inject.Inject; import org.tasks.injection.ApplicationScope; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; @ApplicationScope public class RefreshScheduler { - private final JobManager jobManager; + private final WorkManager workManager; private final SortedSet jobs = new TreeSet<>(); @Inject - public RefreshScheduler(JobManager jobManager) { - this.jobManager = jobManager; + public RefreshScheduler(WorkManager workManager) { + this.workManager = workManager; } public void scheduleRefresh(Task task) { @@ -46,6 +46,7 @@ public class RefreshScheduler { boolean reschedule = upcoming.isEmpty() || timestamp < upcoming.first(); jobs.add(timestamp); if (reschedule) { + workManager.cancelRefresh(); scheduleNext(); } } @@ -54,7 +55,7 @@ public class RefreshScheduler { long now = currentTimeMillis(); jobs.removeAll(newArrayList(jobs.headSet(now + 1))); if (!jobs.isEmpty()) { - jobManager.scheduleRefresh(jobs.first()); + workManager.scheduleRefresh(jobs.first()); } } } diff --git a/app/src/main/java/org/tasks/sync/SyncAdapters.java b/app/src/main/java/org/tasks/sync/SyncAdapters.java index 76ab6ce8e..9f3e4e668 100644 --- a/app/src/main/java/org/tasks/sync/SyncAdapters.java +++ b/app/src/main/java/org/tasks/sync/SyncAdapters.java @@ -4,25 +4,25 @@ import android.app.Activity; import javax.inject.Inject; import org.tasks.data.CaldavDao; import org.tasks.gtasks.GtaskSyncAdapterHelper; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; public class SyncAdapters { private final GtaskSyncAdapterHelper gtaskSyncAdapterHelper; - private final JobManager jobManager; + private final WorkManager workManager; private final CaldavDao caldavDao; @Inject public SyncAdapters( - GtaskSyncAdapterHelper gtaskSyncAdapterHelper, JobManager jobManager, CaldavDao caldavDao) { + GtaskSyncAdapterHelper gtaskSyncAdapterHelper, WorkManager workManager, CaldavDao caldavDao) { this.gtaskSyncAdapterHelper = gtaskSyncAdapterHelper; - this.jobManager = jobManager; + this.workManager = workManager; this.caldavDao = caldavDao; } public boolean syncNow() { if (isGoogleTaskSyncEnabled() || isCaldavSyncEnabled()) { - jobManager.syncNow(); + workManager.syncNow(); return true; } return false; diff --git a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java index 11886f7f3..9d6110342 100644 --- a/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java +++ b/app/src/main/java/org/tasks/sync/SynchronizationPreferences.java @@ -35,7 +35,7 @@ import org.tasks.gtasks.GtaskSyncAdapterHelper; import org.tasks.gtasks.PlayServices; import org.tasks.injection.ActivityComponent; import org.tasks.injection.InjectingPreferenceActivity; -import org.tasks.jobs.JobManager; +import org.tasks.jobs.WorkManager; import org.tasks.preferences.ActivityPermissionRequestor; import org.tasks.preferences.PermissionChecker; import org.tasks.preferences.PermissionRequestor; @@ -61,7 +61,7 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { @Inject GoogleTaskListDao googleTaskListDao; @Inject GoogleAccountManager googleAccountManager; @Inject Preferences preferences; - @Inject JobManager jobManager; + @Inject WorkManager workManager; @Inject CaldavDao caldavDao; @Inject Inventory inventory; @Inject TaskDeleter taskDeleter; @@ -81,13 +81,13 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { findPreference(getString(R.string.p_background_sync_unmetered_only)) .setOnPreferenceChangeListener( (preference, o) -> { - jobManager.updateBackgroundSync(null, null, (Boolean) o); + workManager.updateBackgroundSync(null, null, (Boolean) o); return true; }); findPreference(getString(R.string.p_background_sync)) .setOnPreferenceChangeListener( (preference, o) -> { - jobManager.updateBackgroundSync(null, (Boolean) o, null); + workManager.updateBackgroundSync(null, (Boolean) o, null); return true; }); } @@ -234,12 +234,12 @@ public class SynchronizationPreferences extends InjectingPreferenceActivity { boolean enabled = resultCode == RESULT_OK; if (enabled) { tracker.reportEvent(Tracking.Events.GTASK_ENABLED); - jobManager.updateBackgroundSync(); + workManager.updateBackgroundSync(); restart(); } } else if (requestCode == REQUEST_CALDAV_SETTINGS) { if (resultCode == RESULT_OK) { - jobManager.updateBackgroundSync(); + workManager.updateBackgroundSync(); restart(); } } else if (requestCode == REQUEST_CALDAV_SUBSCRIBE) {