Use android-job for daily backup

pull/699/head
Alex Baker 6 years ago
parent 9c5bb51de6
commit ce89959c07

@ -0,0 +1,62 @@
package org.tasks.jobs;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import static org.tasks.date.DateTimeUtils.newDate;
import android.support.test.runner.AndroidJUnit4;
import java.io.File;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.tasks.time.DateTime;
@RunWith(AndroidJUnit4.class)
public class BackupJobTest {
@Test
public void filterExcludesXmlFiles() {
assertFalse(BackupJob.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")));
}
@Test
public void getDeleteKeepAllFiles() {
File file1 = newFile(newDate(2018, 3, 27));
File file2 = newFile(newDate(2018, 3, 28));
File file3 = newFile(newDate(2018, 3, 29));
assertEquals(
emptyList(),
BackupJob.getDeleteList(new File[] {file2, file1, file3}, 7));
}
@Test
public void getDeleteFromNullFileList() {
assertEquals(emptyList(), BackupJob.getDeleteList(null, 2));
}
@Test
public void sortFiles() {
File file1 = newFile(newDate(2018, 3, 27));
File file2 = newFile(newDate(2018, 3, 28));
File file3 = newFile(newDate(2018, 3, 29));
assertEquals(
singletonList(file1),
BackupJob.getDeleteList(new File[] {file2, file1, file3}, 2));
}
private static File newFile(DateTime lastModified) {
File result = mock(File.class);
stub(result.lastModified()).toReturn(lastModified.getMillis());
return result;
}
}

@ -8,11 +8,7 @@ package org.tasks.jobs;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.andlib.utility.AndroidUtilities;
@ -30,7 +26,6 @@ import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.InjectingTestCase;
import org.tasks.injection.TestComponent;
import org.tasks.preferences.Preferences;
import org.tasks.scheduling.AlarmManager;
@RunWith(AndroidJUnit4.class)
public class BackupServiceTests extends InjectingTestCase {
@ -39,7 +34,7 @@ public class BackupServiceTests extends InjectingTestCase {
private File temporaryDirectory = null;
@Inject TasksJsonExporter xmlExporter;
@Inject TasksJsonExporter jsonExporter;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
@ -90,11 +85,8 @@ public class BackupServiceTests extends InjectingTestCase {
public void testBackup() {
assertEquals(0, temporaryDirectory.list().length);
preferences.setLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0);
// create a backup
BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class), null), xmlExporter, preferences);
BackupJob service = new BackupJob(getTargetContext(), jsonExporter, preferences);
service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
@ -103,47 +95,5 @@ public class BackupServiceTests extends InjectingTestCase {
File[] files = temporaryDirectory.listFiles();
assertEquals(1, files.length);
assertTrue(files[0].getName().matches(BackupJob.BACKUP_FILE_NAME_REGEX));
// assert summary updated
assertTrue(preferences.getLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0) > 0);
}
@Test
public void testDeletion() throws IOException {
// create a bunch of backups
assertEquals(0, temporaryDirectory.list().length);
// create some user files
File myFile = new File(temporaryDirectory, "beans");
myFile.createNewFile();
// create some backup files
for (int i = 0; i < 10; i++) {
String name = String.format("auto.%02d%s.xml", i, newDateTime().toString("MMdd-HHmm"));
File tempFile = new File(temporaryDirectory, name);
tempFile.createNewFile();
}
// make one really old
File[] files = temporaryDirectory.listFiles();
files[4].setLastModified(currentTimeMillis() - 20000);
// assert files created
assertEquals(11, files.length);
// backup
BackupJob service = new BackupJob(getTargetContext(),
new JobManager(getTargetContext(), mock(AlarmManager.class), null), xmlExporter, preferences);
service.startBackup(getTargetContext());
AndroidUtilities.sleepDeep(BACKUP_WAIT_TIME);
// assert the oldest file was deleted
assertTrue(temporaryDirectory.listFiles().length < 11);
assertFalse(files[4].exists());
// assert user file still exists
service.startBackup(getTargetContext());
assertTrue(myFile.exists());
}
}

