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 extends NotificationQueueEntry> 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 extends Worker> 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 extends Worker> 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