Wrote an importer for Astrid 2 backup xml files

pull/14/head
Tim Su 16 years ago
parent b6ec30285b
commit 04ea673680

@ -4,7 +4,7 @@
<classpathentry kind="src" path="src-legacy"/> <classpathentry kind="src" path="src-legacy"/>
<classpathentry kind="src" path="api-src"/> <classpathentry kind="src" path="api-src"/>
<classpathentry kind="src" path="common-src"/> <classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java" kind="src" path="plugin-src"/> <classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/backup/TasksXmlExporter.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/> <classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/> <classpathentry exported="true" kind="lib" path="lib/commons-codec-1.3.jar"/>

@ -2,6 +2,9 @@ package com.todoroo.astrid.backup;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.timsu.astrid.R; import com.timsu.astrid.R;
@ -13,7 +16,38 @@ public class BackupActivity extends Activity {
setContentView(R.layout.backup_activity); setContentView(R.layout.backup_activity);
setTitle(R.string.backup_BAc_title); setTitle(R.string.backup_BAc_title);
((Button)findViewById(R.id.importButton)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
importTasks();
}
});
((Button)findViewById(R.id.exportButton)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
exportTasks();
}
});
}
private void importTasks() {
FilePickerBuilder.OnFilePickedListener listener = new FilePickerBuilder.OnFilePickedListener() {
@Override
public void onFilePicked(String filePath) {
TasksXmlImporter.importTasks(filePath, null);
}
};
new FilePickerBuilder(this,
getString(R.string.import_file_prompt),
BackupConstants.getExportDirectory(),
listener);
}
private void exportTasks() {
/*TasksXmlExporter exporter = new TasksXmlExporter(false);
exporter.setContext(getParent());
exporter.exportTasks();*/
} }
} }

