diff --git a/astrid/.classpath b/astrid/.classpath
index 900abe288..0377f788f 100644
--- a/astrid/.classpath
+++ b/astrid/.classpath
@@ -4,7 +4,7 @@
-
+
diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index b88ec4cf6..bdb8866f7 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -189,6 +189,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java
new file mode 100644
index 000000000..b2d2fea0b
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupActivity.java
@@ -0,0 +1,19 @@
+package com.todoroo.astrid.backup;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.timsu.astrid.R;
+
+public class BackupActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.backup_activity);
+ setTitle(R.string.backup_BAc_title);
+
+
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java
new file mode 100644
index 000000000..bed168a57
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupConstants.java
@@ -0,0 +1,277 @@
+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;
+
+public class BackupConstants {
+
+ private TaskController taskController;
+ 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 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 BackupConstants(boolean isService) {
+ 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);
+ }
+ }
+
+ 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);
+ 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();
+ }
+ }
+
+ 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) {
+ 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));
+ }
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java b/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java
index 36d48f794..17a04eacf 100644
--- a/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/backup/BackupService.java
@@ -11,6 +11,7 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.util.Log;
/**
* Inspired heavily by SynchronizationService
@@ -129,7 +130,8 @@ public class BackupService extends Service {
}
});
for(int i = DAYS_TO_KEEP_BACKUP; i < files.length; i++) {
- files[i].delete();
+ if(!files[i].delete())
+ Log.i("astrid-backups", "Unable to delete: " + files[i]); //$NON-NLS-1$ //$NON-NLS-2$
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java
index a66d938f5..d7e6843f3 100644
--- a/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java
+++ b/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
+import android.util.Log;
import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants;
@@ -36,7 +37,7 @@ public class GCalTaskCompleteListener extends BroadcastReceiver {
task.getValue(Task.TITLE)));
cr.update(Uri.parse(calendarUri), values, null, null);
} catch (Exception e) {
- // do nothing.
+ Log.d("astrid-gcal", "Error updating calendar entry", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
diff --git a/astrid/res/drawable/ic_menu_archive.png b/astrid/res/drawable/ic_menu_archive.png
new file mode 100644
index 000000000..6c1250b45
Binary files /dev/null and b/astrid/res/drawable/ic_menu_archive.png differ
diff --git a/astrid/res/layout/backup_activity.xml b/astrid/res/layout/backup_activity.xml
new file mode 100644
index 000000000..380a2630c
--- /dev/null
+++ b/astrid/res/layout/backup_activity.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/values/strings-backup.xml b/astrid/res/values/strings-backup.xml
index d4b175be6..da2310bc3 100644
--- a/astrid/res/values/strings-backup.xml
+++ b/astrid/res/values/strings-backup.xml
@@ -2,21 +2,37 @@
-
+
+
+
- Automatic Backups
+ Automatic Backups
-
- Perform daily backups to sdcard.
+
+ Perform daily backups to sdcard.
-
- Last backup failed: %s
+
+ Last backup failed: %s
-
- Last backup failed, could not read SD card
+
+ Last backup failed, could not read SD card
-
- Latest backup was on %s
+
+ Latest backup was on %s
+
+
+
+
+ Backups
+
+
+ Manage Your Backups
+
+
+ Import Tasks
+
+
+ Export Tasks
Backed Up %s to %s.
Restore Summary
diff --git a/astrid/res/values/styles-3.0.xml b/astrid/res/values/styles.xml
similarity index 100%
rename from astrid/res/values/styles-3.0.xml
rename to astrid/res/values/styles.xml
diff --git a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
index ee272b9ab..30d73c62f 100644
--- a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
@@ -27,8 +27,9 @@ import com.todoroo.astrid.model.Task;
public class MetadataDao extends GenericDao {
@Autowired
- Database database;
+ private Database database;
+ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UR_UNINIT_READ")
public MetadataDao() {
super(Metadata.class);
DependencyInjectionService.getInstance().inject(this);
diff --git a/astrid/src/com/todoroo/astrid/dao/TaskDao.java b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
index 7f9646cd6..f4c68ec78 100644
--- a/astrid/src/com/todoroo/astrid/dao/TaskDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
@@ -14,7 +14,6 @@ import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
-import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.utility.DateUtilities;
@@ -33,14 +32,12 @@ import com.todoroo.astrid.utility.Preferences;
public class TaskDao extends GenericDao {
@Autowired
- MetadataDao metadataDao;
+ private MetadataDao metadataDao;
@Autowired
- Database database;
-
- @Autowired
- ExceptionService exceptionService;
+ private Database database;
+ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UR_UNINIT_READ")
public TaskDao() {
super(Task.class);
DependencyInjectionService.getInstance().inject(this);