diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index a13c5f2eb..84bff4f78 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -140,7 +140,6 @@
-
diff --git a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java
index 742a1bcc9..f2a8b8dcf 100644
--- a/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java
+++ b/astrid/api-src/com/todoroo/astrid/api/SynchronizationProvider.java
@@ -16,7 +16,6 @@ import android.content.Intent;
import android.content.res.Resources;
import com.timsu.astrid.R;
-import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.service.Autowired;
@@ -38,7 +37,7 @@ import com.todoroo.astrid.utility.Constants;
* @author timsu
*
*/
-public abstract class SynchronizationProvider {
+public abstract class SynchronizationProvider {
// --- abstract methods - your services should implement these
@@ -55,6 +54,13 @@ public abstract class SynchronizationProvider {
*/
abstract protected String getNotificationTitle(Context context);
+ /**
+ * Create a task on the remote server.
+ *
+ * @return task to create
+ */
+ abstract protected void create(TYPE task) throws IOException;
+
/**
* Push variables from given task to the remote server.
*
@@ -63,23 +69,23 @@ public abstract class SynchronizationProvider {
* @param remoteTask
* remote task that we merged with. may be null
*/
- abstract protected void push(Task task, Task remote) throws IOException;
+ abstract protected void push(TYPE task, TYPE remote) throws IOException;
/**
- * Create a task on the remote server.
+ * Fetch remote task. Used to re-read merged tasks
*
- * @return task to create
+ * @param task
+ * task with id's to re-read
+ * @return new Task
*/
- abstract protected void create(Task task) throws IOException;
+ abstract protected TYPE pull(TYPE task) throws IOException;
/**
- * Fetch remote task. Used to re-read merged tasks
+ * Reads a task container from a task in the database
*
* @param task
- * task with id's to re-read
- * @return new Task
*/
- abstract protected Task read(Task task) throws IOException;
+ abstract protected TYPE read(TodorooCursor task) throws IOException;
/**
* Save task. Used to save local tasks that have been updated and remote
@@ -87,7 +93,7 @@ public abstract class SynchronizationProvider {
*
* @param task
*/
- abstract protected void save(Task task) throws IOException;
+ abstract protected void save(TYPE task) throws IOException;
/**
* Finds a task in the list with the same remote identifier(s) as
@@ -95,12 +101,13 @@ public abstract class SynchronizationProvider {
*
* @return task from list if matches, null otherwise
*/
- abstract protected Task matchTask(ArrayList tasks, Task target);
+ abstract protected int matchTask(ArrayList tasks, TYPE target);
/**
* Transfer remote identifier(s) from one task to another
*/
- abstract protected void transferIdentifiers(Task source, Task destination);
+ abstract protected void transferIdentifiers(TYPE source,
+ TYPE destination);
// --- implementation
@@ -175,27 +182,26 @@ public abstract class SynchronizationProvider {
protected void synchronizeTasks(SyncData data) throws IOException {
int length;
- Task task = new Task();
Context context = ContextManager.getContext();
Resources r = context.getResources();
// create internal data structures
- HashMap remoteNewTaskNameMap = new HashMap();
+ HashMap remoteNewTaskNameMap = new HashMap();
length = data.remoteUpdated.size();
for(int i = 0; i < length; i++) {
- Task remote = data.remoteUpdated.get(i);
- if(remote.getId() != Task.NO_ID)
+ TaskContainer remote = data.remoteUpdated.get(i);
+ if(remote.task.getId() != Task.NO_ID)
continue;
- remoteNewTaskNameMap.put(remote.getValue(Task.TITLE), remote);
+ remoteNewTaskNameMap.put(remote.task.getValue(Task.TITLE), i);
}
// 1. CREATE: grab newly created tasks and create them remotely
length = data.localCreated.getCount();
for(int i = 0; i < length; i++) {
data.localCreated.moveToNext();
- task.readFromCursor(data.localCreated);
+ TaskContainer local = read(data.localCreated);
- String taskTitle = task.getValue(Task.TITLE);
+ String taskTitle = local.task.getValue(Task.TITLE);
postUpdate(context, r.getString(R.string.SyP_progress_localtx,
taskTitle));
@@ -205,39 +211,39 @@ public abstract class SynchronizationProvider {
* we create a mapping and do an update.
*/
if (remoteNewTaskNameMap.containsKey(taskTitle)) {
- Task remote = remoteNewTaskNameMap.remove(taskTitle);
- remote.setId(task.getId());
+ int remoteIndex = remoteNewTaskNameMap.remove(taskTitle);
+ TaskContainer remote = data.remoteUpdated.get(remoteIndex);
+ remote.task.setId(local.task.getId());
- transferIdentifiers(remote, task);
- push(task, remote);
+ transferIdentifiers((TYPE)remote, (TYPE)local);
+ push((TYPE)local, (TYPE)remote);
// re-read remote task after merge
- Task newRemote = read(remote);
- remote.mergeWith(newRemote.getMergedValues());
+ data.remoteUpdated.set(remoteIndex, pull((TYPE)remote));
} else {
- create(task);
+ create((TYPE)local);
}
- save(task);
+ save((TYPE)local);
}
// 2. UPDATE: for each updated local task
length = data.localUpdated.getCount();
for(int i = 0; i < length; i++) {
data.localUpdated.moveToNext();
- task.readFromCursor(data.localUpdated);
+ TaskContainer local = read(data.localUpdated);
postUpdate(context, r.getString(R.string.SyP_progress_localtx,
- task.getValue(Task.TITLE)));
+ local.task.getValue(Task.TITLE)));
// if there is a conflict, merge
- Task remote = matchTask(data.remoteUpdated, task);
- if(remote != null) {
- push(task, remote);
+ int remoteIndex = matchTask((ArrayList)data.remoteUpdated, (TYPE)local);
+ if(remoteIndex != -1) {
+ TaskContainer remote = data.remoteUpdated.get(remoteIndex);
+ push((TYPE)local, (TYPE)remote);
// re-read remote task after merge
- Task newRemote = read(remote);
- remote.mergeWith(newRemote.getMergedValues());
+ data.remoteUpdated.set(remoteIndex, pull((TYPE)remote));
} else {
- push(task, null);
+ push((TYPE)local, null);
}
}
@@ -248,11 +254,11 @@ public abstract class SynchronizationProvider {
// the wire, the new version and the completed old version. The new
// version would get merged, then completed, if done in the wrong order.
- Collections.sort(data.remoteUpdated, new Comparator() {
+ Collections.sort(data.remoteUpdated, new Comparator() {
private static final int SENTINEL = -2;
- private final int check(Task o1, Task o2, LongProperty property) {
- long o1Property = o1.getValue(property);
- long o2Property = o2.getValue(property);
+ private final int check(TaskContainer o1, TaskContainer o2, LongProperty property) {
+ long o1Property = o1.task.getValue(property);
+ long o2Property = o2.task.getValue(property);
if(o1Property != 0 && o2Property != 0)
return 0;
else if(o1Property != 0)
@@ -261,7 +267,7 @@ public abstract class SynchronizationProvider {
return 1;
return SENTINEL;
}
- public int compare(Task o1, Task o2) {
+ public int compare(TaskContainer o1, TaskContainer o2) {
int comparison = check(o1, o2, Task.DELETION_DATE);
if(comparison != SENTINEL)
return comparison;
@@ -274,11 +280,11 @@ public abstract class SynchronizationProvider {
length = data.remoteUpdated.size();
for(int i = 0; i < length; i++) {
- task = data.remoteUpdated.get(i);
+ TaskContainer remote = data.remoteUpdated.get(i);
postUpdate(context, r.getString(R.string.SyP_progress_remotetx,
- task.getValue(Task.TITLE)));
+ remote.task.getValue(Task.TITLE)));
- save(task);
+ save((TYPE)remote);
}
}
@@ -286,19 +292,15 @@ public abstract class SynchronizationProvider {
/** data structure builder */
protected static class SyncData {
-
- public final Property>[] properties;
- public final ArrayList remoteUpdated;
+ public final ArrayList remoteUpdated;
public final TodorooCursor localCreated;
public final TodorooCursor localUpdated;
- public SyncData(Property>[] properties,
- ArrayList remoteUpdated,
+ public SyncData(ArrayList remoteUpdated,
TodorooCursor localCreated,
TodorooCursor localUpdated) {
super();
- this.properties = properties;
this.remoteUpdated = remoteUpdated;
this.localCreated = localCreated;
this.localUpdated = localUpdated;
diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java b/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java
new file mode 100644
index 000000000..03da9c61e
--- /dev/null
+++ b/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java
@@ -0,0 +1,21 @@
+package com.todoroo.astrid.api;
+
+import com.todoroo.astrid.model.Metadata;
+import com.todoroo.astrid.model.Task;
+
+/**
+ * Container class for tasks. Synchronization Providers can subclass
+ * this class if desired.
+ *
+ * @author Tim Su
+ *
+ */
+public class TaskContainer {
+ public Task task;
+ public Metadata[] metadata;
+
+ public TaskContainer(Task task, Metadata[] metadata) {
+ this.task = task;
+ this.metadata = metadata;
+ }
+}
\ No newline at end of file
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java
index d863edc2d..2d674ddb3 100644
--- a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMSyncProvider.java
@@ -18,6 +18,7 @@ import com.flurry.android.FlurryAgent;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
@@ -25,6 +26,7 @@ import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.api.SynchronizationProvider;
+import com.todoroo.astrid.api.TaskContainer;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.rmilk.MilkLoginActivity;
import com.todoroo.astrid.rmilk.Utilities;
@@ -43,7 +45,7 @@ import com.todoroo.astrid.rmilk.api.data.RtmTask.Priority;
import com.todoroo.astrid.rmilk.data.MilkDataService;
import com.todoroo.astrid.service.AstridDependencyInjector;
-public class RTMSyncProvider extends SynchronizationProvider {
+public class RTMSyncProvider extends SynchronizationProvider {
private ServiceImpl rtmService = null;
private String timeline = null;
@@ -59,6 +61,10 @@ public class RTMSyncProvider extends SynchronizationProvider {
@Autowired
protected DialogUtilities dialogUtilities;
+ /** Temporary property for storing task tags */
+ private static final StringProperty RTM_TAGS = new StringProperty(Task.TABLE,
+ "tags"); //$NON-NLS-1$
+
public RTMSyncProvider() {
super();
DependencyInjectionService.getInstance().inject(this);
@@ -131,8 +137,6 @@ public class RTMSyncProvider extends SynchronizationProvider {
authenticate(context);
}
-
-
/**
* Perform authentication with RTM. Will open the SyncBrowser if necessary
*/
@@ -228,7 +232,7 @@ public class RTMSyncProvider extends SynchronizationProvider {
dataService.setLists(lists);
// read all tasks
- ArrayList remoteChanges = new ArrayList();
+ ArrayList remoteChanges = new ArrayList();
Date lastSyncDate = new Date(Utilities.getLastSyncDate());
boolean shouldSyncIndividualLists = false;
String filter = null;
@@ -289,42 +293,38 @@ public class RTMSyncProvider extends SynchronizationProvider {
return context.getString(R.string.rmilk_notification_title);
}
+ // all synchronized properties
+ private static final Property>[] PROPERTIES = new Property>[] {
+ Task.ID,
+ Task.TITLE,
+ Task.IMPORTANCE,
+ Task.DUE_DATE,
+ Task.CREATION_DATE,
+ Task.COMPLETION_DATE,
+ Task.DELETION_DATE,
+ };
+
/**
* Populate SyncData data structure
*/
- private SyncData populateSyncData(ArrayList remoteTasks) {
- // all synchronized properties
- Property>[] properties = new Property>[] {
- Task.ID,
- Task.TITLE,
- Task.IMPORTANCE,
- Task.DUE_DATE,
- Task.CREATION_DATE,
- Task.COMPLETION_DATE,
- Task.DELETION_DATE,
- MilkDataService.LIST_ID,
- MilkDataService.TASK_SERIES_ID,
- MilkDataService.TASK_ID,
- MilkDataService.REPEATING,
- // TODO tags
- };
-
+ private SyncData populateSyncData(ArrayList remoteTasks) {
// fetch locally created tasks
- TodorooCursor localCreated = dataService.getLocallyCreated(properties);
+ TodorooCursor localCreated = dataService.getLocallyCreated(PROPERTIES);
// fetch locally updated tasks
- TodorooCursor localUpdated = dataService.getLocallyUpdated(properties);
+ TodorooCursor localUpdated = dataService.getLocallyUpdated(PROPERTIES);
- return new SyncData(properties, remoteTasks, localCreated, localUpdated);
+ return new SyncData(remoteTasks, localCreated, localUpdated);
}
/**
* Add the tasks read from RTM to the given list
*/
- private void addTasksToList(RtmTasks tasks, ArrayList list) {
+ private void addTasksToList(RtmTasks tasks, ArrayList list) {
for (RtmTaskList taskList : tasks.getLists()) {
for (RtmTaskSeries taskSeries : taskList.getSeries()) {
- Task remoteTask = parseRemoteTask(taskSeries);
+ TaskContainer remoteTask = parseRemoteTask(taskSeries);
+ dataService.updateFromLocalCopy(task);
list.add(remoteTask);
}
}
@@ -337,29 +337,24 @@ public class RTMSyncProvider extends SynchronizationProvider {
* @param remoteTask remote task proxy
* @return
*/
- private boolean shouldTransmit(Task task, Property> property, Task remoteTask) {
- if(!task.containsValue(property))
+ private boolean shouldTransmit(TaskContainer task, Property> property, TaskContainer remoteTask) {
+ if(!task.task.containsValue(property))
return false;
if(remoteTask == null)
return true;
- if(!remoteTask.containsValue(property))
+ if(!remoteTask.task.containsValue(property))
return true;
- return !AndroidUtilities.equals(task.getValue(property), remoteTask.getValue(property));
+ return !AndroidUtilities.equals(task.task.getValue(property),
+ remoteTask.task.getValue(property));
}
@Override
- protected void create(Task task) throws IOException {
- String listId = null;
- if(task.containsValue(MilkDataService.LIST_ID) && task.getValue(MilkDataService.LIST_ID) != null)
- listId = Long.toString(task.getValue(MilkDataService.LIST_ID));
- if("0".equals(listId)) //$NON-NLS-1$
- listId = null;
-
- RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, listId,
- task.getValue(Task.TITLE));
- Task newRemoteTask = parseRemoteTask(rtmTask);
- task.mergeWith(newRemoteTask.getMergedValues());
+ protected void create(RTMTaskContainer task) throws IOException {
+ RtmTaskSeries rtmTask = rtmService.tasks_add(timeline, task.listId,
+ task.task.getValue(Task.TITLE));
+ RTMTaskContainer newRemoteTask = parseRemoteTask(rtmTask);
+ transferIdentifiers(newRemoteTask, task);
push(task, newRemoteTask);
}
@@ -369,55 +364,50 @@ public class RTMSyncProvider extends SynchronizationProvider {
* have changed.
*/
@Override
- protected void push(Task task, Task remoteTask) throws IOException {
+ protected void push(RTMTaskContainer local, RTMTaskContainer remote) throws IOException {
// fetch remote task for comparison
- if(remoteTask == null)
- remoteTask = read(task);
- RtmId id = new RtmId(task);
-
- if(shouldTransmit(task, Task.TITLE, remoteTask))
- rtmService.tasks_setName(timeline, id.listId, id.taskSeriesId,
- id.taskId, task.getValue(Task.TITLE));
- if(shouldTransmit(task, Task.IMPORTANCE, remoteTask))
- rtmService.tasks_setPriority(timeline, id.listId, id.taskSeriesId,
- id.taskId, Priority.values()[task.getValue(Task.IMPORTANCE)]);
- if(shouldTransmit(task, Task.DUE_DATE, remoteTask))
- rtmService.tasks_setDueDate(timeline, id.listId, id.taskSeriesId,
- id.taskId, DateUtilities.unixtimeToDate(task.getValue(Task.DUE_DATE)),
- task.hasDueTime());
- if(shouldTransmit(task, Task.COMPLETION_DATE, remoteTask)) {
- if(task.getValue(Task.COMPLETION_DATE) == 0)
- rtmService.tasks_uncomplete(timeline, id.listId, id.taskSeriesId,
- id.taskId);
+ if(remote == null)
+ remote = pull(local);
+
+ if(shouldTransmit(local, Task.TITLE, remote))
+ rtmService.tasks_setName(timeline, local.listId, local.taskSeriesId,
+ local.taskId, local.getValue(Task.TITLE));
+ if(shouldTransmit(local, Task.IMPORTANCE, remote))
+ rtmService.tasks_setPriority(timeline, local.listId, local.taskSeriesId,
+ local.taskId, Priority.values()[local.task.getValue(Task.IMPORTANCE)]);
+ if(shouldTransmit(local, Task.DUE_DATE, remote))
+ rtmService.tasks_setDueDate(timeline, local.listId, local.taskSeriesId,
+ local.taskId, DateUtilities.unixtimeToDate(local.task.getValue(Task.DUE_DATE)),
+ local.task.hasDueTime());
+ if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) {
+ if(local.task.getValue(Task.COMPLETION_DATE) == 0)
+ rtmService.tasks_uncomplete(timeline, local.listId, local.taskSeriesId,
+ local.taskId);
else
- rtmService.tasks_complete(timeline, id.listId, id.taskSeriesId,
- id.taskId);
+ rtmService.tasks_complete(timeline, local.listId, local.taskSeriesId,
+ local.taskId);
}
- if(shouldTransmit(task, Task.DELETION_DATE, remoteTask) &&
- task.getValue(Task.DELETION_DATE) > 0)
- rtmService.tasks_delete(timeline, id.listId, id.taskSeriesId,
- id.taskId);
+ if(shouldTransmit(local, Task.DELETION_DATE, remote) &&
+ local.task.getValue(Task.DELETION_DATE) > 0)
+ rtmService.tasks_delete(timeline, local.listId, local.taskSeriesId,
+ local.taskId);
// TODO tags, notes, url, ...
- if(remoteTask != null && shouldTransmit(task, MilkDataService.LIST_ID, remoteTask) &&
- task.getValue(MilkDataService.LIST_ID) != 0)
- rtmService.tasks_moveTo(timeline, Long.toString(remoteTask.getValue(MilkDataService.LIST_ID)),
- id.listId, id.taskSeriesId, id.taskId);
+ if(remote != null && local.listId != null &&
+ !AndroidUtilities.equals(local.listId, remote.listId))
+ rtmService.tasks_moveTo(timeline, remote.listId,
+ local.listId, local.taskSeriesId, local.taskId);
}
@Override
- protected void save(Task task) throws IOException {
+ protected void save(RTMTaskContainer task) throws IOException {
// if task has no id, try to find a corresponding task
- if(task.getId() == Task.NO_ID) {
- dataService.updateFromLocalCopy(task);
- }
-
dataService.saveTaskAndMetadata(task);
}
/** Create a task proxy for the given RtmTaskSeries */
- private Task parseRemoteTask(RtmTaskSeries rtmTaskSeries) {
+ private RTMTaskContainer parseRemoteTask(RtmTaskSeries rtmTaskSeries) {
Task task = new Task();
task.setValue(MilkDataService.LIST_ID, Long.parseLong(rtmTaskSeries.getList().getId()));
@@ -439,11 +429,13 @@ public class RTMSyncProvider extends SynchronizationProvider {
}
task.setValue(Task.IMPORTANCE, rtmTask.getPriority().ordinal());
+ task.setValue(RTM_TAGS, rtmTaskSeries.getTags());
+
return task;
}
@Override
- protected Task matchTask(ArrayList tasks, Task target) {
+ protected int matchTask(ArrayList tasks, RTMTaskContainer target) {
int length = tasks.size();
for(int i = 0; i < length; i++) {
Task task = tasks.get(i);
@@ -456,7 +448,7 @@ public class RTMSyncProvider extends SynchronizationProvider {
}
@Override
- protected Task read(Task task) throws IOException {
+ protected RTMTaskContainer pull(RTMTaskContainer task) throws IOException {
if(task.getValue(MilkDataService.TASK_SERIES_ID) == 0)
throw new ServiceInternalException("Tried to read an invalid task"); //$NON-NLS-1$
RtmTaskSeries rtmTask = rtmService.tasks_getTask(Long.toString(task.getValue(MilkDataService.TASK_SERIES_ID)),
@@ -467,7 +459,7 @@ public class RTMSyncProvider extends SynchronizationProvider {
}
@Override
- protected void transferIdentifiers(Task source, Task destination) {
+ protected void transferIdentifiers(RTMTaskContainer source, RTMTaskContainer destination) {
destination.setValue(MilkDataService.LIST_ID, source.getValue(MilkDataService.LIST_ID));
destination.setValue(MilkDataService.TASK_SERIES_ID, source.getValue(MilkDataService.TASK_SERIES_ID));
destination.setValue(MilkDataService.TASK_ID, source.getValue(MilkDataService.TASK_ID));
@@ -477,19 +469,6 @@ public class RTMSyncProvider extends SynchronizationProvider {
// ------------------------------------------------------- helper classes
// ----------------------------------------------------------------------
- /** Helper class for storing RTM id's */
- private static class RtmId {
- public String taskId;
- public String taskSeriesId;
- public String listId;
-
- public RtmId(Task task) {
- taskId = Long.toString(task.getValue(MilkDataService.TASK_ID));
- taskSeriesId = Long.toString(task.getValue(MilkDataService.TASK_SERIES_ID));
- listId = Long.toString(task.getValue(MilkDataService.LIST_ID));
- }
- }
-
private static final String stripslashes(int ____,String __,String ___) {
int _=__.charAt(____/92);_=_==115?_-1:_;_=((_>=97)&&(_<=123)?((_-83)%27+97):_);return
TextUtils.htmlEncode(____==31?___:stripslashes(____+1,__.substring(1),___+((char)_)));
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java
new file mode 100644
index 000000000..fd5d48c0b
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/sync/RTMTaskContainer.java
@@ -0,0 +1,17 @@
+package com.todoroo.astrid.rmilk.sync;
+
+import com.todoroo.astrid.api.TaskContainer;
+import com.todoroo.astrid.model.Metadata;
+import com.todoroo.astrid.model.Task;
+
+public class RTMTaskContainer extends TaskContainer {
+ public String listId, taskSeriesId, taskId;
+
+ public RTMTaskContainer(Task task, Metadata[] metadata,
+ String listId, String taskSeriesId, String taskId) {
+ super(task, metadata);
+ this.listId = listId;
+ this.taskSeriesId = taskSeriesId;
+ this.taskId = taskId;
+ }
+}
\ No newline at end of file