Tracked down and fixed a couple bugs with synchronization, handling IllegalStateException,

added more error reporting via flurry. Ready for 2.8 release!
pull/14/head
Tim Su 17 years ago
parent eae64b19e9
commit aebc698298

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.timsu.astrid" package="com.timsu.astrid"
android:versionCode="103" android:versionCode="105"
android:versionName="2.8.3"> android:versionName="2.8.0">
<!-- ============================ Metadata ============================ --> <!-- ============================ Metadata ============================ -->

@ -299,7 +299,9 @@ occur (it is a minor drain on battery).
<string name="rtm_login_label">Please Log In to RTM...</string> <string name="rtm_login_label">Please Log In to RTM...</string>
<string name="rtm_login_error"> <string name="rtm_login_error">
Sorry, there was an error verifying your login. Please let us know about this error: Sorry, there was an error verifying your login. Please try again.
\n\n
Error Message:
</string> </string>

@ -20,6 +20,7 @@
package com.timsu.astrid.activities; package com.timsu.astrid.activities;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.view.View; import android.view.View;
@ -30,6 +31,7 @@ import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.timsu.astrid.utilities.DialogUtilities;
/** /**
* This activity displays a <code>WebView</code> that allows users to log in to the * This activity displays a <code>WebView</code> that allows users to log in to the
@ -61,9 +63,9 @@ public class SyncLoginActivity extends Activity {
* Verifies whether the user's login attempt was successful. Will be * Verifies whether the user's login attempt was successful. Will be
* called off of the UI thread, use the handler to post messages. * called off of the UI thread, use the handler to post messages.
* *
* @return true if activity should be dismissed, false otherwise * @return error string, or null if sync was successful
*/ */
public boolean verifyLogin(Handler handler); public String verifyLogin(Handler handler);
} }
private static SyncLoginCallback callback = null; private static SyncLoginCallback callback = null;
@ -84,7 +86,7 @@ public class SyncLoginActivity extends Activity {
int labelParam = getIntent().getIntExtra(LABEL_TOKEN, 0); int labelParam = getIntent().getIntExtra(LABEL_TOKEN, 0);
TextView label = (TextView)findViewById(R.id.login_label); TextView label = (TextView)findViewById(R.id.login_label);
WebView webView = (WebView)findViewById(R.id.browser); final WebView webView = (WebView)findViewById(R.id.browser);
Button done = (Button)findViewById(R.id.done); Button done = (Button)findViewById(R.id.done);
Button cancel = (Button)findViewById(R.id.cancel); Button cancel = (Button)findViewById(R.id.cancel);
@ -103,10 +105,11 @@ public class SyncLoginActivity extends Activity {
webView.getSettings().setSupportZoom(true); webView.getSettings().setSupportZoom(true);
webView.loadUrl(urlParam); webView.loadUrl(urlParam);
final Handler handler = new Handler();
done.setOnClickListener(new OnClickListener() { done.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final Handler handler = new Handler();
if(callback == null) { if(callback == null) {
finish(); finish();
return; return;
@ -115,11 +118,27 @@ public class SyncLoginActivity extends Activity {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
boolean result = callback.verifyLogin(handler); final String result = callback.verifyLogin(handler);
if(result) {
webView.destroy();
if(result == null) {
TaskList.synchronizeNow = true; TaskList.synchronizeNow = true;
}
finish(); finish();
} else {
// display the error
handler.post(new Runnable() {
@Override
public void run() {
DialogUtilities.okDialog(SyncLoginActivity.this, result,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
TaskListSubActivity.shouldRefreshTaskList = true;
finish();
}
});
}
});
}
} }
}).start(); }).start();
} }

