New JSON backup format

synthesis
Alex Baker 6 years ago
parent 67f3b3d97f
commit ffc2591605

@ -126,6 +126,7 @@ dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
debugCompile 'com.android.support:multidex:1.0.2'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.github.rey5137:material:1.2.4'
compile 'com.nononsenseapps:filepicker:4.1.0'
compile "com.android.support:design:${SUPPORT_VERSION}"

@ -8,7 +8,7 @@ package org.tasks.jobs;
import android.support.test.runner.AndroidJUnit4;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.backup.TasksXmlExporter;
import org.tasks.backup.TasksJsonExporter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
@ -42,7 +42,7 @@ public class BackupServiceTests extends InjectingTestCase {
File temporaryDirectory = null;
@Inject TasksXmlExporter xmlExporter;
@Inject TasksJsonExporter xmlExporter;
@Inject TaskDao taskDao;
@Inject Preferences preferences;
@ -88,7 +88,7 @@ public class BackupServiceTests extends InjectingTestCase {
public void testBackup() {
assertEquals(0, temporaryDirectory.list().length);
preferences.setLong(TasksXmlExporter.PREF_BACKUP_LAST_DATE, 0);
preferences.setLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0);
// create a backup
BackupJob service = new BackupJob(getTargetContext(), new JobManager(getTargetContext(), mock(AlarmManager.class)), xmlExporter, preferences);
@ -102,7 +102,7 @@ public class BackupServiceTests extends InjectingTestCase {
assertTrue(files[0].getName().matches(BackupJob.BACKUP_FILE_NAME_REGEX));
// assert summary updated
assertTrue(preferences.getLong(TasksXmlExporter.PREF_BACKUP_LAST_DATE, 0) > 0);
assertTrue(preferences.getLong(TasksJsonExporter.PREF_BACKUP_LAST_DATE, 0) > 0);
}
@Test

@ -11,7 +11,7 @@ package com.todoroo.astrid.backup;
* @author Tim Su <tim@todoroo.com>
*
*/
class BackupConstants {
public class BackupConstants {
// Do NOT edit the constants in this file! You will break compatibility with old backups
@ -37,24 +37,14 @@ class BackupConstants {
/** Tag containing a metadata item */
public static final String METADATA_TAG = "metadata";
public static final String ALARM_TAG = "alarm";
public static final String LOCATION_TAG = "location";
public static final String TAG_TAG = "tag";
public static final String GOOGLE_TASKS_TAG = "googletasks";
/** Tag containing a tagdata item */
public static final String TAGDATA_TAG = "tagdata";
// --- general
public static final String XML_ENCODING = "utf-8";
public static final String EXPORT_FILE_NAME = "user.%s.xml";
public static final String EXPORT_FILE_NAME = "user.%s.json";
public static final String BACKUP_FILE_NAME = "auto.%s.xml";
public static final String BACKUP_FILE_NAME = "auto.%s.json";
public static final String UPGRADE_FILE_NAME = "upgradefrom.%s.xml";
public static final String UPGRADE_FILE_NAME = "upgradefrom.%s.json";
}

@ -51,7 +51,7 @@ public class TasksXmlImporter {
private final LocalBroadcastManager localBroadcastManager;
private final AlarmDao alarmDao;
private final TagDao tagDao;
private GoogleTaskDao googleTaskDao;
private final GoogleTaskDao googleTaskDao;
private final LocationDao locationDao;
private Activity activity;
@ -117,7 +117,7 @@ public class TasksXmlImporter {
String format = xpp.getAttributeValue(null, BackupConstants.ASTRID_ATTR_FORMAT);
if(TextUtils.equals(format, FORMAT2)) {
new Format2TaskImporter(xpp);
} else if(TextUtils.equals(format, FORMAT3) || TextUtils.equals(format, FORMAT4)) {
} else if(TextUtils.equals(format, FORMAT3)) {
new Format3TaskImporter(xpp);
} else {
throw new UnsupportedOperationException(
@ -162,8 +162,9 @@ public class TasksXmlImporter {
XmlPullParser xpp;
Task currentTask;
public Format2TaskImporter() { }
public Format2TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
Format2TaskImporter() { }
Format2TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
this.xpp = xpp;
while (xpp.next() != XmlPullParser.END_DOCUMENT) {
@ -219,46 +220,6 @@ public class TasksXmlImporter {
userActivityDao.createNew(userActivity);
}
void parseAlarm() {
if (!currentTask.isSaved()) {
return;
}
Alarm alarm = new Alarm(new XmlReader(xpp));
alarm.setTask(currentTask.getId());
alarmDao.insert(alarm);
}
void parseLocation() {
if (!currentTask.isSaved()) {
return;
}
Location location = new Location(new XmlReader(xpp));
location.setTask(currentTask.getId());
locationDao.insert(location);
}
void parseTag() {
if (!currentTask.isSaved()) {
return;
}
Tag tag = new Tag(new XmlReader(xpp));
tag.setTask(currentTask.getId());
tagDao.insert(tag);
}
void parseGoogleTask() {
if (!currentTask.isSaved()) {
return;
}
GoogleTask googleTask = new GoogleTask(new XmlReader(xpp));
googleTask.setTask(currentTask.getId());
googleTaskDao.insert(googleTask);
}
void parseMetadata(int format) {
if(!currentTask.isSaved()) {
return;
@ -314,10 +275,8 @@ public class TasksXmlImporter {
// =============================================================== FORMAT3
private static final String FORMAT3 = "3"; //$NON-NLS-1$
private static final String FORMAT4 = "4"; //$NON-NLS-1$
private class Format3TaskImporter extends Format2TaskImporter {
public Format3TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
Format3TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
this.xpp = xpp;
while (xpp.next() != XmlPullParser.END_DOCUMENT) {
String tag = xpp.getName();
@ -339,18 +298,6 @@ public class TasksXmlImporter {
case BackupConstants.TAGDATA_TAG:
parseTagdata();
break;
case BackupConstants.ALARM_TAG:
parseAlarm();
break;
case BackupConstants.LOCATION_TAG:
parseLocation();
break;
case BackupConstants.TAG_TAG:
parseTag();
break;
case BackupConstants.GOOGLE_TASKS_TAG:
parseGoogleTask();
break;
}
} catch (Exception e) {
errorCount++;

@ -27,7 +27,6 @@ import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.utility.DateUtilities;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
import org.tasks.data.Tag;
import org.tasks.time.DateTime;
@ -60,7 +59,7 @@ public class Task implements Parcelable {
/** ID */
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
public Long id = NO_ID;
public transient Long id = NO_ID;
public static final LongProperty ID = new LongProperty(
TABLE, "_id");
@ -110,8 +109,6 @@ public class Task implements Parcelable {
public static final LongProperty DELETION_DATE = new LongProperty(
TABLE, "deleted");
// --- non-core task metadata
@ColumnInfo(name = "notes")
public String notes = "";
public static final StringProperty NOTES = new StringProperty(
@ -211,16 +208,16 @@ public class Task implements Parcelable {
public static final int IMPORTANCE_NONE = 3;
@Ignore
private int googleTaskIndent;
private transient int googleTaskIndent;
@Ignore
private String tags;
private transient String tags;
@Ignore
private boolean hasFiles;
private transient boolean hasFiles;
@Ignore
private HashMap<String, Object> transitoryData = null;
private transient HashMap<String, Object> transitoryData = null;
// --- data access boilerplate
@ -373,29 +370,6 @@ public class Task implements Parcelable {
remoteId = reader.readString("remoteId");
}
public void writeToXml(XmlWriter writer) {
writer.writeString("calendarUri", calendarUri);
writer.writeLong("completed", completed);
writer.writeLong("created", created);
writer.writeLong("deleted", deleted);
writer.writeLong("dueDate", dueDate);
writer.writeInteger("elapsedSeconds", elapsedSeconds);
writer.writeInteger("estimatedSeconds", estimatedSeconds);
writer.writeLong("hideUntil", hideUntil);
writer.writeInteger("importance", importance);
writer.writeLong("modified", modified);
writer.writeString("notes", notes);
writer.writeString("recurrence", recurrence);
writer.writeInteger("notificationFlags", notificationFlags);
writer.writeLong("lastNotified", lastNotified);
writer.writeLong("notifications", notifications);
writer.writeLong("snoozeTime", snoozeTime);
writer.writeLong("repeatUntil", repeatUntil);
writer.writeLong("timerStart", timerStart);
writer.writeString("title", title);
writer.writeString("remoteId", remoteId);
}
@Ignore
public Task(Parcel parcel) {
calendarUri = parcel.readString();

@ -0,0 +1,48 @@
package org.tasks.backup;
import com.todoroo.astrid.data.Task;
import org.tasks.data.Alarm;
import org.tasks.data.Filter;
import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.Location;
import org.tasks.data.Tag;
import org.tasks.data.TagData;
import org.tasks.data.UserActivity;
import java.util.List;
public class BackupContainer {
List<TaskBackup> tasks;
List<TagData> tags;
List<Filter> filters;
List<GoogleTaskList> googleTaskLists;
BackupContainer(List<TaskBackup> tasks, List<TagData> tags, List<Filter> filters, List<GoogleTaskList> googleTaskLists) {
this.tasks = tasks;
this.tags = tags;
this.filters = filters;
this.googleTaskLists = googleTaskLists;
}
static class TaskBackup {
Task task;
List<Alarm> alarms;
List<Location> locations;
List<Tag> tags;
List<GoogleTask> google;
List<UserActivity> comments;
TaskBackup(Task task, List<Alarm> alarms, List<Location> locations, List<Tag> tags,
List<GoogleTask> google, List<UserActivity> comments) {
this.task = task;
this.alarms = alarms;
this.locations = locations;
this.tags = tags;
this.google = google;
this.comments = comments;
}
}
}

@ -1,44 +1,40 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.backup;
package org.tasks.backup;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.util.Xml;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.backup.BackupConstants;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.BuildConfig;
import org.tasks.R;
import org.tasks.backup.XmlWriter;
import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao;
import org.tasks.data.GoogleTask;
import org.tasks.data.FilterDao;
import org.tasks.data.GoogleTaskDao;
import org.tasks.data.Location;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.LocationDao;
import org.tasks.data.Tag;
import org.tasks.data.TagDao;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDao;
import org.tasks.data.UserActivity;
import org.tasks.data.UserActivityDao;
import org.tasks.preferences.Preferences;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
@ -46,7 +42,7 @@ import timber.log.Timber;
import static org.tasks.date.DateTimeUtils.newDateTime;
public class TasksXmlExporter {
public class TasksJsonExporter {
public static final String PREF_BACKUP_LAST_DATE = "backupDate"; //$NON-NLS-1$
@ -65,14 +61,14 @@ public class TasksXmlExporter {
private final LocationDao locationDao;
private final TagDao tagDao;
private final GoogleTaskDao googleTaskDao;
private final FilterDao filterDao;
private final GoogleTaskListDao googleTaskListDao;
private final TaskDao taskDao;
private UserActivityDao userActivityDao;
private final UserActivityDao userActivityDao;
private final Preferences preferences;
private static final int FORMAT = 4;
private Context context;
private int exportCount = 0;
private XmlSerializer xml;
private ProgressDialog progressDialog;
private Handler handler;
@ -93,9 +89,10 @@ public class TasksXmlExporter {
}
@Inject
public TasksXmlExporter(TagDataDao tagDataDao, TaskDao taskDao, UserActivityDao userActivityDao,
Preferences preferences, AlarmDao alarmDao, LocationDao locationDao,
TagDao tagDao, GoogleTaskDao googleTaskDao) {
public TasksJsonExporter(TagDataDao tagDataDao, TaskDao taskDao, UserActivityDao userActivityDao,
Preferences preferences, AlarmDao alarmDao, LocationDao locationDao,
TagDao tagDao, GoogleTaskDao googleTaskDao, FilterDao filterDao,
GoogleTaskListDao googleTaskListDao) {
this.tagDataDao = tagDataDao;
this.taskDao = taskDao;
this.userActivityDao = userActivityDao;
@ -104,6 +101,8 @@ public class TasksXmlExporter {
this.locationDao = locationDao;
this.tagDao = tagDao;
this.googleTaskDao = googleTaskDao;
this.filterDao = filterDao;
this.googleTaskListDao = googleTaskListDao;
}
public void exportTasks(final Context context, final ExportType exportType, @Nullable final ProgressDialog progressDialog) {
@ -144,108 +143,43 @@ public class TasksXmlExporter {
}
private void doTasksExport(String output, List<Task> tasks) throws IOException {
File xmlFile = new File(output);
xmlFile.createNewFile();
FileOutputStream fos = new FileOutputStream(xmlFile);
xml = Xml.newSerializer();
xml.setOutput(fos, BackupConstants.XML_ENCODING);
xml.startDocument(null, null);
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
xml.startTag(null, BackupConstants.ASTRID_TAG);
xml.attribute(null, BackupConstants.ASTRID_ATTR_VERSION,
Integer.toString(preferences.getLastSetVersion()));
xml.attribute(null, BackupConstants.ASTRID_ATTR_FORMAT,
Integer.toString(FORMAT));
serializeTasks(tasks);
serializeTagDatas();
xml.endTag(null, BackupConstants.ASTRID_TAG);
xml.endDocument();
xml.flush();
fos.close();
}
private void serializeTagDatas() {
for (TagData tag : tagDataDao.allTags()) {
try {
xml.startTag(null, BackupConstants.TAGDATA_TAG);
tag.writeToXml(new XmlWriter(xml));
xml.endTag(null, BackupConstants.TAGDATA_TAG);
} catch(IOException e) {
throw new RuntimeException(e);
}
}
}
private void serializeTasks(List<Task> tasks) throws IOException {
int length = tasks.size();
for(int i = 0; i < length; i++) {
Task task = tasks.get(i);
setProgress(i, length);
xml.startTag(null, BackupConstants.TASK_TAG);
serializeTask(task);
xml.endTag(null, BackupConstants.TASK_TAG);
this.exportCount++;
List<BackupContainer.TaskBackup> taskBackups = new ArrayList<>();
for (Task task : tasks) {
setProgress(taskBackups.size(), tasks.size());
long taskId = task.getId();
taskBackups.add(new BackupContainer.TaskBackup(
task,
alarmDao.getAlarms(taskId),
locationDao.getGeofences(taskId),
tagDao.getTagsForTask(taskId),
googleTaskDao.getAllByTaskId(taskId),
userActivityDao.getCommentsForTask(task.getUuid())));
}
}
private synchronized void serializeTask(Task task) {
XmlWriter writer = new XmlWriter(xml);
task.writeToXml(writer);
for (Alarm alarm : alarmDao.getAlarms(task.getId())) {
try {
xml.startTag(null, BackupConstants.ALARM_TAG);
alarm.writeToXml(writer);
xml.endTag(null, BackupConstants.ALARM_TAG);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
for (Tag tag : tagDao.getTagsForTask(task.getId())) {
try {
xml.startTag(null, BackupConstants.TAG_TAG);
tag.writeToXml(writer);
xml.endTag(null, BackupConstants.TAG_TAG);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
for (Location location : locationDao.getGeofences(task.getId())) {
try {
xml.startTag(null, BackupConstants.LOCATION_TAG);
location.writeToXml(writer);
xml.endTag(null, BackupConstants.LOCATION_TAG);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
for (GoogleTask googleTask : googleTaskDao.getAllByTaskId(task.getId())) {
try {
xml.startTag(null, BackupConstants.GOOGLE_TASKS_TAG);
googleTask.writeToXml(writer);
xml.endTag(null, BackupConstants.GOOGLE_TASKS_TAG);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
for (UserActivity comment : userActivityDao.getCommentsForTask(task.getUuid())) {
try {
xml.startTag(null, BackupConstants.COMMENT_TAG);
comment.writeToXml(new XmlWriter(xml));
xml.endTag(null, BackupConstants.COMMENT_TAG);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Map<String, Object> data = new HashMap<>();
data.put("version", BuildConfig.VERSION_CODE);
data.put("timestamp", System.currentTimeMillis());
data.put("data", new BackupContainer(
taskBackups,
tagDataDao.getAll(),
filterDao.getAll(),
googleTaskListDao.getAll()));
File file = new File(output);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter out = new OutputStreamWriter(fos);
Gson gson = BuildConfig.DEBUG
? new GsonBuilder().setPrettyPrinting().create()
: new Gson();
out.write(gson.toJson(data));
out.close();
fos.close();
exportCount = taskBackups.size();
}
public static final String XML_NULL = "null"; //$NON-NLS-1$
private void onFinishExport(final String outputFile) {
post(() -> {
if(exportCount == 0) {

@ -0,0 +1,189 @@
package org.tasks.backup;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.res.Resources;
import android.os.Handler;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import org.tasks.LocalBroadcastManager;
import org.tasks.R;
import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao;
import org.tasks.data.Filter;
import org.tasks.data.FilterDao;
import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.Location;
import org.tasks.data.LocationDao;
import org.tasks.data.Tag;
import org.tasks.data.TagDao;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDao;
import org.tasks.data.UserActivity;
import org.tasks.data.UserActivityDao;
import org.tasks.dialogs.DialogBuilder;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileReader;
import java.io.IOException;
import javax.inject.Inject;
import timber.log.Timber;
public class TasksJsonImporter {
private final TagDataDao tagDataDao;
private final UserActivityDao userActivityDao;
private final DialogBuilder dialogBuilder;
private final TaskDao taskDao;
private final LocalBroadcastManager localBroadcastManager;
private final AlarmDao alarmDao;
private final TagDao tagDao;
private final GoogleTaskDao googleTaskDao;
private final GoogleTaskListDao googleTaskListDao;
private final FilterDao filterDao;
private final LocationDao locationDao;
private Activity activity;
private Handler handler;
private int taskCount;
private int importCount = 0;
private int skipCount = 0;
private int errorCount = 0;
private ProgressDialog progressDialog;
private String input;
private void setProgressMessage(final String message) {
handler.post(() -> progressDialog.setMessage(message));
}
@Inject
public TasksJsonImporter(TagDataDao tagDataDao, UserActivityDao userActivityDao,
DialogBuilder dialogBuilder, TaskDao taskDao, LocationDao locationDao,
LocalBroadcastManager localBroadcastManager, AlarmDao alarmDao,
TagDao tagDao, GoogleTaskDao googleTaskDao, GoogleTaskListDao googleTaskListDao,
FilterDao filterDao) {
this.tagDataDao = tagDataDao;
this.userActivityDao = userActivityDao;
this.dialogBuilder = dialogBuilder;
this.taskDao = taskDao;
this.locationDao = locationDao;
this.localBroadcastManager = localBroadcastManager;
this.alarmDao = alarmDao;
this.tagDao = tagDao;
this.googleTaskDao = googleTaskDao;
this.googleTaskListDao = googleTaskListDao;
this.filterDao = filterDao;
}
public void importTasks(Activity activity, String input, ProgressDialog progressDialog) {
this.activity = activity;
this.input = input;
this.progressDialog = progressDialog;
handler = new Handler();
new Thread(() -> {
try {
performImport();
} catch (IOException | XmlPullParserException e) {
Timber.e(e, e.getMessage());
}
}).start();
}
private void performImport() throws IOException, XmlPullParserException {
FileReader fileReader = new FileReader(input);
String string = CharStreams.toString(fileReader);
fileReader.close();
Gson gson = new Gson();
JsonObject input = gson.fromJson(string, JsonObject.class);
try {
JsonElement data = input.get("data");
BackupContainer backupContainer = gson.fromJson(data, BackupContainer.class);
for (TagData tagData : backupContainer.tags) {
if (tagDataDao.getByUuid(tagData.getRemoteId()) == null) {
tagDataDao.createNew(tagData);
}
}
for (GoogleTaskList googleTaskList : backupContainer.googleTaskLists) {
if (googleTaskListDao.getByRemoteId(googleTaskList.getRemoteId()) == null) {
googleTaskListDao.insert(googleTaskList);
}
}
for (Filter filter : backupContainer.filters) {
if (filterDao.getByName(filter.getTitle()) == null) {
filterDao.insert(filter);
}
}
for (BackupContainer.TaskBackup backup : backupContainer.tasks) {
taskCount++;
setProgressMessage(activity.getString(R.string.import_progress_read, taskCount));
Task task = backup.task;
if (taskDao.fetch(task.getUuid()) != null) {
skipCount++;
continue;
}
taskDao.createNew(task);
long taskId = task.getId();
String taskUuid = task.getUuid();
for (Alarm alarm : backup.alarms) {
alarm.setTask(taskId);
alarmDao.insert(alarm);
}
for (UserActivity comment : backup.comments) {
comment.setTargetId(taskUuid);
userActivityDao.createNew(comment);
}
for (GoogleTask googleTask : backup.google) {
googleTask.setTask(taskId);
googleTaskDao.insert(googleTask);
}
for (Tag tag : backup.tags) {
tag.setTask(taskId);
tag.setTaskUid(taskUuid);
tagDao.insert(tag);
}
for (Location location : backup.locations) {
location.setTask(taskId);
locationDao.insert(location);
}
importCount++;
}
} finally {
localBroadcastManager.broadcastRefresh();
handler.post(() -> {
if(progressDialog.isShowing()) {
DialogUtilities.dismissDialog(activity, progressDialog);
showSummary();
}
});
}
}
private void showSummary() {
Resources r = activity.getResources();
dialogBuilder.newDialog()
.setTitle(R.string.import_summary_title)
.setMessage(activity.getString(R.string.import_summary_message,
input,
r.getQuantityString(R.plurals.Ntasks, taskCount, taskCount),
r.getQuantityString(R.plurals.Ntasks, importCount, importCount),
r.getQuantityString(R.plurals.Ntasks, skipCount, skipCount),
r.getQuantityString(R.plurals.Ntasks, errorCount, errorCount)))
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.dismiss())
.show();
}
}

@ -2,10 +2,10 @@ package org.tasks.backup;
import org.xmlpull.v1.XmlPullParser;
import static com.todoroo.astrid.backup.TasksXmlExporter.XML_NULL;
public class XmlReader {
private static final String XML_NULL = "null"; //$NON-NLS-1$
public interface ValueWriter<T> {
void write(T value);
}

@ -1,66 +0,0 @@
package org.tasks.backup;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import timber.log.Timber;
import static com.todoroo.astrid.backup.TasksXmlExporter.XML_NULL;
public class XmlWriter {
private final XmlSerializer xml;
public XmlWriter(XmlSerializer xml) {
this.xml = xml;
}
public void writeLong(String name, Long value) {
try {
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
Timber.e(e, e.getMessage());
} catch (IllegalArgumentException | IOException | IllegalStateException e) {
throw new RuntimeException(e);
}
}
public void writeString(String name, String value) {
try {
if(value != null) {
xml.attribute(null, name, value);
}
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
Timber.v(e, e.getMessage());
} catch (IllegalArgumentException | IOException | IllegalStateException e) {
throw new RuntimeException(e);
}
}
public void writeInteger(String name, Integer value) {
try {
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
Timber.e(e, e.getMessage());
} catch (IllegalArgumentException | IOException | IllegalStateException e) {
throw new RuntimeException(e);
}
}
public void writeDouble(String name, Double value) {
try {
String valueString = (value == null) ? XML_NULL : value.toString();
xml.attribute(null, name, valueString);
} catch (UnsupportedOperationException e) {
// didn't read this value, do nothing
Timber.e(e, e.getMessage());
} catch (IllegalArgumentException | IllegalStateException | IOException e) {
throw new RuntimeException(e);
}
}
}

@ -5,18 +5,15 @@ import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
@Entity(tableName = "alarms")
public class Alarm {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "task")
private long task;
private transient long task;
@ColumnInfo(name = "time")
private long time;
@ -31,15 +28,6 @@ public class Alarm {
this.time = time;
}
@Ignore
public Alarm(XmlReader xml) {
xml.readLong("time", this::setTime);
}
public void writeToXml(XmlWriter writer) {
writer.writeLong("time", time);
}
public long getId() {
return id;
}

@ -8,7 +8,7 @@ import android.arch.persistence.room.PrimaryKey;
public class Filter {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "title")
private String title;

@ -23,9 +23,15 @@ public interface FilterDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insertOrUpdate(Filter storeObject);
@Insert
void insert(Filter filter);
@Query("SELECT * FROM filters ORDER BY title ASC")
List<Filter> getFilters();
@Query("SELECT * FROM filters WHERE _id = :id LIMIT 1")
Filter getById(long id);
@Query("SELECT * FROM filters")
List<Filter> getAll();
}

@ -10,7 +10,6 @@ import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.utility.DateUtilities;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
@Entity(tableName = "google_tasks")
public class GoogleTask {
@ -25,10 +24,10 @@ public class GoogleTask {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "task")
private long task;
private transient long task;
@ColumnInfo(name = "remote_id")
private String remoteId = "";
@ -55,7 +54,7 @@ public class GoogleTask {
private long deleted;
@Ignore
private boolean suppressSync;
private transient boolean suppressSync;
public GoogleTask() {
@ -68,29 +67,6 @@ public class GoogleTask {
this.listId = listId;
}
@Ignore
public GoogleTask(XmlReader xml) {
remoteId = xml.readString("remote_id");
listId = xml.readString("list_id");
parent = xml.readLong("parent");
indent = xml.readInteger("indent");
order = xml.readLong("order");
remoteOrder = xml.readLong("remote_order");
lastSync = xml.readLong("last_sync");
deleted = xml.readLong("deleted");
}
public void writeToXml(XmlWriter xml) {
xml.writeString("remote_id", remoteId);
xml.writeString("list_id", listId);
xml.writeLong("parent", parent);
xml.writeInteger("indent", indent);
xml.writeLong("order", order);
xml.writeLong("remote_order", remoteOrder);
xml.writeLong("last_sync", lastSync);
xml.writeLong("deleted", deleted);
}
public long getId() {
return id;
}

@ -11,7 +11,7 @@ import android.os.Parcelable;
public class GoogleTaskList implements Parcelable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "remote_id")
private String remoteId;

@ -16,9 +16,18 @@ public interface GoogleTaskListDao {
@Query("SELECT * FROM google_task_lists WHERE deleted = 0 ORDER BY title ASC")
List<GoogleTaskList> getActiveLists();
@Query("SELECT * FROM google_task_lists WHERE remote_id = :remoteId LIMIT 1")
GoogleTaskList getByRemoteId(String remoteId);
@Query("SELECT * FROM google_task_lists")
List<GoogleTaskList> getAll();
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insertOrReplace(GoogleTaskList googleTaskList);
@Insert
void insert(GoogleTaskList googleTaskList);
@Query("DELETE FROM google_task_lists WHERE _id = :id")
void deleteById(long id);
}

@ -8,7 +8,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
import java.io.Serializable;
@ -16,10 +15,10 @@ import java.io.Serializable;
public class Location implements Serializable, Parcelable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "task")
private long task;
private transient long task;
@ColumnInfo(name = "name")
private String name;
@ -55,13 +54,6 @@ public class Location implements Serializable, Parcelable {
xml.readInteger("radius", this::setRadius);
}
public void writeToXml(XmlWriter xmlWriter) {
xmlWriter.writeString("name", name);
xmlWriter.writeDouble("latitude", latitude);
xmlWriter.writeDouble("longitude", longitude);
xmlWriter.writeInteger("radius", radius);
}
public long getId() {
return id;
}

@ -9,7 +9,6 @@ import android.support.annotation.NonNull;
import com.todoroo.andlib.data.Table;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
@Entity(tableName = "tags")
public class Tag {
@ -21,10 +20,10 @@ public class Tag {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
private transient long id;
@ColumnInfo(name = "task")
private long task;
private transient long task;
@ColumnInfo(name = "name")
private String name;
@ -33,7 +32,7 @@ public class Tag {
private String tagUid;
@ColumnInfo(name = "task_uid")
private String taskUid;
private transient String taskUid;
public Tag() {
@ -54,12 +53,6 @@ public class Tag {
xmlReader.readString("task_uid", this::setTaskUid);
}
public void writeToXml(XmlWriter xmlWriter) {
xmlWriter.writeString("name", name);
xmlWriter.writeString("tag_uid", tagUid);
xmlWriter.writeString("task_uid", taskUid);
}
public long getId() {
return id;
}

@ -10,14 +10,13 @@ import android.os.Parcelable;
import com.todoroo.astrid.data.Task;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
@Entity(tableName = "tagdata")
public final class TagData implements Parcelable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private Long id;
private transient Long id;
@ColumnInfo(name = "remoteId")
private String remoteId = Task.NO_UUID;
@ -51,13 +50,6 @@ public final class TagData implements Parcelable {
tagOrdering = parcel.readString();
}
public void writeToXml(XmlWriter writer) {
writer.writeString("remoteId", remoteId);
writer.writeString("name", name);
writer.writeInteger("color", color);
writer.writeString("tagOrdering", tagOrdering);
}
public Long getId() {
return id;
}

@ -17,9 +17,8 @@ public abstract class TagDataDao {
@Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1")
public abstract TagData getTagByName(String name);
// TODO: does this need to be ordered?
@Query("SELECT * FROM tagdata ORDER BY _id ASC")
public abstract List<TagData> allTags();
@Query("SELECT * FROM tagdata")
public abstract List<TagData> getAll();
@Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1")
public abstract TagData getByUuid(String uuid);

@ -13,7 +13,6 @@ import com.todoroo.astrid.data.Task;
import org.json.JSONException;
import org.json.JSONObject;
import org.tasks.backup.XmlReader;
import org.tasks.backup.XmlWriter;
import java.io.File;
@ -24,7 +23,7 @@ public class UserActivity implements Parcelable {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private Long id;
private transient Long id;
@ColumnInfo(name = "remoteId")
private String remoteId = Task.NO_UUID;
@ -36,7 +35,7 @@ public class UserActivity implements Parcelable {
private String picture = "";
@ColumnInfo(name = "target_id")
private String targetId = Task.NO_UUID;
private transient String targetId = Task.NO_UUID;
@ColumnInfo(name = "created_at")
private Long created = 0L;
@ -63,14 +62,6 @@ public class UserActivity implements Parcelable {
created = parcel.readLong();
}
public void writeToXml(XmlWriter writer) {
writer.writeString("remoteId", remoteId);
writer.writeString("message", message);
writer.writeString("picture", picture);
writer.writeString("target_id", targetId);
writer.writeLong("created_at", created);
}
public Long getId() {
return id;
}

@ -5,7 +5,7 @@ import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.todoroo.astrid.backup.TasksXmlExporter;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.InjectingNativeDialogFragment;
import org.tasks.injection.NativeDialogFragmentComponent;
@ -19,7 +19,7 @@ public class ExportTasksDialog extends InjectingNativeDialogFragment {
}
@Inject DialogBuilder dialogBuilder;
@Inject TasksXmlExporter tasksXmlExporter;
@Inject TasksJsonExporter tasksJsonExporter;
private ProgressDialog progressDialog;
@ -34,7 +34,7 @@ public class ExportTasksDialog extends InjectingNativeDialogFragment {
progressDialog.show();
setCancelable(false);
tasksXmlExporter.exportTasks(getActivity(), TasksXmlExporter.ExportType.EXPORT_TYPE_MANUAL, progressDialog);
tasksJsonExporter.exportTasks(getActivity(), TasksJsonExporter.ExportType.EXPORT_TYPE_MANUAL, progressDialog);
return progressDialog;
}

@ -6,6 +6,7 @@ import android.os.Bundle;
import com.todoroo.astrid.backup.TasksXmlImporter;
import org.tasks.backup.TasksJsonImporter;
import org.tasks.injection.InjectingNativeDialogFragment;
import org.tasks.injection.NativeDialogFragmentComponent;
@ -24,6 +25,7 @@ public class ImportTasksDialog extends InjectingNativeDialogFragment {
private static final String EXTRA_PATH = "extra_path";
@Inject TasksXmlImporter xmlImporter;
@Inject TasksJsonImporter jsonImporter;
@Inject DialogBuilder dialogBuilder;
@Override
@ -36,7 +38,11 @@ public class ImportTasksDialog extends InjectingNativeDialogFragment {
progressDialog.setIndeterminate(true);
progressDialog.show();
setCancelable(false);
xmlImporter.importTasks(getActivity(), path, progressDialog);
if (path.endsWith("\\.xml")) {
xmlImporter.importTasks(getActivity(), path, progressDialog);
} else {
jsonImporter.importTasks(getActivity(), path, progressDialog);
}
return progressDialog;
}

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.support.v4.app.JobIntentService;
import com.todoroo.astrid.backup.TasksXmlExporter;
import org.tasks.backup.TasksJsonExporter;
import org.tasks.injection.ForApplication;
import org.tasks.injection.IntentServiceComponent;
@ -35,7 +35,7 @@ public class BackupJob extends MidnightJob {
@Inject @ForApplication Context context;
@Inject JobManager jobManager;
@Inject TasksXmlExporter tasksXmlExporter;
@Inject TasksJsonExporter tasksJsonExporter;
@Inject Preferences preferences;
@SuppressWarnings("unused")
@ -43,10 +43,10 @@ public class BackupJob extends MidnightJob {
}
BackupJob(Context context, JobManager jobManager, TasksXmlExporter tasksXmlExporter, Preferences preferences) {
BackupJob(Context context, JobManager jobManager, TasksJsonExporter tasksJsonExporter, Preferences preferences) {
this.context = context;
this.jobManager = jobManager;
this.tasksXmlExporter = tasksXmlExporter;
this.tasksJsonExporter = tasksJsonExporter;
this.preferences = preferences;
}
@ -68,7 +68,7 @@ public class BackupJob extends MidnightJob {
}
try {
tasksXmlExporter.exportTasks(context, TasksXmlExporter.ExportType.EXPORT_TYPE_SERVICE, null);
tasksJsonExporter.exportTasks(context, TasksJsonExporter.ExportType.EXPORT_TYPE_SERVICE, null);
} catch (Exception e) {
Timber.e(e, e.getMessage());
}

Loading…
Cancel
Save