@ -1,46 +1,21 @@
package com.todoroo.astrid.backup; package com.todoroo.astrid.backup;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import edu.umd.cs.findbugs.annotations.CheckForNull;
import android.util.Xml;
import android.widget.Toast;
import com.timsu.astrid.R;
import com.timsu.astrid.data.TaskController;
import com.timsu.astrid.data.TaskIdentifier;
import com.timsu.astrid.data.TaskModelForXml;
import com.timsu.astrid.data.alerts.AlertController;
import com.timsu.astrid.data.sync.SyncDataController;
import com.timsu.astrid.data.sync.SyncMapping;
import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.tag.TagModelForView;
/**
* Constants for backup XML attributes and nodes.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public class BackupConstants { public class BackupConstants {
private TaskController taskController; // Do NOT edit the constants in this file! You will break compatibility with old backups
private TagController tagController;
private AlertController alertController;
private SyncDataController syncDataController;
private Context ctx;
private String output;
private final boolean isService;
private int exportCount;
private XmlSerializer xml;
private HashMap<TagIdentifier, TagModelForView> tagMap;
public static final String ASTRID_TAG = "astrid"; public static final String ASTRID_TAG = "astrid";
public static final String ASTRID_ATTR_VERSION = "version"; public static final String ASTRID_ATTR_VERSION = "version";
@ -53,144 +28,15 @@ public class BackupConstants {
public static final String SYNC_ATTR_SERVICE = "service"; public static final String SYNC_ATTR_SERVICE = "service";
public static final String SYNC_ATTR_REMOTE_ID = "remote_id"; public static final String SYNC_ATTR_REMOTE_ID = "remote_id";
public static final String XML_ENCODING = "utf-8"; public static final String XML_ENCODING = "utf-8";
public static final String ASTRID_DIR = "/astrid";
private static final String EXPORT_FILE_NAME = "user.%s.xml";
private static final String BACKUP_FILE_NAME = "auto.%s.xml";
public static final int FILENAME_DATE_BEGIN_INDEX = 5;
public static final int FILENAME_DATE_END_INDEX = 11;
public BackupConstants(boolean isService) {
this.isService = isService;
this.exportCount = 0;
}
private void initTagMap() {
tagMap = tagController.getAllTagsAsMap();
}
private void serializeTags(TaskIdentifier task)
throws IOException {
LinkedList<TagIdentifier> tags = tagController.getTaskTags(task);
for (TagIdentifier tag : tags) {
if(!tagMap.containsKey(tag) || tagMap.get(tag) == null)
continue;
xml.startTag(null, TAG_TAG);
xml.attribute(null, TAG_ATTR_NAME, tagMap.get(tag).toString());
xml.endTag(null, TAG_TAG);
}
}
private void serializeSyncMappings(TaskIdentifier task) public static final String ASTRID_DIR = "/astrid";
throws IOException { public static final String EXPORT_FILE_NAME = "user.%s.xml";
HashSet<SyncMapping> syncMappings = syncDataController.getSyncMappings(task); public static final String BACKUP_FILE_NAME = "auto.%s.xml";
for (SyncMapping sync : syncMappings) {
xml.startTag(null, SYNC_TAG);
xml.attribute(null, SYNC_ATTR_SERVICE,
Integer.toString(sync.getSyncServiceId()));
xml.attribute(null, SYNC_ATTR_REMOTE_ID, sync.getRemoteId());
xml.endTag(null, SYNC_TAG);
}
}
private void serializeAlerts(TaskIdentifier task)
throws IOException {
List<Date> alerts = alertController.getTaskAlerts(task);
for (Date alert : alerts) {
xml.startTag(null, ALERT_TAG);
xml.attribute(null, ALERT_ATTR_DATE, BackupDateUtilities.getIso8601String(alert));
xml.endTag(null, ALERT_TAG);
}
}
private void serializeTasks()
throws IOException {
Cursor c = taskController.getBackupTaskListCursor();
if (! c.moveToFirst()) {
return; // No tasks.
}
do {
TaskModelForXml task = new TaskModelForXml(c);
TaskIdentifier taskId = task.getTaskIdentifier();
xml.startTag(null, TASK_TAG);
HashMap<String, String> taskAttributes = task.getTaskAttributes();
for (String key : taskAttributes.keySet()) {
String value = taskAttributes.get(key);
xml.attribute(null, key, value);
}
serializeTags(taskId);
serializeAlerts(taskId);
serializeSyncMappings(taskId);
xml.endTag(null, TASK_TAG);
this.exportCount++;
} while (c.moveToNext());
c.close();
}
private void doTasksExport() throws IOException {
File xmlFile = new File(this.output);
xmlFile.createNewFile();
FileOutputStream fos = new FileOutputStream(xmlFile);
xml = Xml.newSerializer();
xml.setOutput(fos, XML_ENCODING);
xml.startDocument(null, null);
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
xml.startTag(null, ASTRID_TAG);
xml.attribute(null, ASTRID_ATTR_VERSION,
Integer.toString(Preferences.getCurrentVersion(ctx)));
openControllers();
initTagMap();
serializeTasks();
closeControllers();
xml.endTag(null, ASTRID_TAG);
xml.endDocument();
xml.flush();
fos.close();
if (!isService) {
displayToast();
}
}
private void displayToast() {
CharSequence text = String.format(ctx.getString(R.string.export_toast),
ctx.getResources().getQuantityString(R.plurals.Ntasks, exportCount,
exportCount), output);
Toast.makeText(ctx, text, Toast.LENGTH_LONG).show();
}
private void displayErrorToast(String error) {
Toast.makeText(ctx, error, Toast.LENGTH_LONG).show();
}
private void closeControllers() {
tagController.close();
taskController.close();
alertController.close();
syncDataController.close();
}
private void openControllers() {
taskController.open();
tagController.open();
alertController.open();
syncDataController.open();
}
public void exportTasks(File directory) {
if (isService && !Preferences.isBackupEnabled(ctx)) {
// Automatic backups are disabled.
return;
}
if (setupFile(directory)) {
Thread thread = new Thread(doBackgroundExport);
thread.start();
}
}
/**
* @return export directory for tasks, or null if no SD card
*/
@CheckForNull
public static File getExportDirectory() { public static File getExportDirectory() {
String storageState = Environment.getExternalStorageState(); String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) { if (storageState.equals(Environment.MEDIA_MOUNTED)) {
@ -201,77 +47,5 @@ public class BackupConstants {
return null; return null;
} }
private boolean setupFile(File directory) {
File astridDir = directory;
if (astridDir != null) {
// Check for /sdcard/astrid directory. If it doesn't exist, make it.
if (astridDir.exists() || astridDir.mkdir()) {
String fileName;
if (isService) {
fileName = BACKUP_FILE_NAME;
} else {
fileName = EXPORT_FILE_NAME;
}
fileName = String.format(fileName, BackupDateUtilities.getDateForExport());
setOutput(astridDir.getAbsolutePath() + "/" + fileName);
return true;
} else {
// Unable to make the /sdcard/astrid directory.
String error = ctx.getString(R.string.error_sdcard, astridDir.getAbsolutePath());
Log.e("TasksXmlExporter", error);
if (!isService) {
displayErrorToast(error);
}
return false;
}
} else {
// Unable to access the sdcard because it's not in the mounted state.
String error = ctx.getString(R.string.error_sdcard_general);
Log.e("TasksXmlExporter", error);
if (!isService) {
displayErrorToast(error);
}
return false;
}
}
private void setOutput(String file) {
this.output = file;
}
private final Runnable doBackgroundExport = new Runnable() {
public void run() {
/*Looper.prepare();
try {
doTasksExport();
} catch (IOException e) {
Log.e("TasksXmlExporter", "IOException in doTasksExport " + e.getMessage());
}
Looper.loop();*/
}
};
public void setTaskController(TaskController taskController) {
this.taskController = taskController;
}
public void setTagController(TagController tagController) {
this.tagController = tagController;
}
public void setAlertController(AlertController alertController) {
this.alertController = alertController;
}
public void setSyncDataController(SyncDataController syncDataController) {
this.syncDataController = syncDataController;
}
public void setContext(Context ctx) {
this.ctx = ctx;
setTaskController(new TaskController(ctx));
setTagController(new TagController(ctx));
setAlertController(new AlertController(ctx));
setSyncDataController(new SyncDataController(ctx));
}
} }

@ -1,4 +1,4 @@
package com.todoroo.astrid.ui; package com.todoroo.astrid.backup;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;

