Merge remote-tracking branch 'upstream/120112-ts-syncv2'

pull/14/head
Sam Bosley 14 years ago
commit ec07d3b60f

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

@ -331,12 +331,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<receiver android:name="com.todoroo.astrid.actfm.ActFmSyncActionExposer">
<intent-filter>
<action android:name="com.todoroo.astrid.REQUEST_SYNC_ACTIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity android:name="com.todoroo.astrid.actfm.TagViewActivity"
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/Theme">

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

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

@ -133,15 +133,16 @@ public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
try {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
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<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
/* SyncData<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
try {
synchronizeTasks(syncData);
@ -150,7 +151,8 @@ public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
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<ActFmTaskContainer> {
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
task.task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000);
actFmDataService.saveTaskAndMetadata(task);
}

@ -395,15 +395,17 @@ public final class ActFmSyncService {
JSONObject result = actFmInvoker.invoke("task_save", params.toArray(new Object[params.size()]));
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
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<Task>() {
@Override
protected void mergeAndSave(JSONArray list, HashMap<Long,Long> locals) throws JSONException {
Task remote = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
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<Long, Long> getLocalModels() {
TodorooCursor<Task> 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");
}
/**

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

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

@ -5,6 +5,15 @@
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="@drawable/background_gradient">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<FrameLayout
android:layout_width="fill_parent"

@ -23,6 +23,14 @@
android:ellipsize="start"
style="@style/TextAppearance.TLA_Header"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<ListView
android:id="@android:id/list"

@ -4,8 +4,6 @@
xmlns:astrid="http://schemas.android.com/apk/res/com.timsu.astrid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dip"
android:paddingRight="4dip"
android:minHeight="40dip"
android:orientation="horizontal">

@ -86,6 +86,14 @@
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="5dip"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"
android:layout_weight="1" />
<!-- Body goes here -->
<!-- Footer -->

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

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

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

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

@ -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…
Cancel
Save