diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index c9673f522..effd0aae9 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -376,6 +376,12 @@
+
+
+
+
+
+
@@ -410,6 +416,12 @@
+
+
+
+
+
+
diff --git a/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java b/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
index fac205bcc..8292604e6 100644
--- a/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
+++ b/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
@@ -133,17 +133,16 @@ public class AstridApiConstants {
/**
* Action name for broadcast intent requesting a listing of active
- * sync providers users can activate from the menu
+ * sync actions users can activate from the menu
*/
- public static final String BROADCAST_REQUEST_SYNC_PROVDERS = PACKAGE + ".REQUEST_SYNC_PROVIDERS";
+ public static final String BROADCAST_REQUEST_SYNC_ACTIONS = PACKAGE + ".REQUEST_SYNC_ACTIONS";
/**
* Action name for broadcast intent sending sync provider information back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
- * @extra EXTRAS_NAME label for your sync provider
- * @extra EXTRAS_RESPONSE a PendingIntent to invoke synchronization
+ * @extra EXTRAS_RESPONSE a {@link SyncAction} to invoke synchronization
*/
- public static final String BROADCAST_SEND_SYNC_PROVIDER = PACKAGE + ".SEND_SYNC_PROVIDER";
+ public static final String BROADCAST_SEND_SYNC_ACTIONS = PACKAGE + ".SEND_SYNC_ACTIONS";
// --- Task Actions API
diff --git a/astrid/api-src/com/todoroo/astrid/api/SyncAction.java b/astrid/api-src/com/todoroo/astrid/api/SyncAction.java
new file mode 100644
index 000000000..207bfc3fe
--- /dev/null
+++ b/astrid/api-src/com/todoroo/astrid/api/SyncAction.java
@@ -0,0 +1,87 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.api;
+
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an intent that can be called to perform synchronization
+ *
+ * @author Tim Su
+ *
+ */
+public class SyncAction implements Parcelable {
+
+ /**
+ * Label
+ */
+ public String label = null;
+
+ /**
+ * Intent to call when invoking this operation
+ */
+ public PendingIntent intent = null;
+
+ /**
+ * Create an EditOperation object
+ *
+ * @param label
+ * label to display
+ * @param intent
+ * intent to invoke. {@link EXTRAS_TASK_ID} will be passed
+ */
+ public SyncAction(String label, PendingIntent intent) {
+ super();
+ this.label = label;
+ this.intent = intent;
+ }
+
+ /**
+ * Returns the label of this action
+ */
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ // --- parcelable helpers
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(label);
+ dest.writeParcelable(intent, 0);
+ }
+
+ /**
+ * Parcelable creator
+ */
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ /**
+ * {@inheritDoc}
+ */
+ public SyncAction createFromParcel(Parcel source) {
+ return new SyncAction(source.readString(), (PendingIntent)source.readParcelable(
+ PendingIntent.class.getClassLoader()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public SyncAction[] newArray(int size) {
+ return new SyncAction[size];
+ };
+ };
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevSyncActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevSyncActionExposer.java
new file mode 100644
index 000000000..de3b75500
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevSyncActionExposer.java
@@ -0,0 +1,39 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.producteev;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.SyncAction;
+
+/**
+ * Exposes sync action
+ *
+ */
+public class ProducteevSyncActionExposer extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // if we aren't logged in, don't expose sync action
+ if(!ProducteevUtilities.INSTANCE.isLoggedIn())
+ return;
+
+ Intent syncIntent = new Intent(ProducteevBackgroundService.SYNC_ACTION, null,
+ context, ProducteevBackgroundService.class);
+ PendingIntent pendingIntent = PendingIntent.getService(context, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ SyncAction syncAction = new SyncAction(context.getString(R.string.producteev_PPr_header),
+ pendingIntent);
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, syncAction);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkSyncActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkSyncActionExposer.java
new file mode 100644
index 000000000..396709dc7
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkSyncActionExposer.java
@@ -0,0 +1,39 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.rmilk;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.SyncAction;
+
+/**
+ * Exposes sync action
+ *
+ */
+public class MilkSyncActionExposer extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // if we aren't logged in, don't expose sync action
+ if(!MilkUtilities.isLoggedIn())
+ return;
+
+ Intent syncIntent = new Intent(MilkBackgroundService.SYNC_ACTION, null,
+ context, MilkBackgroundService.class);
+ PendingIntent pendingIntent = PendingIntent.getService(context, 0, syncIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ SyncAction syncAction = new SyncAction(context.getString(R.string.rmilk_MPr_header),
+ pendingIntent);
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, MilkUtilities.IDENTIFIER);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, syncAction);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkNote.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkNote.java
index 3803d9952..aabb2f6ad 100644
--- a/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkNote.java
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/data/MilkNote.java
@@ -46,7 +46,7 @@ public class MilkNote {
/**
* Turn a note's title and text into a string
* @param title
- * @param text
+ * @param label
* @return
*/
@SuppressWarnings("nls")
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index ebec6b994..c2b4d123d 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -1,6 +1,7 @@
package com.todoroo.astrid.activity;
import java.util.Date;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
@@ -9,6 +10,7 @@ import java.util.concurrent.atomic.AtomicReference;
import android.app.AlertDialog;
import android.app.ListActivity;
+import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
@@ -38,6 +40,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -65,6 +68,7 @@ import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.PermaSql;
+import com.todoroo.astrid.api.SyncAction;
import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.backup.BackupActivity;
@@ -110,17 +114,17 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
private static final int MENU_ADDONS_ID = Menu.FIRST + 1;
private static final int MENU_SETTINGS_ID = Menu.FIRST + 2;
private static final int MENU_SORT_ID = Menu.FIRST + 3;
- private static final int MENU_HELP_ID = Menu.FIRST + 4;
- private static final int MENU_ADDON_INTENT_ID = Menu.FIRST + 5;
+ private static final int MENU_SYNC_ID = Menu.FIRST + 4;
+ private static final int MENU_HELP_ID = Menu.FIRST + 5;
+ private static final int MENU_ADDON_INTENT_ID = Menu.FIRST + 6;
- private static final int CONTEXT_MENU_EDIT_TASK_ID = Menu.FIRST + 6;
- private static final int CONTEXT_MENU_DELETE_TASK_ID = Menu.FIRST + 7;
- private static final int CONTEXT_MENU_UNDELETE_TASK_ID = Menu.FIRST + 8;
- private static final int CONTEXT_MENU_PURGE_TASK_ID = Menu.FIRST + 9;
- private static final int CONTEXT_MENU_ADDON_INTENT_ID = Menu.FIRST + 10;
+ private static final int CONTEXT_MENU_EDIT_TASK_ID = Menu.FIRST + 20;
+ private static final int CONTEXT_MENU_DELETE_TASK_ID = Menu.FIRST + 21;
+ private static final int CONTEXT_MENU_UNDELETE_TASK_ID = Menu.FIRST + 22;
+ private static final int CONTEXT_MENU_PURGE_TASK_ID = Menu.FIRST + 23;
+ private static final int CONTEXT_MENU_ADDON_INTENT_ID = Menu.FIRST + 24;
- /** menu code indicating the end of the context menu */
- private static final int CONTEXT_MENU_DEBUG = Menu.FIRST + 11;
+ private static final int CONTEXT_MENU_DEBUG = Menu.FIRST + 30;
// --- constants
@@ -150,6 +154,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
protected TaskAdapter taskAdapter = null;
protected DetailReceiver detailReceiver = new DetailReceiver();
protected RefreshReceiver refreshReceiver = new RefreshReceiver();
+ protected SyncActionReceiver syncActionReceiver = new SyncActionReceiver();
private ImageButton quickAddButton;
private EditText quickAddBox;
@@ -158,6 +163,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
private int sortSort;
private final AtomicReference sqlQueryTemplate = new AtomicReference();
private Timer backgroundTimer;
+ private final LinkedHashSet syncActions = new LinkedHashSet();
/* ======================================================================
* ======================================================= initialization
@@ -240,6 +246,12 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
R.string.TLA_menu_sort);
item.setIcon(android.R.drawable.ic_menu_sort_by_size);
+ if(syncActions.size() > 0) {
+ item = menu.add(Menu.NONE, MENU_SYNC_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.TLA_menu_help);
item.setIcon(android.R.drawable.ic_menu_help);
@@ -430,6 +442,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_ACTIONS));
registerReceiver(refreshReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_EVENT_REFRESH));
+ registerReceiver(syncActionReceiver,
+ new IntentFilter(AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS));
setUpBackgroundJobs();
}
@@ -450,6 +464,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
protected class RefreshReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
+ if(intent == null || !AstridApiConstants.BROADCAST_EVENT_REFRESH.equals(intent.getAction()))
+ return;
+
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -460,6 +477,29 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
}
}
+ /**
+ * Receiver which receives sync provider intents
+ *
+ * @author Tim Su
+ *
+ */
+ protected class SyncActionReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(intent == null || !AstridApiConstants.BROADCAST_SEND_SYNC_ACTIONS.equals(intent.getAction()))
+ return;
+
+ try {
+ Bundle extras = intent.getExtras();
+ SyncAction syncAction = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
+ syncActions.add(syncAction);
+ } catch (Exception e) {
+ exceptionService.reportError("receive-sync-action-" + //$NON-NLS-1$
+ intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
+ }
+ }
+ }
+
/**
* Receiver which receives detail or decoration intents
*
@@ -563,6 +603,11 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
if(oldListItemSelected != ListView.INVALID_POSITION &&
oldListItemSelected < taskCursor.getCount())
getListView().setSelection(oldListItemSelected);
+
+ // also load sync actions
+ syncActions.clear();
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_SYNC_ACTIONS);
+ sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
/**
@@ -785,6 +830,44 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
.show();
}
+ private void performSyncAction() {
+ if(syncActions.size() == 0)
+ return;
+ 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 {
+ final SyncAction[] actions = syncActions.toArray(new SyncAction[syncActions.size()]);
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_dropdown_item, actions);
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface click, int which) {
+ try {
+ actions[which].intent.send();
+ Toast.makeText(TaskListActivity.this, R.string.SyP_progress_toast,
+ Toast.LENGTH_LONG).show();
+ } catch (CanceledException e) {
+ //
+ }
+ }
+ };
+
+ // show a menu of available options
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.SyP_label)
+ .setAdapter(adapter, listener)
+ .show().setOwnerActivity(this);
+
+ }
+ }
+
@Override
public boolean onMenuItemSelected(int featureId, final MenuItem item) {
Intent intent;
@@ -805,6 +888,9 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
this, sortFlags, sortSort);
dialog.show();
return true;
+ case MENU_SYNC_ID:
+ performSyncAction();
+ return true;
case MENU_HELP_ID:
intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://weloveastrid.com/help-user-guide-astrid-v3/active-tasks/")); //$NON-NLS-1$