Added new progress bar to task list activity, now using it to sync task list activity

pull/14/head
Tim Su 13 years ago
parent ab2739d98b
commit 85eda2a62d

@ -510,16 +510,55 @@ public class AndroidUtilities {
* @param args arguments * @param args arguments
* @return method return value, or null if nothing was called or exception * @return method return value, or null if nothing was called or exception
*/ */
@SuppressWarnings("nls")
public static Object callApiMethod(int minSdk, Object receiver, public static Object callApiMethod(int minSdk, Object receiver,
String methodName, Class<?>[] params, Object... args) { String methodName, Class<?>[] params, Object... args) {
if(getSdkVersion() < minSdk) if(getSdkVersion() < minSdk)
return null; 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 { try {
method = receiver.getClass().getMethod(methodName, params); Method method = cls.getMethod(methodName, params);
return method.invoke(receiver, args); Object result = method.invoke(receiver, args);
return result;
} catch (SecurityException e) { } catch (SecurityException e) {
getExceptionService().reportError("call-method", e); getExceptionService().reportError("call-method", e);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {

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

@ -647,10 +647,11 @@ public final class ActFmSyncService {
/** /**
* Fetch all tags * Fetch all tags
* @param serverTime * @param serverTime
* @return new serverTime
*/ */
public void fetchTags(int serverTime) throws JSONException, IOException { public int fetchTags(int serverTime) throws JSONException, IOException {
if(!checkForToken()) if(!checkForToken())
return; return 0;
JSONObject result = actFmInvoker.invoke("tag_list", JSONObject result = actFmInvoker.invoke("tag_list",
"token", token, "modified_after", serverTime); "token", token, "modified_after", serverTime);
@ -666,6 +667,50 @@ public final class ActFmSyncService {
Long[] remoteIdArray = remoteIds.toArray(new Long[remoteIds.size()]); Long[] remoteIdArray = remoteIds.toArray(new Long[remoteIds.size()]);
tagDataService.deleteWhere(Criterion.not(TagData.REMOTE_ID.in(remoteIdArray))); 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);
}
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,145 @@
/**
* 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.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;
/**
* 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
@Override
public void synchronizeActiveTasks(boolean manual,
final SyncResultCallback callback) {
if(!manual)
return;
callback.started();
callback.incrementMax(50);
final AtomicInteger finisher = new AtomicInteger(2);
new Thread(tagFetcher(callback, finisher)).start();
startTaskFetcher(manual, callback, finisher);
pushQueued(callback, finisher);
}
private Runnable tagFetcher(final SyncResultCallback callback,
final AtomicInteger finisher) {
return 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();
}
}
};
}
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();
}
}
}

@ -86,6 +86,14 @@
</LinearLayout> </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 --> <!-- Body goes here -->
<!-- Footer --> <!-- Footer -->

@ -42,6 +42,7 @@ import android.view.View.OnLongClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView; import android.widget.AbsListView;
@ -53,6 +54,7 @@ import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ListView; import android.widget.ListView;
import android.widget.PopupWindow.OnDismissListener; import android.widget.PopupWindow.OnDismissListener;
import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast; import android.widget.Toast;
@ -74,8 +76,6 @@ import com.todoroo.andlib.utility.Preferences;
import com.todoroo.andlib.widget.GestureService; import com.todoroo.andlib.widget.GestureService;
import com.todoroo.andlib.widget.GestureService.GestureInterface; import com.todoroo.andlib.widget.GestureService.GestureInterface;
import com.todoroo.astrid.actfm.ActFmLoginActivity; 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.activity.SortSelectionActivity.OnSortSelectedListener;
import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener; import com.todoroo.astrid.adapter.TaskAdapter.OnCompletedTaskListener;
@ -105,6 +105,8 @@ import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.StartupService; import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.StatisticsConstants; import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService; import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.service.SyncV2Service;
import com.todoroo.astrid.service.SyncV2Service.SyncResultCallback;
import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TagDataService;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.service.ThemeService; import com.todoroo.astrid.service.ThemeService;
@ -163,8 +165,6 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
public static final String TOKEN_OVERRIDE_ANIM = "finishAnim"; //$NON-NLS-1$ 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 // --- instance variables
@Autowired ExceptionService exceptionService; @Autowired ExceptionService exceptionService;
@ -179,7 +179,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
@Autowired UpgradeService upgradeService; @Autowired UpgradeService upgradeService;
@Autowired ActFmPreferenceService actFmPreferenceService; @Autowired protected SyncV2Service syncService;
@Autowired TagDataService tagDataService; @Autowired TagDataService tagDataService;
@ -239,7 +239,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
new StartupService().onStartupApplication(this); new StartupService().onStartupApplication(this);
ThemeService.applyTheme(this); ThemeService.applyTheme(this);
ViewGroup parent = (ViewGroup) getLayoutInflater().inflate(R.layout.task_list_activity, null); ViewGroup parent = (ViewGroup) getLayoutInflater().inflate(R.layout.task_list_activity, null);
parent.addView(getListBody(parent), 1); parent.addView(getListBody(parent), 2);
setContentView(parent); setContentView(parent);
if(database == null) if(database == null)
@ -502,23 +502,6 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER); 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 // Subclasses can override these to customize extras in quickadd intent
protected Intent getOnClickQuickAddIntent(Task t) { protected Intent getOnClickQuickAddIntent(Task t) {
Intent intent = new Intent(TaskListActivity.this, TaskEditActivity.class); Intent intent = new Intent(TaskListActivity.this, TaskEditActivity.class);
@ -1136,8 +1119,94 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
} }
} }
private class TaskListSyncResultCallback implements SyncResultCallback {
private final ProgressBar progressBar;
private int providers = 0;
public TaskListSyncResultCallback() {
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgress(0);
progressBar.setMax(0);
}
@Override
public void finished() {
providers--;
if(providers == 0) {
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);
loadTaskListContent(true);
}
});
new Thread() {
@Override
public void run() {
AndroidUtilities.sleepDeep(1000);
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.GONE);
}
});
}
}.start();
}
}
@Override
public void incrementMax(final int incrementBy) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.setMax(progressBar.getMax() + incrementBy);
}
});
}
@Override
public void incrementProgress(final int incrementBy) {
runOnUiThread(new Runnable() {
@Override
public void run() {
progressBar.incrementProgressBy(incrementBy);
}
});
}
@Override
public void started() {
if(providers == 0) {
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);
}
});
}
providers++;
}
}
private void initiateAutomaticSync() {
syncService.synchronizeActiveTasks(false, new TaskListSyncResultCallback());
}
private void performSyncAction() { private void performSyncAction() {
if (syncActions.size() == 0) { if (syncActions.size() == 0 && !syncService.isActive()) {
String desiredCategory = getString(R.string.SyP_label); String desiredCategory = getString(R.string.SyP_label);
// Get a list of all sync plugins and bring user to the prefs pane // Get a list of all sync plugins and bring user to the prefs pane
@ -1178,32 +1247,20 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
showSyncOptionMenu(actions, listener); showSyncOptionMenu(actions, listener);
} }
else if(syncActions.size() == 1) { else {
SyncAction syncAction = syncActions.iterator().next(); syncService.synchronizeActiveTasks(true, new TaskListSyncResultCallback());
try {
syncAction.intent.send(); if(syncActions.size() > 0) {
Toast.makeText(this, R.string.SyP_progress_toast, for(SyncAction syncAction : syncActions) {
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) {
try { try {
actions[which].intent.send(); syncAction.intent.send();
Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
Toast.LENGTH_LONG).show();
} catch (CanceledException e) { } catch (CanceledException e) {
// //
} }
} }
}; Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
showSyncOptionMenu(actions, listener); Toast.LENGTH_LONG).show();
}
} }
} }

@ -73,6 +73,7 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("tagDataService", TagDataService.class); injectables.put("tagDataService", TagDataService.class);
injectables.put("upgradeService", UpgradeService.class); injectables.put("upgradeService", UpgradeService.class);
injectables.put("addOnService", AddOnService.class); injectables.put("addOnService", AddOnService.class);
injectables.put("syncService", SyncV2Service.class);
// com.timsu.astrid.data // com.timsu.astrid.data
injectables.put("tasksTable", "tasks"); injectables.put("tasksTable", "tasks");

@ -0,0 +1,78 @@
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);
}
/*
* 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);
}
}
}
Loading…
Cancel
Save