diff --git a/api/src/com/todoroo/andlib/sql/Join.java b/api/src/com/todoroo/andlib/sql/Join.java index ac8a479f2..dd2777ada 100644 --- a/api/src/com/todoroo/andlib/sql/Join.java +++ b/api/src/com/todoroo/andlib/sql/Join.java @@ -1,5 +1,6 @@ package com.todoroo.andlib.sql; +import static com.todoroo.andlib.sql.SqlConstants.AND; import static com.todoroo.andlib.sql.SqlConstants.JOIN; import static com.todoroo.andlib.sql.SqlConstants.ON; import static com.todoroo.andlib.sql.SqlConstants.SPACE; @@ -32,12 +33,16 @@ public class Join { } @Override + @SuppressWarnings("nls") public String toString() { StringBuilder sb = new StringBuilder(); - sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON); - for (Criterion criterion : criterions) { - sb.append(SPACE).append(criterion); + sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON).append(SPACE).append("("); + for (int i = 0; i < criterions.length; i++) { + sb.append(criterions[i]); + if (i < criterions.length - 1) + sb.append(SPACE).append(AND).append(SPACE); } + sb.append(")"); return sb.toString(); } } diff --git a/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java b/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java index 2dc07e324..b3a7f7471 100644 --- a/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java +++ b/astrid/plugin-src/com/timsu/astrid/C2DMReceiver.java @@ -159,6 +159,7 @@ public class C2DMReceiver extends BroadcastReceiver { task.readFromCursor(cursor); } + task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); actFmSyncService.fetchTask(task); } catch(NumberFormatException e) { // invalid task id @@ -300,6 +301,7 @@ public class C2DMReceiver extends BroadcastReceiver { task.setValue(Task.TITLE, intent.getStringExtra("title")); task.setValue(Task.REMOTE_ID, Long.parseLong(intent.getStringExtra("task_id"))); task.setValue(Task.USER_ID, Task.USER_ID_UNASSIGNED); + task.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); task.putTransitory(SyncFlags.ACTFM_SUPPRESS_SYNC, true); taskService.save(task); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmPreferences.java b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmPreferences.java index 3980ae393..1e5fb59a3 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmPreferences.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/ActFmPreferences.java @@ -1,5 +1,6 @@ package com.todoroo.astrid.actfm; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.preference.Preference; @@ -7,8 +8,10 @@ import android.preference.PreferenceCategory; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmSyncV2Provider; +import com.todoroo.astrid.gtasks.GtasksPreferenceService; import com.todoroo.astrid.sync.SyncProviderPreferences; import com.todoroo.astrid.sync.SyncProviderUtilities; @@ -22,6 +25,7 @@ import com.todoroo.astrid.sync.SyncProviderUtilities; public class ActFmPreferences extends SyncProviderPreferences { @Autowired ActFmPreferenceService actFmPreferenceService; + @Autowired GtasksPreferenceService gtasksPreferenceService; @Override public int getPreferenceResource() { @@ -31,14 +35,28 @@ public class ActFmPreferences extends SyncProviderPreferences { @Override public void startSync() { if (!actFmPreferenceService.isLoggedIn()) { - Intent intent = new Intent(this, ActFmLoginActivity.class); - startActivityForResult(intent, REQUEST_LOGIN); + if (gtasksPreferenceService.isLoggedIn()) { + DialogUtilities.okCancelDialog(this, getString(R.string.DLG_warning), getString(R.string.actfm_dual_sync_warning), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + startLogin(); + } + }, null); + } else { + startLogin(); + } } else { setResult(RESULT_CODE_SYNCHRONIZE); finish(); } } + private void startLogin() { + Intent intent = new Intent(this, ActFmLoginActivity.class); + startActivityForResult(intent, REQUEST_LOGIN); + } + @Override public void logOut() { new ActFmSyncV2Provider().signOut(); diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java index a4d489c61..f5b0060fe 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java @@ -39,6 +39,7 @@ import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.AndroidUtilities; @@ -59,6 +60,8 @@ import com.todoroo.astrid.data.TaskApiDao; import com.todoroo.astrid.data.Update; import com.todoroo.astrid.data.User; import com.todoroo.astrid.files.FileMetadata; +import com.todoroo.astrid.gtasks.GtasksMetadata; +import com.todoroo.astrid.gtasks.GtasksPreferenceService; import com.todoroo.astrid.helper.ImageDiskCache; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.StatisticsConstants; @@ -84,6 +87,7 @@ public final class ActFmSyncService { @Autowired MetadataService metadataService; @Autowired TaskService taskService; @Autowired ActFmPreferenceService actFmPreferenceService; + @Autowired GtasksPreferenceService gtasksPreferenceService; @Autowired ActFmInvoker actFmInvoker; @Autowired ActFmDataService actFmDataService; @Autowired TaskDao taskDao; @@ -1139,6 +1143,11 @@ public final class ActFmSyncService { remote.putTransitory(SyncFlags.ACTFM_SUPPRESS_SYNC, true); if (remote.getValue(Task.USER_ID) != Task.USER_ID_SELF) remote.putTransitory(SyncFlags.GTASKS_SUPPRESS_SYNC, true); + + if (!remote.isSaved() && gtasksPreferenceService.isLoggedIn()) { + titleMatchOnGoogleTask(remote); + } + taskService.save(remote); ids.add(remote.getId()); metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY)); @@ -1152,6 +1161,21 @@ public final class ActFmSyncService { } } + private void titleMatchOnGoogleTask(Task remote) { + String title = remote.getValue(Task.TITLE); + TodorooCursor match = taskService.query(Query.select(Task.ID) + .join(Join.inner(Metadata.TABLE, Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), Metadata.TASK.eq(Task.ID)))) + .where(Criterion.and(Task.TITLE.eq(title), Task.REMOTE_ID.isNull()))); + try { + if (match.getCount() > 0) { + match.moveToFirst(); + remote.setId(match.get(Task.ID)); + } + } finally { + match.close(); + } + } + protected void deleteExtras(Long[] localIds) { taskService.deleteWhere(Criterion.and(TaskCriteria.activeVisibleMine(), Task.REMOTE_ID.isNotNull(), diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksPreferences.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksPreferences.java index 48ea5f42e..6209bfaa9 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksPreferences.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksPreferences.java @@ -1,11 +1,14 @@ package com.todoroo.astrid.gtasks; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.utility.DialogUtilities; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity; import com.todoroo.astrid.gtasks.sync.GtasksSyncV2Provider; import com.todoroo.astrid.sync.SyncProviderPreferences; @@ -21,6 +24,7 @@ import com.todoroo.astrid.sync.SyncProviderUtilities; public class GtasksPreferences extends SyncProviderPreferences { @Autowired private GtasksPreferenceService gtasksPreferenceService; + @Autowired private ActFmPreferenceService actFmPreferenceService; public GtasksPreferences() { super(); @@ -40,14 +44,28 @@ public class GtasksPreferences extends SyncProviderPreferences { @Override public void startSync() { if (!gtasksPreferenceService.isLoggedIn()) { - Intent intent = new Intent(this, GtasksLoginActivity.class); - startActivityForResult(intent, REQUEST_LOGIN); + if (actFmPreferenceService.isLoggedIn()) { + DialogUtilities.okCancelDialog(this, getString(R.string.DLG_warning), getString(R.string.gtasks_dual_sync_warning), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + startLogin(); + } + }, null); + } else { + startLogin(); + } } else { setResult(RESULT_CODE_SYNCHRONIZE); finish(); } } + private void startLogin() { + Intent intent = new Intent(this, GtasksLoginActivity.class); + startActivityForResult(intent, REQUEST_LOGIN); + } + @Override public void logOut() { GtasksSyncV2Provider.getInstance().signOut(); diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java index 3be83c10d..6a2665816 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncService.java @@ -108,9 +108,6 @@ public final class GtasksSyncService { return; Task toPush = taskDao.fetch(model.getId(), TASK_PROPERTIES); - if (toPush.getValue(Task.USER_ID) != Task.USER_ID_SELF) - return; - operationQueue.offer(new TaskPushOp(toPush)); } }); @@ -154,7 +151,7 @@ public final class GtasksSyncService { } private static final Property[] TASK_PROPERTIES = { Task.ID, Task.TITLE, - Task.NOTES, Task.DUE_DATE, Task.COMPLETION_DATE, Task.DELETION_DATE }; + Task.NOTES, Task.DUE_DATE, Task.COMPLETION_DATE, Task.DELETION_DATE, Task.USER_ID }; /** * Checks to see if any of the values changed are among the properties we sync @@ -197,6 +194,18 @@ public final class GtasksSyncService { com.google.api.services.tasks.model.Task remoteModel = null; boolean newlyCreated = false; + if (values.containsKey(Task.USER_ID.name) && values.getAsLong(Task.USER_ID.name) != Task.USER_ID_SELF) { + if (gtasksMetadata != null && !TextUtils.isEmpty(gtasksMetadata.getValue(GtasksMetadata.ID))) { + try { + invoker.deleteGtask(gtasksMetadata.getValue(GtasksMetadata.LIST_ID), gtasksMetadata.getValue(GtasksMetadata.ID)); + metadataDao.delete(gtasksMetadata.getId()); + } catch (IOException e) { + // + } + } + return; + } + String remoteId = null; String listId = Preferences.getStringValue(GtasksPreferenceService.PREF_DEFAULT_LIST); if (listId == null) { diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java index 3063e1a20..74d449679 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/sync/GtasksSyncV2Provider.java @@ -22,6 +22,7 @@ import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.Preferences; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.StoreObjectDao; @@ -53,6 +54,7 @@ public class GtasksSyncV2Provider extends SyncV2Provider { @Autowired TaskService taskService; @Autowired MetadataService metadataService; @Autowired StoreObjectDao storeObjectDao; + @Autowired ActFmPreferenceService actFmPreferenceService; @Autowired GtasksPreferenceService gtasksPreferenceService; @Autowired GtasksSyncService gtasksSyncService; @Autowired GtasksListService gtasksListService; @@ -126,8 +128,6 @@ public class GtasksSyncV2Provider extends SyncV2Provider { callback.incrementMax(25 * lists.length); final AtomicInteger finisher = new AtomicInteger(lists.length); - pushUpdated(invoker, callback); - for (final StoreObject list : lists) { new Thread(new Runnable() { @Override @@ -135,23 +135,23 @@ public class GtasksSyncV2Provider extends SyncV2Provider { synchronizeListHelper(list, invoker, manual, handler, callback); callback.incrementProgress(25); if (finisher.decrementAndGet() == 0) { + pushUpdated(invoker, callback); finishSync(callback); } } }).start(); } + } }).start(); } private synchronized void pushUpdated(GtasksInvoker invoker, SyncResultCallback callback) { TodorooCursor queued = taskService.query(Query.select(Task.PROPERTIES). - join(Join.left(Metadata.TABLE, Task.ID.eq(Metadata.TASK))).where( - Criterion.and(Task.USER_ID.eq(Task.USER_ID_SELF), - Criterion.or( - Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), - Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC)), - Metadata.KEY.isNull())))); + join(Join.left(Metadata.TABLE, Criterion.and(MetadataCriteria.withKey(GtasksMetadata.METADATA_KEY), Task.ID.eq(Metadata.TASK)))).where( + Criterion.or(Task.MODIFICATION_DATE.gt(GtasksMetadata.LAST_SYNC), + Criterion.and(Task.USER_ID.neq(Task.USER_ID_SELF), GtasksMetadata.ID.isNotNull()), + Metadata.KEY.isNull()))); callback.incrementMax(queued.getCount() * 10); try { Task task = new Task(); @@ -303,6 +303,9 @@ public class GtasksSyncV2Provider extends SyncV2Provider { private void write(GtasksTaskContainer task) throws IOException { // merge astrid dates with google dates + if (!task.task.isSaved() && actFmPreferenceService.isLoggedIn()) + titleMatchWithActFm(task.task); + if(task.task.isSaved()) { Task local = PluginServices.getTaskService().fetchById(task.task.getId(), Task.DUE_DATE, Task.COMPLETION_DATE); if (local == null) { @@ -312,7 +315,9 @@ public class GtasksSyncV2Provider extends SyncV2Provider { if(task.task.isCompleted() && !local.isCompleted()) StatisticsService.reportEvent(StatisticsConstants.GTASKS_TASK_COMPLETED); } - } else { // Set default reminders for remotely created tasks + } else { // Set default importance and reminders for remotely created tasks + task.task.setValue(Task.IMPORTANCE, Preferences.getIntegerFromString( + R.string.p_default_importance_key, Task.IMPORTANCE_SHOULD_DO)); TaskDao.setDefaultReminders(task.task); } if (!TextUtils.isEmpty(task.task.getValue(Task.TITLE))) { @@ -321,6 +326,21 @@ public class GtasksSyncV2Provider extends SyncV2Provider { } } + private void titleMatchWithActFm(Task task) { + String title = task.getValue(Task.TITLE); + TodorooCursor match = taskService.query(Query.select(Task.ID) + .join(Join.left(Metadata.TABLE, Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), Metadata.TASK.eq(Task.ID)))) + .where(Criterion.and(Task.TITLE.eq(title), GtasksMetadata.ID.isNull()))); + try { + if (match.getCount() > 0) { + match.moveToFirst(); + task.setId(match.get(Task.ID)); + } + } finally { + match.close(); + } + } + private void mergeDates(Task remote, Task local) { if(remote.hasDueDate() && local.hasDueTime()) { Date newDate = new Date(remote.getValue(Task.DUE_DATE)); diff --git a/astrid/res/values/strings-actfm.xml b/astrid/res/values/strings-actfm.xml index 43415617a..7fc6d3fb0 100644 --- a/astrid/res/values/strings-actfm.xml +++ b/astrid/res/values/strings-actfm.xml @@ -287,5 +287,9 @@ New comments received / click for more details + You are currently synchronizing with Google Tasks. + Be advised that synchronizing with both services can in some cases lead to unexpected results. + Are you sure you want to sync with Astrid.com? + diff --git a/astrid/res/values/strings-core.xml b/astrid/res/values/strings-core.xml index 4c806f404..5742df960 100644 --- a/astrid/res/values/strings-core.xml +++ b/astrid/res/values/strings-core.xml @@ -67,6 +67,8 @@ Undo + Warning + diff --git a/astrid/res/values/strings-gtasks.xml b/astrid/res/values/strings-gtasks.xml index 9c3c3f65d..e9754ebb3 100644 --- a/astrid/res/values/strings-gtasks.xml +++ b/astrid/res/values/strings-gtasks.xml @@ -104,6 +104,9 @@ Error authenticating in background. Please try initiating a sync while Astrid is running. + You are currently synchronizing with Astrid.com. + Be advised that synchronizing with both services can in some cases lead to unexpected results. + Are you sure you want to sync with Google Tasks? diff --git a/astrid/src/com/todoroo/astrid/helper/SyncActionHelper.java b/astrid/src/com/todoroo/astrid/helper/SyncActionHelper.java index f1fdc3d04..5e384738a 100644 --- a/astrid/src/com/todoroo/astrid/helper/SyncActionHelper.java +++ b/astrid/src/com/todoroo/astrid/helper/SyncActionHelper.java @@ -9,7 +9,6 @@ import org.weloveastrid.rmilk.MilkUtilities; import android.app.Activity; import android.app.AlertDialog; -import android.app.PendingIntent.CanceledException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -20,7 +19,6 @@ import android.content.pm.ResolveInfo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.widget.ArrayAdapter; -import android.widget.Toast; import com.timsu.astrid.R; import com.todoroo.andlib.service.Autowired; @@ -229,35 +227,7 @@ public class SyncActionHelper { showSyncOptionMenu(actions, listener); } else { - // We have sync actions, pop up a dialogue so the user can - // select just one of them (only sync one at a time) - final Object[] actions = new Object[activeSyncs]; - - int i; - for (i = 0; i < activeV2Providers.size(); i++) - actions[i] = activeV2Providers.get(i); - for (SyncAction syncAction : syncActions) - actions[i++] = syncAction; - - DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface click, int which) { - if (actions[which] instanceof SyncAction) { - try { - ((SyncAction) actions[which]).intent.send(); - Toast.makeText(activity, - R.string.SyP_progress_toast, - Toast.LENGTH_LONG).show(); - } catch (CanceledException e) { - // - } - } else { - ((SyncV2Provider) actions[which]).synchronizeActiveTasks( - true, syncResultCallback); - } - } - }; - showSyncOptionMenu(actions, listener); + syncService.synchronizeActiveTasks(true, syncResultCallback); } } diff --git a/astrid/src/com/todoroo/astrid/service/SyncV2Service.java b/astrid/src/com/todoroo/astrid/service/SyncV2Service.java index eb50ea826..0265450f0 100644 --- a/astrid/src/com/todoroo/astrid/service/SyncV2Service.java +++ b/astrid/src/com/todoroo/astrid/service/SyncV2Service.java @@ -24,8 +24,8 @@ public class SyncV2Service { * for responding to sync requests through this new API. */ private static final SyncV2Provider[] providers = new SyncV2Provider[] { + GtasksSyncV2Provider.getInstance(), new ActFmSyncV2Provider(), - GtasksSyncV2Provider.getInstance() }; /** @@ -55,10 +55,39 @@ public class SyncV2Service { * @param manual if manual sync * @param callback result callback */ - public void synchronizeActiveTasks(boolean manual, SyncResultCallback callback) { - for(SyncV2Provider provider : providers) { - if(provider.isActive()) - provider.synchronizeActiveTasks(manual, callback); + public void synchronizeActiveTasks(final boolean manual, final SyncResultCallback callback) { + final List active = activeProviders(); + + if (active.size() > 1) { + SyncResultCallback newCallback = new SyncResultCallback() { + private int next = 1; + + @Override + public void started() { + callback.started(); + } + + @Override + public void incrementProgress(int incrementBy) { + callback.incrementProgress(incrementBy); + } + + @Override + public void incrementMax(int incrementBy) { + callback.incrementMax(incrementBy); + } + + @Override + public void finished() { + callback.finished(); + if (next < active.size()) + active.get(next++).synchronizeActiveTasks(manual, this); + } + }; + + active.get(0).synchronizeActiveTasks(manual, newCallback); + } else if (active.size() == 1) { + active.get(0).synchronizeActiveTasks(manual, callback); } }