Merge pull request #186 from sbosley/120611_sb_dual_sync_improvements

Gtasks+Astrid.com sync improvements
pull/14/head
sbosley 12 years ago
commit 692aa202e9

@ -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();
}
}

@ -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);

@ -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();

@ -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<Task> 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(),

@ -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();

@ -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) {

@ -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<Task> 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<Task> 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));

@ -287,5 +287,9 @@
<!-- text for notification when comments are received -->
<string name="actfm_notification_comments">New comments received / click for more details</string>
<string name="actfm_dual_sync_warning">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?</string>
</resources>

@ -67,6 +67,8 @@
<string name="DLG_undo">Undo</string>
<string name="DLG_warning">Warning</string>
<!-- =============================================================== UI == -->
<!-- Label for DateButtons with no value -->

@ -104,6 +104,9 @@
<!-- Error when authorization error happens in background sync -->
<string name="gtasks_error_background_sync_auth">Error authenticating in background. Please try initiating a sync while Astrid is running.</string>
<string name="gtasks_dual_sync_warning">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?</string>
</resources>

@ -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);
}
}

@ -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<SyncV2Provider> 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);
}
}

Loading…
Cancel
Save