diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index cef991eb2..f38144f62 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -214,7 +214,13 @@
-
+
+
+
+
+
+
+
-
+
diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
index 55a73f62a..f96d15eb4 100644
--- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
@@ -72,4 +72,9 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose
return notes;
}
+ @Override
+ public String getPluginIdentifier() {
+ return NotesPlugin.IDENTIFIER;
+ }
+
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java
index be7b51452..78b274e84 100644
--- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java
@@ -135,4 +135,9 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo
return null;
}
+ @Override
+ public String getPluginIdentifier() {
+ return RepeatsPlugin.IDENTIFIER;
+ }
+
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
index db816776d..f266b69dd 100644
--- a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java
@@ -88,4 +88,9 @@ public class MilkDetailExposer extends BroadcastReceiver implements DetailExpose
return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length());
}
+ @Override
+ public String getPluginIdentifier() {
+ return Utilities.IDENTIFIER;
+ }
+
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
index a91ae0614..d8c4ea6fc 100644
--- a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
@@ -52,4 +52,9 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer
return context.getString(R.string.tag_TLA_detail, tagList);
}
+ @Override
+ public String getPluginIdentifier() {
+ return TagsPlugin.IDENTIFIER;
+ }
+
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
new file mode 100644
index 000000000..c7122c8a7
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
@@ -0,0 +1,76 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.timers;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.TaskAction;
+import com.todoroo.astrid.api.TaskDecoration;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.TaskService;
+
+/**
+ * Exposes {@link TaskDecoration} for timers
+ *
+ * @author Tim Su
+ *
+ */
+public class TimerActionExposer extends BroadcastReceiver {
+
+ @Autowired
+ private TaskService taskService;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
+ if(taskId == -1)
+ return;
+
+ synchronized(TimerDecorationExposer.class) {
+ if(TimerDecorationExposer.staticTaskService == null) {
+ DependencyInjectionService.getInstance().inject(this);
+ TimerDecorationExposer.staticTaskService = taskService;
+ } else {
+ taskService = TimerDecorationExposer.staticTaskService;
+ }
+ }
+
+ Task task = taskService.fetchById(taskId, Task.TIMER_START);
+
+ // was part of a broadcast for actions
+ if(AstridApiConstants.BROADCAST_REQUEST_ACTIONS.equals(intent.getAction())) {
+ String label;
+ if(task.getValue(Task.TIMER_START) == 0)
+ label = context.getString(R.string.TAE_startTimer);
+ else
+ label = context.getString(R.string.TAE_stopTimer);
+ Intent newIntent = new Intent(context, TimerActionExposer.class);
+ TaskAction action = new TaskAction(label,
+ PendingIntent.getBroadcast(context, 0, newIntent, 0));
+
+ // transmit
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ACTIONS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TimerPlugin.IDENTIFIER);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, action);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ } else {
+ // toggle the timer
+ if(task.getValue(Task.TIMER_START) == 0)
+ task.setValue(Task.TIMER_START, DateUtilities.now());
+ else
+ task.setValue(Task.TIMER_START, 0L);
+ taskService.save(task, true);
+ }
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
index cf8ebd870..9093f1c34 100644
--- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
@@ -30,7 +30,7 @@ public class TimerDecorationExposer extends BroadcastReceiver {
private static final int TASK_BG_COLOR = Color.argb(200, 220, 50, 0);
- private static TaskService staticTaskService;
+ static TaskService staticTaskService;
private static HashMap decorations =
new HashMap();
private static HashMap timers =
diff --git a/astrid/res/values/strings-timers.xml b/astrid/res/values/strings-timers.xml
new file mode 100644
index 000000000..ca97fc583
--- /dev/null
+++ b/astrid/res/values/strings-timers.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+ Start Timer
+
+
+ Stop Timer
+
+
+ Elapsed Time: %s
+
+
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index ce45edc46..d63465e73 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -52,6 +52,7 @@ import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.adapter.TaskAdapter.ViewHolder;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.CoreFilterExposer;
import com.todoroo.astrid.dao.Database;
@@ -380,6 +381,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
registerReceiver(detailReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DECORATIONS));
+ registerReceiver(detailReceiver,
+ new IntentFilter(AstridApiConstants.BROADCAST_SEND_ACTIONS));
}
@Override
@@ -400,16 +403,20 @@ public class TaskListActivity extends ListActivity implements OnScrollListener {
try {
Bundle extras = intent.getExtras();
long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
+ String addOn = extras.getString(AstridApiConstants.EXTRAS_ADDON);
if(AstridApiConstants.BROADCAST_SEND_DECORATIONS.equals(intent.getAction())) {
TaskDecoration deco = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
- taskAdapter.addDecorations(getListView(), taskId,
- deco);
- } else {
+ taskAdapter.decorationManager.addNew(taskId, addOn, deco);
+ } else if(AstridApiConstants.BROADCAST_SEND_DETAILS.equals(intent.getAction())) {
String detail = extras.getString(AstridApiConstants.EXTRAS_RESPONSE);
- taskAdapter.addDetails(getListView(), taskId,
- extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED),
- detail);
+ if(extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED))
+ taskAdapter.detailManager.addNew(taskId, addOn, detail);
+ else
+ taskAdapter.extendedDetailManager.addNew(taskId, addOn, detail);
+ } else if(AstridApiConstants.BROADCAST_SEND_ACTIONS.equals(intent.getAction())) {
+ TaskAction action = extras.getParcelable(AstridApiConstants.BROADCAST_SEND_ACTIONS);
+ taskAdapter.taskActionManager.addNew(taskId, addOn, action);
}
} catch (Exception e) {
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
index b1ba04bf0..fe1aa4c12 100644
--- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
+++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
@@ -1,19 +1,18 @@
package com.todoroo.astrid.adapter;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
+import android.graphics.Color;
import android.graphics.Paint;
import android.text.Html;
import android.view.ContextMenu;
@@ -41,9 +40,9 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.SoftHashMap;
import com.todoroo.astrid.activity.TaskEditActivity;
-import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.DetailExposer;
+import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.notes.NoteDetailExposer;
@@ -111,9 +110,11 @@ public class TaskAdapter extends CursorAdapter {
// --- task detail and decoration soft caches
- private static final TaskCache detailCache = new TaskCache();
- private static final TaskCache extendedDetailCache = new TaskCache();
- private static final TaskCache decorationCache = new TaskCache();
+ public final DetailManager detailManager = new DetailManager(false);
+ public final DetailManager extendedDetailManager = new DetailManager(true);
+ public final DecorationManager decorationManager =
+ new DecorationManager();
+ public final TaskActionManager taskActionManager = new TaskActionManager();
/**
* Constructor
@@ -285,18 +286,9 @@ public class TaskAdapter extends CursorAdapter {
}
// details and decorations
- viewHolder.details.setText(""); //$NON-NLS-1$
- if(viewHolder.decorations != null) {
- for(View decoration: viewHolder.decorations)
- viewHolder.taskRow.removeView(decoration);
- }
- viewHolder.taskRow.setBackgroundDrawable(null);
if(!isFling) {
- // task details - send out a request for it
- retrieveDetails(viewHolder, false);
-
- // task decoration - send out a request for it
- retrieveDecorations(viewHolder);
+ detailManager.request(viewHolder);
+ decorationManager.request(viewHolder);
}
}
@@ -321,217 +313,198 @@ public class TaskAdapter extends CursorAdapter {
* ============================================================== add-ons
* ====================================================================== */
- private void retrieveDetails(final ViewHolder viewHolder, final boolean extended) {
- final long taskId = viewHolder.task.getId();
-
- final TaskCache cache = extended ? extendedDetailCache : detailCache;
- ArrayList list = cache.initialize(taskId);
- if(list != null) {
- spanifyAndAdd(viewHolder, extended, list);
- return;
- }
-
- // request details
- Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
- activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
-
- // load internal details
- new Thread() {
- @Override
- public void run() {
- for(DetailExposer exposer : EXPOSERS) {
- final String detail = exposer.getTaskDetails(activity,
- taskId, extended);
- if(detail == null)
- continue;
- final ArrayList cacheList = cache.addIfNotExists(taskId, detail);
- if(cacheList != null) {
- if(taskId != viewHolder.task.getId())
- continue;
- activity.runOnUiThread(new Runnable() {
- public void run() {
- spanifyAndAdd(viewHolder, extended, cacheList);
- }
- });
- }
- }
- };
- }.start();
- }
-
- @SuppressWarnings("nls")
- private void spanifyAndAdd(ViewHolder viewHolder, boolean extended, ArrayList details) {
- if(details == null)
- return;
- TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
- view.setVisibility(details.size() > 0 ? View.VISIBLE : View.GONE);
- if(details.size() == 0 || (extended && !viewHolder.expanded)) {
- view.setVisibility(View.GONE);
- return;
- } else {
- view.setVisibility(View.VISIBLE);
- }
- StringBuilder detailText = new StringBuilder();
- for(Iterator iterator = details.iterator(); iterator.hasNext(); ) {
- detailText.append(iterator.next());
- if(iterator.hasNext())
- detailText.append(DETAIL_SEPARATOR);
- }
- String string = detailText.toString();
- if(string.contains("<"))
- view.setText(Html.fromHtml(string.trim().replace("\n", "
")));
- else
- view.setText(string.trim());
- }
-
/**
* Called to tell the cache to be cleared
*/
public void flushCaches() {
- detailCache.clear();
- extendedDetailCache.clear();
- decorationCache.clear();
+ detailManager.clearCache();
+ extendedDetailManager.clearCache();
+ decorationManager.clearCache();
}
/**
- * Respond to a request to add details for a task
+ * AddOnManager for Details
+ * @author Tim Su
*
- * @param taskId
*/
- public synchronized void addDetails(ListView listView, long taskId,
- boolean extended, String detail) {
- if(detail == null)
- return;
+ public class DetailManager extends AddOnManager {
- final TaskCache cache = extended ? extendedDetailCache : detailCache;
-
- ArrayList cacheList = cache.addIfNotExists(taskId, detail);
- if(cacheList != null) {
- // update view if it is visible
- int length = listView.getChildCount();
- for(int i = 0; i < length; i++) {
- ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
- if(viewHolder == null || viewHolder.task.getId() != taskId)
- continue;
- spanifyAndAdd(viewHolder, extended, cacheList);
- break;
- }
+ private final boolean extended;
+ public DetailManager(boolean extended) {
+ this.extended = extended;
}
- }
- private final View.OnClickListener completeBoxListener = new View.OnClickListener() {
- public void onClick(View v) {
- ViewHolder viewHolder = (ViewHolder)((View)v.getParent().getParent()).getTag();
- Task task = viewHolder.task;
-
- completeTask(task, ((CheckBox)v).isChecked());
- // set check box to actual action item state
- setTaskAppearance(viewHolder, task.isCompleted());
+ @Override
+ Intent createBroadcastIntent(long taskId) {
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended);
+ return broadcastIntent;
}
- };
-
- private void retrieveDecorations(final ViewHolder viewHolder) {
- final long taskId = viewHolder.task.getId();
- ArrayList list = decorationCache.initialize(taskId);
- if(list != null) {
- decorate(viewHolder, list);
- return;
+ @Override
+ public boolean request(final ViewHolder viewHolder) {
+ if(super.request(viewHolder)) {
+ final long taskId = viewHolder.task.getId();
+ // load internal details
+ new Thread() {
+ @Override
+ public void run() {
+ for(DetailExposer exposer : EXPOSERS) {
+ final String detail = exposer.getTaskDetails(activity,
+ taskId, extended);
+ if(detail == null)
+ continue;
+ final Collection cacheList =
+ addIfNotExists(taskId, exposer.getPluginIdentifier(),
+ detail);
+ if(cacheList != null) {
+ if(taskId != viewHolder.task.getId())
+ continue;
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ draw(viewHolder, cacheList);
+ }
+ });
+ }
+ }
+ };
+ }.start();
+ return true;
+ }
+ return false;
}
- // request details
- Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
- activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
-
- if(timer == null) {
- timer = new Timer();
- timer.scheduleAtFixedRate(decorationUpdater, 0, 1000L);
+ @SuppressWarnings("nls")
+ @Override
+ void draw(ViewHolder viewHolder, Collection details) {
+ if(details == null)
+ return;
+ TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
+ view.setVisibility(!details.isEmpty() ? View.VISIBLE : View.GONE);
+ if(details.isEmpty() || (extended && !viewHolder.expanded)) {
+ view.setVisibility(View.GONE);
+ return;
+ } else {
+ view.setVisibility(View.VISIBLE);
+ }
+ StringBuilder detailText = new StringBuilder();
+ for(Iterator iterator = details.iterator(); iterator.hasNext(); ) {
+ detailText.append(iterator.next());
+ if(iterator.hasNext())
+ detailText.append(DETAIL_SEPARATOR);
+ }
+ String string = detailText.toString();
+ if(string.contains("<"))
+ view.setText(Html.fromHtml(string.trim().replace("\n", "
")));
+ else
+ view.setText(string.trim());
}
- }
- private Timer timer = null;
+ }
/**
- * Task to update decorations every second
+ * AddOnManager for TaskDecorations
+ *
+ * @author Tim Su
+ *
*/
- private final TimerTask decorationUpdater = new TimerTask() {
+ public class DecorationManager extends AddOnManager {
@Override
- public void run() {
- ListView listView = activity.getListView();
- int length = listView.getChildCount();
- for(int i = 0; i < length; i++) {
- ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
- ArrayList list = decorationCache.get(viewHolder.task.getId());
- if(list == null)
- continue;
-
- for(int j = 0; j < list.size(); j++) {
- final TaskDecoration decoration = list.get(j);
- if(decoration.decoration == null)
- continue;
- final View view = viewHolder.decorations[j];
- if(view == null)
- continue;
- activity.runOnUiThread(new Runnable() {
- public void run() {
- decoration.decoration.reapply(activity, view);
- }
- });
+ Intent createBroadcastIntent(long taskId) {
+ Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ return intent;
+ }
+
+ @Override
+ void draw(ViewHolder viewHolder, Collection decorations) {
+ if(decorations == null || decorations.size() == 0)
+ return;
+
+ if(viewHolder.decorations != null) {
+ for(View view : viewHolder.decorations)
+ viewHolder.taskRow.removeView(view);
+ ((View)viewHolder.taskRow.getParent()).setBackgroundColor(Color.TRANSPARENT);
+ }
+
+
+ int i = 0;
+ boolean colorSet = false;
+ viewHolder.decorations = new View[decorations.size()];
+ for(TaskDecoration decoration : decorations) {
+ if(decoration.color != 0 && !colorSet) {
+ colorSet = true;
+ ((View)viewHolder.taskRow.getParent()).setBackgroundColor(decoration.color);
+ }
+ if(decoration.decoration != null) {
+ View view = decoration.decoration.apply(activity, viewHolder.taskRow);
+ viewHolder.decorations[i] = view;
+ switch(decoration.position) {
+ case TaskDecoration.POSITION_LEFT:
+ viewHolder.taskRow.addView(view, 1);
+ break;
+ case TaskDecoration.POSITION_RIGHT:
+ viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 2);
+ }
}
+ i++;
}
}
- };
+ }
/**
- * Respond to a request to add details for a task
+ * AddOnManager for TaskActions
+ *
+ * @author Tim Su
*
- * @param taskId
*/
- public synchronized void addDecorations(ListView listView, long taskId,
- TaskDecoration taskDecoration) {
- if(taskDecoration == null)
- return;
-
- ArrayList cacheList = decorationCache.addIfNotExists(taskId,
- taskDecoration);
- if(cacheList != null) {
- // update view if it is visible
- int length = listView.getChildCount();
- for(int i = 0; i < length; i++) {
- ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
- if(viewHolder == null || viewHolder.task.getId() != taskId)
- continue;
- decorate(viewHolder, cacheList);
- break;
- }
+ public class TaskActionManager extends AddOnManager {
+ @Override
+ Intent createBroadcastIntent(long taskId) {
+ Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS);
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ return intent;
}
- }
- private void decorate(ViewHolder viewHolder, ArrayList decorations) {
- if(decorations == null || decorations.size() == 0)
- return;
+ @Override
+ void draw(final ViewHolder viewHolder, Collection actions) {
+ if(actions == null)
+ return;
- // apply decorations in reverse so top priority appears at the ends &
- // color is set bu the most important decoration
- viewHolder.decorations = new View[decorations.size()];
- for(int i = decorations.size() - 1; i >= 0; i--) {
- TaskDecoration deco = decorations.get(i);
- if(deco.color != 0)
- ((View)viewHolder.taskRow.getParent()).setBackgroundColor(deco.color);
- if(deco.decoration != null) {
- View view = deco.decoration.apply(activity, viewHolder.taskRow);
- viewHolder.decorations[i] = view;
- switch(deco.position) {
- case TaskDecoration.POSITION_LEFT:
- viewHolder.taskRow.addView(view, 1);
- break;
- case TaskDecoration.POSITION_RIGHT:
- viewHolder.taskRow.addView(view, viewHolder.taskRow.getChildCount() - 2);
+ if(!viewHolder.expanded) {
+ viewHolder.actions.setVisibility(View.GONE);
+ return;
+ }
+ viewHolder.actions.setVisibility(View.VISIBLE);
+ viewHolder.actions.removeAllViews();
+
+ LinearLayout.LayoutParams params =
+ new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT, 1f);
+
+ Button editButton = new Button(activity);
+ editButton.setText(R.string.TAd_actionEditTask);
+ editButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ Intent intent = new Intent(activity, TaskEditActivity.class);
+ long taskId = viewHolder.task.getId();
+ intent.putExtra(TaskEditActivity.ID_TOKEN, taskId);
+ activity.startActivity(intent);
}
+ });
+ editButton.setLayoutParams(params);
+ viewHolder.actions.addView(editButton);
+
+ for(TaskAction action : actions) {
+ Button view = new Button(activity);
+ view.setText(action.text);
+ view.setOnClickListener(new ActionClickListener(action));
+ view.setLayoutParams(params);
+ viewHolder.actions.addView(view);
}
+
}
}
@@ -545,11 +518,39 @@ public class TaskAdapter extends CursorAdapter {
fontSize = Preferences.getIntegerFromString(R.string.p_fontSize);
}
- class TaskRowListener implements OnCreateContextMenuListener, OnClickListener {
+ private final View.OnClickListener completeBoxListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ ViewHolder viewHolder = (ViewHolder)((View)v.getParent().getParent()).getTag();
+ Task task = viewHolder.task;
+
+ completeTask(task, ((CheckBox)v).isChecked());
+ // set check box to actual action item state
+ setTaskAppearance(viewHolder, task.isCompleted());
+ }
+ };
+
+ private final class ActionClickListener implements View.OnClickListener {
+ TaskAction action;
+
+ public ActionClickListener(TaskAction action) {
+ this.action = action;
+ }
+
+ public void onClick(View v) {
+ try {
+ action.intent.send();
+ } catch (Exception e) {
+ // oh too bad.
+ }
+ }
+ };
+
+ private class TaskRowListener implements OnCreateContextMenuListener, OnClickListener {
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- // this is all a big sham. it's actually handled in Task List Activity
+ // this is all a big sham. it's actually handled in Task List
+ // Activity. however, we need this to be here.
}
@Override
@@ -557,34 +558,10 @@ public class TaskAdapter extends CursorAdapter {
// expand view
final ViewHolder viewHolder = (ViewHolder)v.getTag();
viewHolder.expanded = !viewHolder.expanded;
- LinearLayout actions = viewHolder.actions;
- TextView extendedDetails = viewHolder.extendedDetails;
- actions.setVisibility(viewHolder.expanded ? View.VISIBLE : View.GONE);
- if(!viewHolder.expanded) {
- viewHolder.extendedDetails.setVisibility(View.GONE);
- return;
- }
-
- final long taskId = viewHolder.task.getId();
- extendedDetails.setText(""); //$NON-NLS-1$
- retrieveDetails(viewHolder, true);
-
- if(actions.getChildCount() == 0) {
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1f);
- Button edit = new Button(activity);
- edit.setText(R.string.TAd_actionEditTask);
- edit.setLayoutParams(params);
- edit.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = new Intent(activity, TaskEditActivity.class);
- intent.putExtra(TaskEditActivity.ID_TOKEN, taskId);
- activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK);
- }
- });
- actions.addView(edit);
+ if(viewHolder.expanded) {
+ extendedDetailManager.request(viewHolder);
+ taskActionManager.request(viewHolder);
}
}
}
@@ -646,13 +623,71 @@ public class TaskAdapter extends CursorAdapter {
}
/* ======================================================================
- * =============================================================== caches
+ * ========================================================= addon helper
* ====================================================================== */
- private static class TaskCache {
+ abstract private class AddOnManager {
+
+ private final Map> cache =
+ Collections.synchronizedMap(new SoftHashMap>());
+
+ // --- interface
+
+ /**
+ * Request add-ons for the given task
+ * @return true if cache miss, false if cache hit
+ */
+ public boolean request(ViewHolder viewHolder) {
+ long taskId = viewHolder.task.getId();
+
+ Collection list = initialize(taskId);
+ if(list != null) {
+ draw(viewHolder, list);
+ return false;
+ }
+
+ // request details
+ Intent broadcastIntent = createBroadcastIntent(taskId);
+ activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ draw(viewHolder, get(taskId));
+ return true;
+ }
+
+ /** creates a broadcast intent for requesting */
+ abstract Intent createBroadcastIntent(long taskId);
+
+ /** updates the given view */
+ abstract void draw(ViewHolder viewHolder, Collection list);
+
+ /** on receive an intent */
+ public void addNew(long taskId, String addOn, TYPE item) {
+ if(item == null)
+ return;
+
+ Collection cacheList = addIfNotExists(taskId, addOn, item);
+ if(cacheList != null) {
+ ListView listView = activity.getListView();
+ // update view if it is visible
+ int length = listView.getChildCount();
+ for(int i = 0; i < length; i++) {
+ ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag();
+ if(viewHolder == null || viewHolder.task.getId() != taskId)
+ continue;
+ draw(viewHolder, cacheList);
+ break;
+ }
+ }
+ }
- private final Map> cache =
- Collections.synchronizedMap(new SoftHashMap>());
+ /**
+ * Clears the cache
+ */
+ public void clearCache() {
+ cache.clear();
+ }
+
+ // --- internal goodies
/**
* Retrieves a list. If it doesn't exist, list is created, but
@@ -660,10 +695,10 @@ public class TaskAdapter extends CursorAdapter {
* @param taskId
* @return list if there was already one
*/
- public ArrayList initialize(long taskId) {
+ protected Collection initialize(long taskId) {
if(cache.containsKey(taskId))
- return cache.get(taskId);
- cache.put(taskId, new ArrayList());
+ return get(taskId);
+ cache.put(taskId, new HashMap());
return null;
}
@@ -671,25 +706,26 @@ public class TaskAdapter extends CursorAdapter {
* Adds an item to the cache if it doesn't exist
* @param taskId
* @param item
- * @return list if item was added, null if it already existed
+ * @return iterator if item was added, null if it already existed
*/
- public ArrayList addIfNotExists(long taskId, TYPE item) {
- ArrayList list = cache.get(taskId);
- if(list == null || list.contains(item))
+ protected Collection addIfNotExists(long taskId, String addOn,
+ TYPE item) {
+ HashMap list = cache.get(taskId);
+ if(list == null)
return null;
- list.add(item);
- return list;
+ if(list.containsKey(addOn) && list.get(addOn).equals(item))
+ return null;
+ list.put(addOn, item);
+ return get(taskId);
}
/**
- * Clears the cache
+ * Gets an item at the given index
+ * @param taskId
+ * @return
*/
- public void clear() {
- cache.clear();
- }
-
- public ArrayList get(long taskId) {
- return cache.get(taskId);
+ protected Collection get(long taskId) {
+ return cache.get(taskId).values();
}
}
diff --git a/astrid/src/com/todoroo/astrid/api/DetailExposer.java b/astrid/src/com/todoroo/astrid/api/DetailExposer.java
index 804b7c846..90ef95b9d 100644
--- a/astrid/src/com/todoroo/astrid/api/DetailExposer.java
+++ b/astrid/src/com/todoroo/astrid/api/DetailExposer.java
@@ -21,4 +21,6 @@ public interface DetailExposer {
*/
public String getTaskDetails(Context context, long id, boolean extended);
+ public String getPluginIdentifier();
+
}
diff --git a/astrid/src/com/todoroo/astrid/model/Task.java b/astrid/src/com/todoroo/astrid/model/Task.java
index bc0e1256b..e728c639b 100644
--- a/astrid/src/com/todoroo/astrid/model/Task.java
+++ b/astrid/src/com/todoroo/astrid/model/Task.java
@@ -84,7 +84,7 @@ public final class Task extends AbstractModel {
public static final IntegerProperty ELAPSED_SECONDS = new IntegerProperty(
TABLE, "elapsedSeconds");
- public static final IntegerProperty TIMER_START = new IntegerProperty(
+ public static final LongProperty TIMER_START = new LongProperty(
TABLE, "timerStart");
public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(