@ -191,16 +191,6 @@ public class TasksXmlExporter {
} }
} }
public static File getExportDirectory() {
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
path = path + ASTRID_DIR;
return new File(path);
}
return null;
}
private boolean setupFile(File directory) { private boolean setupFile(File directory) {
File astridDir = directory; File astridDir = directory;
if (astridDir != null) { if (astridDir != null) {

@ -1,8 +1,10 @@
package com.todoroo.astrid.backup; package com.todoroo.astrid.backup;
import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.StringTokenizer;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@ -16,52 +18,53 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.google.ical.values.RRule;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController; import com.todoroo.andlib.data.TodorooCursor;
import com.timsu.astrid.data.TaskController; import com.todoroo.andlib.service.ContextManager;
import com.timsu.astrid.data.TaskIdentifier; import com.todoroo.andlib.service.ExceptionService;
import com.timsu.astrid.data.TaskModelForXml; import com.todoroo.andlib.sql.Query;
import com.timsu.astrid.data.alerts.AlertController; import com.todoroo.andlib.utility.DateUtilities;
import com.timsu.astrid.data.sync.SyncDataController; import com.todoroo.astrid.core.PluginServices;
import com.timsu.astrid.data.sync.SyncMapping; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.timsu.astrid.data.tag.TagController; import com.todoroo.astrid.legacy.LegacyImportance;
import com.timsu.astrid.data.tag.TagIdentifier; import com.todoroo.astrid.legacy.LegacyRepeatInfo;
import com.timsu.astrid.data.tag.TagModelForView; import com.todoroo.astrid.legacy.LegacyRepeatInfo.LegacyRepeatInterval;
import com.todoroo.astrid.legacy.LegacyTaskModel;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.rmilk.data.MilkTask;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService;
public class TasksXmlImporter { public class TasksXmlImporter {
public static final String TAG = "TasksXmlImporter";
public static final String ASTRID_TAG = TasksXmlExporter.ASTRID_TAG; // --- public interface
public static final String ASTRID_ATTR_VERSION = TasksXmlExporter.ASTRID_ATTR_VERSION;
public static final String TASK_TAG = TasksXmlExporter.TASK_TAG; /**
public static final String TAG_TAG = TasksXmlExporter.TAG_TAG; * Import tasks from the given file
public static final String ALERT_TAG = TasksXmlExporter.ALERT_TAG; *
public static final String SYNC_TAG = TasksXmlExporter.SYNC_TAG; * @param input
public static final String TASK_ID = AbstractController.KEY_ROWID; * @param runAfterImport
public static final String TASK_NAME = TaskModelForXml.NAME; */
public static final String TASK_CREATION_DATE = TaskModelForXml.CREATION_DATE; public static void importTasks(String input, Runnable runAfterImport) {
public static final String TAG_ATTR_NAME = TasksXmlExporter.TAG_ATTR_NAME; new TasksXmlImporter(input, runAfterImport);
public static final String ALERT_ATTR_DATE = TasksXmlExporter.ALERT_ATTR_DATE; }
public static final String SYNC_ATTR_SERVICE = TasksXmlExporter.SYNC_ATTR_SERVICE;
public static final String SYNC_ATTR_REMOTE_ID = TasksXmlExporter.SYNC_ATTR_REMOTE_ID; // --- implementation
private TaskController taskController; private final Handler importHandler;
private TagController tagController;
private AlertController alertController;
private SyncDataController syncDataController;
private XmlPullParser xpp;
private String input;
private Handler importHandler;
private final Context context;
private int taskCount; private int taskCount;
private int importCount; private int importCount;
private int skipCount; private int skipCount;
private final String input;
static ProgressDialog progressDialog; private final Context context = ContextManager.getContext();
private final TaskService taskService = PluginServices.getTaskService();
public TasksXmlImporter(Context context) { private final MetadataService metadataService = PluginServices.getMetadataService();
this.context = context; private final ExceptionService exceptionService = PluginServices.getExceptionService();
setContext(context); protected ProgressDialog progressDialog;
}
private void setProgressMessage(final String message) { private void setProgressMessage(final String message) {
importHandler.post(new Runnable() { importHandler.post(new Runnable() {
@ -71,12 +74,18 @@ public class TasksXmlImporter {
}); });
} }
public void importTasks(final Runnable runAfterImport) { /**
* Import tasks.
* @param runAfterImport optional runnable after import
*/
private TasksXmlImporter(String input, final Runnable runAfterImport) {
this.input = input;
importHandler = new Handler(); importHandler = new Handler();
importHandler.post(new Runnable() { importHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
TasksXmlImporter.progressDialog = new ProgressDialog(context); progressDialog = new ProgressDialog(context);
progressDialog.setIcon(android.R.drawable.ic_dialog_info); progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(R.string.import_progress_title); progressDialog.setTitle(R.string.import_progress_title);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
@ -95,153 +104,61 @@ public class TasksXmlImporter {
if (runAfterImport != null) { if (runAfterImport != null) {
importHandler.post(runAfterImport); importHandler.post(runAfterImport);
} }
} catch (FileNotFoundException e) { } catch (IOException e) {
Log.e("TasksXmlImporter", e.getMessage()); exceptionService.displayAndReportError(context,
context.getString(R.string.backup_TXI_error), e);
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
Log.e("TasksXmlImporter", e.getMessage()); exceptionService.displayAndReportError(context,
context.getString(R.string.backup_TXI_error), e);
} }
Looper.loop(); Looper.loop();
} }
}).start(); }).start();
} }
private void performImport() throws FileNotFoundException, XmlPullParserException { @SuppressWarnings("nls")
private void performImport() throws IOException, XmlPullParserException {
taskCount = 0; taskCount = 0;
importCount = 0; importCount = 0;
skipCount = 0; skipCount = 0;
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
xpp = factory.newPullParser(); XmlPullParser xpp = factory.newPullParser();
xpp.setInput(new FileReader(input)); xpp.setInput(new FileReader(input));
setProgressMessage(context.getString(R.string.import_progress_opened)); setProgressMessage(context.getString(R.string.import_progress_opened));
openControllers();
try { try {
TaskModelForXml currentTask = null;
while (xpp.next() != XmlPullParser.END_DOCUMENT) { while (xpp.next() != XmlPullParser.END_DOCUMENT) {
String tag = xpp.getName(); String tag = xpp.getName();
if (xpp.getEventType() == XmlPullParser.END_TAG) { if (xpp.getEventType() == XmlPullParser.END_TAG) {
// Ignore end tag. // Ignore end tags
continue; continue;
} }
if (tag != null) { if (tag != null) {
if (tag.equals(ASTRID_TAG)) { // Process <astrid ... >
// Process <astrid ... > if (tag.equals(BackupConstants.ASTRID_TAG)) {
// Perform version compatibility check? String version = xpp.getAttributeValue(null, BackupConstants.ASTRID_ATTR_VERSION);
} int intVersion;
else if (tag.equals(TASK_TAG)) { try {
// Parse <task ... > intVersion = Integer.parseInt(version);
currentTask = parseTask(); } catch (Exception e) {
} else if (currentTask != null) { throw new UnsupportedOperationException(
// These tags all require that we have a task to associate them with. "Did not know how to import tasks with version '" +
if (tag.equals(TAG_TAG)) { version + "'");
// Process <tag ... >
parseTag(currentTask.getTaskIdentifier());
} else if (tag.equals(ALERT_TAG)) {
// Process <alert ... >
parseAlert(currentTask.getTaskIdentifier());
} else if (tag.equals(SYNC_TAG)) {
// Process <sync ... >
parseSync(currentTask.getTaskIdentifier());
} }
if(intVersion <= 135)
new Astrid2TaskImporter(xpp);
else
new Astrid3TaskImporter(xpp);
break;
} }
} }
} }
} catch (Exception e) {
Log.e(TAG, "import error " + e.getMessage());
} finally { } finally {
closeControllers();
progressDialog.dismiss(); progressDialog.dismiss();
showSummary(); showSummary();
} }
} }
private boolean parseSync(TaskIdentifier taskId) {
String service = xpp.getAttributeValue(null, SYNC_ATTR_SERVICE);
String remoteId = xpp.getAttributeValue(null, SYNC_ATTR_REMOTE_ID);
if (service != null && remoteId != null) {
int serviceInt = Integer.parseInt(service);
SyncMapping sm = new SyncMapping(taskId, serviceInt, remoteId);
syncDataController.saveSyncMapping(sm);
return true;
}
return false;
}
private boolean parseAlert(TaskIdentifier taskId) {
String alert = xpp.getAttributeValue(null, ALERT_ATTR_DATE);
if (alert != null) {
Date alertDate = BackupDateUtilities.getDateFromIso8601String(alert);
if (alertDate != null) {
if (! alertController.addAlert(taskId, alertDate)) {
return false;
}
} else {
return false;
}
} else {
return false;
}
return true;
}
private boolean parseTag(TaskIdentifier taskId) {
String tagName = xpp.getAttributeValue(null, TAG_ATTR_NAME);
if (tagName != null) {
TagIdentifier tagId;
TagModelForView tagModel;
tagModel = tagController.fetchTagFromName(tagName);
if (tagModel == null) {
// Tag not found, create a new one.
tagId = tagController.createTag(tagName);
} else {
tagId = tagModel.getTagIdentifier();
}
if (! tagController.addTag(taskId, tagId)) {
return false;
}
} else {
return false;
}
return true;
}
private TaskModelForXml parseTask() {
taskCount++;
setProgressMessage(context.getString(R.string.import_progress_read, taskCount));
TaskModelForXml task = null;
String taskName = xpp.getAttributeValue(null, TASK_NAME);
Date creationDate = null;
String createdString = xpp.getAttributeValue(null, TASK_CREATION_DATE);
if (createdString != null) {
creationDate = BackupDateUtilities.getDateFromIso8601String(createdString);
}
// If the task's name and creation date match an existing task, skip it.
if (creationDate != null && taskName != null) {
task = taskController.fetchTaskForXml(taskName, creationDate);
}
if (task != null) {
// Skip this task.
skipCount++;
setProgressMessage(context.getString(R.string.import_progress_skip, taskCount));
// Set currentTask to null so we skip its alerts/syncs/tags, too.
return null;
}
// Else, make a new task model and add away.
task = new TaskModelForXml();
int numAttributes = xpp.getAttributeCount();
for (int i = 0; i < numAttributes; i++) {
String fieldName = xpp.getAttributeName(i);
String fieldValue = xpp.getAttributeValue(i);
task.setField(fieldName, fieldValue);
}
// Save the task to the database.
taskController.saveTask(task, false);
importCount++;
setProgressMessage(context.getString(R.string.import_progress_add, taskCount));
return task;
}
private void showSummary() { private void showSummary() {
AlertDialog.Builder builder = new AlertDialog.Builder(context); AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.import_summary_title); builder.setTitle(R.string.import_summary_title);
@ -257,44 +174,248 @@ public class TasksXmlImporter {
builder.show(); builder.show();
} }
public void setTaskController(TaskController taskController) { // --- importers
this.taskController = taskController;
}
public void setTagController(TagController tagController) { private class Astrid3TaskImporter {
this.tagController = tagController; @SuppressWarnings("unused")
} private final XmlPullParser xpp;
public void setAlertController(AlertController alertController) { public Astrid3TaskImporter(XmlPullParser xpp) {
this.alertController = alertController; this.xpp = xpp;
// TODO
}
} }
public void setSyncDataController(SyncDataController syncDataController) { private class Astrid2TaskImporter {
this.syncDataController = syncDataController; private final XmlPullParser xpp;
} private Task currentTask = null;
private String upgradeNotes = null;
private boolean syncOnComplete = false;
public void setContext(Context ctx) { private final ArrayList<String> tags = new ArrayList<String>();
setTaskController(new TaskController(ctx));
setTagController(new TagController(ctx));
setAlertController(new AlertController(ctx));
setSyncDataController(new SyncDataController(ctx));
}
private void closeControllers() { public Astrid2TaskImporter(XmlPullParser xpp) throws XmlPullParserException, IOException {
taskController.close(); this.xpp = xpp;
tagController.close();
alertController.close();
syncDataController.close();
}
private void openControllers() { while (xpp.next() != XmlPullParser.END_DOCUMENT) {
taskController.open(); String tag = xpp.getName();
tagController.open(); if (tag == null || xpp.getEventType() == XmlPullParser.END_TAG) {
alertController.open(); saveTags();
syncDataController.open(); continue;
} }
public void setInput(String input) { if (tag.equals(BackupConstants.TASK_TAG)) {
this.input = input; // Parse <task ... >
currentTask = parseTask();
} else if (currentTask != null) {
// These tags all require that we have a task to associate
// them with.
if (tag.equals(BackupConstants.TAG_TAG)) {
// Process <tag ... >
parseTag();
} else if (tag.equals(BackupConstants.ALERT_TAG)) {
// Process <alert ... >
parseAlert();
} else if (tag.equals(BackupConstants.SYNC_TAG)) {
// Process <sync ... >
parseSync();
}
}
}
}
private boolean parseSync() {
String service = xpp.getAttributeValue(null, BackupConstants.SYNC_ATTR_SERVICE);
String remoteId = xpp.getAttributeValue(null, BackupConstants.SYNC_ATTR_REMOTE_ID);
if (service != null && remoteId != null) {
StringTokenizer strtok = new StringTokenizer(remoteId, "|"); //$NON-NLS-1$
String taskId = strtok.nextToken();
String taskSeriesId = strtok.nextToken();
String listId = strtok.nextToken();
Metadata metadata = new Metadata();
metadata.setValue(Metadata.TASK, currentTask.getId());
metadata.setValue(MilkTask.LIST_ID, Long.parseLong(listId));
metadata.setValue(MilkTask.TASK_SERIES_ID, Long.parseLong(taskSeriesId));
metadata.setValue(MilkTask.TASK_ID, Long.parseLong(taskId));
metadata.setValue(MilkTask.REPEATING, syncOnComplete ? 1 : 0);
metadataService.save(metadata);
return true;
}
return false;
}
private boolean parseAlert() {
// drop it
return false;
}
private boolean parseTag() {
String tagName = xpp.getAttributeValue(null, BackupConstants.TAG_ATTR_NAME);
tags.add(tagName);
return true;
}
private void saveTags() {
if(currentTask != null && tags.size() > 0) {
TagService.getInstance().synchronizeTags(currentTask.getId(), tags);
}
tags.clear();
}
@SuppressWarnings("nls")
private Task parseTask() {
taskCount++;
setProgressMessage(context.getString(R.string.import_progress_read,
taskCount));
String taskName = xpp.getAttributeValue(null, LegacyTaskModel.NAME);
Date creationDate = null;
String createdString = xpp.getAttributeValue(null,
LegacyTaskModel.CREATION_DATE);
if (createdString != null) {
creationDate = BackupDateUtilities.getDateFromIso8601String(createdString);
}
// if we don't have task name or creation date, skip
if (creationDate == null || taskName == null) {
skipCount++;
return null;
}
// if the task's name and creation date match an existing task, skip
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.ID).where(TaskCriteria.createdOn(creationDate.getTime())));
try {
if(cursor.getCount() > 0) {
skipCount++;
return null;
}
} finally {
cursor.close();
}
// else, make a new task model and add away.
Task task = new Task();
int numAttributes = xpp.getAttributeCount();
for (int i = 0; i < numAttributes; i++) {
String fieldName = xpp.getAttributeName(i);
String fieldValue = xpp.getAttributeValue(i);
if(!setTaskField(task, fieldName, fieldValue)) {
Log.i("astrid-xml-import", "Task: " + taskName + ": Unknown field '" +
fieldName + "' with value '" + fieldValue + "' disregarded.");
}
}
if(upgradeNotes != null) {
if(task.containsValue(Task.NOTES) && task.getValue(Task.NOTES).length() > 0)
task.setValue(Task.NOTES, task.getValue(Task.NOTES) + "\n" + upgradeNotes);
else
task.setValue(Task.NOTES, upgradeNotes);
upgradeNotes = null;
}
// Save the task to the database.
taskService.save(task, false);
importCount++;
setProgressMessage(context.getString(R.string.import_progress_add, taskCount));
return task;
}
/** helper method to set field on a task */
@SuppressWarnings("nls")
private final boolean setTaskField(Task task, String field, String value) {
if(field.equals(LegacyTaskModel.NAME)) {
task.setValue(Task.TITLE, value);
}
else if(field.equals(LegacyTaskModel.NOTES)) {
task.setValue(Task.NOTES, value);
}
else if(field.equals(LegacyTaskModel.PROGRESS_PERCENTAGE)) {
// ignore
}
else if(field.equals(LegacyTaskModel.IMPORTANCE)) {
task.setValue(Task.IMPORTANCE, LegacyImportance.valueOf(value).ordinal());
}
else if(field.equals(LegacyTaskModel.ESTIMATED_SECONDS)) {
task.setValue(Task.ESTIMATED_SECONDS, Integer.parseInt(value));
}
else if(field.equals(LegacyTaskModel.ELAPSED_SECONDS)) {
task.setValue(Task.ELAPSED_SECONDS, Integer.parseInt(value));
}
else if(field.equals(LegacyTaskModel.TIMER_START)) {
task.setValue(Task.TIMER_START,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.DEFINITE_DUE_DATE)) {
String preferred = xpp.getAttributeValue(null, LegacyTaskModel.PREFERRED_DUE_DATE);
if(preferred != null) {
Date preferredDate = BackupDateUtilities.getDateFromIso8601String(value);
upgradeNotes = "Goal Deadline: " +
DateUtilities.getFormattedDate(ContextManager.getContext(),
preferredDate);
}
task.setValue(Task.DUE_DATE,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.PREFERRED_DUE_DATE)) {
String definite = xpp.getAttributeValue(null, LegacyTaskModel.DEFINITE_DUE_DATE);
if(definite != null)
; // handled above
else
task.setValue(Task.DUE_DATE,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.HIDDEN_UNTIL)) {
task.setValue(Task.HIDE_UNTIL,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.BLOCKING_ON)) {
// ignore
}
else if(field.equals(LegacyTaskModel.POSTPONE_COUNT)) {
task.setValue(Task.POSTPONE_COUNT, Integer.parseInt(value));
}
else if(field.equals(LegacyTaskModel.NOTIFICATIONS)) {
task.setValue(Task.REMINDER_PERIOD, Integer.parseInt(value) * 1000L);
}
else if(field.equals(LegacyTaskModel.CREATION_DATE)) {
task.setValue(Task.CREATION_DATE,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.COMPLETION_DATE)) {
task.setValue(Task.COMPLETION_DATE,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals(LegacyTaskModel.NOTIFICATION_FLAGS)) {
task.setValue(Task.REMINDER_FLAGS, Integer.parseInt(value));
}
else if(field.equals(LegacyTaskModel.LAST_NOTIFIED)) {
task.setValue(Task.REMINDER_LAST,
BackupDateUtilities.getDateFromIso8601String(value).getTime());
}
else if(field.equals("repeat_interval")) {
// handled below
}
else if(field.equals("repeat_value")) {
int repeatValue = Integer.parseInt(value);
String repeatInterval = xpp.getAttributeValue(null, "repeat_interval");
if(repeatValue > 0 && repeatInterval != null) {
LegacyRepeatInterval interval = LegacyRepeatInterval.valueOf(repeatInterval);
LegacyRepeatInfo repeatInfo = new LegacyRepeatInfo(interval, repeatValue);
RRule rrule = repeatInfo.toRRule();
task.setValue(Task.RECURRENCE, rrule.toIcal());
}
}
else if(field.equals(LegacyTaskModel.FLAGS)) {
if(Integer.parseInt(value) == LegacyTaskModel.FLAG_SYNC_ON_COMPLETE)
syncOnComplete = true;
}
else {
return false;
}
return true;
}
} }
} }