@ -412,12 +412,6 @@
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<receiver android:name=".jobs.BackupJob$Broadcast" />
<service
android:name=".jobs.BackupJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<receiver android:name=".jobs.MidnightRefreshJob$Broadcast" />
<service
android:name=".jobs.MidnightRefreshJob"

@ -41,8 +41,6 @@ import timber.log.Timber;
public class TasksJsonExporter {
public static final String PREF_BACKUP_LAST_DATE = "backupDate"; //$NON-NLS-1$
// --- public interface
private final TagDataDao tagDataDao;
@ -123,8 +121,6 @@ public class TasksJsonExporter {
doTasksExport(output, tasks);
}
preferences.setLong(PREF_BACKUP_LAST_DATE, DateUtilities.now());
if (exportType == ExportType.EXPORT_TYPE_MANUAL) {
onFinishExport(output);
}

@ -2,7 +2,6 @@ package org.tasks.injection;
import dagger.Subcomponent;
import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.jobs.BackupJob;
import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.RefreshJob;
import org.tasks.locale.receiver.TaskerIntentService;
@ -23,8 +22,6 @@ public interface IntentServiceComponent {
void inject(NotificationSchedulerIntentService notificationSchedulerIntentService);
void inject(BackupJob backupJob);
void inject(MidnightRefreshJob midnightRefreshJob);
void inject(RefreshJob refreshJob);

@ -1,50 +1,46 @@
package org.tasks.jobs;
import android.content.BroadcastReceiver;
import static com.google.common.collect.Iterables.skip;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.emptyList;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import android.support.annotation.NonNull;
import com.evernote.android.job.Job;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import javax.inject.Inject;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.ForApplication;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class BackupJob extends MidnightJob {
public class BackupJob extends Job {
public static final String TAG = "job_backup";
public static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.xml"; //$NON-NLS-1$
private static final int DAYS_TO_KEEP_BACKUP = 7;
@Inject @ForApplication Context context;
@Inject JobManager jobManager;
@Inject TasksJsonExporter tasksJsonExporter;
@Inject Preferences preferences;
@SuppressWarnings("unused")
public BackupJob() {
static final String BACKUP_FILE_NAME_REGEX = "auto\\.[-\\d]+\\.json";
static final FileFilter FILE_FILTER = f -> f.getName().matches(BACKUP_FILE_NAME_REGEX);
private static final Comparator<File> BY_LAST_MODIFIED = (f1, f2) ->
Long.compare(f2.lastModified(), f1.lastModified());
}
private static final int DAYS_TO_KEEP_BACKUP = 7;
private final Context context;
private final TasksJsonExporter tasksJsonExporter;
private final Preferences preferences;
BackupJob(Context context, JobManager jobManager, TasksJsonExporter tasksJsonExporter,
Preferences preferences) {
BackupJob(Context context, TasksJsonExporter tasksJsonExporter, Preferences preferences) {
this.context = context;
this.jobManager = jobManager;
this.tasksJsonExporter = tasksJsonExporter;
this.preferences = preferences;
}
@NonNull
@Override
protected void run() {
protected Result onRunJob(@NonNull Params params) {
startBackup(context);
}
@Override
protected void scheduleNext() {
jobManager.scheduleMidnightBackup();
return Result.SUCCESS;
}
void startBackup(Context context) {
@ -63,42 +59,27 @@ public class BackupJob extends MidnightJob {
}
private void deleteOldBackups() {
FileFilter backupFileFilter = file -> {
if (file.getName().matches(BACKUP_FILE_NAME_REGEX)) {
return true;
}
return false;
};
File astridDir = preferences.getBackupDirectory();
if (astridDir == null) {
return;
}
// grab all backup files, sort by modified date, delete old ones
File[] files = astridDir.listFiles(backupFileFilter);
if (files == null) {
return;
}
Arrays.sort(files,
(file1, file2) -> -Long.valueOf(file1.lastModified()).compareTo(file2.lastModified()));
for (int i = DAYS_TO_KEEP_BACKUP; i < files.length; i++) {
if (!files[i].delete()) {
Timber.i("Unable to delete: %s", files[i]);
File[] fileArray = astridDir.listFiles(FILE_FILTER);
for (File file : getDeleteList(fileArray, DAYS_TO_KEEP_BACKUP)) {
if (!file.delete()) {
Timber.e("Unable to delete: %s", file);
}
}
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(context, BackupJob.class, JobManager.JOB_ID_BACKUP, intent);
static List<File> getDeleteList(File[] fileArray, int keepNewest) {
if (fileArray == null) {
return emptyList();
}
List<File> files = Arrays.asList(fileArray);
Collections.sort(files, BY_LAST_MODIFIED);
return newArrayList(skip(files, keepNewest));
}
}

@ -1,25 +1,33 @@
package org.tasks.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.evernote.android.job.Job;
import javax.inject.Inject;
import org.tasks.Notifier;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
@ApplicationScope
public class JobCreator implements com.evernote.android.job.JobCreator {
private final Context context;
private final Preferences preferences;
private final Notifier notifier;
private final NotificationQueue notificationQueue;
private final TasksJsonExporter tasksJsonExporter;
@Inject
public JobCreator(Preferences preferences, Notifier notifier, NotificationQueue notificationQueue) {
public JobCreator(@ForApplication Context context, Preferences preferences, Notifier notifier,
NotificationQueue notificationQueue, TasksJsonExporter tasksJsonExporter) {
this.context = context;
this.preferences = preferences;
this.notifier = notifier;
this.notificationQueue = notificationQueue;
this.tasksJsonExporter = tasksJsonExporter;
}
@Nullable
@ -28,6 +36,8 @@ public class JobCreator implements com.evernote.android.job.JobCreator {
switch (tag) {
case NotificationJob.TAG:
return new NotificationJob(preferences, notifier, notificationQueue);
case BackupJob.TAG:
return new BackupJob(context, tasksJsonExporter, preferences);
default:
throw new IllegalArgumentException("Unhandled tag: " + tag);
}

@ -7,7 +7,10 @@ import static org.tasks.time.DateTimeUtils.printTimestamp;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.evernote.android.job.DailyJob;
import com.evernote.android.job.JobRequest;
import com.evernote.android.job.JobRequest.Builder;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.tasks.injection.ApplicationScope;
import org.tasks.injection.ForApplication;
@ -26,7 +29,6 @@ public class JobManager {
public static final int JOB_ID_TASKER = 11;
static final int JOB_ID_REFRESH = 1;
static final int JOB_ID_MIDNIGHT_REFRESH = 6;
static final int JOB_ID_BACKUP = 7;
private final Context context;
private final AlarmManager alarmManager;
private final com.evernote.android.job.JobManager jobManager;
@ -58,10 +60,8 @@ public class JobManager {
alarmManager.noWakeup(adjust(time), getPendingBroadcast(MidnightRefreshJob.Broadcast.class));
}
public void scheduleMidnightBackup() {
long time = nextMidnight();
Timber.d("%s: %s", BackupJob.TAG, printTimestamp(time));
alarmManager.wakeup(adjust(time), getPendingBroadcast(BackupJob.Broadcast.class));
public void scheduleBackup() {
DailyJob.schedule(new Builder(BackupJob.TAG), 0, TimeUnit.HOURS.toMillis(24) - 1);
}
public void cancelNotifications() {

@ -35,7 +35,7 @@ public class BackgroundScheduler extends InjectingJobIntentService {
CalendarNotificationIntentService.enqueueWork(context);
GeofenceSchedulingIntentService.enqueueWork(context);
jobManager.scheduleMidnightBackup();
jobManager.scheduleBackup();
jobManager.scheduleMidnightRefresh();
refreshScheduler.clear();

Loading…
Cancel
Save