@ -286,7 +286,7 @@ public class TaskEdit extends TaskModificationTabbedActivity<TaskModelForEdit> {
try { try {
// write out to database // write out to database
controller.saveTask(model); controller.saveTask(model, false);
saveTags(); saveTags();
saveAlerts(); saveAlerts();
Notifications.updateAlarm(this, controller, alertController, model); Notifications.updateAlarm(this, controller, alertController, model);

@ -618,7 +618,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
Importance i = Importance.values()[keyCode - KeyEvent.KEYCODE_1]; Importance i = Importance.values()[keyCode - KeyEvent.KEYCODE_1];
TaskModelForList task = (TaskModelForList)v.getTag(); TaskModelForList task = (TaskModelForList)v.getTag();
task.setImportance(i); task.setImportance(i);
hooks.taskController().saveTask(task); hooks.taskController().saveTask(task, false);
setFieldContentsAndVisibility(v, task); setFieldContentsAndVisibility(v, task);
return true; return true;
} }
@ -683,7 +683,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
private void setTaskProgress(final TaskModelForList task, View view, int progress) { private void setTaskProgress(final TaskModelForList task, View view, int progress) {
final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft)); final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
task.setProgressPercentage(progress); task.setProgressPercentage(progress);
hooks.taskController().saveTask(task); hooks.taskController().saveTask(task, false);
// show this task as completed even if it has repeats // show this task as completed even if it has repeats
if(progress == 100) { if(progress == 100) {
@ -704,7 +704,7 @@ public class TaskListAdapter extends ArrayAdapter<TaskModelForList> {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
task.stopTimerAndUpdateElapsedTime(); task.stopTimerAndUpdateElapsedTime();
hooks.taskController().saveTask(task); hooks.taskController().saveTask(task, false);
timer.setVisibility(View.GONE); timer.setVisibility(View.GONE);
} }
}) })