@ -2,9 +2,17 @@ package com.todoroo.astrid.core;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
/**
* Utility class for getting dependency-injected services from plugins
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class PluginServices { public final class PluginServices {
@Autowired @Autowired
@ -13,6 +21,12 @@ public final class PluginServices {
@Autowired @Autowired
Database database; Database database;
@Autowired
ExceptionService exceptionService;
@Autowired
MetadataService metadataService;
private static PluginServices instance; private static PluginServices instance;
private PluginServices() { private PluginServices() {
@ -30,4 +44,13 @@ public final class PluginServices {
return getInstance().taskService; return getInstance().taskService;
} }
public static ExceptionService getExceptionService() {
return getInstance().exceptionService;
}
public static MetadataService getMetadataService() {
getInstance().database.openForWriting();
return getInstance().metadataService;
}
} }

@ -34,6 +34,11 @@
<!-- backup activity export button --> <!-- backup activity export button -->
<string name="backup_BAc_export">Export Tasks</string> <string name="backup_BAc_export">Export Tasks</string>
<!-- ============================================== Importer / Exporter == -->
<!-- Message displayed when error occurs -->
<string name="backup_TXI_error">Import Error</string>
<string name="export_toast">Backed Up %s to %s.</string> <string name="export_toast">Backed Up %s to %s.</string>
<string name="import_summary_title">Restore Summary</string> <string name="import_summary_title">Restore Summary</string>
<string name="import_summary_message"> <string name="import_summary_message">

@ -98,6 +98,11 @@ public class TaskDao extends GenericDao<Task> {
return Criterion.or(Task.TITLE.isNull(), Task.TITLE.eq("")); return Criterion.or(Task.TITLE.isNull(), Task.TITLE.eq(""));
} }
/** @return tasks with this exact creation date */
public static Criterion createdOn(long date) {
return Criterion.or(Task.CREATION_DATE.eq(date));
}
} }
// --- custom operations // --- custom operations

