From e80e062735be1faa2c6571df5cec6693c874f9a6 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Fri, 15 May 2009 03:49:07 +0000 Subject: [PATCH] Special sync-on-complete hooks for completing tasks that remember the milk is handling the repeat for. --- src/com/mdt/rtm/data/RtmTaskSeries.java | 201 +++++++++--------- .../activities/TaskListSubActivity.java | 2 +- .../astrid/data/sync/SyncDataController.java | 22 +- .../astrid/data/task/AbstractTaskModel.java | 27 ++- .../astrid/data/task/TaskController.java | 39 ++-- ...rRepeat.java => TaskModelForHandlers.java} | 13 +- .../astrid/data/task/TaskModelForSync.java | 11 + .../timsu/astrid/sync/RTMSyncProvider.java | 35 ++- .../astrid/sync/SynchronizationProvider.java | 40 ++-- src/com/timsu/astrid/sync/Synchronizer.java | 30 ++- src/com/timsu/astrid/sync/TaskProxy.java | 48 ++--- 11 files changed, 300 insertions(+), 168 deletions(-) rename src/com/timsu/astrid/data/task/{TaskModelForRepeat.java => TaskModelForHandlers.java} (92%) diff --git a/src/com/mdt/rtm/data/RtmTaskSeries.java b/src/com/mdt/rtm/data/RtmTaskSeries.java index 58b3dfa82..37c88b916 100644 --- a/src/com/mdt/rtm/data/RtmTaskSeries.java +++ b/src/com/mdt/rtm/data/RtmTaskSeries.java @@ -22,127 +22,132 @@ package com.mdt.rtm.data; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.logging.Logger; import org.w3c.dom.Element; +import android.util.Log; + /** * * @author Will Ross Jun 22, 2007 */ public class RtmTaskSeries extends RtmData { - private static final Logger log = Logger.getLogger("TaskSeries"); + private final String id; - private final String id; + private final Date created; - private final Date created; + private final Date modified; - private final Date modified; + private final String name; - private final String name; + private final String source; - private final String source; + private final RtmTask task; - private final RtmTask task; + private final LinkedList tags; - private final LinkedList tags; + private final RtmTaskNotes notes; - private final RtmTaskNotes notes; + private final String locationId; - private final String locationId; + private final String url; - private final String url; + private final boolean hasRecurrence; - public RtmTaskSeries(String id, Date created, Date modified, String name, String source, RtmTask task) { - this.id = id; - this.created = created; - this.modified = modified; - this.name = name; - this.source = source; - this.task = task; - this.locationId = null; - notes = null; - url = null; - tags = null; - } + public RtmTaskSeries(String id, Date created, Date modified, String name, + String source, RtmTask task) { + this.id = id; + this.created = created; + this.modified = modified; + this.name = name; + this.source = source; + this.task = task; + this.locationId = null; + notes = null; + url = null; + tags = null; + hasRecurrence = false; + } - public RtmTaskSeries(Element elt) { - id = elt.getAttribute("id"); - created = parseDate(elt.getAttribute("created")); - modified = parseDate(elt.getAttribute("modified")); - name = elt.getAttribute("name"); - source = elt.getAttribute("source"); - task = new RtmTask(child(elt, "task")); + public RtmTaskSeries(Element elt) { + id = elt.getAttribute("id"); + created = parseDate(elt.getAttribute("created")); + modified = parseDate(elt.getAttribute("modified")); + name = elt.getAttribute("name"); + source = elt.getAttribute("source"); + task = new RtmTask(child(elt, "task")); + + if (children(elt, "task").size() > 1) { + Log.e("rtmsync", "WARANING: Assumption incorrect: found a " + + "TaskSeries with more than one child Task."); + } + notes = new RtmTaskNotes(child(elt, "notes")); + locationId = elt.getAttribute("location_id"); + url = elt.getAttribute("url"); + hasRecurrence = children(elt, "rrule").size() > 0; + + Element elementTags = child(elt, "tags"); + if (elementTags.getChildNodes().getLength() > 0) { + List elementTagList = children(elementTags, "tag"); + tags = new LinkedList(); + for (Element elementTag : elementTagList) { + String tag = text(elementTag); + if (tag != null) + tags.add(tag); + } + } else { + tags = null; + } + } - if (children(elt, "task").size() > 1) { - log.severe("WARANING: Assumption incorrect: found a TaskSeries with more than one child Task."); + public String getId() { + return id; } - notes = new RtmTaskNotes(child(elt, "notes")); - locationId = elt.getAttribute("location_id"); - url = elt.getAttribute("url"); - - Element elementTags = child(elt, "tags"); - if(elementTags.getChildNodes().getLength() > 0) { - List elementTagList = children(elementTags, "tag"); - tags = new LinkedList(); - for (Element elementTag : elementTagList) { - String tag = text(elementTag); - if(tag != null) - tags.add(tag); - } - } else { - tags = null; + + public Date getCreated() { + return created; + } + + public Date getModified() { + return modified; + } + + public String getName() { + return name; + } + + public String getSource() { + return source; + } + + public RtmTask getTask() { + return task; + } + + public LinkedList getTags() { + return tags; + } + + public RtmTaskNotes getNotes() { + return notes; + } + + public String getLocationId() { + return locationId; + } + + @Override + public String toString() { + return "TaskSeries<" + id + "," + name + ">"; + } + + public String getURL() { + return url; + } + + public boolean hasRecurrence() { + return hasRecurrence; } - } - - public String getId() { - return id; - } - - public Date getCreated() { - return created; - } - - public Date getModified() { - return modified; - } - - public String getName() { - return name; - } - - public String getSource() { - return source; - } - - public RtmTask getTask() { - return task; - } - - public LinkedList getTags() { - return tags; - } - - public RtmTaskNotes getNotes() - { - return notes; - } - - public String getLocationId() - { - return locationId; - } - - @Override - public String toString() - { - return "TaskSeries<" + id + "," + name + ">"; - } - - public String getURL() - { - return url; - } } diff --git a/src/com/timsu/astrid/activities/TaskListSubActivity.java b/src/com/timsu/astrid/activities/TaskListSubActivity.java index 900309e10..ad80a2868 100644 --- a/src/com/timsu/astrid/activities/TaskListSubActivity.java +++ b/src/com/timsu/astrid/activities/TaskListSubActivity.java @@ -131,7 +131,7 @@ public class TaskListSubActivity extends SubActivity { // indicator flag set if task list should be refreshed (something changed // in another activity) - static boolean shouldRefreshTaskList = false; + public static boolean shouldRefreshTaskList = false; // indicator flag set if synchronization window has been opened & closed static boolean syncPreferencesOpened = false; diff --git a/src/com/timsu/astrid/data/sync/SyncDataController.java b/src/com/timsu/astrid/data/sync/SyncDataController.java index d1b95ba7f..424de3e09 100644 --- a/src/com/timsu/astrid/data/sync/SyncDataController.java +++ b/src/com/timsu/astrid/data/sync/SyncDataController.java @@ -59,7 +59,7 @@ public class SyncDataController extends AbstractController { // --- sync mapping /** Get all mappings for the given synchronization service */ - public HashSet getSyncMapping(int syncServiceId) throws SQLException { + public HashSet getSyncMappings(int syncServiceId) throws SQLException { HashSet list = new HashSet(); Cursor cursor = syncDatabase.query(SYNC_TABLE_NAME, SyncMapping.FIELD_LIST, @@ -80,6 +80,26 @@ public class SyncDataController extends AbstractController { } } + /** Get mapping for given task */ + public SyncMapping getSyncMapping(int syncServiceId, TaskIdentifier taskId) + throws SQLException { + Cursor cursor = syncDatabase.query(SYNC_TABLE_NAME, + SyncMapping.FIELD_LIST, + SyncMapping.SYNC_SERVICE + " = ? AND " + + SyncMapping.TASK + " = ?", + new String[] { "" + syncServiceId, "" + taskId.getId() }, + null, null, null); + + try { + if(cursor.getCount() == 0) + return null; + cursor.moveToNext(); + return new SyncMapping(cursor); + } finally { + cursor.close(); + } + } + /** Saves the given task to the database. Returns true on success. */ public boolean saveSyncMapping(SyncMapping mapping) { long newRow = syncDatabase.insert(SYNC_TABLE_NAME, SyncMapping.TASK, diff --git a/src/com/timsu/astrid/data/task/AbstractTaskModel.java b/src/com/timsu/astrid/data/task/AbstractTaskModel.java index 6d58e9b6f..0d796a753 100644 --- a/src/com/timsu/astrid/data/task/AbstractTaskModel.java +++ b/src/com/timsu/astrid/data/task/AbstractTaskModel.java @@ -45,7 +45,7 @@ import com.timsu.astrid.utilities.Preferences; public abstract class AbstractTaskModel extends AbstractModel { /** Version number of this model */ - static final int VERSION = 5; + static final int VERSION = 6; public static final int COMPLETE_PERCENTAGE = 100; @@ -69,6 +69,7 @@ public abstract class AbstractTaskModel extends AbstractModel { static final String CREATION_DATE = "creationDate"; static final String COMPLETION_DATE = "completionDate"; static final String CALENDAR_URI = "calendarUri"; + static final String FLAGS = "flags"; // reserved fields --- static final String BLOCKING_ON = "blockingOn"; @@ -78,6 +79,9 @@ public abstract class AbstractTaskModel extends AbstractModel { 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; + /** Number of bits to shift repeat value by */ public static final int REPEAT_VALUE_OFFSET = 3; @@ -103,6 +107,7 @@ public abstract class AbstractTaskModel extends AbstractModel { defaultValues.put(REPEAT, 0); defaultValues.put(COMPLETION_DATE, (Long)null); defaultValues.put(CALENDAR_URI, (String)null); + defaultValues.put(FLAGS, 0); } // --- database helper @@ -137,6 +142,7 @@ public abstract class AbstractTaskModel extends AbstractModel { append(NOTIFICATION_FLAGS).append(" integer,"). append(LAST_NOTIFIED).append(" integer,"). append(REPEAT).append(" integer,"). + append(FLAGS).append(" integer,"). append(CREATION_DATE).append(" integer,"). append(COMPLETION_DATE).append(" integer,"). append(CALENDAR_URI).append(" text"). @@ -202,6 +208,15 @@ public abstract class AbstractTaskModel extends AbstractModel { Log.e("astrid", "Error updating table!", e); } + case 5: + sql = new StringBuilder().append("ALTER TABLE "). + append(tableName).append(" ADD COLUMN "). + append(FLAGS).append(" integer").toString(); + try { + db.execSQL(sql); + } catch (Exception e) { + Log.e("astrid", "Error updating table!", e); + } break; default: @@ -282,6 +297,8 @@ public abstract class AbstractTaskModel extends AbstractModel { getLastNotificationDate(); else if(field.equals(REPEAT)) getRepeat(); + else if(field.equals(FLAGS)) + getFlags(); } } @@ -451,6 +468,10 @@ public abstract class AbstractTaskModel extends AbstractModel { return uri; } + protected int getFlags() { + return retrieveInteger(FLAGS); + } + // --- setters protected void setName(String name) { @@ -542,6 +563,10 @@ public abstract class AbstractTaskModel extends AbstractModel { putIfChangedFromDatabase(CALENDAR_URI, uri); } + protected void setFlags(int flags) { + putIfChangedFromDatabase(FLAGS, flags); + } + // --- utility methods protected void putDate(String fieldName, Date date) { diff --git a/src/com/timsu/astrid/data/task/TaskController.java b/src/com/timsu/astrid/data/task/TaskController.java index 18e1c7273..47b97f66b 100644 --- a/src/com/timsu/astrid/data/task/TaskController.java +++ b/src/com/timsu/astrid/data/task/TaskController.java @@ -36,11 +36,14 @@ import android.net.Uri; import android.util.Log; import com.timsu.astrid.activities.TaskEdit; +import com.timsu.astrid.activities.TaskListSubActivity; import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.sync.SyncDataController; import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo; import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper; +import com.timsu.astrid.sync.Synchronizer; +import com.timsu.astrid.sync.Synchronizer.SynchronizerListener; import com.timsu.astrid.utilities.Notifications; /** @@ -239,6 +242,13 @@ public class TaskController extends AbstractController { saveSucessful = database.update(TASK_TABLE_NAME, values, KEY_ROWID + "=" + id, null) > 0; + // task was completed + if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) && + values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE) + == AbstractTaskModel.COMPLETE_PERCENTAGE) { + onTaskSetCompleted(task, values); + } + if(!(task instanceof TaskModelForSync)) { SyncDataController syncController = new SyncDataController(context); syncController.open(); @@ -258,13 +268,6 @@ public class TaskController extends AbstractController { */ private void onTaskSave(AbstractTaskModel task, ContentValues values) { - // task was completed - if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) && - values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE) - == AbstractTaskModel.COMPLETE_PERCENTAGE) { - onTaskSetCompleted(task, values); - } - // task timer was updated, update notification bar if(values.containsKey(AbstractTaskModel.TIMER_START)) { // show notification bar if timer was started @@ -322,16 +325,26 @@ public class TaskController extends AbstractController { */ private void onTaskSetCompleted(AbstractTaskModel task, ContentValues values) { values.put(AbstractTaskModel.COMPLETION_DATE, System.currentTimeMillis()); + Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(), + TaskModelForHandlers.FIELD_LIST); + TaskModelForHandlers model = new TaskModelForHandlers(cursor, values); // handle repeat - Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(), - TaskModelForRepeat.FIELD_LIST); - TaskModelForRepeat repeatModel = new TaskModelForRepeat(cursor, values); - RepeatInfo repeatInfo = repeatModel.getRepeat(); + RepeatInfo repeatInfo = model.getRepeat(); if(repeatInfo != null) - repeatModel.repeatTaskBy(context, this, repeatInfo); - cursor.close(); + model.repeatTaskBy(context, this, repeatInfo); + + // handle sync-on-complete + if((model.getFlags() & TaskModelForHandlers.FLAG_SYNC_ON_COMPLETE) > 0) { + Synchronizer synchronizer = new Synchronizer(model.getTaskIdentifier()); + synchronizer.synchronize(context, new SynchronizerListener() { + public void onSynchronizerFinished(int numServicesSynced) { + TaskListSubActivity.shouldRefreshTaskList = true; + } + }); + } + cursor.close(); cleanupTask(task.getTaskIdentifier(), repeatInfo != null); } diff --git a/src/com/timsu/astrid/data/task/TaskModelForRepeat.java b/src/com/timsu/astrid/data/task/TaskModelForHandlers.java similarity index 92% rename from src/com/timsu/astrid/data/task/TaskModelForRepeat.java rename to src/com/timsu/astrid/data/task/TaskModelForHandlers.java index a28480592..58cb3b5cf 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForRepeat.java +++ b/src/com/timsu/astrid/data/task/TaskModelForHandlers.java @@ -33,8 +33,9 @@ import com.timsu.astrid.utilities.Notifications.Notifiable; -/** Fields that you would want to edit pertaining to repeats */ -public class TaskModelForRepeat extends AbstractTaskModel implements Notifiable { +/** Fields that you would want to read or edit in the onTaskSave and onTaskComplete + * event handlers */ +public class TaskModelForHandlers extends AbstractTaskModel implements Notifiable { static String[] FIELD_LIST = new String[] { AbstractController.KEY_ROWID, @@ -47,6 +48,7 @@ public class TaskModelForRepeat extends AbstractTaskModel implements Notifiable LAST_NOTIFIED, NOTIFICATIONS, NOTIFICATION_FLAGS, + FLAGS, }; /** @@ -96,7 +98,7 @@ public class TaskModelForRepeat extends AbstractTaskModel implements Notifiable // --- constructors - public TaskModelForRepeat(Cursor cursor, ContentValues setValues) { + public TaskModelForHandlers(Cursor cursor, ContentValues setValues) { super(cursor); this.setValues = setValues; } @@ -147,6 +149,11 @@ public class TaskModelForRepeat extends AbstractTaskModel implements Notifiable return super.getLastNotificationDate(); } + @Override + public int getFlags() { + return super.getFlags(); + } + @Override public void setDefiniteDueDate(Date definiteDueDate) { super.setDefiniteDueDate(definiteDueDate); diff --git a/src/com/timsu/astrid/data/task/TaskModelForSync.java b/src/com/timsu/astrid/data/task/TaskModelForSync.java index b43da8bba..59320bbe9 100644 --- a/src/com/timsu/astrid/data/task/TaskModelForSync.java +++ b/src/com/timsu/astrid/data/task/TaskModelForSync.java @@ -50,6 +50,7 @@ public class TaskModelForSync extends AbstractTaskModel implements Notifiable { LAST_NOTIFIED, NOTIFICATIONS, NOTIFICATION_FLAGS, + FLAGS, }; // --- constructors @@ -151,6 +152,11 @@ public class TaskModelForSync extends AbstractTaskModel implements Notifiable { return super.getLastNotificationDate(); } + @Override + public int getFlags() { + return super.getFlags(); + } + // --- setters @Override @@ -222,5 +228,10 @@ public class TaskModelForSync extends AbstractTaskModel implements Notifiable { public void setNotificationIntervalSeconds(Integer intervalInSeconds) { super.setNotificationIntervalSeconds(intervalInSeconds); } + + @Override + public void setFlags(int flags) { + super.setFlags(flags); + } } diff --git a/src/com/timsu/astrid/sync/RTMSyncProvider.java b/src/com/timsu/astrid/sync/RTMSyncProvider.java index 976bdde20..98c0cf2cd 100644 --- a/src/com/timsu/astrid/sync/RTMSyncProvider.java +++ b/src/com/timsu/astrid/sync/RTMSyncProvider.java @@ -174,6 +174,23 @@ public class RTMSyncProvider extends SynchronizationProvider { final String timeline = rtmService.timelines_create(); postUpdate(new ProgressUpdater(1, 5)); + // push task if single task sync is requested + if(getSingleTaskForSync() != null) { + SyncMapping mapping = synchronizer.getSyncController(context). + getSyncMapping(getId(), getSingleTaskForSync()); + if(mapping == null) { + Log.w("astrid-rtm", "Couldn't find sync mapping for updated task"); + return; + } + + TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId()); + TaskModelForSync task = synchronizer.getTaskController(context). + fetchTaskForSync(getSingleTaskForSync()); + localTask.readFromTaskModel(task); + postUpdate(new ProgressLabelUpdater("Synchronizing repeating task")); + pushLocalTask(timeline, localTask, null, mapping); + } + // load RTM lists RtmLists lists = rtmService.lists_getList(); for(RtmList list : lists.getLists().values()) { @@ -238,11 +255,12 @@ public class RTMSyncProvider extends SynchronizationProvider { Date syncTime = new Date(System.currentTimeMillis() + 1000); Preferences.setSyncRTMLastSync(context, syncTime); - // on with the synchronization - synchronizer.continueSynchronization(context); - } catch (Exception e) { showError(context, e, null); + + } finally { + // on with the synchronization + synchronizer.continueSynchronization(context); } } @@ -261,7 +279,7 @@ public class RTMSyncProvider extends SynchronizationProvider { /** Get a task proxy with default RTM values */ private TaskProxy getDefaultTaskProxy() { - TaskProxy taskProxy = new TaskProxy(0, "", false); + TaskProxy taskProxy = new TaskProxy(0, ""); taskProxy.progressPercentage = 0; taskProxy.tags = new LinkedList(); taskProxy.notes = ""; @@ -350,8 +368,7 @@ public class RTMSyncProvider extends SynchronizationProvider { /** Create a task proxy for the given RtmTaskSeries */ private TaskProxy parseRemoteTask(String listId, RtmTaskSeries rtmTaskSeries) { TaskProxy task = new TaskProxy(getId(), - new RtmId(listId, rtmTaskSeries).toString(), - rtmTaskSeries.getTask().getDeleted() != null); + new RtmId(listId, rtmTaskSeries).toString()); task.name = rtmTaskSeries.getName(); @@ -363,6 +380,10 @@ public class RTMSyncProvider extends SynchronizationProvider { if(sb.length() > 0) task.notes = sb.toString().trim(); + // repeat + if(rtmTaskSeries.hasRecurrence()) + task.syncOnComplete = true; + // list / tags LinkedList tagsList = rtmTaskSeries.getTags(); String listName = listIdToNameMap.get(listId); @@ -381,6 +402,7 @@ public class RTMSyncProvider extends SynchronizationProvider { } task.creationDate = rtmTaskSeries.getCreated(); task.completionDate = rtmTask.getCompleted(); + task.isDeleted = rtmTask.getDeleted() != null; if(rtmTask.getDue() != null) { Date due = rtmTask.getDue(); @@ -392,7 +414,6 @@ public class RTMSyncProvider extends SynchronizationProvider { task.dueDate = due; } task.progressPercentage = (rtmTask.getCompleted() == null) ? 0 : 100; - task.importance = Importance.values()[rtmTask.getPriority().ordinal()]; return task; diff --git a/src/com/timsu/astrid/sync/SynchronizationProvider.java b/src/com/timsu/astrid/sync/SynchronizationProvider.java index 66ba203d8..789b307ed 100644 --- a/src/com/timsu/astrid/sync/SynchronizationProvider.java +++ b/src/com/timsu/astrid/sync/SynchronizationProvider.java @@ -108,6 +108,12 @@ public abstract class SynchronizationProvider { return synchronizer.isService(); } + /** Check whether the synchronization request wants to only transmit + * one specific task. Returns null if this is not the case */ + protected TaskIdentifier getSingleTaskForSync() { + return synchronizer.getSingleTaskForSync(); + } + /** Utility method for showing synchronization errors. If message is null, * the contents of the throwable is displayed. */ @@ -232,7 +238,7 @@ public abstract class SynchronizationProvider { syncController.saveSyncMapping(mapping); data.localIdToSyncMapping.put(taskId, mapping); - TaskProxy localTask = new TaskProxy(getId(), remoteId, false); + TaskProxy localTask = new TaskProxy(getId(), remoteId); localTask.readFromTaskModel(task); localTask.readTagsFromController(taskId, tagController, data.tags); helper.pushTask(localTask, null, mapping); @@ -264,8 +270,7 @@ public abstract class SynchronizationProvider { // 3. UPDATE: for each updated local task for(SyncMapping mapping : data.localChanges) { - TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId(), - false); + TaskProxy localTask = new TaskProxy(getId(), mapping.getRemoteId()); TaskModelForSync task = taskController.fetchTaskForSync( mapping.getTask()); localTask.readFromTaskModel(task); @@ -298,7 +303,7 @@ public abstract class SynchronizationProvider { } // re-fetch remote task - if(remoteConflict != null) { + if(remoteConflict != null || getSingleTaskForSync() != null) { TaskProxy newTask = helper.refetchTask(remoteConflict); remoteTasks.remove(remoteConflict); remoteTasks.add(newTask); @@ -441,7 +446,7 @@ public abstract class SynchronizationProvider { public SyncData(Context context, LinkedList remoteTasks) { // 1. get data out of the database - mappings = synchronizer.getSyncController(context).getSyncMapping(getId()); + mappings = synchronizer.getSyncController(context).getSyncMappings(getId()); activeTasks = synchronizer.getTaskController(context).getActiveTaskIdentifiers(); allTasks = synchronizer.getTaskController(context).getAllTaskIdentifiers(); tags = synchronizer.getTagController(context).getAllTagsAsMap(); @@ -449,11 +454,8 @@ public abstract class SynchronizationProvider { // 2. build helper data structures remoteIdToSyncMapping = new HashMap(); localIdToSyncMapping = new HashMap(); - localChanges = new HashSet(); mappedTasks = new HashSet(); for(SyncMapping mapping : mappings) { - if(mapping.isUpdated()) - localChanges.add(mapping); remoteIdToSyncMapping.put(mapping.getRemoteId(), mapping); localIdToSyncMapping.put(mapping.getTask(), mapping); mappedTasks.add(mapping.getTask()); @@ -475,10 +477,21 @@ public abstract class SynchronizationProvider { } // 4. build data structures of things to do - newlyCreatedTasks = new HashSet(activeTasks); - newlyCreatedTasks.removeAll(mappedTasks); - deletedTasks = new HashSet(mappedTasks); - deletedTasks.removeAll(allTasks); + if(getSingleTaskForSync() != null) { + newlyCreatedTasks = new HashSet(); + deletedTasks = new HashSet(); + localChanges = new HashSet(); + } else { + newlyCreatedTasks = new HashSet(activeTasks); + newlyCreatedTasks.removeAll(mappedTasks); + deletedTasks = new HashSet(mappedTasks); + deletedTasks.removeAll(allTasks); + localChanges = new HashSet(); + for(SyncMapping mapping : localIdToSyncMapping.values()) { + if(mapping.isUpdated()) + localChanges.add(mapping); + } + } } } @@ -500,7 +513,8 @@ public abstract class SynchronizationProvider { progressDialog.hide(); Resources r = context.getResources(); - if(Preferences.shouldSuppressSyncDialogs(context)) { + if(Preferences.shouldSuppressSyncDialogs(context) || + getSingleTaskForSync() != null) { return; } diff --git a/src/com/timsu/astrid/sync/Synchronizer.java b/src/com/timsu/astrid/sync/Synchronizer.java index cc7f92691..54d5bd40d 100644 --- a/src/com/timsu/astrid/sync/Synchronizer.java +++ b/src/com/timsu/astrid/sync/Synchronizer.java @@ -31,6 +31,7 @@ import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.sync.SyncDataController; import com.timsu.astrid.data.tag.TagController; import com.timsu.astrid.data.task.TaskController; +import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.utilities.Preferences; /** @@ -50,8 +51,16 @@ public class Synchronizer { // --- public interface + /** Synchronize all tasks */ public Synchronizer(boolean isService) { this.isService = isService; + singleTaskForSync = null; + } + + /** Synchronize a specific task only */ + public Synchronizer(TaskIdentifier task) { + isService = false; + singleTaskForSync = task; } public interface SynchronizerListener { @@ -122,20 +131,28 @@ public class Synchronizer { // Internal state for the synchronization process /** Current step in the sync process */ - private int currentStep; + private int currentStep = 0; /** # of services synchronized */ - private int servicesSynced; + private int servicesSynced = 0; /** On finished callback */ - private SynchronizerListener callback; + private SynchronizerListener callback = null; + + /** Whether this sync is initiated by a background service */ + private final boolean isService; - private boolean isService; + /** The single task to synchronize, if applicable */ + private final TaskIdentifier singleTaskForSync; boolean isService() { return isService; } + TaskIdentifier getSingleTaskForSync() { + return singleTaskForSync; + } + /** Called to do the next step of synchronization. */ void continueSynchronization(Context context) { ServiceWrapper serviceWrapper = @@ -161,12 +178,13 @@ public class Synchronizer { /** Called at the end of sync. */ private void finishSynchronization(final Context context) { closeControllers(); - Preferences.setSyncLastSync(context, new Date()); if(callback != null) callback.onSynchronizerFinished(servicesSynced); + if(getSingleTaskForSync() != null) + Preferences.setSyncLastSync(context, new Date()); if(!isService) - SynchronizationService.start(); + SynchronizationService.start(); } // --- controller stuff diff --git a/src/com/timsu/astrid/sync/TaskProxy.java b/src/com/timsu/astrid/sync/TaskProxy.java index f109e3cb9..b0ed14167 100644 --- a/src/com/timsu/astrid/sync/TaskProxy.java +++ b/src/com/timsu/astrid/sync/TaskProxy.java @@ -41,10 +41,12 @@ import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo; */ public class TaskProxy { - TaskProxy(int syncServiceId, String syncTaskId, boolean isDeleted) { + public static final Date NO_DATE_SET = new Date(0); + public static final RepeatInfo NO_REPEAT_SET = new RepeatInfo(RepeatInterval.DAYS, 0); + + TaskProxy(int syncServiceId, String syncTaskId) { this.syncServiceId = syncServiceId; this.syncTaskId = syncTaskId; - this.isDeleted = isDeleted; } // --- fill these out @@ -67,7 +69,12 @@ public class TaskProxy { Integer estimatedSeconds = null; Integer elapsedSeconds = null; - Integer repeatEveryNSeconds = null; + RepeatInfo repeatInfo = null; + + Boolean syncOnComplete = null; + + /** was the task deleted on the remote server */ + boolean isDeleted = false; // --- internal state @@ -77,8 +84,6 @@ public class TaskProxy { /** id of this particular remote task */ private String syncTaskId; - /** was the task deleted on the remote server */ - private boolean isDeleted = false; public int getSyncServiceId() { return syncServiceId; @@ -125,8 +130,10 @@ public class TaskProxy { estimatedSeconds = other.estimatedSeconds; if(other.elapsedSeconds != null) elapsedSeconds = other.elapsedSeconds; - if(other.repeatEveryNSeconds != null) - repeatEveryNSeconds = other.repeatEveryNSeconds; + if(other.repeatInfo != null) + repeatInfo = other.repeatInfo; + if(other.syncOnComplete != null) + syncOnComplete = other.syncOnComplete; } /** Read from the given task model */ @@ -143,10 +150,8 @@ public class TaskProxy { hiddenUntil = task.getHiddenUntil(); estimatedSeconds = task.getEstimatedSeconds(); elapsedSeconds = task.getElapsedSeconds(); - RepeatInfo repeatInfo = task.getRepeat(); - if(repeatInfo != null) { - repeatEveryNSeconds = (int)(repeatInfo.shiftDate(new Date(0)).getTime()/1000); - } + syncOnComplete = (task.getFlags() & TaskModelForSync.FLAG_SYNC_ON_COMPLETE) > 0; + repeatInfo = task.getRepeat(); } /** Read tags from the given tag controller */ @@ -199,22 +204,15 @@ public class TaskProxy { task.setEstimatedSeconds(estimatedSeconds); if(elapsedSeconds != null) task.setElapsedSeconds(elapsedSeconds); + if(syncOnComplete != null) + task.setFlags(task.getFlags() | TaskModelForSync.FLAG_SYNC_ON_COMPLETE); // this is inaccurate. =/ - if(repeatEveryNSeconds != null) { - RepeatInterval repeatInterval; - int repeatValue; - if(repeatEveryNSeconds < 7 * 24 * 3600) { - repeatInterval = RepeatInterval.DAYS; - repeatValue = repeatEveryNSeconds / (24 * 3600); - } else if(repeatEveryNSeconds < 30 * 24 * 3600) { - repeatInterval = RepeatInterval.WEEKS; - repeatValue = repeatEveryNSeconds / (7 * 24 * 3600); - } else { - repeatInterval = RepeatInterval.MONTHS; - repeatValue = repeatEveryNSeconds / (30 * 24 * 3600); - } - task.setRepeat(new RepeatInfo(repeatInterval, repeatValue)); + if(repeatInfo != null) { + if(repeatInfo == NO_REPEAT_SET) + task.setRepeat(null); + else + task.setRepeat(repeatInfo); } } }