@ -823,7 +823,6 @@ public class TaskListSubActivity extends SubActivity {
private void reloadList() { private void reloadList() {
if (context.loadingThread != null && context.loadingThread.isAlive()) { if (context.loadingThread != null && context.loadingThread.isAlive()) {
context.loadingThread.interrupt(); context.loadingThread.interrupt();
context.loadingThread.stop();
} }
context.loadingThread = new Thread(reLoadRunnable); context.loadingThread = new Thread(reLoadRunnable);
context.loadingThread.start(); context.loadingThread.start();
@ -971,7 +970,7 @@ public class TaskListSubActivity extends SubActivity {
FlurryAgent.onEvent("stop-timer"); FlurryAgent.onEvent("stop-timer");
task.stopTimerAndUpdateElapsedTime(); task.stopTimerAndUpdateElapsedTime();
} }
getTaskController().saveTask(task); getTaskController().saveTask(task, false);
context.listAdapter.refreshItem(listView, context.taskArray context.listAdapter.refreshItem(listView, context.taskArray
.indexOf(task)); .indexOf(task));
} }
@ -1095,7 +1094,7 @@ public class TaskListSubActivity extends SubActivity {
} }
task.setPostponeCount(postponeCount); task.setPostponeCount(postponeCount);
getTaskController().saveTask(task); getTaskController().saveTask(task, false);
getTaskController().updateAlarmForTask( getTaskController().updateAlarmForTask(
task.getTaskIdentifier()); task.getTaskIdentifier());
context.listAdapter.refreshItem(listView, context.listAdapter.refreshItem(listView,
@ -1120,8 +1119,7 @@ public class TaskListSubActivity extends SubActivity {
showTagsView(); showTagsView();
return true; return true;
case SYNC_ID: case SYNC_ID:
onActivityResult(ACTIVITY_SYNCHRONIZE, synchronize();
Constants.RESULT_SYNCHRONIZE, null);
return true; return true;
case MORE_ID: case MORE_ID:
layout.showContextMenu(); layout.showContextMenu();

@ -221,8 +221,11 @@ public class TaskController extends AbstractController {
return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0; return database.delete(TASK_TABLE_NAME, KEY_ROWID + "=" + id, null) > 0;
} }
/** Saves the given task to the database. Returns true on success. */ /** Saves the given task to the database. Returns true on success.
public boolean saveTask(AbstractTaskModel task) { *
* @param duringSync set to true when save is part of a synchronize
*/
public boolean saveTask(AbstractTaskModel task, boolean duringSync) {
boolean saveSucessful; boolean saveSucessful;
if(task.getTaskIdentifier() == null) { if(task.getTaskIdentifier() == null) {
@ -238,7 +241,7 @@ public class TaskController extends AbstractController {
if(values.size() == 0) // nothing changed if(values.size() == 0) // nothing changed
return true; return true;
onTaskSave(task, values); onTaskSave(task, values, duringSync);
saveSucessful = database.update(TASK_TABLE_NAME, values, saveSucessful = database.update(TASK_TABLE_NAME, values,
KEY_ROWID + "=" + id, null) > 0; KEY_ROWID + "=" + id, null) > 0;
@ -247,7 +250,7 @@ public class TaskController extends AbstractController {
if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) && if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) &&
values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE) values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE)
== AbstractTaskModel.COMPLETE_PERCENTAGE) { == AbstractTaskModel.COMPLETE_PERCENTAGE) {
onTaskCompleted(task, values); onTaskCompleted(task, values, duringSync);
} }
if(!(task instanceof TaskModelForSync)) { if(!(task instanceof TaskModelForSync)) {
@ -267,7 +270,7 @@ public class TaskController extends AbstractController {
* @param task * @param task
* @param values * @param values
*/ */
private void onTaskSave(AbstractTaskModel task, ContentValues values) { private void onTaskSave(AbstractTaskModel task, ContentValues values, boolean duringSync) {
// save task completed date // save task completed date
if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) && if(values.containsKey(AbstractTaskModel.PROGRESS_PERCENTAGE) &&
values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE) values.getAsInteger(AbstractTaskModel.PROGRESS_PERCENTAGE)
@ -330,7 +333,7 @@ public class TaskController extends AbstractController {
* @param task task to process * @param task task to process
* @param values mutable map of values to save * @param values mutable map of values to save
*/ */
private void onTaskCompleted(AbstractTaskModel task, ContentValues values) { private void onTaskCompleted(AbstractTaskModel task, ContentValues values, boolean duringSync) {
Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(), Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(),
TaskModelForHandlers.FIELD_LIST); TaskModelForHandlers.FIELD_LIST);
TaskModelForHandlers model = new TaskModelForHandlers(cursor, values); TaskModelForHandlers model = new TaskModelForHandlers(cursor, values);
@ -344,7 +347,8 @@ public class TaskController extends AbstractController {
} }
// handle sync-on-complete // handle sync-on-complete
if((model.getFlags() & TaskModelForHandlers.FLAG_SYNC_ON_COMPLETE) > 0) { if((model.getFlags() & TaskModelForHandlers.FLAG_SYNC_ON_COMPLETE) > 0 &&
!duringSync) {
Synchronizer synchronizer = new Synchronizer(model.getTaskIdentifier()); Synchronizer synchronizer = new Synchronizer(model.getTaskIdentifier());
synchronizer.synchronize(context, new SynchronizerListener() { synchronizer.synchronize(context, new SynchronizerListener() {
public void onSynchronizerFinished(int numServicesSynced) { public void onSynchronizerFinished(int numServicesSynced) {

@ -127,46 +127,40 @@ public class RTMSyncProvider extends SynchronizationProvider {
} }
} }
// open up a dialog and have the user go to browser
if(isBackgroundService()) if(isBackgroundService())
return; return;
// open up a dialog and have the user go to browser
FlurryAgent.onEvent("rtm-login-dialog"); FlurryAgent.onEvent("rtm-login-dialog");
rtmService = new ServiceImpl(new ApplicationInfo( rtmService = new ServiceImpl(new ApplicationInfo(
apiKey, sharedSecret, appName)); apiKey, sharedSecret, appName));
final String url = rtmService.beginAuthorization(Perms.delete); final String url = rtmService.beginAuthorization(Perms.delete);
if(progressDialog != null)
progressDialog.dismiss(); progressDialog.dismiss();
Intent intent = new Intent(context, SyncLoginActivity.class); Intent intent = new Intent(context, SyncLoginActivity.class);
SyncLoginActivity.setCallback(new SyncLoginCallback() { SyncLoginActivity.setCallback(new SyncLoginCallback() {
@Override @Override
public boolean verifyLogin(final Handler syncLoginHandler) { public String verifyLogin(final Handler syncLoginHandler) {
if(rtmService == null) { if(rtmService == null) {
Log.e("rtmsync", "Error: sync login activity displayed with no service!"); Log.e("rtmsync", "Error: sync login activity displayed with no service!");
return true; return null;
} }
try { try {
String token = rtmService.completeAuthorization(); String token = rtmService.completeAuthorization();
Log.w("astrid", "got RTM token: " + token); Log.w("astrid", "got RTM token: " + token);
Preferences.setSyncRTMToken(context, token); Preferences.setSyncRTMToken(context, token);
return true; return null;
} catch (final Exception e) { } catch (final Exception e) {
// didn't work // didn't work
FlurryAgent.onError("rtm-verify-login", AstridUtilities.throwableToString(e), FlurryAgent.onError("rtm-verify-login", AstridUtilities.throwableToString(e),
SynchronizationProvider.class.getSimpleName()); SynchronizationProvider.class.getSimpleName());
syncLoginHandler.post(new Runnable() { rtmService = null;
@Override return r.getString(R.string.rtm_login_error) +
public void run() { " " + e.getMessage();
DialogUtilities.okDialog(context,
r.getString(R.string.rtm_login_error) +
" " + e.getMessage(), null);
}
});
return false;
} }
} }
}); });
@ -304,6 +298,13 @@ public class RTMSyncProvider extends SynchronizationProvider {
Preferences.setSyncRTMLastSync(context, syncTime); Preferences.setSyncRTMLastSync(context, syncTime);
FlurryAgent.onEvent("rtm-sync-finished"); FlurryAgent.onEvent("rtm-sync-finished");
} catch (IllegalStateException e) {
// occurs when application was closed
FlurryAgent.onError("rtm-sync-caught", AstridUtilities.throwableToString(e),
SynchronizationProvider.class.getSimpleName());
Log.e("rtmsync", "Illegal State during Sync", e);
} catch (Exception e) { } catch (Exception e) {
FlurryAgent.onError("rtm-sync", AstridUtilities.throwableToString(e), FlurryAgent.onError("rtm-sync", AstridUtilities.throwableToString(e),

@ -70,9 +70,9 @@ public abstract class SynchronizationProvider {
*/ */
void synchronizeService(final Context activity, Synchronizer caller) { void synchronizeService(final Context activity, Synchronizer caller) {
this.synchronizer = caller; this.synchronizer = caller;
this.syncHandler = caller.getHandler();
if(!isBackgroundService()) { if(!isBackgroundService()) {
syncHandler = caller.getHandler();
syncHandler.post(new Runnable() { syncHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -368,7 +368,7 @@ public abstract class SynchronizationProvider {
// save the data // save the data
remoteTask.writeToTaskModel(task); remoteTask.writeToTaskModel(task);
taskController.saveTask(task); taskController.saveTask(task, true);
// save tags // save tags
if(remoteTask.tags != null) { if(remoteTask.tags != null) {

@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
import com.flurry.android.FlurryAgent;
import com.timsu.astrid.activities.TaskListSubActivity; import com.timsu.astrid.activities.TaskListSubActivity;
import com.timsu.astrid.data.AbstractController; import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController; import com.timsu.astrid.data.alerts.AlertController;
@ -35,6 +36,7 @@ import com.timsu.astrid.data.sync.SyncDataController;
import com.timsu.astrid.data.tag.TagController; import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.task.TaskController; import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskIdentifier; import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.utilities.AstridUtilities;
import com.timsu.astrid.utilities.Preferences; import com.timsu.astrid.utilities.Preferences;
/** /**
@ -172,6 +174,10 @@ public class Synchronizer {
/** Called to do the next step of synchronization. */ /** Called to do the next step of synchronization. */
void continueSynchronization(Context context) { void continueSynchronization(Context context) {
try {
if(currentStep > ServiceWrapper.values().length)
currentStep = ServiceWrapper.values().length - 1;
ServiceWrapper serviceWrapper = ServiceWrapper serviceWrapper =
ServiceWrapper.values()[currentStep]; ServiceWrapper.values()[currentStep];
currentStep++; currentStep++;
@ -190,6 +196,12 @@ public class Synchronizer {
case _LAST_SERVICE: case _LAST_SERVICE:
finishSynchronization(context); finishSynchronization(context);
} }
} catch (Exception e) {
Log.e("sync", "Error continuing synchronization", e);
FlurryAgent.onError("sync-continue", AstridUtilities.throwableToString(e),
SynchronizationProvider.class.getSimpleName());
finishSynchronization(context);
}
} }
/** Called at the end of sync. */ /** Called at the end of sync. */

Loading…
Cancel
Save