diff --git a/api/src/com/todoroo/andlib/utility/AndroidUtilities.java b/api/src/com/todoroo/andlib/utility/AndroidUtilities.java
index 81cf3def7..53d708de8 100644
--- a/api/src/com/todoroo/andlib/utility/AndroidUtilities.java
+++ b/api/src/com/todoroo/andlib/utility/AndroidUtilities.java
@@ -510,16 +510,55 @@ public class AndroidUtilities {
* @param args arguments
* @return method return value, or null if nothing was called or exception
*/
- @SuppressWarnings("nls")
public static Object callApiMethod(int minSdk, Object receiver,
String methodName, Class>[] params, Object... args) {
if(getSdkVersion() < minSdk)
return null;
- Method method;
+ return AndroidUtilities.callMethod(receiver.getClass(),
+ receiver, methodName, params, args);
+ }
+
+ /**
+ * Call a static method via reflection if API level is at least minSdk
+ * @param minSdk minimum sdk number (i.e. 8)
+ * @param className fully qualified class to call method on
+ * @param methodName method name to call
+ * @param params method parameter types
+ * @param args arguments
+ * @return method return value, or null if nothing was called or exception
+ */
+ @SuppressWarnings("nls")
+ public static Object callApiStaticMethod(int minSdk, String className,
+ String methodName, Class>[] params, Object... args) {
+ if(getSdkVersion() < minSdk)
+ return null;
+
+ try {
+ return AndroidUtilities.callMethod(Class.forName(className),
+ null, methodName, params, args);
+ } catch (ClassNotFoundException e) {
+ getExceptionService().reportError("call-method", e);
+ return null;
+ }
+ }
+
+ /**
+ * Call a method via reflection
+ * @param class class to call method on
+ * @param receiver object to call method on (can be null)
+ * @param methodName method name to call
+ * @param params method parameter types
+ * @param args arguments
+ * @return method return value, or null if nothing was called or exception
+ */
+ @SuppressWarnings("nls")
+ public static Object callMethod(Class> cls, Object receiver,
+ String methodName, Class>[] params, Object... args) {
try {
- method = receiver.getClass().getMethod(methodName, params);
- return method.invoke(receiver, args);
+ Method method = cls.getMethod(methodName, params);
+ Object result = method.invoke(receiver, args);
+ return result;
} catch (SecurityException e) {
getExceptionService().reportError("call-method", e);
} catch (NoSuchMethodException e) {
diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index 6fb0366cc..e13742870 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -331,12 +331,6 @@
-
-
-
-
-
-
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java
index 22155a939..8cd793a1a 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagUpdatesActivity.java
@@ -1,7 +1,6 @@
package com.todoroo.astrid.actfm;
import android.app.ListActivity;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -28,7 +27,6 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
-import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback;
import com.todoroo.astrid.actfm.ActFmCameraModule.ClearImageCallback;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
@@ -38,6 +36,7 @@ import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
+import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.TagDataService;
@@ -141,7 +140,7 @@ public class TagUpdatesActivity extends ListActivity {
});
refreshUpdatesList();
- refreshActivity(null); // start a pull in the background
+ refreshActivity(false); // start a pull in the background
}
private void refreshUpdatesList() {
@@ -193,8 +192,7 @@ public class TagUpdatesActivity extends ListActivity {
case MENU_REFRESH_ID: {
- final ProgressDialog progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
- refreshActivity(progressDialog);
+ refreshActivity(true);
return true;
}
@@ -202,20 +200,25 @@ public class TagUpdatesActivity extends ListActivity {
}
}
- private void refreshActivity(final ProgressDialog progressDialog) {
- actFmSyncService.fetchUpdatesForTag(tagData, true, new Runnable() {
+ private void refreshActivity(boolean manual) {
+ final ProgressBarSyncResultCallback callback = new ProgressBarSyncResultCallback(
+ this, R.id.progressBar, new Runnable() {
+ @Override
+ public void run() {
+ refreshUpdatesList();
+ }
+ });
+
+ callback.started();
+ callback.incrementMax(100);
+ actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
@Override
public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- refreshUpdatesList();
- if (progressDialog != null)
- DialogUtilities.dismissDialog(TagUpdatesActivity.this, progressDialog);
- }
- });
+ callback.incrementProgress(50);
+ callback.finished();
}
});
+ callback.incrementProgress(50);
}
@SuppressWarnings("nls")
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java
index 19d5a88f4..7f23d8bd1 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewActivity.java
@@ -2,13 +2,10 @@ package com.todoroo.astrid.actfm;
import greendroid.widget.AsyncImageView;
-import java.io.IOException;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -16,7 +13,6 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
@@ -50,9 +46,9 @@ import com.todoroo.astrid.core.SortHelper;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
+import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.tags.TagFilterExposer;
-import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag;
import com.todoroo.astrid.welcome.HelpInfoPopover;
@@ -71,7 +67,7 @@ public class TagViewActivity extends TaskListActivity {
private static final int REQUEST_CODE_SETTINGS = 0;
- public static final String TOKEN_START_ACTIVITY = "startActivity";
+ public static final String TOKEN_START_ACTIVITY = "startActivity"; //$NON-NLS-1$
private TagData tagData;
@@ -214,17 +210,6 @@ public class TagViewActivity extends TaskListActivity {
cursor.close();
}
- if(tagData.getValue(TagData.REMOTE_ID) > 0) {
- String fetchKey = LAST_FETCH_KEY + tagData.getId();
- long lastFetchDate = Preferences.getLong(fetchKey, 0);
- if(DateUtilities.now() > lastFetchDate + 300000L) {
- refreshData(false, false);
- Preferences.setLong(fetchKey, DateUtilities.now());
- }
- } else {
- ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
- }
-
setUpMembersGallery();
super.onNewIntent(intent);
@@ -258,81 +243,33 @@ public class TagViewActivity extends TaskListActivity {
// --------------------------------------------------------- refresh data
- /** refresh the list with latest data from the web */
- private void refreshData(final boolean manual, boolean bypassTagShow) {
- final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
-
- final ProgressDialog progressDialog;
- if(manual && !noRemoteId)
- progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
- else
- progressDialog = null;
- Thread tagShowThread = new Thread(new Runnable() {
- @SuppressWarnings("nls")
- @Override
- public void run() {
- try {
- String oldName = tagData.getValue(TagData.NAME);
- actFmSyncService.fetchTag(tagData);
-
- DialogUtilities.dismissDialog(TagViewActivity.this, progressDialog);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if(noRemoteId && tagData.getValue(TagData.REMOTE_ID) > 0)
- refreshData(manual, true);
- }
- });
-
- if(!oldName.equals(tagData.getValue(TagData.NAME))) {
- TagService.getInstance().rename(oldName,
- tagData.getValue(TagData.NAME));
- }
-
- } catch (IOException e) {
- Log.e("tag-view-activity", "error-fetching-task-io", e);
- } catch (JSONException e) {
- Log.e("tag-view-activity", "error-fetching-task", e);
- }
- }
- });
- if(!bypassTagShow)
- tagShowThread.start();
+ @Override
+ protected void initiateAutomaticSync() {
+ long lastAutoSync = Preferences.getLong(LAST_FETCH_KEY + tagData.getId(), 0);
+ if(DateUtilities.now() - lastAutoSync > DateUtilities.ONE_HOUR)
+ refreshData(false);
+ }
- if(noRemoteId) {
- ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
- return;
- }
+ /** refresh the list with latest data from the web */
+ private void refreshData(final boolean manual) {
+ ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.DLG_loading);
- setUpMembersGallery();
- actFmSyncService.fetchTasksForTag(tagData, manual, new Runnable() {
+ syncService.synchronizeList(tagData, manual, new ProgressBarSyncResultCallback(this,
+ R.id.progressBar, new Runnable() {
@Override
public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- loadTaskListContent(true);
- ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
- DialogUtilities.dismissDialog(TagViewActivity.this, progressDialog);
- }
- });
+ setUpMembersGallery();
+ loadTaskListContent(true);
+ ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
}
- });
+ }));
+ Preferences.setLong(LAST_FETCH_KEY + tagData.getId(), DateUtilities.now());
- actFmSyncService.fetchUpdatesForTag(tagData, manual, new Runnable() {
- @Override
- public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- //refreshUpdatesList();
- DialogUtilities.dismissDialog(TagViewActivity.this, progressDialog);
- }
- });
- }
- });
+ final boolean noRemoteId = tagData.getValue(TagData.REMOTE_ID) == 0;
+ if(noRemoteId && !manual)
+ ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
}
private void setUpMembersGallery() {
@@ -479,7 +416,7 @@ public class TagViewActivity extends TaskListActivity {
//refreshUpdatesList();
}
});
- refreshData(false, true);
+ refreshData(false);
NotificationManager nm = new AndroidNotificationManager(ContextManager.getContext());
nm.cancel(tagData.getValue(TagData.REMOTE_ID).intValue());
@@ -528,7 +465,7 @@ public class TagViewActivity extends TaskListActivity {
// handle my own menus
switch (item.getItemId()) {
case MENU_REFRESH_ID:
- refreshData(true, false);
+ refreshData(true);
return true;
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncProvider.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncProvider.java
index f54a91b8c..89bfc7b88 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncProvider.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncProvider.java
@@ -133,15 +133,16 @@ public class ActFmSyncProvider extends SyncProvider {
try {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
+
ArrayList remoteTasks = new ArrayList();
- int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
+ // int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
if (serverTime == 0) { // If we've never synced, we may lose some empty tags
pushUnsavedTagData();
}
- fetchRemoteTagData(serverTime);
+ // fetchRemoteTagData(serverTime);
- SyncData syncData = populateSyncData(remoteTasks);
+ /* SyncData syncData = populateSyncData(remoteTasks);
try {
synchronizeTasks(syncData);
@@ -150,7 +151,8 @@ public class ActFmSyncProvider extends SyncProvider {
syncData.localUpdated.close();
}
- Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime);
+ Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime); */
+
actFmPreferenceService.recordSuccessfulSync();
syncSuccess = getFinalSyncStatus();
@@ -321,6 +323,7 @@ public class ActFmSyncProvider extends SyncProvider {
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
+ task.task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000);
actFmDataService.saveTaskAndMetadata(task);
}
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 4382389d3..2e517038c 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java
@@ -395,15 +395,17 @@ public final class ActFmSyncService {
JSONObject result = actFmInvoker.invoke("task_save", params.toArray(new Object[params.size()]));
ArrayList metadata = new ArrayList();
JsonHelper.taskFromJson(result, task, metadata);
- Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
- taskDao.saveExisting(task);
} catch (JSONException e) {
handleException("task-save-json", e);
} catch (IOException e) {
if (notPermanentError(e))
addFailedPush(new FailedPush(PUSH_TYPE_TASK, task.getId()));
handleException("task-save-io", e);
+ task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000L);
}
+
+ Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
+ taskDao.saveExisting(task);
}
/**
@@ -647,10 +649,11 @@ public final class ActFmSyncService {
/**
* Fetch all tags
* @param serverTime
+ * @return new serverTime
*/
- public void fetchTags(int serverTime) throws JSONException, IOException {
+ public int fetchTags(int serverTime) throws JSONException, IOException {
if(!checkForToken())
- return;
+ return 0;
JSONObject result = actFmInvoker.invoke("tag_list",
"token", token, "modified_after", serverTime);
@@ -666,6 +669,53 @@ public final class ActFmSyncService {
Long[] remoteIdArray = remoteIds.toArray(new Long[remoteIds.size()]);
tagDataService.deleteWhere(Criterion.not(TagData.REMOTE_ID.in(remoteIdArray)));
}
+
+ return result.optInt("time", 0);
+ }
+
+ /**
+ * Fetch active tasks asynchronously
+ * @param manual
+ * @param done
+ */
+ public void fetchActiveTasks(final boolean manual, Runnable done) {
+ invokeFetchList("task", manual, new ListItemProcessor() {
+ @Override
+ protected void mergeAndSave(JSONArray list, HashMap locals) throws JSONException {
+ Task remote = new Task();
+
+ ArrayList metadata = new ArrayList();
+ for(int i = 0; i < list.length(); i++) {
+ JSONObject item = list.getJSONObject(i);
+ readIds(locals, item, remote);
+ JsonHelper.taskFromJson(item, remote, metadata);
+
+ if(remote.getValue(Task.USER_ID) == 0) {
+ if(!remote.isSaved())
+ StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_CREATED);
+ else if(remote.isCompleted())
+ StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_COMPLETED);
+ }
+
+ if(!remote.isSaved() && remote.hasDueDate() &&
+ remote.getValue(Task.DUE_DATE) < DateUtilities.now())
+ remote.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
+
+ Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
+ taskService.save(remote);
+ metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY));
+ remote.clear();
+ }
+ }
+
+ @Override
+ protected HashMap getLocalModels() {
+ TodorooCursor cursor = taskService.query(Query.select(Task.ID,
+ Task.REMOTE_ID).where(Task.REMOTE_ID.in(remoteIds)).orderBy(
+ Order.asc(Task.REMOTE_ID)));
+ return cursorToMap(cursor, taskDao, Task.REMOTE_ID, Task.ID);
+ }
+ }, done, "active_tasks");
}
/**
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java
new file mode 100644
index 000000000..78dc16837
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java
@@ -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 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();
+ }
+ });
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java b/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java
index 86e4f2b0d..8850a9082 100644
--- a/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java
+++ b/astrid/plugin-src/com/todoroo/astrid/notes/EditNoteActivity.java
@@ -10,7 +10,6 @@ import java.util.List;
import org.json.JSONObject;
import android.app.ListActivity;
-import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
@@ -37,7 +36,6 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
-import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService;
@@ -47,9 +45,11 @@ import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
+import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
+import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
import com.todoroo.astrid.utility.Flags;
public class EditNoteActivity extends ListActivity {
@@ -95,12 +95,12 @@ public class EditNoteActivity extends ListActivity {
findViewById(R.id.add_comment).setVisibility(View.VISIBLE);
if(task.getValue(Task.REMOTE_ID) == 0)
- refreshData(true);
+ refreshData(true, null);
else {
String fetchKey = LAST_FETCH_KEY + task.getId();
long lastFetchDate = Preferences.getLong(fetchKey, 0);
if(DateUtilities.now() > lastFetchDate + 300000L) {
- refreshData(false);
+ refreshData(false, null);
Preferences.setLong(fetchKey, DateUtilities.now());
} else {
loadingText.setText(R.string.ENA_no_comments);
@@ -223,21 +223,32 @@ public class EditNoteActivity extends ListActivity {
// --- events
- private void refreshData(boolean manual) {
- final ProgressDialog progressDialog;
- if(manual)
- progressDialog = DialogUtilities.progressDialog(this, getString(R.string.DLG_please_wait));
- else
- progressDialog = null;
+ private void refreshData(boolean manual, SyncResultCallback existingCallback) {
+ final SyncResultCallback callback;
+ if(existingCallback != null)
+ callback = existingCallback;
+ else {
+ callback = new ProgressBarSyncResultCallback(
+ this, R.id.progressBar, new Runnable() {
+ @Override
+ public void run() {
+ setUpListAdapter();
+ loadingText.setText(R.string.ENA_no_comments);
+ loadingText.setVisibility(items.size() == 0 ? View.VISIBLE : View.GONE);
+ }
+ });
+
+ callback.started();
+ callback.incrementMax(100);
+ }
+ // push task if it hasn't been pushed
if(task.getValue(Task.REMOTE_ID) == 0) {
- // push task if it hasn't been pushed
new Thread(new Runnable() {
@Override
public void run() {
actFmSyncService.pushTask(task.getId());
- refreshData(false);
- DialogUtilities.dismissDialog(EditNoteActivity.this, progressDialog);
+ refreshData(false, callback);
}
}).start();
return;
@@ -246,17 +257,11 @@ public class EditNoteActivity extends ListActivity {
actFmSyncService.fetchUpdatesForTask(task, manual, new Runnable() {
@Override
public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- setUpListAdapter();
- loadingText.setText(R.string.ENA_no_comments);
- loadingText.setVisibility(items.size() == 0 ? View.VISIBLE : View.GONE);
- DialogUtilities.dismissDialog(EditNoteActivity.this, progressDialog);
- }
- });
+ callback.incrementProgress(50);
+ callback.finished();
}
});
+ callback.incrementProgress(50);
}
private void addComment() {
@@ -288,7 +293,7 @@ public class EditNoteActivity extends ListActivity {
switch (item.getItemId()) {
case MENU_REFRESH_ID: {
- refreshData(true);
+ refreshData(true, null);
return true;
}
diff --git a/astrid/res/layout/edit_note_activity.xml b/astrid/res/layout/edit_note_activity.xml
index b371ae39f..951355cf1 100644
--- a/astrid/res/layout/edit_note_activity.xml
+++ b/astrid/res/layout/edit_note_activity.xml
@@ -5,6 +5,15 @@
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@drawable/background_gradient">
+
+
+
+
+
diff --git a/astrid/res/layout/task_list_activity.xml b/astrid/res/layout/task_list_activity.xml
index fd58f4c46..27d9c841f 100644
--- a/astrid/res/layout/task_list_activity.xml
+++ b/astrid/res/layout/task_list_activity.xml
@@ -86,6 +86,14 @@
+
+
diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
index 0b22c0356..63920c95c 100644
--- a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
@@ -222,10 +222,6 @@ public class FilterListActivity extends ExpandableListActivity {
R.string.FLA_menu_search);
item.setIcon(android.R.drawable.ic_menu_search);
- item = menu.add(Menu.NONE, MENU_REFRESH_ID, Menu.NONE,
- R.string.TLA_menu_sync);
- item.setIcon(R.drawable.ic_menu_refresh);
-
item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
R.string.FLA_menu_help);
item.setIcon(android.R.drawable.ic_menu_help);
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index e0b254b88..0a86a410c 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -74,8 +74,6 @@ import com.todoroo.andlib.utility.Preferences;
import com.todoroo.andlib.widget.GestureService;
import com.todoroo.andlib.widget.GestureService.GestureInterface;
import com.todoroo.astrid.actfm.ActFmLoginActivity;
-import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
-import com.todoroo.astrid.actfm.sync.ActFmSyncProvider;
import com.todoroo.astrid.activity.SortSelectionActivity.OnSortSelectedListener;
import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
@@ -96,6 +94,7 @@ import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.GCalHelper;
import com.todoroo.astrid.helper.MetadataHelper;
+import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader.ContextMenuItem;
import com.todoroo.astrid.reminders.ReminderDebugContextActions;
@@ -105,6 +104,7 @@ import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
+import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService;
@@ -163,8 +163,6 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
public static final String TOKEN_OVERRIDE_ANIM = "finishAnim"; //$NON-NLS-1$
- private static final String LAST_AUTOSYNC_ATTEMPT = "last-autosync"; //$NON-NLS-1$
-
// --- instance variables
@Autowired ExceptionService exceptionService;
@@ -179,7 +177,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
@Autowired UpgradeService upgradeService;
- @Autowired ActFmPreferenceService actFmPreferenceService;
+ @Autowired protected SyncV2Service syncService;
@Autowired TagDataService tagDataService;
@@ -239,7 +237,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
new StartupService().onStartupApplication(this);
ThemeService.applyTheme(this);
ViewGroup parent = (ViewGroup) getLayoutInflater().inflate(R.layout.task_list_activity, null);
- parent.addView(getListBody(parent), 1);
+ parent.addView(getListBody(parent), 2);
setContentView(parent);
if(database == null)
@@ -502,23 +500,6 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER);
}
- private void initiateAutomaticSync() {
- if (!actFmPreferenceService.isLoggedIn()) return;
- long lastFetchDate = actFmPreferenceService.getLastSyncDate();
- long lastAutosyncAttempt = Preferences.getLong(LAST_AUTOSYNC_ATTEMPT, 0);
-
- long lastTry = Math.max(lastFetchDate, lastAutosyncAttempt);
- if(DateUtilities.now() < lastTry + 300000L)
- return;
- new Thread() {
- @Override
- public void run() {
- Preferences.setLong(LAST_AUTOSYNC_ATTEMPT, DateUtilities.now());
- new ActFmSyncProvider().synchronize(TaskListActivity.this, false);
- }
- }.start();
- }
-
// Subclasses can override these to customize extras in quickadd intent
protected Intent getOnClickQuickAddIntent(Task t) {
Intent intent = new Intent(TaskListActivity.this, TaskEditActivity.class);
@@ -616,9 +597,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
Preferences.setBoolean(R.string.p_showed_lists_help, true);
}
- if (filter.title != null && filter.title.equals(getString(R.string.BFE_Active))) {
- initiateAutomaticSync();
- }
+ initiateAutomaticSync();
}
@Override
@@ -1136,8 +1115,31 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
}
}
- private void performSyncAction() {
- if (syncActions.size() == 0) {
+ private static final String PREF_LAST_AUTO_SYNC = "taskListLastAutoSync"; //$NON-NLS-1$
+
+ protected void initiateAutomaticSync() {
+ if (filter.title == null || !filter.title.equals(getString(R.string.BFE_Active)))
+ return;
+
+ long lastAutoSync = Preferences.getLong(PREF_LAST_AUTO_SYNC, 0);
+ if(DateUtilities.now() - lastAutoSync > DateUtilities.ONE_HOUR) {
+ performSyncServiceV2Sync(false);
+ }
+ }
+
+ protected void performSyncServiceV2Sync(boolean manual) {
+ syncService.synchronizeActiveTasks(manual, new ProgressBarSyncResultCallback(this,
+ R.id.progressBar, new Runnable() {
+ @Override
+ public void run() {
+ loadTaskListContent(true);
+ }
+ }));
+ Preferences.setLong(PREF_LAST_AUTO_SYNC, DateUtilities.now());
+ }
+
+ protected void performSyncAction() {
+ if (syncActions.size() == 0 && !syncService.isActive()) {
String desiredCategory = getString(R.string.SyP_label);
// Get a list of all sync plugins and bring user to the prefs pane
@@ -1178,32 +1180,20 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
showSyncOptionMenu(actions, listener);
}
- else if(syncActions.size() == 1) {
- SyncAction syncAction = syncActions.iterator().next();
- try {
- syncAction.intent.send();
- Toast.makeText(this, R.string.SyP_progress_toast,
- Toast.LENGTH_LONG).show();
- } catch (CanceledException e) {
- //
- }
- } else {
- // We have >1 sync actions, pop up a dialogue so the user can
- // select just one of them (only sync one at a time)
- final SyncAction[] actions = syncActions.toArray(new SyncAction[syncActions.size()]);
- DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface click, int which) {
+ else {
+ performSyncServiceV2Sync(true);
+
+ if(syncActions.size() > 0) {
+ for(SyncAction syncAction : syncActions) {
try {
- actions[which].intent.send();
- Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
- Toast.LENGTH_LONG).show();
+ syncAction.intent.send();
} catch (CanceledException e) {
//
}
}
- };
- showSyncOptionMenu(actions, listener);
+ Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
+ Toast.LENGTH_LONG).show();
+ }
}
}
diff --git a/astrid/src/com/todoroo/astrid/helper/ProgressBarSyncResultCallback.java b/astrid/src/com/todoroo/astrid/helper/ProgressBarSyncResultCallback.java
new file mode 100644
index 000000000..eda27d80b
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/helper/ProgressBarSyncResultCallback.java
@@ -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);
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
index bc355cebf..b849a3783 100644
--- a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
+++ b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
@@ -73,6 +73,7 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("tagDataService", TagDataService.class);
injectables.put("upgradeService", UpgradeService.class);
injectables.put("addOnService", AddOnService.class);
+ injectables.put("syncService", SyncV2Service.class);
// com.timsu.astrid.data
injectables.put("tasksTable", "tasks");
diff --git a/astrid/src/com/todoroo/astrid/service/SyncV2Service.java b/astrid/src/com/todoroo/astrid/service/SyncV2Service.java
new file mode 100644
index 000000000..b6545e8d0
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/service/SyncV2Service.java
@@ -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
+ *
+ */
+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);
+ }
+ }
+
+}