diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ee31a4daf..6b81fc5d0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -47,6 +47,9 @@
+
+
+
diff --git a/res/layout/sync_login.xml b/res/layout/sync_login.xml
new file mode 100644
index 000000000..150a7dee9
--- /dev/null
+++ b/res/layout/sync_login.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0ebe95ebe..c58acecb3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -175,9 +175,9 @@
Summary
- Task Description
How Important is it?
Tags:
+ Tag
How Long Will it Take?
Time Already Spent on Task
@@ -267,11 +267,6 @@ If you don\'t want to see the new task right after you complete the old one, you
Auto-Sync Wifi Only
If set, auto-sync only happens when Wifi is active
Sync Error! Sorry for the inconvenience! Error:
-
-In order to synchronize, please log in to your %s account and authorize Astrid to read your data.
-\n\n
-When finished, restart Astrid.
-
Astrid 2.7 now performs synchronization with RTM in the background. You will
be directed to the preferences page to configure how often you want this to
@@ -296,9 +291,14 @@ occur (it is a minor drain on battery).
Reading Remote Data
Reading List: %s
Synchronizing Repeating Task
- Sending Task: %s
+ Transmitting: %s
Locally Deleted Tasks
- Receiving Task: %s
+ Receiving: %s
+
+ Please Log In to RTM...
+
+Sorry, there was an error verifying your login. Please let us know about this error:
+
@@ -307,6 +307,7 @@ occur (it is a minor drain on battery).
Updating List...
Information
Question
+ Done
View Task
Already Done!
Snooze
diff --git a/src/com/timsu/astrid/activities/SyncLoginActivity.java b/src/com/timsu/astrid/activities/SyncLoginActivity.java
new file mode 100644
index 000000000..b2f444503
--- /dev/null
+++ b/src/com/timsu/astrid/activities/SyncLoginActivity.java
@@ -0,0 +1,137 @@
+/*
+ * ASTRID: Android's Simple Task Recording Dashboard
+ *
+ * Copyright (c) 2009 Tim Su
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.timsu.astrid.activities;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.timsu.astrid.R;
+import com.timsu.astrid.utilities.Constants;
+
+/**
+ * This activity displays a WebView that allows users to log in to the
+ * synchronization provider requested. A callback method determines whether
+ * their login was successful and therefore whether to dismiss the dialog.
+ *
+ * @author timsu
+ *
+ */
+public class SyncLoginActivity extends Activity {
+
+ // --- bundle arguments
+
+ /**
+ * URL to display
+ */
+ public static final String URL_TOKEN = "u";
+
+ /**
+ * Resource for the label to display at the top of the screen
+ */
+ public static final String LABEL_TOKEN = "l";
+
+ // --- callback
+
+ /** Callback interface */
+ public interface SyncLoginCallback {
+ /**
+ * Verifies whether the user's login attempt was successful. Will be
+ * called off of the UI thread, use the handler to post messages.
+ *
+ * @return true if activity should be dismissed, false otherwise
+ */
+ public boolean verifyLogin(Handler handler);
+ }
+
+ private static SyncLoginCallback callback = null;
+
+ /** Sets callback method */
+ public static void setCallback(SyncLoginCallback newCallback) {
+ callback = newCallback;
+ }
+
+ // --- ui initialization
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.sync_login);
+
+ String urlParam = getIntent().getStringExtra(URL_TOKEN);
+ int labelParam = getIntent().getIntExtra(LABEL_TOKEN, 0);
+
+ TextView label = (TextView)findViewById(R.id.login_label);
+ WebView webView = (WebView)findViewById(R.id.browser);
+ Button done = (Button)findViewById(R.id.done);
+ Button cancel = (Button)findViewById(R.id.cancel);
+
+ if(labelParam != 0)
+ label.setText(labelParam);
+
+ webView.setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ view.loadUrl(url);
+ return true;
+ }
+ });
+ webView.getSettings().setJavaScriptEnabled(true);
+ webView.getSettings().setSavePassword(false);
+ webView.getSettings().setSupportZoom(false);
+ webView.loadUrl(urlParam);
+
+ final Handler handler = new Handler();
+ done.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(callback == null) {
+ finish();
+ return;
+ }
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ boolean result = callback.verifyLogin(handler);
+ if(result) {
+ setResult(Constants.RESULT_SYNCHRONIZE);
+ finish();
+ }
+ }
+ }).start();
+ }
+ });
+
+ cancel.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/com/timsu/astrid/activities/SyncPreferences.java b/src/com/timsu/astrid/activities/SyncPreferences.java
index 243babec7..8c2bc8a77 100644
--- a/src/com/timsu/astrid/activities/SyncPreferences.java
+++ b/src/com/timsu/astrid/activities/SyncPreferences.java
@@ -77,6 +77,8 @@ public class SyncPreferences extends PreferenceActivity {
public void onClick(DialogInterface dialog,
int which) {
Synchronizer.clearUserData(SyncPreferences.this);
+ // force a synchronization if sync preference is still set
+ rtmSyncPreference = false;
}
}, null);
}
diff --git a/src/com/timsu/astrid/activities/TaskListAdapter.java b/src/com/timsu/astrid/activities/TaskListAdapter.java
index 7681583b5..6f021f9ca 100644
--- a/src/com/timsu/astrid/activities/TaskListAdapter.java
+++ b/src/com/timsu/astrid/activities/TaskListAdapter.java
@@ -19,7 +19,6 @@ package com.timsu.astrid.activities;
import java.text.Format;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -93,7 +92,7 @@ public class TaskListAdapter extends ArrayAdapter {
private static Format alarmFormat = null;
private final Activity activity;
- private ArrayList objects;
+ private List objects;
private int resource;
private LayoutInflater inflater;
private TaskListAdapterHooks hooks;
@@ -111,7 +110,7 @@ public class TaskListAdapter extends ArrayAdapter {
*
*/
public interface TaskListAdapterHooks {
- ArrayList getTaskArray();
+ List getTaskArray();
String getTagsFor(TaskModelForList task);
TaskController taskController();
TagController tagController();
@@ -133,7 +132,7 @@ public class TaskListAdapter extends ArrayAdapter {
* @param hooks
*/
public TaskListAdapter(Activity activity, int resource,
- ArrayList objects, TaskListAdapterHooks hooks) {
+ List objects, TaskListAdapterHooks hooks) {
super(activity, resource, objects);
inflater = (LayoutInflater)activity.getSystemService(
diff --git a/src/com/timsu/astrid/activities/TaskListSubActivity.java b/src/com/timsu/astrid/activities/TaskListSubActivity.java
index 517542064..5dccddaf2 100644
--- a/src/com/timsu/astrid/activities/TaskListSubActivity.java
+++ b/src/com/timsu/astrid/activities/TaskListSubActivity.java
@@ -18,14 +18,13 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.timsu.astrid.activities;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
-import java.util.Map;
+import java.util.List;
import java.util.Random;
import android.app.AlertDialog;
@@ -138,8 +137,8 @@ public class TaskListSubActivity extends SubActivity {
// other instance variables
static class TaskListContext {
- Map tagMap;
- ArrayList taskArray;
+ HashMap tagMap;
+ List taskArray;
HashMap tasksById;
HashMap taskTags;
TaskModelForList selectedTask = null;
@@ -504,7 +503,8 @@ public class TaskListSubActivity extends SubActivity {
tasksCursor = getTaskController().getActiveTaskListCursor();
}
startManagingCursor(tasksCursor);
- context.taskArray = getTaskController().createTaskListFromCursor(tasksCursor);
+ context.taskArray = Collections.synchronizedList(getTaskController().
+ createTaskListFromCursor(tasksCursor));
// read tags and apply filters
context.tagMap = getTagController().getAllTagsAsMap();
@@ -650,7 +650,7 @@ public class TaskListSubActivity extends SubActivity {
class TaskListHooks implements TaskListAdapterHooks {
private HashMap myTaskTags;
- private ArrayList myTaskArray;
+ private List myTaskArray;
public TaskListHooks() {
this.myTaskTags = context.taskTags;
@@ -665,7 +665,7 @@ public class TaskListSubActivity extends SubActivity {
return myTaskTags.get(task);
}
- public ArrayList getTaskArray() {
+ public List getTaskArray() {
return myTaskArray;
}
diff --git a/src/com/timsu/astrid/sync/RTMSyncProvider.java b/src/com/timsu/astrid/sync/RTMSyncProvider.java
index c20b5a67e..e2c80fb1c 100644
--- a/src/com/timsu/astrid/sync/RTMSyncProvider.java
+++ b/src/com/timsu/astrid/sync/RTMSyncProvider.java
@@ -28,10 +28,9 @@ import java.util.StringTokenizer;
import java.util.Map.Entry;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
-import android.net.Uri;
+import android.os.Handler;
import android.util.Log;
import com.mdt.rtm.ApplicationInfo;
@@ -48,7 +47,8 @@ import com.mdt.rtm.data.RtmTasks;
import com.mdt.rtm.data.RtmAuth.Perms;
import com.mdt.rtm.data.RtmTask.Priority;
import com.timsu.astrid.R;
-import com.timsu.astrid.activities.TaskList;
+import com.timsu.astrid.activities.SyncLoginActivity;
+import com.timsu.astrid.activities.SyncLoginActivity.SyncLoginCallback;
import com.timsu.astrid.data.enums.Importance;
import com.timsu.astrid.data.sync.SyncMapping;
import com.timsu.astrid.data.tag.TagController;
@@ -93,6 +93,8 @@ public class RTMSyncProvider extends SynchronizationProvider {
/** Perform authentication with RTM. Will open the SyncBrowser if necessary */
private void authenticate(final Context context) {
+ final Resources r = context.getResources();
+
try {
String apiKey = "bd9883b3384a21ead17501da38bb1e68";
String sharedSecret = "a19b2a020345219b";
@@ -130,17 +132,40 @@ public class RTMSyncProvider extends SynchronizationProvider {
apiKey, sharedSecret, appName));
final String url = rtmService.beginAuthorization(Perms.delete);
progressDialog.dismiss();
- Resources r = context.getResources();
- DialogUtilities.okCancelDialog(context,
- r.getString(R.string.sync_auth_request, "RTM"),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- TaskList.synchronizeNow = true;
- Intent intent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(url));
- context.startActivity(intent);
+
+ Intent intent = new Intent(context, SyncLoginActivity.class);
+ SyncLoginActivity.setCallback(new SyncLoginCallback() {
+ @Override
+ public boolean verifyLogin(final Handler syncLoginHandler) {
+ if(rtmService == null) {
+ Log.e("rtmsync", "Error: sync login activity displayed with no service!");
+ return true;
+ }
+
+ try {
+ String token = rtmService.completeAuthorization();
+ Log.w("astrid", "got RTM token: " + token);
+ Preferences.setSyncRTMToken(context, token);
+ return true;
+ } catch (final Exception e) {
+ // didn't work
+
+ syncLoginHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.okDialog(context,
+ r.getString(R.string.rtm_login_error) +
+ " " + e.getMessage(), null);
+ }
+ });
+
+ return false;
+ }
}
- }, null);
+ });
+ intent.putExtra(SyncLoginActivity.URL_TOKEN, url);
+ intent.putExtra(SyncLoginActivity.LABEL_TOKEN, R.string.rtm_login_label);
+ context.startActivity(intent);
} else {
performSync(context);