@ -0,0 +1,10 @@
package com.todoroo.astrid.legacy;
/** Legacy alert class */
@SuppressWarnings("nls")
abstract public class LegacyAlertModel {
public static final String TASK = "task";
public static final String DATE = "date";
}

@ -0,0 +1,32 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy;
public enum LegacyImportance {
// MOST IMPORTANT
LEVEL_1,
LEVEL_2,
LEVEL_3,
LEVEL_4;
// LEAST IMPORTANT
}

@ -0,0 +1,63 @@
package com.todoroo.astrid.legacy;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule;
/** Legacy repeatInfo class */
public class LegacyRepeatInfo {
public static final int REPEAT_VALUE_OFFSET = 3;
/** Legacy repeat interval class */
public enum LegacyRepeatInterval {
DAYS,
WEEKS,
MONTHS,
HOURS
}
private final LegacyRepeatInterval interval;
private final int value;
public LegacyRepeatInfo(LegacyRepeatInterval repeatInterval, int value) {
this.interval = repeatInterval;
this.value = value;
}
public LegacyRepeatInterval getInterval() {
return interval;
}
public int getValue() {
return value;
}
public static LegacyRepeatInfo fromSingleField(int repeat) {
if(repeat == 0)
return null;
int value = repeat >> REPEAT_VALUE_OFFSET;
LegacyRepeatInterval interval = LegacyRepeatInterval.values()
[repeat - (value << REPEAT_VALUE_OFFSET)];
return new LegacyRepeatInfo(interval, value);
}
public RRule toRRule() {
RRule rrule = new RRule();
rrule.setInterval(getValue());
switch(getInterval()) {
case DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case WEEKS:
rrule.setFreq(Frequency.WEEKLY);
break;
case MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case HOURS:
rrule.setFreq(Frequency.HOURLY);
}
return rrule;
}
}

