mirror of https://github.com/tasks/tasks
Merge remote-tracking branch 'upstream/120112-ts-syncv2'
commit
ec07d3b60f
@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* See the file "LICENSE" for the full license governing this code.
|
||||||
|
*/
|
||||||
|
package com.todoroo.astrid.actfm.sync;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.data.TodorooCursor;
|
||||||
|
import com.todoroo.andlib.service.Autowired;
|
||||||
|
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||||
|
import com.todoroo.andlib.service.ExceptionService;
|
||||||
|
import com.todoroo.andlib.sql.Criterion;
|
||||||
|
import com.todoroo.andlib.sql.Query;
|
||||||
|
import com.todoroo.andlib.utility.Preferences;
|
||||||
|
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
|
||||||
|
import com.todoroo.astrid.data.TagData;
|
||||||
|
import com.todoroo.astrid.data.Task;
|
||||||
|
import com.todoroo.astrid.service.AstridDependencyInjector;
|
||||||
|
import com.todoroo.astrid.service.StartupService;
|
||||||
|
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
|
||||||
|
import com.todoroo.astrid.service.SyncV2Service.SyncV2Provider;
|
||||||
|
import com.todoroo.astrid.service.TaskService;
|
||||||
|
import com.todoroo.astrid.tags.TagService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes sync action
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ActFmSyncV2Provider implements SyncV2Provider {
|
||||||
|
|
||||||
|
@Autowired ActFmPreferenceService actFmPreferenceService;
|
||||||
|
|
||||||
|
@Autowired ActFmSyncService actFmSyncService;
|
||||||
|
|
||||||
|
@Autowired ExceptionService exceptionService;
|
||||||
|
|
||||||
|
@Autowired TaskService taskService;
|
||||||
|
|
||||||
|
static {
|
||||||
|
AstridDependencyInjector.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActFmSyncV2Provider() {
|
||||||
|
DependencyInjectionService.getInstance().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return actFmPreferenceService.isLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String LAST_TAG_FETCH_TIME = "actfm_lastTag"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
// --- synchronize active tasks
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void synchronizeActiveTasks(boolean manual,
|
||||||
|
final SyncResultCallback callback) {
|
||||||
|
|
||||||
|
callback.started();
|
||||||
|
callback.incrementMax(100);
|
||||||
|
|
||||||
|
final AtomicInteger finisher = new AtomicInteger(2);
|
||||||
|
|
||||||
|
startTagFetcher(callback, finisher);
|
||||||
|
|
||||||
|
startTaskFetcher(manual, callback, finisher);
|
||||||
|
|
||||||
|
pushQueued(callback, finisher);
|
||||||
|
|
||||||
|
callback.incrementProgress(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** fetch changes to tags */
|
||||||
|
private void startTagFetcher(final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int time = Preferences.getInt(LAST_TAG_FETCH_TIME, 0);
|
||||||
|
try {
|
||||||
|
time = actFmSyncService.fetchTags(time);
|
||||||
|
Preferences.setInt(LAST_TAG_FETCH_TIME, time);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
exceptionService.reportError("actfm-sync", e); //$NON-NLS-1$
|
||||||
|
} catch (IOException e) {
|
||||||
|
exceptionService.reportError("actfm-sync", e); //$NON-NLS-1$
|
||||||
|
} finally {
|
||||||
|
callback.incrementProgress(20);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return runnable to fetch changes to tags */
|
||||||
|
private void startTaskFetcher(final boolean manual, final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
actFmSyncService.fetchActiveTasks(manual, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.incrementProgress(30);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pushQueued(final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
TodorooCursor<Task> cursor = taskService.query(Query.select(Task.PROPERTIES).
|
||||||
|
where(Criterion.or(
|
||||||
|
Criterion.and(TaskCriteria.isActive(),
|
||||||
|
Task.ID.gt(StartupService.INTRO_TASK_SIZE),
|
||||||
|
Task.REMOTE_ID.eq(0)),
|
||||||
|
Criterion.and(Task.REMOTE_ID.gt(0),
|
||||||
|
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC)))));
|
||||||
|
|
||||||
|
try {
|
||||||
|
callback.incrementMax(cursor.getCount() * 20);
|
||||||
|
finisher.addAndGet(cursor.getCount());
|
||||||
|
|
||||||
|
for(int i = 0; i < cursor.getCount(); i++) {
|
||||||
|
cursor.moveToNext();
|
||||||
|
final Task task = new Task(cursor);
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
actFmSyncService.pushTaskOnSave(task, task.getMergedValues());
|
||||||
|
} finally {
|
||||||
|
callback.incrementProgress(20);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- synchronize list
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void synchronizeList(Object list, boolean manual,
|
||||||
|
final SyncResultCallback callback) {
|
||||||
|
|
||||||
|
if(!(list instanceof TagData))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TagData tagData = (TagData) list;
|
||||||
|
final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
|
||||||
|
|
||||||
|
if(noRemoteId && !manual)
|
||||||
|
return;
|
||||||
|
|
||||||
|
callback.started();
|
||||||
|
callback.incrementMax(100);
|
||||||
|
|
||||||
|
final AtomicInteger finisher = new AtomicInteger(3);
|
||||||
|
|
||||||
|
fetchTagData(tagData, noRemoteId, manual, callback, finisher);
|
||||||
|
|
||||||
|
if(!noRemoteId) {
|
||||||
|
fetchTasksForTag(tagData, manual, callback, finisher);
|
||||||
|
fetchUpdatesForTag(tagData, manual, callback, finisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.incrementProgress(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchTagData(final TagData tagData, final boolean noRemoteId,
|
||||||
|
final boolean manual, final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
String oldName = tagData.getValue(TagData.NAME);
|
||||||
|
try {
|
||||||
|
actFmSyncService.fetchTag(tagData);
|
||||||
|
|
||||||
|
if(noRemoteId) {
|
||||||
|
fetchTasksForTag(tagData, manual, callback, finisher);
|
||||||
|
fetchUpdatesForTag(tagData, manual, callback, finisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!oldName.equals(tagData.getValue(TagData.NAME))) {
|
||||||
|
TagService.getInstance().rename(oldName,
|
||||||
|
tagData.getValue(TagData.NAME));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
exceptionService.reportError("sync-io", e); //$NON-NLS-1$
|
||||||
|
} catch (JSONException e) {
|
||||||
|
exceptionService.reportError("sync-json", e); //$NON-NLS-1$
|
||||||
|
} finally {
|
||||||
|
callback.incrementProgress(20);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchUpdatesForTag(TagData tagData, boolean manual, final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.incrementProgress(20);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchTasksForTag(TagData tagData, boolean manual, final SyncResultCallback callback,
|
||||||
|
final AtomicInteger finisher) {
|
||||||
|
actFmSyncService.fetchTasksForTag(tagData, manual, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
callback.incrementProgress(30);
|
||||||
|
if(finisher.decrementAndGet() == 0)
|
||||||
|
callback.finished();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
package com.todoroo.astrid.helper;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||||
|
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
|
||||||
|
|
||||||
|
public class ProgressBarSyncResultCallback implements SyncResultCallback {
|
||||||
|
|
||||||
|
private final ProgressBar progressBar;
|
||||||
|
private final Activity activity;
|
||||||
|
private final Runnable onFinished;
|
||||||
|
|
||||||
|
private final AtomicInteger providers = new AtomicInteger(0);
|
||||||
|
|
||||||
|
public ProgressBarSyncResultCallback(Activity activity,
|
||||||
|
int progressBarId, Runnable onFinished) {
|
||||||
|
this.progressBar = (ProgressBar) activity.findViewById(progressBarId);
|
||||||
|
this.activity = activity;
|
||||||
|
this.onFinished = onFinished;
|
||||||
|
progressBar.setProgress(0);
|
||||||
|
progressBar.setMax(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finished() {
|
||||||
|
if(providers.decrementAndGet() == 0) {
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setMax(100);
|
||||||
|
progressBar.setProgress(100);
|
||||||
|
AlphaAnimation animation = new AlphaAnimation(1, 0);
|
||||||
|
animation.setFillAfter(true);
|
||||||
|
animation.setDuration(1000L);
|
||||||
|
progressBar.startAnimation(animation);
|
||||||
|
|
||||||
|
onFinished.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
AndroidUtilities.sleepDeep(1000);
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementMax(final int incrementBy) {
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setMax(progressBar.getMax() + incrementBy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementProgress(final int incrementBy) {
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.incrementProgressBy(incrementBy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void started() {
|
||||||
|
if(providers.incrementAndGet() == 1) {
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
AlphaAnimation animation = new AlphaAnimation(0, 1);
|
||||||
|
animation.setFillAfter(true);
|
||||||
|
animation.setDuration(1000L);
|
||||||
|
progressBar.startAnimation(animation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package com.todoroo.astrid.service;
|
||||||
|
|
||||||
|
import com.todoroo.astrid.actfm.sync.ActFmSyncV2Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SyncV2Service is a simplified synchronization interface for supporting
|
||||||
|
* next-generation sync interfaces such as Google Tasks and Astrid.com
|
||||||
|
*
|
||||||
|
* @author Tim Su <tim@astrid.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SyncV2Service {
|
||||||
|
|
||||||
|
public interface SyncResultCallback {
|
||||||
|
/**
|
||||||
|
* Increment max sync progress
|
||||||
|
* @param incrementBy
|
||||||
|
*/
|
||||||
|
public void incrementMax(int incrementBy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment current sync progress
|
||||||
|
* @param incrementBy
|
||||||
|
*/
|
||||||
|
public void incrementProgress(int incrementBy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider started sync
|
||||||
|
*/
|
||||||
|
public void started();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider finished sync
|
||||||
|
*/
|
||||||
|
public void finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SyncV2Provider {
|
||||||
|
public boolean isActive();
|
||||||
|
public void synchronizeActiveTasks(boolean manual, SyncResultCallback callback);
|
||||||
|
public void synchronizeList(Object list, boolean manual, SyncResultCallback callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At present, sync provider interactions are handled through code. If
|
||||||
|
* there is enough interest, the Astrid team could create an interface
|
||||||
|
* for responding to sync requests through this new API.
|
||||||
|
*/
|
||||||
|
private final SyncV2Provider[] providers = new SyncV2Provider[] {
|
||||||
|
new ActFmSyncV2Provider()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if synchronization is available
|
||||||
|
*
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
public boolean isActive() {
|
||||||
|
for(SyncV2Provider provider : providers) {
|
||||||
|
if(provider.isActive())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate synchronization of active tasks
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate synchronization of task list
|
||||||
|
*
|
||||||
|
* @param list list object
|
||||||
|
* @param manual if manual sync
|
||||||
|
* @param callback result callback
|
||||||
|
*/
|
||||||
|
public void synchronizeList(Object list, boolean manual, SyncResultCallback callback) {
|
||||||
|
for(SyncV2Provider provider : providers) {
|
||||||
|
if(provider.isActive())
|
||||||
|
provider.synchronizeList(list, manual, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue