From df9a1acac480be82a1c70605f2e6633fe6593ae4 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 9 Aug 2018 14:41:01 -0500 Subject: [PATCH] Use unique workers --- .../java/org/tasks/date/DateTimeUtils.java | 4 + .../org/tasks/injection/InjectingWorker.java | 15 +++- .../main/java/org/tasks/jobs/BackupWork.java | 8 +- .../main/java/org/tasks/jobs/CleanupWork.java | 4 +- .../main/java/org/tasks/jobs/DailyWork.java | 31 ------- .../org/tasks/jobs/MidnightRefreshWork.java | 4 +- .../org/tasks/jobs/NotificationQueue.java | 2 +- .../java/org/tasks/jobs/NotificationWork.java | 11 ++- .../main/java/org/tasks/jobs/RefreshWork.java | 11 ++- .../java/org/tasks/jobs/RepeatingWorker.java | 17 ++++ .../main/java/org/tasks/jobs/SyncWork.java | 4 +- .../main/java/org/tasks/jobs/WorkManager.java | 84 +++++++++---------- .../org/tasks/preferences/Preferences.java | 8 ++ .../tasks/scheduling/RefreshScheduler.java | 1 - app/src/main/res/values/keys.xml | 1 + 15 files changed, 109 insertions(+), 96 deletions(-) delete mode 100644 app/src/main/java/org/tasks/jobs/DailyWork.java create mode 100644 app/src/main/java/org/tasks/jobs/RepeatingWorker.java diff --git a/app/src/main/java/org/tasks/date/DateTimeUtils.java b/app/src/main/java/org/tasks/date/DateTimeUtils.java index eef1b464a..849f7416c 100644 --- a/app/src/main/java/org/tasks/date/DateTimeUtils.java +++ b/app/src/main/java/org/tasks/date/DateTimeUtils.java @@ -18,6 +18,10 @@ public class DateTimeUtils { return new DateTime(); } + public static long midnight() { + return newDateTime().plusDays(1).startOfDay().getMillis(); + } + public static DateTime newDateTime(long timestamp) { return new DateTime(timestamp); } diff --git a/app/src/main/java/org/tasks/injection/InjectingWorker.java b/app/src/main/java/org/tasks/injection/InjectingWorker.java index f7a3666c8..ca5bc1061 100644 --- a/app/src/main/java/org/tasks/injection/InjectingWorker.java +++ b/app/src/main/java/org/tasks/injection/InjectingWorker.java @@ -2,17 +2,30 @@ package org.tasks.injection; import android.support.annotation.NonNull; import androidx.work.Worker; +import javax.inject.Inject; +import org.tasks.analytics.Tracker; +import timber.log.Timber; public abstract class InjectingWorker extends Worker { + @Inject Tracker tracker; + @NonNull @Override public Result doWork() { + Timber.d("%s.doWork()", getClass().getSimpleName()); JobComponent component = ((InjectingApplication) getApplicationContext()).getComponent().plus(new WorkModule()); inject(component); - return Result.SUCCESS; + try { + return run(); + } catch (Exception e) { + tracker.reportException(e); + return Result.FAILURE; + } } + protected abstract Result run(); + protected abstract void inject(JobComponent component); } diff --git a/app/src/main/java/org/tasks/jobs/BackupWork.java b/app/src/main/java/org/tasks/jobs/BackupWork.java index c446ab343..a1343a6b5 100644 --- a/app/src/main/java/org/tasks/jobs/BackupWork.java +++ b/app/src/main/java/org/tasks/jobs/BackupWork.java @@ -2,6 +2,7 @@ package org.tasks.jobs; import static com.google.common.collect.Iterables.skip; import static com.google.common.collect.Lists.newArrayList; +import static com.todoroo.andlib.utility.DateUtilities.now; import static java.util.Collections.emptyList; import android.content.Context; @@ -12,13 +13,14 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.inject.Inject; +import org.tasks.R; import org.tasks.backup.TasksJsonExporter; import org.tasks.injection.ForApplication; import org.tasks.injection.JobComponent; import org.tasks.preferences.Preferences; import timber.log.Timber; -public class BackupWork extends DailyWork { +public class BackupWork extends RepeatingWorker { static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.json"; static final FileFilter FILE_FILTER = f -> f.getName().matches(BACKUP_FILE_NAME_REGEX); @@ -31,6 +33,7 @@ public class BackupWork extends DailyWork { @Inject Preferences preferences; @Inject WorkManager workManager; + @SuppressWarnings("unused") public BackupWork() {} BackupWork(Context context, TasksJsonExporter tasksJsonExporter, Preferences preferences) { @@ -40,8 +43,9 @@ public class BackupWork extends DailyWork { } @Override - protected Result doDailyWork() { + protected Result run() { startBackup(context); + preferences.setLong(R.string.p_last_backup, now()); return Result.SUCCESS; } diff --git a/app/src/main/java/org/tasks/jobs/CleanupWork.java b/app/src/main/java/org/tasks/jobs/CleanupWork.java index 758b4b28e..db21eb7ce 100644 --- a/app/src/main/java/org/tasks/jobs/CleanupWork.java +++ b/app/src/main/java/org/tasks/jobs/CleanupWork.java @@ -23,9 +23,7 @@ public class CleanupWork extends InjectingWorker { @NonNull @Override - public Result doWork() { - super.doWork(); - + public Result run() { long[] tasks = getInputData().getLongArray(EXTRA_TASK_IDS); if (tasks == null) { Timber.e("No task ids provided"); diff --git a/app/src/main/java/org/tasks/jobs/DailyWork.java b/app/src/main/java/org/tasks/jobs/DailyWork.java deleted file mode 100644 index 41b6b04df..000000000 --- a/app/src/main/java/org/tasks/jobs/DailyWork.java +++ /dev/null @@ -1,31 +0,0 @@ -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/MidnightRefreshWork.java b/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java index ac7cb14dc..88ce94b03 100644 --- a/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java +++ b/app/src/main/java/org/tasks/jobs/MidnightRefreshWork.java @@ -4,13 +4,13 @@ import javax.inject.Inject; import org.tasks.LocalBroadcastManager; import org.tasks.injection.JobComponent; -public class MidnightRefreshWork extends DailyWork { +public class MidnightRefreshWork extends RepeatingWorker { @Inject WorkManager workManager; @Inject LocalBroadcastManager localBroadcastManager; @Override - protected Result doDailyWork() { + protected Result run() { localBroadcastManager.broadcastRefresh(); return Result.SUCCESS; } diff --git a/app/src/main/java/org/tasks/jobs/NotificationQueue.java b/app/src/main/java/org/tasks/jobs/NotificationQueue.java index 2325aa9e0..5ce21e44f 100644 --- a/app/src/main/java/org/tasks/jobs/NotificationQueue.java +++ b/app/src/main/java/org/tasks/jobs/NotificationQueue.java @@ -81,7 +81,7 @@ public class NotificationQueue { workManager.cancelNotifications(); } } else { - workManager.scheduleNotification(nextScheduledTime(), cancelCurrent); + workManager.scheduleNotification(nextScheduledTime()); } } diff --git a/app/src/main/java/org/tasks/jobs/NotificationWork.java b/app/src/main/java/org/tasks/jobs/NotificationWork.java index 30506cb20..10b1c7778 100644 --- a/app/src/main/java/org/tasks/jobs/NotificationWork.java +++ b/app/src/main/java/org/tasks/jobs/NotificationWork.java @@ -9,7 +9,7 @@ import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.preferences.Preferences; -public class NotificationWork extends InjectingWorker { +public class NotificationWork extends RepeatingWorker { @Inject Preferences preferences; @Inject Notifier notifier; @@ -17,8 +17,7 @@ public class NotificationWork extends InjectingWorker { @NonNull @Override - public Result doWork() { - super.doWork(); + public Result run() { if (!preferences.isCurrentlyQuietHours()) { List overdueJobs = notificationQueue.getOverdueJobs(); notifier.triggerTaskNotifications(overdueJobs); @@ -27,7 +26,6 @@ public class NotificationWork extends InjectingWorker { throw new RuntimeException("Failed to remove jobs from queue"); } } - notificationQueue.scheduleNext(); return Result.SUCCESS; } @@ -35,4 +33,9 @@ public class NotificationWork extends InjectingWorker { protected void inject(JobComponent component) { component.inject(this); } + + @Override + protected void scheduleNext() { + notificationQueue.scheduleNext(); + } } diff --git a/app/src/main/java/org/tasks/jobs/RefreshWork.java b/app/src/main/java/org/tasks/jobs/RefreshWork.java index a32188a32..30dc51669 100644 --- a/app/src/main/java/org/tasks/jobs/RefreshWork.java +++ b/app/src/main/java/org/tasks/jobs/RefreshWork.java @@ -7,17 +7,15 @@ import org.tasks.injection.InjectingWorker; import org.tasks.injection.JobComponent; import org.tasks.scheduling.RefreshScheduler; -public class RefreshWork extends InjectingWorker { +public class RefreshWork extends RepeatingWorker { @Inject RefreshScheduler refreshScheduler; @Inject LocalBroadcastManager localBroadcastManager; @NonNull @Override - public Result doWork() { - super.doWork(); + public Result run() { localBroadcastManager.broadcastRefresh(); - refreshScheduler.scheduleNext(); return Result.SUCCESS; } @@ -25,4 +23,9 @@ public class RefreshWork extends InjectingWorker { protected void inject(JobComponent component) { component.inject(this); } + + @Override + protected void scheduleNext() { + refreshScheduler.scheduleNext(); + } } diff --git a/app/src/main/java/org/tasks/jobs/RepeatingWorker.java b/app/src/main/java/org/tasks/jobs/RepeatingWorker.java new file mode 100644 index 000000000..af08a0bcf --- /dev/null +++ b/app/src/main/java/org/tasks/jobs/RepeatingWorker.java @@ -0,0 +1,17 @@ +package org.tasks.jobs; + +import android.support.annotation.NonNull; +import org.tasks.injection.InjectingWorker; + +public abstract class RepeatingWorker extends InjectingWorker { + + @NonNull + @Override + public final Result doWork() { + Result result = super.doWork(); + scheduleNext(); + return result; + } + + protected abstract void scheduleNext(); +} diff --git a/app/src/main/java/org/tasks/jobs/SyncWork.java b/app/src/main/java/org/tasks/jobs/SyncWork.java index b0d1805dc..050623b89 100644 --- a/app/src/main/java/org/tasks/jobs/SyncWork.java +++ b/app/src/main/java/org/tasks/jobs/SyncWork.java @@ -21,9 +21,7 @@ public class SyncWork extends InjectingWorker { @NonNull @Override - public Result doWork() { - super.doWork(); - + public Result run() { synchronized (LOCK) { if (preferences.isSyncOngoing()) { 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 index 95f5ee83a..d26776ee1 100644 --- a/app/src/main/java/org/tasks/jobs/WorkManager.java +++ b/app/src/main/java/org/tasks/jobs/WorkManager.java @@ -1,13 +1,18 @@ package org.tasks.jobs; -import static org.tasks.time.DateTimeUtils.currentTimeMillis; +import static com.todoroo.andlib.utility.DateUtilities.now; +import static org.tasks.date.DateTimeUtils.midnight; +import static org.tasks.date.DateTimeUtils.newDateTime; import static org.tasks.time.DateTimeUtils.printTimestamp; import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.Data; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; +import androidx.work.OneTimeWorkRequest.Builder; import androidx.work.PeriodicWorkRequest; import androidx.work.Worker; import com.google.common.primitives.Longs; @@ -20,7 +25,6 @@ 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 @@ -61,11 +65,19 @@ public class WorkManager { } public void syncNow() { - workManager.enqueue( + Constraints constraints = + new Constraints.Builder() + .setRequiredNetworkType( + preferences.getBoolean(R.string.p_background_sync_unmetered_only, false) + ? NetworkType.UNMETERED + : NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(SyncWork.class) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) - .addTag(TAG_SYNC) - .build()); + .setConstraints(constraints) + .build(); + workManager.beginUniqueWork(TAG_SYNC, ExistingWorkPolicy.KEEP, request).enqueue(); } public void updateBackgroundSync() { @@ -92,10 +104,11 @@ public class WorkManager { } 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( + workManager.enqueueUniquePeriodicWork( + TAG_BACKGROUND_SYNC, + ExistingPeriodicWorkPolicy.KEEP, new PeriodicWorkRequest.Builder(SyncWork.class, 1, TimeUnit.HOURS) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) .setConstraints( @@ -104,66 +117,49 @@ public class WorkManager { onlyOnUnmetered ? NetworkType.UNMETERED : NetworkType.CONNECTED) .build()) .build()); + } else { + workManager.cancelUniqueWork(TAG_BACKGROUND_SYNC); } } public void scheduleRefresh(long time) { - enqueue(RefreshWork.class, time, TAG_REFRESH); + enqueueUnique(TAG_REFRESH, RefreshWork.class, time); } void scheduleMidnightRefresh() { - enqueue( - MidnightRefreshWork.class, - new DateTime(currentTimeMillis()).plusDays(1).startOfDay().getMillis(), - TAG_MIDNIGHT_REFRESH); + enqueueUnique(TAG_MIDNIGHT_REFRESH, MidnightRefreshWork.class, midnight()); } - void scheduleNotification(long time, boolean cancelCurrent) { - if (cancelCurrent) { - cancelNotifications(); - } - enqueue(NotificationWork.class, time, TAG_NOTIFICATION); + void scheduleNotification(long time) { + enqueueUnique(TAG_NOTIFICATION, NotificationWork.class, time); } void scheduleBackup() { - enqueue( + long lastBackup = preferences.getLong(R.string.p_last_backup, 0L); + enqueueUnique( + TAG_BACKUP, 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()); + Math.min(newDateTime(lastBackup).plusDays(1).getMillis(), midnight())); } - public void cancelRefresh() { - cancelAllForTag(TAG_REFRESH); + private void enqueueUnique(String key, Class c, long time) { + long delay = time - now(); + OneTimeWorkRequest.Builder builder = new Builder(c); + if (delay > 0) { + builder.setInitialDelay(delay, TimeUnit.MILLISECONDS); + } + Timber.d("%s: %s (%sms)", key, printTimestamp(time), delay); + workManager.beginUniqueWork(key, ExistingWorkPolicy.REPLACE, builder.build()).enqueue(); } 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()); + Timber.d("cancelNotifications"); + workManager.cancelAllWorkByTag(TAG_NOTIFICATION); } public void onStartup() { updateBackgroundSync(); - cancelAllForTag(TAG_MIDNIGHT_REFRESH); scheduleMidnightRefresh(); - cancelAllForTag(TAG_BACKUP); scheduleBackup(); } } diff --git a/app/src/main/java/org/tasks/preferences/Preferences.java b/app/src/main/java/org/tasks/preferences/Preferences.java index ab8cdb3f6..9bb68d9ed 100644 --- a/app/src/main/java/org/tasks/preferences/Preferences.java +++ b/app/src/main/java/org/tasks/preferences/Preferences.java @@ -327,10 +327,18 @@ public class Preferences { editor.commit(); } + public long getLong(int resourceId, long defValue) { + return getLong(context.getString(resourceId), defValue); + } + public long getLong(String key, long defValue) { return prefs.getLong(key, defValue); } + public void setLong(int resourceId, long value) { + setLong(context.getString(resourceId), value); + } + public void setLong(String key, long value) { Editor editor = prefs.edit(); editor.putLong(key, value); diff --git a/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java b/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java index 53a9a2f3e..d5d97d2ef 100644 --- a/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java +++ b/app/src/main/java/org/tasks/scheduling/RefreshScheduler.java @@ -46,7 +46,6 @@ public class RefreshScheduler { boolean reschedule = upcoming.isEmpty() || timestamp < upcoming.first(); jobs.add(timestamp); if (reschedule) { - workManager.cancelRefresh(); scheduleNext(); } } diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 2a06931eb..6aa33952b 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -292,4 +292,5 @@ background_sync_unmetered_only purchases sync_ongoing + last_backup