@ -0,0 +1,32 @@
/*
* ASTRID: Android's Simple Task Recording Dashboard
*
* Copyright (c) 2009 Tim Su
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.todoroo.astrid.legacy;
import com.timsu.astrid.data.AbstractModel;
/** Legacy tag model */
@SuppressWarnings("nls")
abstract public class LegacyTagModel extends AbstractModel {
public static final String NAME = "name";
public static final String NOTES = "notes";
public static final String CREATION_DATE = "creationDate";
}

@ -0,0 +1,37 @@
package com.todoroo.astrid.legacy;
/** Legacy task class */
@SuppressWarnings("nls")
abstract public class LegacyTaskModel {
public static final String NAME = "name";
public static final String NOTES = "notes";
public static final String PROGRESS_PERCENTAGE = "progressPercentage";
public static final String IMPORTANCE = "importance";
public static final String ESTIMATED_SECONDS = "estimatedSeconds";
public static final String ELAPSED_SECONDS = "elapsedSeconds";
public static final String TIMER_START = "timerStart";
public static final String DEFINITE_DUE_DATE = "definiteDueDate";
public static final String PREFERRED_DUE_DATE = "preferredDueDate";
public static final String HIDDEN_UNTIL = "hiddenUntil";
public static final String POSTPONE_COUNT = "postponeCount";
public static final String NOTIFICATIONS = "notifications";
public static final String NOTIFICATION_FLAGS = "notificationFlags";
public static final String LAST_NOTIFIED = "lastNotified";
public static final String REPEAT = "repeat";
public static final String CREATION_DATE = "creationDate";
public static final String COMPLETION_DATE = "completionDate";
public static final String CALENDAR_URI = "calendarUri";
public static final String FLAGS = "flags";
public static final String BLOCKING_ON = "blockingOn";
// notification flags
public static final int NOTIFY_BEFORE_DEADLINE = 1 << 0;
public static final int NOTIFY_AT_DEADLINE = 1 << 1;
public static final int NOTIFY_AFTER_DEADLINE = 1 << 2;
public static final int NOTIFY_NONSTOP = 1 << 3;
// other flags
public static final int FLAG_SYNC_ON_COMPLETE = 1 << 0;
}

