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