diff --git a/astrid/.classpath b/astrid/.classpath
index ec4fdbf6a..0377f788f 100644
--- a/astrid/.classpath
+++ b/astrid/.classpath
@@ -4,7 +4,7 @@
-
+
diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index bdb8866f7..583884b4f 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -132,13 +132,13 @@
-
+
-
+
diff --git a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
index d5c296012..7bba4e675 100644
--- a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
+++ b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
@@ -154,7 +154,7 @@ public abstract class AbstractModel implements Parcelable {
if(setValues != null && setValues.containsKey(property.name))
value = setValues.get(property.name);
- else if(values != null && values.containsKey(property.name) && (values.get(property.name) != null))
+ else if(values != null && values.containsKey(property.name))
value = values.get(property.name);
else if(getDefaultValues().containsKey(property.name))
@@ -169,8 +169,10 @@ public abstract class AbstractModel implements Parcelable {
return (TYPE) Long.valueOf((String)value);
else if(value instanceof String && property instanceof IntegerProperty)
return (TYPE) Integer.valueOf((String)value);
- if(value instanceof String && property instanceof DoubleProperty)
+ else if(value instanceof String && property instanceof DoubleProperty)
return (TYPE) Double.valueOf((String)value);
+ else if(value instanceof Integer && property instanceof LongProperty)
+ return (TYPE) Long.valueOf(((Number)value).longValue());
return (TYPE) value;
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java
index 0f6ef3d4c..dff0637fc 100644
--- a/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java
@@ -51,9 +51,8 @@ public class BackupActivity extends Activity {
}
private void exportTasks() {
- /*TasksXmlExporter exporter = new TasksXmlExporter(false);
- exporter.setContext(getParent());
- exporter.exportTasks();*/
+ TasksXmlExporter.exportTasks(this, false);
+ finish();
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java
index 63d5638a7..c5e344d49 100644
--- a/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java
@@ -17,7 +17,9 @@ public class BackupConstants {
// Do NOT edit the constants in this file! You will break compatibility with old backups
- /** Astrid tag (contains everything else) */
+ // --- format 2
+
+ /** Tag containing Astrid backup data */
public static final String ASTRID_TAG = "astrid";
/** Attribute indicating application version */
@@ -26,7 +28,11 @@ public class BackupConstants {
/** Attribute indicating backup file format */
public static final String ASTRID_ATTR_FORMAT = "format";
+ /** Tag containing a task */
public static final String TASK_TAG = "task";
+
+ // --- format 1
+
public static final String TAG_TAG = "tag";
public static final String TAG_ATTR_NAME = "name";
public static final String ALERT_TAG = "alert";
@@ -34,12 +40,19 @@ public class BackupConstants {
public static final String SYNC_TAG = "sync";
public static final String SYNC_ATTR_SERVICE = "service";
public static final String SYNC_ATTR_REMOTE_ID = "remote_id";
+
+ // --- general
+
public static final String XML_ENCODING = "utf-8";
public static final String ASTRID_DIR = "/astrid";
+
public static final String EXPORT_FILE_NAME = "user.%s.xml";
+
public static final String BACKUP_FILE_NAME = "auto.%s.xml";
+ // --- methods
+
/**
* @return export directory for tasks, or null if no SD card
*/
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/TasksXmlExporter.java b/astrid/plugin-src/com/todoroo/astrid/backup/TasksXmlExporter.java
index f0830ebf4..9b8881131 100644
--- a/astrid/plugin-src/com/todoroo/astrid/backup/TasksXmlExporter.java
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/TasksXmlExporter.java
@@ -3,265 +3,256 @@ package com.todoroo.astrid.backup;
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.util.Log;
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;
+import com.todoroo.andlib.data.AbstractModel;
+import com.todoroo.andlib.data.Property;
+import com.todoroo.andlib.data.Property.PropertyVisitor;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.sql.Order;
+import com.todoroo.andlib.sql.Query;
+import com.todoroo.astrid.core.PluginServices;
+import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
+import com.todoroo.astrid.model.Metadata;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.MetadataService;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.utility.Preferences;
public class TasksXmlExporter {
- private TaskController taskController;
- private TagController tagController;
- private AlertController alertController;
- private SyncDataController syncDataController;
- private Context ctx;
- private String output;
+ // --- public interface
+
+ /**
+ * Import tasks from the given file
+ *
+ * @param input
+ * @param runAfterImport
+ */
+ public static void exportTasks(Context context, boolean isService) {
+ new TasksXmlExporter(context, isService);
+ }
+
+ // --- implementation
+
+ private static final int FORMAT = 2;
+
+ private final Context context;
private final boolean isService;
private int exportCount;
private XmlSerializer xml;
- private HashMap tagMap;
-
- public static final String ASTRID_TAG = "astrid";
- public static final String ASTRID_ATTR_VERSION = "version";
- public static final String TASK_TAG = "task";
- public static final String TAG_TAG = "tag";
- public static final String TAG_ATTR_NAME = "name";
- public static final String ALERT_TAG = "alert";
- public static final String ALERT_ATTR_DATE = "date";
- public static final String SYNC_TAG = "sync";
- public static final String SYNC_ATTR_SERVICE = "service";
- public static final String SYNC_ATTR_REMOTE_ID = "remote_id";
- 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 TasksXmlExporter(boolean isService) {
+ private final TaskService taskService = PluginServices.getTaskService();
+ private final MetadataService metadataService = PluginServices.getMetadataService();
+ private final ExceptionService exceptionService = PluginServices.getExceptionService();
+
+ private TasksXmlExporter(Context context, boolean isService) {
+ this.context = context;
this.isService = isService;
this.exportCount = 0;
- }
-
- private void initTagMap() {
- tagMap = tagController.getAllTagsAsMap();
- }
- private void serializeTags(TaskIdentifier task)
- throws IOException {
- LinkedList 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);
+ try {
+ String output = setupFile(BackupConstants.getExportDirectory());
+ doTasksExport(output);
+ } catch (Exception e) {
+ if(!isService)
+ displayErrorToast(e);
+ exceptionService.reportError("backup-exception", e); //$NON-NLS-1$
+ // TODO record last backup error
}
}
- private void serializeSyncMappings(TaskIdentifier task)
- throws IOException {
- HashSet syncMappings = syncDataController.getSyncMappings(task);
- 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 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 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);
+ @SuppressWarnings("nls")
+ private void doTasksExport(String output) throws IOException {
+ File xmlFile = new File(output);
xmlFile.createNewFile();
FileOutputStream fos = new FileOutputStream(xmlFile);
xml = Xml.newSerializer();
- xml.setOutput(fos, XML_ENCODING);
+ 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, ASTRID_TAG);
- xml.attribute(null, ASTRID_ATTR_VERSION,
- Integer.toString(Preferences.getCurrentVersion(ctx)));
+ xml.startTag(null, BackupConstants.ASTRID_TAG);
+ xml.attribute(null, BackupConstants.ASTRID_ATTR_VERSION,
+ Integer.toString(Preferences.getCurrentVersion()));
+ xml.attribute(null, BackupConstants.ASTRID_ATTR_FORMAT,
+ Integer.toString(FORMAT));
- openControllers();
- initTagMap();
serializeTasks();
- closeControllers();
- xml.endTag(null, ASTRID_TAG);
+ xml.endTag(null, BackupConstants.ASTRID_TAG);
xml.endDocument();
xml.flush();
fos.close();
if (!isService) {
- displayToast();
+ displayToast(output);
}
}
- 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 serializeTasks() throws IOException {
+ TodorooCursor cursor = taskService.query(Query.select(
+ Task.PROPERTIES).orderBy(Order.asc(Task.ID)));
+ try {
+ Task task = new Task();
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ task.readFromCursor(cursor);
+
+ xml.startTag(null, BackupConstants.TASK_TAG);
+ serializeModel(task, Task.PROPERTIES);
+ serializeMetadata(task);
+ xml.endTag(null, BackupConstants.TASK_TAG);
+ this.exportCount++;
+ }
+ } finally {
+ cursor.close();
+ }
}
- private void displayErrorToast(String error) {
- Toast.makeText(ctx, error, Toast.LENGTH_LONG).show();
+ private void serializeMetadata(Task task) throws IOException {
+ TodorooCursor cursor = metadataService.query(Query.select(
+ Metadata.PROPERTIES).where(MetadataCriteria.byTask(task.getId())));
+ try {
+ Metadata metadata = new Metadata();
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ metadata.readFromCursor(cursor);
+
+ xml.startTag(null, BackupConstants.TAG_TAG);
+ serializeModel(metadata, Metadata.PROPERTIES);
+ xml.endTag(null, BackupConstants.TAG_TAG);
+ }
+ } finally {
+ cursor.close();
+ }
}
- private void closeControllers() {
- tagController.close();
- taskController.close();
- alertController.close();
- syncDataController.close();
+ /**
+ * Turn a model into xml attributes
+ * @param model
+ */
+ private void serializeModel(AbstractModel model, Property>[] properties) {
+ for(Property> property : properties) {
+ try {
+ Log.e("read", "reading " + property.name + " from " + model.getDatabaseValues());
+ property.accept(xmlWritingVisitor, model);
+ } catch (Exception e) {
+ Log.e("caught", "caught while reading " + property.name + " from " + model.getDatabaseValues(), e);
+ }
+ }
}
- private void openControllers() {
- taskController.open();
- tagController.open();
- alertController.open();
- syncDataController.open();
- }
+ private final XmlWritingPropertyVisitor xmlWritingVisitor = new XmlWritingPropertyVisitor();
+
+ private class XmlWritingPropertyVisitor implements PropertyVisitor {
+ @Override
+ public Void visitInteger(Property property, AbstractModel data) {
+ try {
+ xml.attribute(null, property.name, data.getValue(property).toString());
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalStateException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitLong(Property property, AbstractModel data) {
+ try {
+ xml.attribute(null, property.name, data.getValue(property).toString());
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalStateException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
- public void exportTasks(File directory) {
- if (isService && !Preferences.isBackupEnabled(ctx)) {
- // Automatic backups are disabled.
- return;
+ @Override
+ public Void visitDouble(Property property, AbstractModel data) {
+ try {
+ xml.attribute(null, property.name, data.getValue(property).toString());
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalStateException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
}
- if (setupFile(directory)) {
- Thread thread = new Thread(doBackgroundExport);
- thread.start();
+
+ @Override
+ public Void visitString(Property property, AbstractModel data) {
+ try {
+ String value = data.getValue(property);
+ if(value == null)
+ return null;
+ xml.attribute(null, property.name, value);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalStateException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
}
+
+ }
+
+ private void displayToast(String output) {
+ CharSequence text = String.format(context.getString(R.string.export_toast),
+ context.getResources().getQuantityString(R.plurals.Ntasks, exportCount,
+ exportCount), output);
+ Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+ }
+
+ private void displayErrorToast(Exception error) {
+ Toast.makeText(context, error.toString(), Toast.LENGTH_LONG).show();
}
- private boolean setupFile(File directory) {
+ /**
+ * Creates directories if necessary and returns fully qualified file
+ * @param directory
+ * @return output file name
+ * @throws IOException
+ */
+ private String setupFile(File directory) throws IOException {
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;
+ fileName = BackupConstants.BACKUP_FILE_NAME;
} else {
- fileName = EXPORT_FILE_NAME;
+ fileName = BackupConstants.EXPORT_FILE_NAME;
}
fileName = String.format(fileName, BackupDateUtilities.getDateForExport());
- setOutput(astridDir.getAbsolutePath() + "/" + fileName);
- return true;
+ return astridDir.getAbsolutePath() + File.separator + fileName;
} 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;
+ throw new IOException(context.getString(R.string.DLG_error_sdcard,
+ astridDir.getAbsolutePath()));
}
} 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;
+ throw new IOException(context.getString(R.string.DLG_error_sdcard_general));
}
}
- 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));
- }
}
diff --git a/astrid/res/layout/filter_list_activity.xml b/astrid/res/layout/filter_list_activity.xml
index ed91846a3..cc2623a60 100644
--- a/astrid/res/layout/filter_list_activity.xml
+++ b/astrid/res/layout/filter_list_activity.xml
@@ -5,13 +5,6 @@
android:layout_height="fill_parent"
android:background="@drawable/background_gradient">
-
-
-
-
+
diff --git a/samples/filters/bin/astrid-samples-filters.apk b/samples/filters/bin/astrid-samples-filters.apk
deleted file mode 100644
index 8a2c9b961..000000000
Binary files a/samples/filters/bin/astrid-samples-filters.apk and /dev/null differ
diff --git a/samples/filters/bin/classes.dex b/samples/filters/bin/classes.dex
deleted file mode 100644
index aaf3363d6..000000000
Binary files a/samples/filters/bin/classes.dex and /dev/null differ
diff --git a/samples/filters/bin/resources.ap_ b/samples/filters/bin/resources.ap_
deleted file mode 100644
index 4f170fa16..000000000
Binary files a/samples/filters/bin/resources.ap_ and /dev/null differ