@ -18,7 +18,6 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.google.ical.values.Frequency;
import com.google.ical.values.RRule; import com.google.ical.values.RRule;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.utilities.LegacyTasksXmlExporter; import com.timsu.astrid.utilities.LegacyTasksXmlExporter;
@ -36,6 +35,9 @@ import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.legacy.LegacyAlertModel;
import com.todoroo.astrid.legacy.LegacyRepeatInfo;
import com.todoroo.astrid.legacy.LegacyTaskModel;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.rmilk.data.MilkTask; import com.todoroo.astrid.rmilk.data.MilkTask;
@ -128,24 +130,24 @@ public class Astrid2To3UpgradeHelper {
HashMap<String, Property<?>> propertyMap = HashMap<String, Property<?>> propertyMap =
new HashMap<String, Property<?>>(); new HashMap<String, Property<?>>();
propertyMap.put("_id", Task.ID); //$NON-NLS-1$ propertyMap.put("_id", Task.ID); //$NON-NLS-1$
propertyMap.put(AbstractTaskModel.NAME, Task.TITLE); propertyMap.put(LegacyTaskModel.NAME, Task.TITLE);
propertyMap.put(AbstractTaskModel.NOTES, Task.NOTES); propertyMap.put(LegacyTaskModel.NOTES, Task.NOTES);
// (don't update progress percentage, we don't use this anymore) // (don't update progress percentage, we don't use this anymore)
propertyMap.put(AbstractTaskModel.IMPORTANCE, Task.IMPORTANCE); propertyMap.put(LegacyTaskModel.IMPORTANCE, Task.IMPORTANCE);
propertyMap.put(AbstractTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS); propertyMap.put(LegacyTaskModel.ESTIMATED_SECONDS, Task.ESTIMATED_SECONDS);
propertyMap.put(AbstractTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS); propertyMap.put(LegacyTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
propertyMap.put(AbstractTaskModel.TIMER_START, Task.TIMER_START); propertyMap.put(LegacyTaskModel.TIMER_START, Task.TIMER_START);
propertyMap.put(AbstractTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE); propertyMap.put(LegacyTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
propertyMap.put(AbstractTaskModel.HIDDEN_UNTIL, Task.HIDE_UNTIL); propertyMap.put(LegacyTaskModel.HIDDEN_UNTIL, Task.HIDE_UNTIL);
propertyMap.put(AbstractTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT); propertyMap.put(LegacyTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD); propertyMap.put(LegacyTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD);
propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS); propertyMap.put(LegacyTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS);
propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST); propertyMap.put(LegacyTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST);
propertyMap.put(AbstractTaskModel.REPEAT, Task.RECURRENCE); propertyMap.put(LegacyTaskModel.REPEAT, Task.RECURRENCE);
propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE); propertyMap.put(LegacyTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE); propertyMap.put(LegacyTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
propertyMap.put(AbstractTaskModel.CALENDAR_URI, Task.CALENDAR_URI); propertyMap.put(LegacyTaskModel.CALENDAR_URI, Task.CALENDAR_URI);
propertyMap.put(AbstractTaskModel.FLAGS, Task.FLAGS); propertyMap.put(LegacyTaskModel.FLAGS, Task.FLAGS);
upgradeTable(context, tasksTable, upgradeTable(context, tasksTable,
propertyMap, new Task(), taskDao); propertyMap, new Task(), taskDao);
@ -157,8 +159,8 @@ public class Astrid2To3UpgradeHelper {
alarmsDatabase.openForWriting(); alarmsDatabase.openForWriting();
propertyMap.clear(); propertyMap.clear();
propertyMap.put("_id", Alarm.ID); //$NON-NLS-1$ propertyMap.put("_id", Alarm.ID); //$NON-NLS-1$
propertyMap.put(Alert.TASK, Alarm.TASK); propertyMap.put(LegacyAlertModel.TASK, Alarm.TASK);
propertyMap.put(Alert.DATE, Alarm.TIME); propertyMap.put(LegacyAlertModel.DATE, Alarm.TIME);
upgradeTable(context, alertsTable, propertyMap, new Alarm(), upgradeTable(context, alertsTable, propertyMap, new Alarm(),
alarmsDatabase.getDao()); alarmsDatabase.getDao());
alarmsDatabase.close(); alarmsDatabase.close();
@ -235,7 +237,7 @@ public class Astrid2To3UpgradeHelper {
// special handling for due date // special handling for due date
if(property == Task.DUE_DATE) { if(property == Task.DUE_DATE) {
long preferredDueDate = data.cursor.getLong(data.cursor.getColumnIndex(AbstractTaskModel.PREFERRED_DUE_DATE)); long preferredDueDate = data.cursor.getLong(data.cursor.getColumnIndex(LegacyTaskModel.PREFERRED_DUE_DATE));
if(value == 0) if(value == 0)
value = preferredDueDate; value = preferredDueDate;
else if(preferredDueDate != 0) { else if(preferredDueDate != 0) {
@ -262,25 +264,11 @@ public class Astrid2To3UpgradeHelper {
String value = data.cursor.getString(data.columnIndex); String value = data.cursor.getString(data.columnIndex);
if(property == Task.RECURRENCE) { if(property == Task.RECURRENCE) {
RepeatInfo repeatInfo = RepeatInfo.fromSingleField(data.cursor.getInt(data.columnIndex)); LegacyRepeatInfo repeatInfo = LegacyRepeatInfo.fromSingleField(data.cursor.getInt(data.columnIndex));
if(repeatInfo == null) if(repeatInfo == null)
data.model.setValue(property, ""); data.model.setValue(property, "");
else { else {
RRule rrule = new RRule(); RRule rrule = repeatInfo.toRRule();
rrule.setInterval(repeatInfo.getValue());
switch(repeatInfo.getInterval()) {
case DAYS:
rrule.setFreq(Frequency.DAILY);
break;
case WEEKS:
rrule.setFreq(Frequency.WEEKLY);
break;
case MONTHS:
rrule.setFreq(Frequency.MONTHLY);
break;
case HOURS:
rrule.setFreq(Frequency.HOURLY);
}
data.model.setValue(property, rrule.toIcal()); data.model.setValue(property, rrule.toIcal());
} }
} else { } else {
@ -334,6 +322,7 @@ public class Astrid2To3UpgradeHelper {
container.model.getValue(Task.NOTES) + "\n\n" + container.model.getValue(Task.NOTES) + "\n\n" +
container.upgradeNotes); container.upgradeNotes);
} }
container.upgradeNotes = null;
} }
dao.createNew(container.model); dao.createNew(container.model);
} }
@ -440,6 +429,7 @@ public class Astrid2To3UpgradeHelper {
metadata.setValue(MilkTask.LIST_ID, Long.parseLong(listId)); metadata.setValue(MilkTask.LIST_ID, Long.parseLong(listId));
metadata.setValue(MilkTask.TASK_SERIES_ID, Long.parseLong(taskSeriesId)); metadata.setValue(MilkTask.TASK_SERIES_ID, Long.parseLong(taskSeriesId));
metadata.setValue(MilkTask.TASK_ID, Long.parseLong(taskId)); metadata.setValue(MilkTask.TASK_ID, Long.parseLong(taskId));
metadata.setValue(MilkTask.REPEATING, 0); // not accurate, but not important
metadataDao.createNew(metadata); metadataDao.createNew(metadata);
metadata.clearValue(Metadata.ID); metadata.clearValue(Metadata.ID);
} }
@ -448,76 +438,4 @@ public class Astrid2To3UpgradeHelper {
} }
} }
// --- legacy data structures
/** Legacy repeatInfo class */
private static class RepeatInfo {
public static final int REPEAT_VALUE_OFFSET = 3;
private final RepeatInterval interval;
private final int value;
public RepeatInfo(RepeatInterval repeatInterval, int value) {
this.interval = repeatInterval;
this.value = value;
}
public RepeatInterval getInterval() {
return interval;
}
public int getValue() {
return value;
}
public static RepeatInfo fromSingleField(int repeat) {
if(repeat == 0)
return null;
int value = repeat >> REPEAT_VALUE_OFFSET;
RepeatInterval interval = RepeatInterval.values()
[repeat - (value << REPEAT_VALUE_OFFSET)];
return new RepeatInfo(interval, value);
}
}
/** Legacy repeat interval class */
private enum RepeatInterval {
DAYS,
WEEKS,
MONTHS,
HOURS
}
/** Legacy task class */
@SuppressWarnings("nls")
private static abstract class AbstractTaskModel {
public static final String NAME = "name";
public static final String NOTES = "notes";
public static final String IMPORTANCE = "importance";
public static final String ESTIMATED_SECONDS = "estimatedSeconds";
public static final String ELAPSED_SECONDS = "elapsedSeconds";
public static final String TIMER_START = "timerStart";
public static final String DEFINITE_DUE_DATE = "definiteDueDate";
public static final String PREFERRED_DUE_DATE = "preferredDueDate";
public static final String HIDDEN_UNTIL = "hiddenUntil";
public static final String POSTPONE_COUNT = "postponeCount";
public static final String NOTIFICATIONS = "notifications";
public static final String NOTIFICATION_FLAGS = "notificationFlags";
public static final String LAST_NOTIFIED = "lastNotified";
public static final String REPEAT = "repeat";
public static final String CREATION_DATE = "creationDate";
public static final String COMPLETION_DATE = "completionDate";
public static final String CALENDAR_URI = "calendarUri";
public static final String FLAGS = "flags";
}
/** Legacy alert class */
@SuppressWarnings("nls")
private static class Alert {
static final String TASK = "task";
static final String DATE = "date";
}
} }

Loading…
Cancel
Save