From 95621e597c541a8b88403d788d769630ee56660b Mon Sep 17 00:00:00 2001 From: Tim Su Date: Sat, 25 Sep 2010 16:21:39 -0700 Subject: [PATCH] First pass at gtasks sync provider --- astrid/.classpath | 1 + astrid/AndroidManifest.xml | 4 +- astrid/libs/todoroo-g.jar | Bin 0 -> 30647 bytes .../astrid/gtasks/GtasksListService.java | 13 +- .../todoroo/astrid/gtasks/GtasksMetadata.java | 2 +- .../gtasks/sync/GtasksSyncProvider.java | 472 ++++++++++++++++++ 6 files changed, 482 insertions(+), 10 deletions(-) create mode 100644 astrid/libs/todoroo-g.jar create mode 100644 astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java diff --git a/astrid/.classpath b/astrid/.classpath index 0025f06fe..22ecde1ad 100644 --- a/astrid/.classpath +++ b/astrid/.classpath @@ -14,5 +14,6 @@ + diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 1f3fadb87..1e9da83d4 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -212,7 +212,7 @@ - + diff --git a/astrid/libs/todoroo-g.jar b/astrid/libs/todoroo-g.jar new file mode 100644 index 0000000000000000000000000000000000000000..4978ab38d49533a3b6be72d7bb5684ba82466a9b GIT binary patch literal 30647 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 z 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 PI758!ARqk6g9rZy!Q)<~ literal 0 HcmV?d00001 diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksListService.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksListService.java index e75d8116f..7eb7780a7 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksListService.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksListService.java @@ -1,8 +1,6 @@ package com.todoroo.astrid.gtasks; -import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; @@ -11,6 +9,7 @@ import com.todoroo.andlib.sql.Query; import com.todoroo.astrid.dao.StoreObjectDao; import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria; import com.todoroo.astrid.data.StoreObject; +import com.todoroo.gtasks.GoogleTaskListInfo; public class GtasksListService { @@ -62,12 +61,12 @@ public class GtasksListService { } @SuppressWarnings("nls") - public void updateLists(JSONArray newLists) throws JSONException { + public void updateLists(GoogleTaskListInfo[] remoteLists) throws JSONException { readLists(); - for(int i = 0; i < newLists.length(); i++) { - JSONObject remote = newLists.getJSONObject(i); + for(int i = 0; i < remoteLists.length; i++) { + GoogleTaskListInfo remote = remoteLists[i]; - String id = remote.getString("id"); + String id = remote.getId(); StoreObject local = null; for(StoreObject list : lists) { if(list.getValue(GtasksList.REMOTE_ID).equals(id)) { @@ -81,7 +80,7 @@ public class GtasksListService { local.setValue(StoreObject.TYPE, GtasksList.TYPE); local.setValue(GtasksList.REMOTE_ID, id); - local.setValue(GtasksList.NAME, remote.getString("title")); + local.setValue(GtasksList.NAME, remote.getName()); local.setValue(GtasksList.ORDER, i); storeObjectDao.persist(local); } diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksMetadata.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksMetadata.java index a362c0984..3d5a189ef 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksMetadata.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksMetadata.java @@ -20,7 +20,7 @@ public class GtasksMetadata { public static final String METADATA_KEY = "gtasks"; //$NON-NLS-1$ /** task id in google */ - public static final LongProperty ID = new LongProperty(Metadata.TABLE, + public static final StringProperty ID = new StringProperty(Metadata.TABLE, Metadata.VALUE1.name); public static final StringProperty LIST_ID = new StringProperty(Metadata.TABLE, diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java new file mode 100644 index 000000000..9e764cff0 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncProvider.java @@ -0,0 +1,472 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.gtasks.sync; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.json.JSONException; + +import android.app.Activity; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; + +import com.flurry.android.FlurryAgent; +import com.timsu.astrid.R; +import com.todoroo.andlib.data.AbstractModel; +import com.todoroo.andlib.data.Property; +import com.todoroo.andlib.data.TodorooCursor; +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.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.astrid.data.Metadata; +import com.todoroo.astrid.data.StoreObject; +import com.todoroo.astrid.data.Task; +import com.todoroo.astrid.gtasks.GtasksList; +import com.todoroo.astrid.gtasks.GtasksListService; +import com.todoroo.astrid.gtasks.GtasksMetadata; +import com.todoroo.astrid.gtasks.GtasksMetadataService; +import com.todoroo.astrid.gtasks.GtasksPreferenceService; +import com.todoroo.astrid.gtasks.GtasksPreferences; +import com.todoroo.astrid.producteev.ProducteevBackgroundService; +import com.todoroo.astrid.producteev.ProducteevLoginActivity; +import com.todoroo.astrid.producteev.ProducteevUtilities; +import com.todoroo.astrid.producteev.api.ApiServiceException; +import com.todoroo.astrid.service.AstridDependencyInjector; +import com.todoroo.astrid.sync.SyncContainer; +import com.todoroo.astrid.sync.SyncProvider; +import com.todoroo.astrid.utility.Constants; +import com.todoroo.astrid.utility.Preferences; +import com.todoroo.gtasks.GoogleTaskService; +import com.todoroo.gtasks.GoogleTaskTask; +import com.todoroo.gtasks.GoogleTaskView; +import com.todoroo.gtasks.GoogleTasksException; +import com.todoroo.gtasks.GoogleTaskService.ConvenientTaskCreator; +import com.todoroo.gtasks.actions.Action; +import com.todoroo.gtasks.actions.Actions; +import com.todoroo.gtasks.actions.GetTasksAction; +import com.todoroo.gtasks.actions.ListAction; +import com.todoroo.gtasks.actions.ListActions; +import com.todoroo.gtasks.actions.ListActions.TaskBuilder; +import com.todoroo.gtasks.actions.ListActions.TaskModifier; + +@SuppressWarnings("nls") +public class GtasksSyncProvider extends SyncProvider { + + @Autowired private GtasksListService gtasksListService; + @Autowired private GtasksMetadataService gtasksMetadataService; + @Autowired private GtasksPreferenceService gtasksPreferenceService; + + /** google task service fields */ + private GoogleTaskService taskService = null; + private static final Actions a = new Actions(); + private static final ListActions l = new ListActions(); + + /** batched actions to execute */ + private final ArrayList actions = new ArrayList(); + private final HashMap> listActions = + new HashMap>(); + + + static { + AstridDependencyInjector.initialize(); + } + + @Autowired + protected ExceptionService exceptionService; + + public GtasksSyncProvider() { + super(); + DependencyInjectionService.getInstance().inject(this); + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------ utility methods + // ---------------------------------------------------------------------- + + /** + * Sign out of service, deleting all synchronization metadata + */ + public void signOut() { + gtasksPreferenceService.clearLastSyncDate(); + gtasksPreferenceService.setToken(null); + + gtasksMetadataService.clearMetadata(); + } + + /** + * Deal with a synchronization exception. If requested, will show an error + * to the user (unless synchronization is happening in background) + * + * @param context + * @param tag + * error tag + * @param e + * exception + * @param showError + * whether to display a dialog + */ + @Override + protected void handleException(String tag, Exception e, boolean displayError) { + final Context context = ContextManager.getContext(); + gtasksPreferenceService.setLastError(e.toString()); + + String message = null; + + // occurs when application was closed + if(e instanceof IllegalStateException) { + exceptionService.reportError(tag + "-caught", e); //$NON-NLS-1$ + + // occurs when network error + } else if(!(e instanceof ApiServiceException) && e instanceof IOException) { + message = context.getString(R.string.producteev_ioerror); + } else { + message = context.getString(R.string.DLG_error, e.toString()); + exceptionService.reportError(tag + "-unhandled", e); //$NON-NLS-1$ + } + + if(displayError && context instanceof Activity && message != null) { + DialogUtilities.okDialog((Activity)context, + message, null); + } + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------ initiating sync + // ---------------------------------------------------------------------- + + /** + * initiate sync in background + */ + @Override + protected void initiateBackground(Service service) { + try { + String authToken = gtasksPreferenceService.getToken(); + + String email = "tasktest@todoroo.com"; // TODO + String password = "tasktest0000"; + + taskService = new GoogleTaskService(email, password); + + // check if we have a token & it works + if(authToken != null) { + taskService.getTaskView(); + performSync(); + } else { + if (email == null && password == null) { + // we can't do anything, user is not logged in + } else { + authToken = null; // TODO set up auth token + performSync(); + } + } + } catch (IllegalStateException e) { + // occurs when application was closed + } catch (Exception e) { + handleException("gtasks-authenticate", e, true); + } finally { + gtasksPreferenceService.stopOngoing(); + } + } + + /** + * If user isn't already signed in, show sign in dialog. Else perform sync. + */ + @Override + protected void initiateManual(Activity activity) { + String authToken = gtasksPreferenceService.getToken(); + ProducteevUtilities.INSTANCE.stopOngoing(); + + // check if we have a token & it works + if(authToken == null) { + // display login-activity + Intent intent = new Intent(activity, ProducteevLoginActivity.class); + activity.startActivityForResult(intent, 0); + } else { + activity.startService(new Intent(ProducteevBackgroundService.SYNC_ACTION, null, + activity, ProducteevBackgroundService.class)); + } + } + + // ---------------------------------------------------------------------- + // ----------------------------------------------------- synchronization! + // ---------------------------------------------------------------------- + + protected void performSync() { + FlurryAgent.onEvent("gtasks-started"); + gtasksPreferenceService.recordSyncStart(); + + try { + GoogleTaskView taskView = taskService.getTaskView(); + Preferences.setString(GtasksPreferenceService.PREF_DEFAULT_LIST, + taskView.getActiveTaskList().getInfo().getId()); + + gtasksListService.updateLists(taskView.getAllLists()); + + // batched read tasks for each list + ArrayList remoteTasks = new ArrayList(); + ArrayList getTasksActions = new ArrayList(); + for(StoreObject dashboard : gtasksListService.getLists()) { + String listId = dashboard.getValue(GtasksList.REMOTE_ID); + getTasksActions.add(a.getTasks(listId, true)); + } + taskService.executeActions(getTasksActions.toArray(new GetTasksAction[getTasksActions.size()])); + for(GetTasksAction action : getTasksActions) { + List remoteTasksInList = action.getGoogleTasks(); + for(GoogleTaskTask remoteTask : remoteTasksInList) { + GtasksTaskContainer remote = parseRemoteTask(remoteTask); + // update reminder flags for incoming remote tasks to prevent annoying + if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now()) + remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false); + gtasksMetadataService.findLocalMatch(remote); + remoteTasks.add(remote); + } + } + + SyncData syncData = populateSyncData(remoteTasks); + try { + synchronizeTasks(syncData); + } finally { + syncData.localCreated.close(); + syncData.localUpdated.close(); + } + + gtasksPreferenceService.recordSuccessfulSync(); + FlurryAgent.onEvent("gtasks-sync-finished"); //$NON-NLS-1$ + } catch (IllegalStateException e) { + // occurs when application was closed + } catch (Exception e) { + handleException("gtasks-sync", e, true); //$NON-NLS-1$ + } + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------------------ sync data + // ---------------------------------------------------------------------- + + // 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, + Task.REMINDER_FLAGS, + Task.NOTES, + }; + + /** + * Populate SyncData data structure + * @throws JSONException + */ + private SyncData populateSyncData(ArrayList remoteTasks) throws JSONException { + // fetch locally created tasks + TodorooCursor localCreated = gtasksMetadataService.getLocallyCreated(PROPERTIES); + + // fetch locally updated tasks + TodorooCursor localUpdated = gtasksMetadataService.getLocallyUpdated(PROPERTIES); + + return new SyncData(remoteTasks, localCreated, localUpdated); + } + + // ---------------------------------------------------------------------- + // ------------------------------------------------- create / push / pull + // ---------------------------------------------------------------------- + + @Override + protected GtasksTaskContainer create(GtasksTaskContainer local) throws IOException { + String list = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST); + if(local.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID)) + list = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID); + + ConvenientTaskCreator createdTask; + try { + createdTask = taskService.createTask(list, local.task.getValue(Task.TITLE)); + } catch (JSONException e) { + throw new RuntimeException(e); + } + + updateTaskHelper(local, null, createdTask); + String remoteId = createdTask.go(); + local.gtaskMetadata.setValue(GtasksMetadata.LIST_ID, remoteId); + + return local; + } + + private void updateTaskHelper(GtasksTaskContainer local, + GtasksTaskContainer remote, TaskBuilder builder) { + + String idTask = local.gtaskMetadata.getValue(GtasksMetadata.ID); + String idList = local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID); + + // fetch remote task for comparison + if(remote == null && idTask != null) + remote = pull(local); + + // moving between lists + if(remote != null && !idList.equals(remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID))) { + a.moveTask(idTask, idList, remote.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), null); + } + + // other properties + if(shouldTransmit(local, Task.TITLE, remote)) + ((TaskModifier)builder).name(local.task.getValue(Task.TITLE)); + if(shouldTransmit(local, Task.DUE_DATE, remote)) + builder.taskDate(local.task.getValue(Task.DUE_DATE)); + if(shouldTransmit(local, Task.COMPLETION_DATE, remote)) + builder.completed(local.task.isCompleted()); + if(shouldTransmit(local, Task.DELETION_DATE, remote)) + builder.deleted(local.task.isDeleted()); + if(shouldTransmit(local, Task.NOTES, remote)) + builder.notes(local.task.getValue(Task.NOTES)); + + // TODO indentation + } + + /** Create a task container for the given RtmTaskSeries + * @throws JSONException */ + private GtasksTaskContainer parseRemoteTask(GoogleTaskTask remoteTask) { + Task task = new Task(); + ArrayList metadata = new ArrayList(); + + task.setValue(Task.TITLE, remoteTask.getName()); + task.setValue(Task.CREATION_DATE, DateUtilities.now()); + task.setValue(Task.COMPLETION_DATE, remoteTask.getCompleted_date()); + task.setValue(Task.DELETION_DATE, remoteTask.isDeleted() ? DateUtilities.now() : 0); + + long dueDate = remoteTask.getTask_date(); + task.setValue(Task.DUE_DATE, task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, dueDate)); + task.setValue(Task.NOTES, remoteTask.getNotes()); + + Metadata gtasksMetadata = GtasksMetadata.createEmptyMetadata(AbstractModel.NO_ID); + gtasksMetadata.setValue(GtasksMetadata.LIST_ID, remoteTask.getList_id()); + // TODO gtasksMetadata.setValue(GtasksMetadata.INDENT, remoteTask.???); + + GtasksTaskContainer container = new GtasksTaskContainer(task, metadata, + gtasksMetadata); + + return container; + } + + @Override + protected GtasksTaskContainer pull(GtasksTaskContainer task) throws IOException { + if(!task.gtaskMetadata.containsNonNullValue(GtasksMetadata.ID) || + !task.gtaskMetadata.containsNonNullValue(GtasksMetadata.LIST_ID)) + throw new ApiServiceException("Tried to read an invalid task"); //$NON-NLS-1$ + + String idToMatch = task.gtaskMetadata.getValue(GtasksMetadata.ID); + List tasks = taskService.getTasks(task.gtaskMetadata.getValue(GtasksMetadata.LIST_ID)); + for(GoogleTaskTask remoteTask : tasks) { + if(remoteTask.getId().equals(idToMatch)) { + return parseRemoteTask(remoteTask); + } + } + + throw new GoogleTasksException("Could not find remote task to pull."); + } + + /** + * Send changes for the given Task across the wire. If a remoteTask is + * supplied, we attempt to intelligently only transmit the values that + * have changed. + */ + @Override + protected void push(GtasksTaskContainer local, GtasksTaskContainer remote) throws IOException { + TaskModifier modifyTask = l.modifyTask(remote.gtaskMetadata.getValue(GtasksMetadata.ID)); + updateTaskHelper(local, remote, modifyTask); + ListAction action = modifyTask.done(); + batch(local.gtaskMetadata.getValue(GtasksMetadata.LIST_ID), action); + } + + /** add action to batch */ + private void batch(String list, ListAction action) { + if(!listActions.containsKey(list)) + listActions.put(list, new ArrayList()); + listActions.get(list).add(action); + } + + // ---------------------------------------------------------------------- + // --------------------------------------------------------- read / write + // ---------------------------------------------------------------------- + + @Override + protected GtasksTaskContainer read(TodorooCursor cursor) throws IOException { + return gtasksMetadataService.readTaskAndMetadata(cursor); + } + + @Override + protected void write(GtasksTaskContainer task) throws IOException { + gtasksMetadataService.saveTaskAndMetadata(task); + } + + // ---------------------------------------------------------------------- + // --------------------------------------------------------- misc helpers + // ---------------------------------------------------------------------- + + @Override + protected int matchTask(ArrayList tasks, GtasksTaskContainer target) { + int length = tasks.size(); + for(int i = 0; i < length; i++) { + GtasksTaskContainer task = tasks.get(i); + if(AndroidUtilities.equals(task.gtaskMetadata, target.gtaskMetadata)) + return i; + } + return -1; + } + + /** + * Determine whether this task's property should be transmitted + * @param task task to consider + * @param property property to consider + * @param remoteTask remote task proxy + * @return + */ + private boolean shouldTransmit(SyncContainer task, Property property, SyncContainer remoteTask) { + if(!task.task.containsValue(property)) + return false; + + if(remoteTask == null) + return true; + if(!remoteTask.task.containsValue(property)) + return true; + + // special cases - match if they're zero or nonzero + if(property == Task.COMPLETION_DATE || + property == Task.DELETION_DATE) + return !AndroidUtilities.equals((Long)task.task.getValue(property) == 0, + (Long)remoteTask.task.getValue(property) == 0); + + return !AndroidUtilities.equals(task.task.getValue(property), + remoteTask.task.getValue(property)); + } + + @Override + protected int updateNotification(Context context, Notification notification) { + String notificationTitle = context.getString(R.string.gtasks_notification_title); + Intent intent = new Intent(context, GtasksPreferences.class); + PendingIntent notificationIntent = PendingIntent.getActivity(context, 0, + intent, 0); + notification.setLatestEventInfo(context, + notificationTitle, context.getString(R.string.SyP_progress), + notificationIntent); + return Constants.NOTIFICATION_SYNC; + } + + @Override + protected void transferIdentifiers(GtasksTaskContainer source, + GtasksTaskContainer destination) { + destination.gtaskMetadata = source.gtaskMetadata; + } +}