From 8f757ac9fa9ec678318d8281e637f9b9a4a6bf4d Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 18:05:11 -0700 Subject: [PATCH 1/6] First pass at task decorations, timer. Works but doesn't update. --- astrid/AndroidManifest.xml | 8 + .../astrid/api/AstridApiConstants.java | 47 ++- .../{EditOperation.java => TaskAction.java} | 28 +- .../com/todoroo/astrid/api/TaskContainer.java | 5 +- .../todoroo/astrid/api/TaskDecoration.java | 101 +++++ .../com/todoroo/astrid/api/TaskDetail.java | 84 ----- .../andlib/utility/AndroidUtilities.java | 15 +- .../com/todoroo/astrid/core/CorePlugin.java | 3 +- .../astrid/notes/NoteDetailExposer.java | 14 +- .../com/todoroo/astrid/notes/NotesPlugin.java | 1 + .../astrid/repeats/RepeatDetailExposer.java | 14 +- .../astrid/rmilk/MilkDetailExposer.java | 48 +-- .../todoroo/astrid/tags/TagDetailExposer.java | 15 +- .../com/todoroo/astrid/tags/TagsPlugin.java | 3 +- .../astrid/timers/TimerDecorationExposer.java | 116 ++++++ .../todoroo/astrid/timers/TimerPlugin.java | 25 ++ astrid/res/drawable/timers_decoration.png | Bin 0 -> 897 bytes astrid/res/layout/task_adapter_row.xml | 7 +- astrid/res/layout/timer_decoration.xml | 20 + .../astrid/activity/TaskListActivity.java | 21 +- .../todoroo/astrid/adapter/TaskAdapter.java | 357 +++++++++++++----- .../com/todoroo/astrid/api/DetailExposer.java | 7 +- 22 files changed, 692 insertions(+), 247 deletions(-) rename astrid/api-src/com/todoroo/astrid/api/{EditOperation.java => TaskAction.java} (57%) create mode 100644 astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java delete mode 100644 astrid/api-src/com/todoroo/astrid/api/TaskDetail.java create mode 100644 astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java create mode 100644 astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java create mode 100644 astrid/res/drawable/timers_decoration.png create mode 100644 astrid/res/layout/timer_decoration.xml diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 89957c65d..cef991eb2 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -207,6 +207,14 @@ + + + + + + + + * */ -public final class EditOperation implements Parcelable { - - /** - * Plugin Id - */ - public final String plugin; +public class TaskAction implements Parcelable { /** * Label @@ -33,15 +28,13 @@ public final class EditOperation implements Parcelable { /** * Create an EditOperation object * - * @param plugin - * {@link Addon} identifier that encompasses object * @param text * label to display * @param intent * intent to invoke. {@link EXTRAS_TASK_ID} will be passed */ - public EditOperation(String plugin, String text, Intent intent) { - this.plugin = plugin; + public TaskAction(String text, Intent intent) { + super(); this.text = text; this.intent = intent; } @@ -59,7 +52,6 @@ public final class EditOperation implements Parcelable { * {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { - dest.writeString(plugin); dest.writeString(text); dest.writeParcelable(intent, 0); } @@ -67,20 +59,20 @@ public final class EditOperation implements Parcelable { /** * Parcelable creator */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { /** * {@inheritDoc} */ - public EditOperation createFromParcel(Parcel source) { - return new EditOperation(source.readString(), source.readString(), - (Intent)source.readParcelable(Intent.class.getClassLoader())); + public TaskAction createFromParcel(Parcel source) { + return new TaskAction(source.readString(), (Intent)source.readParcelable( + Intent.class.getClassLoader())); } /** * {@inheritDoc} */ - public EditOperation[] newArray(int size) { - return new EditOperation[size]; + public TaskAction[] newArray(int size) { + return new TaskAction[size]; }; }; diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java b/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java index a12e08394..770bf51da 100644 --- a/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java +++ b/astrid/api-src/com/todoroo/astrid/api/TaskContainer.java @@ -6,9 +6,10 @@ import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Task; /** - * Container class for tasks. Synchronization Providers can subclass - * this class if desired. + * Container class for transmitting tasks and including local and remote + * metadata. Synchronization Providers can subclass this class if desired. * + * @see {@link SynchronizationProvider} * @author Tim Su * */ diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java b/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java new file mode 100644 index 000000000..91367fd53 --- /dev/null +++ b/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java @@ -0,0 +1,101 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.api; + +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.RemoteViews; +import android.widget.RemoteViews.RemoteView; + +/** + * Represents a line of text displayed in the Task List + * + * @author Tim Su + * + */ +public final class TaskDecoration implements Parcelable { + + /** + * Place decoration between completion box and task title + */ + public static final int POSITION_LEFT = 0; + + /** + * Place decoration between task title and importance bar + */ + public static final int POSITION_RIGHT = 1; + + /** + * {@link RemoteView} decoration + */ + public RemoteViews decoration = null; + + /** + * Decoration update interval (minimum of 1000 millis), 0 to never update + */ + public long updateInterval = 0; + + /** + * Decoration position + */ + public int position = POSITION_LEFT; + + /** + * Decorated task background color. 0 is default + */ + public int color = 0; + + /** + * Creates a TaskDetail object + * @param text + * text to display + * @param color + * color to use for text. Use 0 for default color + */ + public TaskDecoration(RemoteViews decoration, int position, int color) { + this.decoration = decoration; + this.position = position; + this.color = color; + } + + // --- parcelable helpers + + /** + * {@inheritDoc} + */ + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(decoration, 0); + dest.writeInt(position); + dest.writeInt(color); + } + + /** + * Parcelable creator + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + /** + * {@inheritDoc} + */ + public TaskDecoration createFromParcel(Parcel source) { + return new TaskDecoration((RemoteViews)source.readParcelable( + RemoteViews.class.getClassLoader()), + source.readInt(), source.readInt()); + } + + /** + * {@inheritDoc} + */ + public TaskDecoration[] newArray(int size) { + return new TaskDecoration[size]; + }; + }; + +} diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java b/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java deleted file mode 100644 index 4d24501e8..000000000 --- a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * See the file "LICENSE" for the full license governing this code. - */ -package com.todoroo.astrid.api; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Represents a line of text displayed in the Task List - * - * @author Tim Su - * - */ -public final class TaskDetail implements Parcelable { - - /** - * Text of detail - */ - public String text = null; - - /** - * Color to use for text. 0 is default - */ - public int color = 0; - - /** - * Creates a TaskDetail object - * @param text - * text to display - * @param color - * color to use for text. Use 0 for default color - */ - public TaskDetail(String text, int color) { - this.text = text; - this.color = color; - } - - /** - * Convenience constructor to make a TaskDetail with default color - * @param text - * text to display - */ - public TaskDetail(String text) { - this(text, 0); - } - - // --- parcelable helpers - - /** - * {@inheritDoc} - */ - public int describeContents() { - return 0; - } - - /** - * {@inheritDoc} - */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(text); - dest.writeInt(color); - } - - /** - * Parcelable creator - */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - /** - * {@inheritDoc} - */ - public TaskDetail createFromParcel(Parcel source) { - return new TaskDetail(source.readString(), source.readInt()); - } - - /** - * {@inheritDoc} - */ - public TaskDetail[] newArray(int size) { - return new TaskDetail[size]; - }; - }; - -} diff --git a/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java b/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java index 894f87467..0a76591ec 100644 --- a/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java +++ b/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java @@ -214,12 +214,13 @@ public class AndroidUtilities { /** * Copy a file from one place to another + * * @param in * @param out * @throws Exception */ public static void copyFile(File in, File out) throws Exception { - FileInputStream fis = new FileInputStream(in); + FileInputStream fis = new FileInputStream(in); FileOutputStream fos = new FileOutputStream(out); try { byte[] buf = new byte[1024]; @@ -227,13 +228,11 @@ public class AndroidUtilities { while ((i = fis.read(buf)) != -1) { fos.write(buf, 0, i); } - } - catch (Exception e) { + } catch (Exception e) { throw e; + } finally { + fis.close(); + fos.close(); } - finally { - if (fis != null) fis.close(); - if (fos != null) fos.close(); - } - } + } } diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CorePlugin.java b/astrid/plugin-src/com/todoroo/astrid/core/CorePlugin.java index c76847423..7f8848108 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CorePlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CorePlugin.java @@ -12,9 +12,10 @@ public class CorePlugin extends BroadcastReceiver { static final String IDENTIFIER = "core"; //$NON-NLS-1$ @Override + @SuppressWarnings("nls") public void onReceive(Context context, Intent intent) { Addon plugin = new Addon(IDENTIFIER, "Core Filters", "Todoroo", - "Provides 'Inbox' and 'All Tasks' Filters"); + "Provides 'Inbox', 'Search', and 'More...' Filters"); Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin); diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java index f5ef99e88..55a73f62a 100644 --- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java @@ -11,12 +11,11 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; -import com.todoroo.astrid.api.TaskDetail; import com.todoroo.astrid.model.Task; import com.todoroo.astrid.service.TaskService; /** - * Exposes {@link TaskDetail} for tags, i.e. "Tags: frogs, animals" + * Exposes Task Detail for notes * * @author Tim Su * @@ -35,7 +34,8 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose if(taskId == -1) return; - TaskDetail taskDetail = getTaskDetails(context, taskId); + boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); + String taskDetail = getTaskDetails(context, taskId, extended); if(taskDetail == null) return; @@ -43,12 +43,16 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, NotesPlugin.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } @Override - public TaskDetail getTaskDetails(Context context, long id) { + public String getTaskDetails(Context context, long id, boolean extended) { + if(!extended) + return null; + synchronized(NoteDetailExposer.class) { if(staticTaskService == null) { DependencyInjectionService.getInstance().inject(this); @@ -65,7 +69,7 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose if(notes.length() == 0) return null; - return new TaskDetail(notes); + return notes; } } diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NotesPlugin.java b/astrid/plugin-src/com/todoroo/astrid/notes/NotesPlugin.java index 2948c27b8..88038e84f 100644 --- a/astrid/plugin-src/com/todoroo/astrid/notes/NotesPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/notes/NotesPlugin.java @@ -12,6 +12,7 @@ public class NotesPlugin extends BroadcastReceiver { static final String IDENTIFIER = "notes"; //$NON-NLS-1$ @Override + @SuppressWarnings("nls") public void onReceive(Context context, Intent intent) { Addon plugin = new Addon(IDENTIFIER, "Notes", "Todoroo", "Lets you add and view notes for a task."); diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java index 894643997..be7b51452 100644 --- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java @@ -20,12 +20,11 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; -import com.todoroo.astrid.api.TaskDetail; import com.todoroo.astrid.model.Task; import com.todoroo.astrid.service.TaskService; /** - * Exposes {@link TaskDetail} for tags, i.e. "Tags: frogs, animals" + * Exposes Task Detail for repeats, i.e. "Repeats every 2 days" * * @author Tim Su * @@ -44,7 +43,8 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo if(taskId == -1) return; - TaskDetail taskDetail = getTaskDetails(context, taskId); + boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); + String taskDetail = getTaskDetails(context, taskId, extended); if(taskDetail == null) return; @@ -52,11 +52,15 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, RepeatsPlugin.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } - public TaskDetail getTaskDetails(Context context, long id) { + public String getTaskDetails(Context context, long id, boolean extended) { + if(extended) + return null; + synchronized(RepeatDetailExposer.class) { if(staticTaskService == null) { DependencyInjectionService.getInstance().inject(this); @@ -126,7 +130,7 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo else detail = context.getString(R.string.repeat_detail_duedate, interval); - return new TaskDetail(detail); + return detail; } return null; } diff --git a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java index 60d1ee278..db816776d 100644 --- a/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/rmilk/MilkDetailExposer.java @@ -12,17 +12,16 @@ import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; -import com.todoroo.astrid.api.TaskDetail; import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.rmilk.data.MilkDataService; import com.todoroo.astrid.rmilk.data.MilkNote; import com.todoroo.astrid.rmilk.data.MilkTask; /** - * Exposes {@link TaskDetail}s for Remember the Milk: + * Exposes Task Details for Remember the Milk: * - RTM list * - RTM repeat information - * - whether task has been changed + * - RTM notes * * @author Tim Su * @@ -39,49 +38,54 @@ public class MilkDetailExposer extends BroadcastReceiver implements DetailExpose if(taskId == -1) return; - TaskDetail taskDetail = getTaskDetails(context, taskId); + boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); + String taskDetail = getTaskDetails(context, taskId, extended); if(taskDetail == null) return; Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, Utilities.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } @Override - public TaskDetail getTaskDetails(Context context, long id) { + public String getTaskDetails(Context context, long id, boolean extended) { Metadata metadata = MilkDataService.getInstance().getTaskMetadata(id); if(metadata == null) return null; StringBuilder builder = new StringBuilder(); - long listId = metadata.getValue(MilkTask.LIST_ID); - if(listId > 0) { - builder.append(context.getString(R.string.rmilk_TLA_list, - MilkDataService.getInstance().getListName(listId))).append(TaskAdapter.DETAIL_SEPARATOR); - } - int repeat = metadata.getValue(MilkTask.REPEATING); - if(repeat != 0) { - builder.append(context.getString(R.string.rmilk_TLA_repeat)).append(TaskAdapter.DETAIL_SEPARATOR); - } + if(!extended) { + long listId = metadata.getValue(MilkTask.LIST_ID); + if(listId > 0) { + builder.append(context.getString(R.string.rmilk_TLA_list, + MilkDataService.getInstance().getListName(listId))).append(TaskAdapter.DETAIL_SEPARATOR); + } - TodorooCursor notesCursor = MilkDataService.getInstance().getTaskNotesCursor(id); - try { - for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) { - metadata.readFromCursor(notesCursor); - builder.append(MilkNote.toTaskDetail(metadata)).append(TaskAdapter.DETAIL_SEPARATOR); + int repeat = metadata.getValue(MilkTask.REPEATING); + if(repeat != 0) { + builder.append(context.getString(R.string.rmilk_TLA_repeat)).append(TaskAdapter.DETAIL_SEPARATOR); + } + } else { + TodorooCursor notesCursor = MilkDataService.getInstance().getTaskNotesCursor(id); + try { + for(notesCursor.moveToFirst(); !notesCursor.isAfterLast(); notesCursor.moveToNext()) { + metadata.readFromCursor(notesCursor); + builder.append(MilkNote.toTaskDetail(metadata)).append(TaskAdapter.DETAIL_SEPARATOR); + } + } finally { + notesCursor.close(); } - } finally { - notesCursor.close(); } if(builder.length() == 0) return null; String result = builder.toString(); - return new TaskDetail(result.substring(0, result.length() - 3)); + return result.substring(0, result.length() - TaskAdapter.DETAIL_SEPARATOR.length()); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java index 16a7b4668..a91ae0614 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java @@ -10,10 +10,9 @@ import android.content.Intent; import com.timsu.astrid.R; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; -import com.todoroo.astrid.api.TaskDetail; /** - * Exposes {@link TaskDetail} for tags, i.e. "Tags: frogs, animals" + * Exposes Task Detail for tags, i.e. "Tags: frogs, animals" * * @author Tim Su * @@ -27,7 +26,8 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer if(taskId == -1) return; - TaskDetail taskDetail = getTaskDetails(context, taskId); + boolean extended = intent.getBooleanExtra(AstridApiConstants.EXTRAS_EXTENDED, false); + String taskDetail = getTaskDetails(context, taskId, extended); if(taskDetail == null) return; @@ -35,18 +35,21 @@ public class TagDetailExposer extends BroadcastReceiver implements DetailExposer Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TagsPlugin.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, taskDetail); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_EXTENDED, extended); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } @Override - public TaskDetail getTaskDetails(Context context, long id) { - String tagList = TagService.getInstance().getTagsAsString(id); + public String getTaskDetails(Context context, long id, boolean extended) { + if(extended) + return null; + String tagList = TagService.getInstance().getTagsAsString(id); if(tagList.length() == 0) return null; - return new TaskDetail(context.getString(R.string.tag_TLA_detail, tagList)); + return context.getString(R.string.tag_TLA_detail, tagList); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java index 42a0e3617..7a3f168fd 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java @@ -9,8 +9,9 @@ import com.todoroo.astrid.api.AstridApiConstants; public class TagsPlugin extends BroadcastReceiver { - static final String IDENTIFIER = "tags"; + static final String IDENTIFIER = "tags"; //$NON-NLS-1$ + @SuppressWarnings("nls") @Override public void onReceive(Context context, Intent intent) { Addon plugin = new Addon(IDENTIFIER, "Tags", "Todoroo", diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java new file mode 100644 index 000000000..cf8ebd870 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java @@ -0,0 +1,116 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.timers; + +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.widget.RemoteViews; + +import com.timsu.astrid.R; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.api.TaskDecoration; +import com.todoroo.astrid.service.TaskService; + +/** + * Exposes {@link TaskDecoration} for timers + * + * @author Tim Su + * + */ +public class TimerDecorationExposer extends BroadcastReceiver { + + private static final int TASK_BG_COLOR = Color.argb(200, 220, 50, 0); + + private static TaskService staticTaskService; + private static HashMap decorations = + new HashMap(); + private static HashMap timers = + new HashMap(); + + @Autowired + private TaskService taskService; + + private static class TimerTimerTask extends TimerTask { + int time; + RemoteViews remoteViews; + + public TimerTimerTask(int time, RemoteViews remoteViews) { + super(); + this.time = time; + this.remoteViews = remoteViews; + } + + @Override + public void run() { + time++; + int seconds = time % 60; + int minutes = time / 60; + if(minutes > 59) { + int hours = minutes / 60; + minutes %= 60; + remoteViews.setTextViewText(R.id.timer, + String.format("%02d:%02d:%02d", //$NON-NLS-1$ + hours, minutes, seconds)); + } else { + remoteViews.setTextViewText(R.id.timer, + String.format("%02d:%02d", //$NON-NLS-1$ + minutes, seconds)); + } + } + } + + public void removeFromCache(long taskId) { + decorations.remove(taskId); + timers.get(taskId).cancel(); + timers.remove(taskId); + } + + @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(staticTaskService == null) { + DependencyInjectionService.getInstance().inject(this); + staticTaskService = taskService; + } else { + taskService = staticTaskService; + } + } + + TaskDecoration decoration; + + if(!decorations.containsKey(taskId)) { + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), + R.layout.timer_decoration); + decoration = new TaskDecoration(remoteViews, + TaskDecoration.POSITION_LEFT, TASK_BG_COLOR); + decorations.put(taskId, decoration); + Timer timer = new Timer(); + timers.put(taskId, timer); + timer.scheduleAtFixedRate(new TimerTimerTask(0, + remoteViews), 0, 1000L); + } else { + decoration = decorations.get(taskId); + } + + // transmit + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DECORATIONS); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TimerPlugin.IDENTIFIER); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, decoration); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java new file mode 100644 index 000000000..45bf1e168 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java @@ -0,0 +1,25 @@ +package com.todoroo.astrid.timers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.todoroo.astrid.api.Addon; +import com.todoroo.astrid.api.AstridApiConstants; + +public class TimerPlugin extends BroadcastReceiver { + + static final String IDENTIFIER = "timer"; //$NON-NLS-1$ + + @Override + @SuppressWarnings("nls") + public void onReceive(Context context, Intent intent) { + Addon plugin = new Addon(IDENTIFIER, "Timer", "Todoroo", + "Lets you time how long it takes to complete tasks."); + + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ADDONS); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, plugin); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + } + +} diff --git a/astrid/res/drawable/timers_decoration.png b/astrid/res/drawable/timers_decoration.png new file mode 100644 index 0000000000000000000000000000000000000000..18b7c6781edadc1d075728dd41546f7010ee7037 GIT binary patch literal 897 zcmV-{1AhF8P)#>It74)E>!>k0}e?< zK~y-)Ws*xwTxA@^f8TfSyqLLnU?K?8$|G1EY%rF-;-axT+?a~Nx*!@hCPY`YH6|=g zV;Ucc#zal5HL;66@DWW+qb{%oO;sobI#?^(5$Q~)%nZZKy~7N5zR!(qD4yi(&N=@_ zj$jjx92gnWk{1QyVU!LCLwZ_@8&$P_=AF-8e`T{?Y%um{_;mNaM|-yS#tBEl2mx9{ zaka$ERDw&F=F@}2=Z^I}k_sYql2G!rxxsv4aCpEj8;Vt*gDAdsXNui%jCkNd8aq?X zjm?!eJ-u_)(7&t*w_Qm9jXpj&JYe5gE71N>h-mfgyVkVpEw*lJp?7bNXu~dpz`G5| zr=!3QdE~&z(C&RZc9yh9TUR43TjHC~L`sPe0y7XpvP7kH0;e<+1pR{q2DbOcSuSU3 zZEdEI|A+YFr$}dgcRx!ADqbI+=qHt|a`M9ms(A98snL7Fkua7QLMesMXHj;Ggz79t zUfPD=-pg}sIc(cP$QaUp5W~C4!qmsnp%?mt5~vpfnwlEO7xE~js40!(6gm3d??htV zOiWCmltOETVH%i*1f>PgWdbW&6Dzw_Iy%~^RMt>RQ7jhe?(XL0!>@Af+Er?`8d6HM zQfLiGG_W8A;-XV>So}MUQVP%W@H~%0hYpiSB*QJv%5E8_kF0oOuSo0O-88Yi?mS`RxAv zkNN-nW071T&;4y%2nK=#gCYFB05|{2kiWjd$q&xz*`?X{r{;f}1F{CdOmaH!H~m+Z zu4eW$HN>KAEv->#4UcH#K>>F>S)(m+mZ=t@1%`Q)zt z=kL2`>r-|9x)v#bCe_ X$s}Y37Q!~)00000NkvXXu0mjf?g64A literal 0 HcmV?d00001 diff --git a/astrid/res/layout/task_adapter_row.xml b/astrid/res/layout/task_adapter_row.xml index 20549d7c8..bad4d561a 100644 --- a/astrid/res/layout/task_adapter_row.xml +++ b/astrid/res/layout/task_adapter_row.xml @@ -10,7 +10,7 @@ android:paddingRight="6dip" android:orientation="vertical"> - + + + + + + + + + + + diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java index bb35ff5ce..ce45edc46 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java @@ -52,7 +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.TaskDetail; +import com.todoroo.astrid.api.TaskDecoration; import com.todoroo.astrid.core.CoreFilterExposer; import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; @@ -378,6 +378,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { super.onResume(); registerReceiver(detailReceiver, new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS)); + registerReceiver(detailReceiver, + new IntentFilter(AstridApiConstants.BROADCAST_SEND_DECORATIONS)); } @Override @@ -387,7 +389,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { } /** - * Receiver which receives intents to add items to the filter list + * Receiver which receives detail or decoration intents * * @author Tim Su * @@ -398,8 +400,17 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { try { Bundle extras = intent.getExtras(); long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID); - TaskDetail detail = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE); - taskAdapter.addDetails(getListView(), taskId, detail); + + if(AstridApiConstants.BROADCAST_SEND_DECORATIONS.equals(intent.getAction())) { + TaskDecoration deco = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE); + taskAdapter.addDecorations(getListView(), taskId, + deco); + } else { + String detail = extras.getString(AstridApiConstants.EXTRAS_RESPONSE); + taskAdapter.addDetails(getListView(), taskId, + extras.getBoolean(AstridApiConstants.EXTRAS_EXTENDED), + detail); + } } catch (Exception e) { exceptionService.reportError("receive-detail-" + //$NON-NLS-1$ intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e); @@ -463,7 +474,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { if(requery) { taskCursor.requery(); - taskAdapter.flushDetailCache(); + taskAdapter.flushCaches(); taskAdapter.notifyDataSetChanged(); } startManagingCursor(taskCursor); diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java index c806340c9..b1ba04bf0 100644 --- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java @@ -1,11 +1,15 @@ package com.todoroo.astrid.adapter; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; -import android.app.Activity; +import android.app.ListActivity; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -19,11 +23,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.CheckBox; import android.widget.CursorAdapter; import android.widget.LinearLayout; -import android.widget.LinearLayout.LayoutParams; import android.widget.ListView; import android.widget.TextView; @@ -40,7 +44,7 @@ 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.TaskDetail; +import com.todoroo.astrid.api.TaskDecoration; import com.todoroo.astrid.model.Task; import com.todoroo.astrid.notes.NoteDetailExposer; import com.todoroo.astrid.repeats.RepeatDetailExposer; @@ -97,15 +101,19 @@ public class TaskAdapter extends CursorAdapter { @Autowired DialogUtilities dialogUtilities; - protected final Activity activity; + protected final ListActivity activity; protected final HashMap completedItems; public boolean isFling = false; private final int resource; private final LayoutInflater inflater; protected OnCompletedTaskListener onCompletedTaskListener = null; private int fontSize; - private static final SoftHashMap> detailCache = - new SoftHashMap>(); + + // --- 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(); /** * Constructor @@ -120,7 +128,7 @@ public class TaskAdapter extends CursorAdapter { * @param onCompletedTaskListener * task listener. can be null */ - public TaskAdapter(Activity activity, int resource, + public TaskAdapter(ListActivity activity, int resource, TodorooCursor c, boolean autoRequery, OnCompletedTaskListener onCompletedTaskListener) { super(activity, c, autoRequery); @@ -155,7 +163,9 @@ public class TaskAdapter extends CursorAdapter { viewHolder.completeBox = (CheckBox)view.findViewById(R.id.completeBox); viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate); viewHolder.details = (TextView)view.findViewById(R.id.details); + viewHolder.extendedDetails = (TextView)view.findViewById(R.id.extendedDetails); viewHolder.actions = (LinearLayout)view.findViewById(R.id.actions); + viewHolder.taskRow = (LinearLayout)view.findViewById(R.id.task_row); viewHolder.importance = (View)view.findViewById(R.id.importance); view.setTag(viewHolder); @@ -204,9 +214,13 @@ public class TaskAdapter extends CursorAdapter { public CheckBox completeBox; public TextView dueDate; public TextView details; + public TextView extendedDetails; public View importance; public LinearLayout actions; + public LinearLayout taskRow; public boolean expanded; + + public View[] decorations; } /** Helper method to set the contents and visibility of each field */ @@ -251,7 +265,6 @@ public class TaskAdapter extends CursorAdapter { dueDateView.setText(r.getString(R.string.TAd_completed, dateValue)); dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails); setVisibility(dueDateView); - } else { dueDateView.setVisibility(View.GONE); } @@ -265,43 +278,63 @@ public class TaskAdapter extends CursorAdapter { completeBox.setChecked(task.isCompleted()); } - // task details - send out a request for it (only if not fling) - viewHolder.details.setText(""); //$NON-NLS-1$ - if(!isFling) { - retrieveDetails(viewHolder); - } - - // importance bar - must be set at end when view height is determined + // importance bar final View importanceView = viewHolder.importance; { int value = task.getValue(Task.IMPORTANCE); importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]); } + + // 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); + } } - // --- task details + protected TaskRowListener listener = new TaskRowListener(); + /** + * Set listeners for this view. This is called once per view when it is + * created. + */ + private void addListeners(final View container) { + // check box listener + final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox)); + completeBox.setOnClickListener(completeBoxListener); + + // context menu listener + container.setOnCreateContextMenuListener(listener); - private void retrieveDetails(final ViewHolder viewHolder) { - final long taskId = viewHolder.task.getId(); + // tap listener + container.setOnClickListener(listener); + } - // check the cache - boolean inCache = false; - final LinkedHashSet details; - synchronized(detailCache) { - if(detailCache.containsKey(taskId)) - inCache = true; - else - detailCache.put(taskId, new LinkedHashSet()); - details = detailCache.get(taskId); - } + /* ====================================================================== + * ============================================================== add-ons + * ====================================================================== */ - if(inCache) { - spanifyAndAdd(viewHolder.details, details); + 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 @@ -309,35 +342,40 @@ public class TaskAdapter extends CursorAdapter { @Override public void run() { for(DetailExposer exposer : EXPOSERS) { - if(Thread.interrupted()) - return; - - final TaskDetail detail = exposer.getTaskDetails(activity, taskId); - if(detail == null || details.contains(detail)) - continue; - - details.add(detail); - if(taskId != viewHolder.task.getId()) + final String detail = exposer.getTaskDetails(activity, + taskId, extended); + if(detail == null) continue; - - activity.runOnUiThread(new Runnable() { - public void run() { - spanifyAndAdd(viewHolder.details, details); - } - }); + 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(TextView view, LinkedHashSet details) { + 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) + 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().text); + for(Iterator iterator = details.iterator(); iterator.hasNext(); ) { + detailText.append(iterator.next()); if(iterator.hasNext()) detailText.append(DETAIL_SEPARATOR); } @@ -351,14 +389,10 @@ public class TaskAdapter extends CursorAdapter { /** * Called to tell the cache to be cleared */ - public void flushDetailCache() { + public void flushCaches() { detailCache.clear(); - } - - @Override - public void notifyDataSetChanged() { - super.notifyDataSetChanged(); - fontSize = Preferences.getIntegerFromString(R.string.p_fontSize); + extendedDetailCache.clear(); + decorationCache.clear(); } /** @@ -366,23 +400,24 @@ public class TaskAdapter extends CursorAdapter { * * @param taskId */ - public synchronized void addDetails(ListView list, long taskId, TaskDetail detail) { + public synchronized void addDetails(ListView listView, long taskId, + boolean extended, String detail) { if(detail == null) return; - LinkedHashSet details = detailCache.get(taskId); - if(details.contains(detail)) - return; - details.add(detail); - - // update view if it is visible - int length = list.getChildCount(); - for(int i = 0; i < length; i++) { - ViewHolder viewHolder = (ViewHolder) list.getChildAt(i).getTag(); - if(viewHolder == null || viewHolder.task.getId() != taskId) - continue; - spanifyAndAdd(viewHolder.details, details); - break; + 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; + } } } @@ -397,29 +432,120 @@ public class TaskAdapter extends CursorAdapter { } }; - protected ContextMenuListener listener = new ContextMenuListener(); + private void retrieveDecorations(final ViewHolder viewHolder) { + final long taskId = viewHolder.task.getId(); + + ArrayList list = decorationCache.initialize(taskId); + if(list != null) { + decorate(viewHolder, list); + return; + } + + // 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); + } + } + + private Timer timer = null; + /** - * Set listeners for this view. This is called once per view when it is - * created. + * Task to update decorations every second */ - private void addListeners(final View container) { - // check box listener - final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox)); - completeBox.setOnClickListener(completeBoxListener); + private final TimerTask decorationUpdater = new TimerTask() { + @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); + } + }); + } + } + } + }; - // context menu listener - container.setOnCreateContextMenuListener(listener); + /** + * Respond to a request to add details for a task + * + * @param taskId + */ + public synchronized void addDecorations(ListView listView, long taskId, + TaskDecoration taskDecoration) { + if(taskDecoration == null) + return; - // tap listener - container.setOnClickListener(listener); + 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; + } + } + } + + private void decorate(ViewHolder viewHolder, ArrayList decorations) { + if(decorations == null || decorations.size() == 0) + 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); + } + } + } } /* ====================================================================== * ======================================================= event handlers * ====================================================================== */ + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + fontSize = Preferences.getIntegerFromString(R.string.p_fontSize); + } - class ContextMenuListener implements OnCreateContextMenuListener, OnClickListener { + class TaskRowListener implements OnCreateContextMenuListener, OnClickListener { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { @@ -428,11 +554,23 @@ public class TaskAdapter extends CursorAdapter { @Override public void onClick(View v) { + // 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 && actions.getChildCount() == 0) { + 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); @@ -442,7 +580,7 @@ public class TaskAdapter extends CursorAdapter { @Override public void onClick(View view) { Intent intent = new Intent(activity, TaskEditActivity.class); - intent.putExtra(TaskEditActivity.ID_TOKEN, viewHolder.task.getId()); + intent.putExtra(TaskEditActivity.ID_TOKEN, taskId); activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK); } }); @@ -507,4 +645,53 @@ public class TaskAdapter extends CursorAdapter { } } + /* ====================================================================== + * =============================================================== caches + * ====================================================================== */ + + private static class TaskCache { + + private final Map> cache = + Collections.synchronizedMap(new SoftHashMap>()); + + /** + * Retrieves a list. If it doesn't exist, list is created, but + * the method will return null + * @param taskId + * @return list if there was already one + */ + public ArrayList initialize(long taskId) { + if(cache.containsKey(taskId)) + return cache.get(taskId); + cache.put(taskId, new ArrayList()); + return null; + } + + /** + * 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 + */ + public ArrayList addIfNotExists(long taskId, TYPE item) { + ArrayList list = cache.get(taskId); + if(list == null || list.contains(item)) + return null; + list.add(item); + return list; + } + + /** + * Clears the cache + */ + public void clear() { + cache.clear(); + } + + public ArrayList get(long taskId) { + return cache.get(taskId); + } + + } + } diff --git a/astrid/src/com/todoroo/astrid/api/DetailExposer.java b/astrid/src/com/todoroo/astrid/api/DetailExposer.java index 2a4e84e60..804b7c846 100644 --- a/astrid/src/com/todoroo/astrid/api/DetailExposer.java +++ b/astrid/src/com/todoroo/astrid/api/DetailExposer.java @@ -12,8 +12,13 @@ public interface DetailExposer { /** * @param id + * task id + * @param extended + * whether this request is for extended details (which are + * displayed when user presses a task), or standard (which are + * always displayed) * @return null if no details, or task details */ - public TaskDetail getTaskDetails(Context context, long id); + public String getTaskDetails(Context context, long id, boolean extended); } From 1a331afa62fd306ef6a5480bc874ac893e0dd6e1 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 20:31:00 -0700 Subject: [PATCH 2/6] Made the action exposer --- astrid/AndroidManifest.xml | 8 +- .../com/todoroo/astrid/api/TaskAction.java | 10 +- .../todoroo/astrid/api/TaskDecoration.java | 6 + astrid/astrid.launch | 2 +- .../astrid/notes/NoteDetailExposer.java | 5 + .../astrid/repeats/RepeatDetailExposer.java | 5 + .../astrid/rmilk/MilkDetailExposer.java | 5 + .../todoroo/astrid/tags/TagDetailExposer.java | 5 + .../astrid/timers/TimerActionExposer.java | 76 +++ .../astrid/timers/TimerDecorationExposer.java | 2 +- astrid/res/values/strings-timers.xml | 16 + .../astrid/activity/TaskListActivity.java | 19 +- .../todoroo/astrid/adapter/TaskAdapter.java | 524 ++++++++++-------- .../com/todoroo/astrid/api/DetailExposer.java | 2 + astrid/src/com/todoroo/astrid/model/Task.java | 2 +- 15 files changed, 428 insertions(+), 259 deletions(-) create mode 100644 astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java create mode 100644 astrid/res/values/strings-timers.xml 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( From fa844146764e0bcf308950c639fc0630be5b92a1 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 21:47:24 -0700 Subject: [PATCH 3/6] Awesome! timer works like it should. No notification yet... --- astrid/AndroidManifest.xml | 13 +++ .../com/todoroo/astrid/api/Filter.java | 8 ++ .../todoroo/astrid/api/TaskDecoration.java | 11 --- astrid/astrid.launch | 2 +- .../todoroo/astrid/core/PluginServices.java | 28 +++++++ .../astrid/gcal/GCalTaskCompleteListener.java | 11 +-- .../astrid/notes/NoteDetailExposer.java | 20 +---- .../astrid/repeats/RepeatDetailExposer.java | 20 +---- .../astrid/timers/TimerActionExposer.java | 37 ++++---- .../astrid/timers/TimerDecorationExposer.java | 79 +++++------------- .../astrid/timers/TimerFilterExposer.java | 71 ++++++++++++++++ .../timers/TimerTaskCompleteListener.java | 40 +++++++++ astrid/res/drawable/tango_clock.png | Bin 0 -> 1411 bytes astrid/res/layout/timer_decoration.xml | 7 +- astrid/res/values/strings-timers.xml | 6 ++ .../astrid/activity/TaskListActivity.java | 5 +- .../todoroo/astrid/adapter/TaskAdapter.java | 11 +++ .../todoroo/astrid/service/TaskService.java | 15 +++- 18 files changed, 244 insertions(+), 140 deletions(-) create mode 100644 astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java create mode 100644 astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java create mode 100644 astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java create mode 100644 astrid/res/drawable/tango_clock.png diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index f38144f62..553a7bb4f 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -218,6 +218,19 @@ + + + + + + + + + + + + + diff --git a/astrid/api-src/com/todoroo/astrid/api/Filter.java b/astrid/api-src/com/todoroo/astrid/api/Filter.java index 404f5c075..9c8c258c8 100644 --- a/astrid/api-src/com/todoroo/astrid/api/Filter.java +++ b/astrid/api-src/com/todoroo/astrid/api/Filter.java @@ -22,6 +22,14 @@ import com.todoroo.andlib.sql.QueryTemplate; */ public final class Filter extends FilterListItem { + // --- constants + + /** Constant for valuesForNewTasks to indicate the value should be replaced + * with the current time as long */ + public static final long VALUE_NOW = Long.MIN_VALUE + 1; + + // --- instance variables + /** * Expanded title of this filter. This is displayed at the top * of the screen when user is viewing this filter. diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java b/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java index bf0e0d096..e1cf4115c 100644 --- a/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java +++ b/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java @@ -3,7 +3,6 @@ */ package com.todoroo.astrid.api; -import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; import android.widget.RemoteViews; @@ -32,16 +31,6 @@ public final class TaskDecoration implements Parcelable { */ public RemoteViews decoration = null; - /** - * Decoration update interval (minimum of 1000 millis), 0 to never update - */ - public long updateInterval = 0; - - /** - * Intent to call to update the decoration - */ - public Intent updateIntent = null; - /** * Decoration position */ diff --git a/astrid/astrid.launch b/astrid/astrid.launch index 7763a5bfc..065607e32 100644 --- a/astrid/astrid.launch +++ b/astrid/astrid.launch @@ -4,7 +4,7 @@ - + diff --git a/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java b/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java new file mode 100644 index 000000000..d2e348553 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/PluginServices.java @@ -0,0 +1,28 @@ +package com.todoroo.astrid.core; + +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.astrid.service.TaskService; + +public final class PluginServices { + + @Autowired + TaskService taskService; + + private static PluginServices instance; + + private PluginServices() { + DependencyInjectionService.getInstance().inject(this); + } + + private synchronized static PluginServices getInstance() { + if(instance == null) + instance = new PluginServices(); + return instance; + } + + public static TaskService getTaskService() { + return getInstance().taskService; + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java index 685598f0c..80f3de628 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java +++ b/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java @@ -9,17 +9,12 @@ import android.net.Uri; import android.text.TextUtils; import com.timsu.astrid.R; -import com.todoroo.andlib.service.Autowired; -import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.service.TaskService; public class GCalTaskCompleteListener extends BroadcastReceiver { - @Autowired - private TaskService taskService; - @SuppressWarnings("nls") @Override public void onReceive(Context context, Intent intent) { @@ -27,9 +22,7 @@ public class GCalTaskCompleteListener extends BroadcastReceiver { if(taskId == -1) return; - DependencyInjectionService.getInstance().inject(this); - - Task task = taskService.fetchById(taskId, Task.ID, Task.TITLE, Task.CALENDAR_URI); + Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.TITLE, Task.CALENDAR_URI); if(task == null) return; diff --git a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java index f96d15eb4..2a14ed1e6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java @@ -7,12 +7,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import com.todoroo.andlib.service.Autowired; -import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; +import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.service.TaskService; /** * Exposes Task Detail for notes @@ -22,11 +20,6 @@ import com.todoroo.astrid.service.TaskService; */ public class NoteDetailExposer extends BroadcastReceiver implements DetailExposer { - private static TaskService staticTaskService; - - @Autowired - private TaskService taskService; - @Override public void onReceive(Context context, Intent intent) { // get tags associated with this task @@ -53,16 +46,7 @@ public class NoteDetailExposer extends BroadcastReceiver implements DetailExpose if(!extended) return null; - synchronized(NoteDetailExposer.class) { - if(staticTaskService == null) { - DependencyInjectionService.getInstance().inject(this); - staticTaskService = taskService; - } else { - taskService = staticTaskService; - } - } - - Task task = taskService.fetchById(id, Task.NOTES); + Task task = PluginServices.getTaskService().fetchById(id, Task.NOTES); if(task == null) return null; String notes = task.getValue(Task.NOTES); diff --git a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java index 78b274e84..46d3af6f7 100644 --- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java @@ -16,12 +16,10 @@ import com.google.ical.values.Frequency; import com.google.ical.values.RRule; import com.google.ical.values.WeekdayNum; import com.timsu.astrid.R; -import com.todoroo.andlib.service.Autowired; -import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.DetailExposer; +import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.service.TaskService; /** * Exposes Task Detail for repeats, i.e. "Repeats every 2 days" @@ -31,11 +29,6 @@ import com.todoroo.astrid.service.TaskService; */ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExposer { - private static TaskService staticTaskService = null; - - @Autowired - TaskService taskService; - @Override public void onReceive(Context context, Intent intent) { // get tags associated with this task @@ -61,16 +54,7 @@ public class RepeatDetailExposer extends BroadcastReceiver implements DetailExpo if(extended) return null; - synchronized(RepeatDetailExposer.class) { - if(staticTaskService == null) { - DependencyInjectionService.getInstance().inject(this); - staticTaskService = taskService; - } else { - taskService = staticTaskService; - } - } - - Task task = taskService.fetchById(id, Task.FLAGS, Task.RECURRENCE); + Task task = PluginServices.getTaskService().fetchById(id, Task.FLAGS, Task.RECURRENCE); if(task == null) return null; diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java index c7122c8a7..373091982 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java @@ -9,14 +9,12 @@ 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.core.PluginServices; import com.todoroo.astrid.model.Task; -import com.todoroo.astrid.service.TaskService; /** * Exposes {@link TaskDecoration} for timers @@ -26,8 +24,7 @@ import com.todoroo.astrid.service.TaskService; */ public class TimerActionExposer extends BroadcastReceiver { - @Autowired - private TaskService taskService; + private static final String TIMER_ACTION = "com.todoroo.astrid.TIMER_BUTTON"; //$NON-NLS-1$ @Override public void onReceive(Context context, Intent intent) { @@ -35,16 +32,8 @@ public class TimerActionExposer extends BroadcastReceiver { 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); + Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.TIMER_START, + Task.ELAPSED_SECONDS); // was part of a broadcast for actions if(AstridApiConstants.BROADCAST_REQUEST_ACTIONS.equals(intent.getAction())) { @@ -53,7 +42,8 @@ public class TimerActionExposer extends BroadcastReceiver { label = context.getString(R.string.TAE_startTimer); else label = context.getString(R.string.TAE_stopTimer); - Intent newIntent = new Intent(context, TimerActionExposer.class); + Intent newIntent = new Intent(TIMER_ACTION); + newIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); TaskAction action = new TaskAction(label, PendingIntent.getBroadcast(context, 0, newIntent, 0)); @@ -63,13 +53,20 @@ public class TimerActionExposer extends BroadcastReceiver { broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, action); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); - } else { + } else if(TIMER_ACTION.equals(intent.getAction())) { // 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); + else { + TimerTaskCompleteListener.stopTimer(task); + } + PluginServices.getTaskService().save(task, true); + TimerDecorationExposer.removeFromCache(taskId); + + // transmit new intents TimerDecoration + new TimerDecorationExposer().onReceive(context, intent); + intent.setAction(AstridApiConstants.BROADCAST_REQUEST_ACTIONS); + onReceive(context, intent); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java index 9093f1c34..b8b980b7c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java @@ -4,21 +4,20 @@ package com.todoroo.astrid.timers; import java.util.HashMap; -import java.util.Timer; -import java.util.TimerTask; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.os.SystemClock; import android.widget.RemoteViews; 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.TaskDecoration; -import com.todoroo.astrid.service.TaskService; +import com.todoroo.astrid.core.PluginServices; +import com.todoroo.astrid.model.Task; /** * Exposes {@link TaskDecoration} for timers @@ -28,50 +27,13 @@ import com.todoroo.astrid.service.TaskService; */ public class TimerDecorationExposer extends BroadcastReceiver { - private static final int TASK_BG_COLOR = Color.argb(200, 220, 50, 0); + private static final int TIMING_BG_COLOR = Color.argb(200, 220, 50, 0); - static TaskService staticTaskService; private static HashMap decorations = new HashMap(); - private static HashMap timers = - new HashMap(); - @Autowired - private TaskService taskService; - - private static class TimerTimerTask extends TimerTask { - int time; - RemoteViews remoteViews; - - public TimerTimerTask(int time, RemoteViews remoteViews) { - super(); - this.time = time; - this.remoteViews = remoteViews; - } - - @Override - public void run() { - time++; - int seconds = time % 60; - int minutes = time / 60; - if(minutes > 59) { - int hours = minutes / 60; - minutes %= 60; - remoteViews.setTextViewText(R.id.timer, - String.format("%02d:%02d:%02d", //$NON-NLS-1$ - hours, minutes, seconds)); - } else { - remoteViews.setTextViewText(R.id.timer, - String.format("%02d:%02d", //$NON-NLS-1$ - minutes, seconds)); - } - } - } - - public void removeFromCache(long taskId) { + public static void removeFromCache(long taskId) { decorations.remove(taskId); - timers.get(taskId).cancel(); - timers.remove(taskId); } @Override @@ -80,31 +42,32 @@ public class TimerDecorationExposer extends BroadcastReceiver { if(taskId == -1) return; - synchronized(TimerDecorationExposer.class) { - if(staticTaskService == null) { - DependencyInjectionService.getInstance().inject(this); - staticTaskService = taskService; - } else { - taskService = staticTaskService; - } - } + Task task = PluginServices.getTaskService().fetchById(taskId, Task.ELAPSED_SECONDS, Task.TIMER_START); + if(task == null || (task.getValue(Task.ELAPSED_SECONDS) == 0 && + task.getValue(Task.TIMER_START) == 0)) + return; TaskDecoration decoration; - if(!decorations.containsKey(taskId)) { RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.timer_decoration); decoration = new TaskDecoration(remoteViews, - TaskDecoration.POSITION_LEFT, TASK_BG_COLOR); + TaskDecoration.POSITION_LEFT, 0); decorations.put(taskId, decoration); - Timer timer = new Timer(); - timers.put(taskId, timer); - timer.scheduleAtFixedRate(new TimerTimerTask(0, - remoteViews), 0, 1000L); } else { decoration = decorations.get(taskId); } + long elapsed = task.getValue(Task.ELAPSED_SECONDS) * 1000L; + if(task.getValue(Task.TIMER_START) != 0) { + decoration.color = TIMING_BG_COLOR; + elapsed += DateUtilities.now() - task.getValue(Task.TIMER_START); + } + + // update timer + decoration.decoration.setChronometer(R.id.timer, SystemClock.elapsedRealtime() - + elapsed, null, decoration.color != 0); + // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DECORATIONS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TimerPlugin.IDENTIFIER); diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java new file mode 100644 index 000000000..0bb994146 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java @@ -0,0 +1,71 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.timers; + +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; + +import com.timsu.astrid.R; +import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.sql.QueryTemplate; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.activity.FilterListActivity; +import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.api.FilterListItem; +import com.todoroo.astrid.core.PluginServices; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.model.Task; + +/** + * Exposes "working on" filter to the {@link FilterListActivity} + * + * @author Tim Su + * + */ +public final class TimerFilterExposer extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Resources r = context.getResources(); + + if(PluginServices.getTaskService().count(Query.select(Task.ID). + where(Task.TIMER_START.gt(0))) == 0) + return; + + ContentValues values = new ContentValues(); + values.put(Task.TIMER_START.name, Filter.VALUE_NOW); + Filter workingOn = new Filter(r.getString(R.string.TFE_workingOn), + r.getString(R.string.TFE_workingOn), + new QueryTemplate().where(Task.TIMER_START.gt(0)), + values); + workingOn.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clock)).getBitmap(); + + // transmit filter list + FilterListItem[] list = new FilterListItem[1]; + list[0] = workingOn; + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + } + + /** + * Build inbox filter + * @return + */ + public static Filter buildInboxFilter(Resources r) { + Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title), + new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), + TaskCriteria.isVisible(DateUtilities.now()))), + null); + inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap(); + return inbox; + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java new file mode 100644 index 000000000..6f8404496 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java @@ -0,0 +1,40 @@ +package com.todoroo.astrid.timers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.core.PluginServices; +import com.todoroo.astrid.model.Task; + +public class TimerTaskCompleteListener extends BroadcastReceiver { + + /** + * stops timer and sets elapsed time. you still need to save the task. + * @param task + */ + public static void stopTimer(Task task) { + int newElapsed = (int)((DateUtilities.now() - task.getValue(Task.TIMER_START)) / 1000L); + task.setValue(Task.TIMER_START, 0L); + task.setValue(Task.ELAPSED_SECONDS, + task.getValue(Task.ELAPSED_SECONDS) + newElapsed); + } + + @Override + public void onReceive(Context context, Intent intent) { + long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1); + if(taskId == -1) + return; + + Task task = PluginServices.getTaskService().fetchById(taskId, Task.ID, Task.ELAPSED_SECONDS, + Task.TIMER_START); + if(task == null || task.getValue(Task.TIMER_START) == 0) + return; + + stopTimer(task); + PluginServices.getTaskService().save(task, true); + } + +} diff --git a/astrid/res/drawable/tango_clock.png b/astrid/res/drawable/tango_clock.png new file mode 100644 index 0000000000000000000000000000000000000000..d676ffd4632b2e2271a9906e6d871eed2c14df31 GIT binary patch literal 1411 zcmV-}1$_F6P)#?6Cz*=ZsGs{1rSL@ zK~y-)eU)8oTt^khe>3;)-L==d{#bisH*r3kIM|59#&w&xrKlj`A}S=3D(wR=RDI|J zs!#z5$?(iWMO7YV+C}2&uOX#iXt*B{?#JfuSk5tl{ney0r;ywj6j~pUTVPAdmmV8G!c1m?@$oSd ziB^2~Hqvr1%B3x`h;3U4ArL~KlpzvrCK@{gq5yhPRLZ58FjSFZMFM>9#Kfx)jvbDz zx@Cq&K8IF5m5NJu-%|(yDkxxr0zx_sun-pYdJ`cBu*ApsrC(FXPdWd6r!R*T|CRs| z%RbgW*oSW_q|+&+ogk@1^V=VtL`);?37b zdqvYi!3MAtsdzF@wB}-rK`DjpB-y=pfcd#8`uZQ_>X|QLjA=lPF&HDLhF?bOQPxUj zlJO|I_F*r8{LV8!uyv?xDFu6W?__ED7C{i8wZ>r6vul8>|9OLqIaeuZMGisQs_92+zafCO5$yokme?qrtnDjr&Elu{_A=-Kx;0JF2RoH+6HHgKW1 z!A4=7L~F~oinD>Duq~yvu{_UbWcVQflv0h*GnotkgM)*dK79(Ub;G(kcc7FaVn^__ z!AKDT$di9~*_eo|cqNbZe6b-=N)d)3uIn;9JOV&2m*wcuquan*Yhtk`Dz1-L@=!t- zwo*Y=)u-oX=aChI5OP}@cJ0~?KsI}~v9@JfU@0ZHuP-q-J5Sw&=Ne#F2G31hyrvV8 zW-?3J%>uY5o}4^GCbN8Bs8WiKjufkzbvhHRy!Y<3@*?Va01M#C%!RctetvAMy5S!< z*tZWm;UF!6)|zxWjWLF5t=f2ITb_1wq^OjAbiU5plNVULyZpP?{`|@t4KN^P=CAy7 zPkP{4+lZ#lL_5Zjw6(Vph5^RFt)&H&QY2bhiAEig$yTy=@&wr`m*1V{(&g!uU!H#D zc|ZaF7C0J)L2I#Ao{E~}ljYTt)6?07?^m%RHu07?-8*_nwWo+g9P)QJ$j;{wuHx;< z3%qybTJiViPQFmBRZ4&ll()bJXwI*%gwr=K|D&y?dvxyog?14b5yK%U1^D?2Ue3eK zx>Ry5bJI8Z+n>&vYYVd<{$}#kpZHZT2NZ!7p#I7K*a02@(m?tP0}qWoJn+b~sd&c@ zQwt^G=09Nxv@O({x1Kxw&L4lVQOqp@d0-W|36$>Fngq}WbOY@`GSSl3xwCuk{;pI{ zf3@bjtNF~*?K^X~j4>6U3>1K6AP0n>+4DXTLDQziHfMl3P~FU>2h{E-{vR + android:layout_height="fill_parent" + android:orientation="vertical" + android:paddingLeft="3px"> - diff --git a/astrid/res/values/strings-timers.xml b/astrid/res/values/strings-timers.xml index ca97fc583..1e1c695c5 100644 --- a/astrid/res/values/strings-timers.xml +++ b/astrid/res/values/strings-timers.xml @@ -12,5 +12,11 @@ Elapsed Time: %s + + + Timer Filters + + + Tasks Being Timed diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java index d63465e73..f5730dd7a 100644 --- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java @@ -46,6 +46,7 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.utility.AndroidUtilities; +import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.Pair; import com.todoroo.astrid.adapter.TaskAdapter; @@ -325,6 +326,8 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { ContentValues forTask = new ContentValues(); forMetadata = new ContentValues(); for(Entry item : filter.valuesForNewTasks.valueSet()) { + if(Long.valueOf(Filter.VALUE_NOW).equals(item.getValue())) + item.setValue(DateUtilities.now()); if(item.getKey().startsWith(Task.TABLE.name)) AndroidUtilities.putInto(forTask, item.getKey(), item.getValue()); else @@ -415,7 +418,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener { else taskAdapter.extendedDetailManager.addNew(taskId, addOn, detail); } else if(AstridApiConstants.BROADCAST_SEND_ACTIONS.equals(intent.getAction())) { - TaskAction action = extras.getParcelable(AstridApiConstants.BROADCAST_SEND_ACTIONS); + TaskAction action = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE); taskAdapter.taskActionManager.addNew(taskId, addOn, action); } } catch (Exception e) { diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java index fe1aa4c12..5c803eb96 100644 --- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java @@ -15,6 +15,7 @@ import android.database.Cursor; import android.graphics.Color; import android.graphics.Paint; import android.text.Html; +import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -320,6 +321,7 @@ public class TaskAdapter extends CursorAdapter { detailManager.clearCache(); extendedDetailManager.clearCache(); decorationManager.clearCache(); + taskActionManager.clearCache(); } /** @@ -524,6 +526,7 @@ public class TaskAdapter extends CursorAdapter { Task task = viewHolder.task; completeTask(task, ((CheckBox)v).isChecked()); + // set check box to actual action item state setTaskAppearance(viewHolder, task.isCompleted()); } @@ -539,8 +542,13 @@ public class TaskAdapter extends CursorAdapter { public void onClick(View v) { try { action.intent.send(); + + // refresh ourselves + getCursor().requery(); + notifyDataSetChanged(); } catch (Exception e) { // oh too bad. + Log.i("astrid-action-error", "Error launching intent", e); //$NON-NLS-1$ //$NON-NLS-2$ } } }; @@ -562,6 +570,9 @@ public class TaskAdapter extends CursorAdapter { if(viewHolder.expanded) { extendedDetailManager.request(viewHolder); taskActionManager.request(viewHolder); + } else { + viewHolder.extendedDetails.setVisibility(View.GONE); + viewHolder.actions.setVisibility(View.GONE); } } } diff --git a/astrid/src/com/todoroo/astrid/service/TaskService.java b/astrid/src/com/todoroo/astrid/service/TaskService.java index 9ae6ec64e..e29ea62c8 100644 --- a/astrid/src/com/todoroo/astrid/service/TaskService.java +++ b/astrid/src/com/todoroo/astrid/service/TaskService.java @@ -10,8 +10,8 @@ import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.dao.MetadataDao; -import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; +import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.model.Task; @@ -173,6 +173,19 @@ public class TaskService { Task.IMPORTANCE + " + " + Task.COMPLETION_DATE); } + /** + * @param query + * @return how many tasks are matched by this query + */ + public int count(Query query) { + TodorooCursor cursor = taskDao.query(query); + try { + return cursor.getCount(); + } finally { + cursor.close(); + } + } + } From 7f3f92eb6347191a893dfb46407f8877463724d8 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 22:19:55 -0700 Subject: [PATCH 4/6] Timers work. Task adapter cache is messy. --- astrid/AndroidManifest.xml | 2 +- .../astrid/timers/TimerActionExposer.java | 16 +--- .../astrid/timers/TimerFilterExposer.java | 33 +++----- .../todoroo/astrid/timers/TimerPlugin.java | 72 ++++++++++++++++++ .../timers/TimerTaskCompleteListener.java | 15 +--- astrid/res/drawable/timers_notification.png | Bin 0 -> 1499 bytes astrid/res/layout/timer_decoration.xml | 3 +- astrid/res/values/strings-timers.xml | 4 +- .../astrid/activity/FilterListActivity.java | 11 +-- .../astrid/activity/ShortcutActivity.java | 14 ++++ .../com/todoroo/astrid/utility/Constants.java | 5 +- 11 files changed, 111 insertions(+), 64 deletions(-) create mode 100644 astrid/res/drawable/timers_notification.png diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 553a7bb4f..e5ca88c42 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -250,7 +250,7 @@ + android:clearTaskOnLaunch="true" /> diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java index 373091982..fed1e858e 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java @@ -9,7 +9,6 @@ import android.content.Context; import android.content.Intent; import com.timsu.astrid.R; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.TaskAction; import com.todoroo.astrid.api.TaskDecoration; @@ -54,19 +53,10 @@ public class TimerActionExposer extends BroadcastReceiver { broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } else if(TIMER_ACTION.equals(intent.getAction())) { - // toggle the timer if(task.getValue(Task.TIMER_START) == 0) - task.setValue(Task.TIMER_START, DateUtilities.now()); - else { - TimerTaskCompleteListener.stopTimer(task); - } - PluginServices.getTaskService().save(task, true); - TimerDecorationExposer.removeFromCache(taskId); - - // transmit new intents TimerDecoration - new TimerDecorationExposer().onReceive(context, intent); - intent.setAction(AstridApiConstants.BROADCAST_REQUEST_ACTIONS); - onReceive(context, intent); + TimerPlugin.updateTimer(context, task, true); + else + TimerPlugin.updateTimer(context, task, false); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java index 0bb994146..224dd34fc 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java @@ -11,16 +11,13 @@ import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; import com.timsu.astrid.R; -import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.QueryTemplate; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.activity.FilterListActivity; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.core.PluginServices; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.model.Task; /** @@ -33,19 +30,12 @@ public final class TimerFilterExposer extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Resources r = context.getResources(); if(PluginServices.getTaskService().count(Query.select(Task.ID). where(Task.TIMER_START.gt(0))) == 0) return; - ContentValues values = new ContentValues(); - values.put(Task.TIMER_START.name, Filter.VALUE_NOW); - Filter workingOn = new Filter(r.getString(R.string.TFE_workingOn), - r.getString(R.string.TFE_workingOn), - new QueryTemplate().where(Task.TIMER_START.gt(0)), - values); - workingOn.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clock)).getBitmap(); + Filter workingOn = createFilter(context); // transmit filter list FilterListItem[] list = new FilterListItem[1]; @@ -55,17 +45,16 @@ public final class TimerFilterExposer extends BroadcastReceiver { context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } - /** - * Build inbox filter - * @return - */ - public static Filter buildInboxFilter(Resources r) { - Filter inbox = new Filter(r.getString(R.string.BFE_Active), r.getString(R.string.BFE_Active_title), - new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(), - TaskCriteria.isVisible(DateUtilities.now()))), - null); - inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap(); - return inbox; + public static Filter createFilter(Context context) { + Resources r = context.getResources(); + ContentValues values = new ContentValues(); + values.put(Task.TIMER_START.name, Filter.VALUE_NOW); + Filter workingOn = new Filter(r.getString(R.string.TFE_workingOn), + r.getString(R.string.TFE_workingOn), + new QueryTemplate().where(Task.TIMER_START.gt(0)), + values); + workingOn.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_clock)).getBitmap(); + return workingOn; } } diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java index 45bf1e168..a2fbe632b 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java @@ -1,11 +1,24 @@ package com.todoroo.astrid.timers; +import android.app.Notification; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; +import com.timsu.astrid.R; +import com.todoroo.andlib.service.NotificationManager; +import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager; +import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.utility.DateUtilities; +import com.todoroo.astrid.activity.ShortcutActivity; import com.todoroo.astrid.api.Addon; import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.api.Filter; +import com.todoroo.astrid.core.PluginServices; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.utility.Constants; public class TimerPlugin extends BroadcastReceiver { @@ -22,4 +35,63 @@ public class TimerPlugin extends BroadcastReceiver { context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } + /** + * stops timer and sets elapsed time. you still need to save the task. + * @param task + * @param start if true, start timer. else, stop it + */ + public static void updateTimer(Context context, Task task, boolean start) { + if(start) { + if(task.getValue(Task.TIMER_START) == 0) + task.setValue(Task.TIMER_START, DateUtilities.now()); + } else { + if(task.getValue(Task.TIMER_START) > 0) { + int newElapsed = (int)((DateUtilities.now() - task.getValue(Task.TIMER_START)) / 1000L); + task.setValue(Task.TIMER_START, 0L); + task.setValue(Task.ELAPSED_SECONDS, + task.getValue(Task.ELAPSED_SECONDS) + newElapsed); + } + } + PluginServices.getTaskService().save(task, true); + TimerDecorationExposer.removeFromCache(task.getId()); + + // transmit new intents + Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_ACTIONS); + intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId()); + new TimerDecorationExposer().onReceive(context, intent); + new TimerActionExposer().onReceive(context, intent); + + // update notification + TimerPlugin.updateNotifications(context); + } + + private static void updateNotifications(Context context) { + NotificationManager nm = new AndroidNotificationManager(context); + + int count = PluginServices.getTaskService().count(Query.select(Task.ID). + where(Task.TIMER_START.gt(0))); + if(count == 0) { + nm.cancel(Constants.NOTIFICATION_TIMER); + } else { + Filter filter = TimerFilterExposer.createFilter(context); + Intent notifyIntent = ShortcutActivity.createIntent(filter); + notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(context, + 0, notifyIntent, 0); + + Resources r = context.getResources(); + String appName = r.getString(R.string.app_name); + String text = r.getString(R.string.TPl_notification, + r.getQuantityString(R.plurals.Ntasks, count, count)); + Notification notification = new Notification( + R.drawable.timers_notification, text, System.currentTimeMillis()); + notification.setLatestEventInfo(context, appName, + text, pendingIntent); + notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR; + notification.flags &= ~Notification.FLAG_AUTO_CANCEL; + + nm.notify(Constants.NOTIFICATION_TIMER, notification); + } + } + } diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java index 6f8404496..8da52acaf 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java @@ -4,24 +4,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.model.Task; public class TimerTaskCompleteListener extends BroadcastReceiver { - /** - * stops timer and sets elapsed time. you still need to save the task. - * @param task - */ - public static void stopTimer(Task task) { - int newElapsed = (int)((DateUtilities.now() - task.getValue(Task.TIMER_START)) / 1000L); - task.setValue(Task.TIMER_START, 0L); - task.setValue(Task.ELAPSED_SECONDS, - task.getValue(Task.ELAPSED_SECONDS) + newElapsed); - } - @Override public void onReceive(Context context, Intent intent) { long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1); @@ -33,8 +21,7 @@ public class TimerTaskCompleteListener extends BroadcastReceiver { if(task == null || task.getValue(Task.TIMER_START) == 0) return; - stopTimer(task); - PluginServices.getTaskService().save(task, true); + TimerPlugin.updateTimer(context, task, false); } } diff --git a/astrid/res/drawable/timers_notification.png b/astrid/res/drawable/timers_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..6484e6e7d0fc57cf663af80fc45667f5018acd16 GIT binary patch literal 1499 zcmV<11tj{3P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igY| z1p_o5c`$cfiEkZjy(#elRec`Oo)%=RfEF=br&~v5Q^&2hiKwD|L5w=WS=#-QBHc>w{!E z;o7xpxovH2aR4}Q;6Ndzv=Kl7fT}f<2>^?#st!y}PA;s?k=okY#5)nz)6?S$27?Iz zsI07P6+(Qa>-vW-mkXw8LRD1&0HqX`Wg(GBAel_|OOo`{^z`&^YaApP4BBr^SVu>P z`{Kom%ZCpiu4Rl}%FWHKE-WlWZf>q4=v7ck5s$|)KR=InJpPBOs$Y+ej*fJ6bSSA* zN?g8tS-g?3_V)Jou3WkDqPn{J8%@)Cd_JF+pPvs!QFat*#u#F;7#0>5QmItx)X2!l z&+YB)ZnxX5tEw6eg+d!fP~MF2+_`h4va-_X?d?q*J9g|_!!UmE`~7lWULFV`ux)!s z7a;_OVIU_bN2Zj1vUl&^)jN0Y{C?!f5zn4IdkQ0wNGz*})zs8{B+GK2*Xxx{(_Htg zs;UZ&jg2^T=n!=MWdbIXNsNz=V{mW~6B85b>@CYeG#cfc^H1;HyZ39>TUlFMTL9oM zx7%Id@pznZUDwgt+KPsT24`$=a1e<^0)arlId{Xu!?=F^I+DqxWB2^|b1W|}F9ImP zd-v|*X1=9u0(MqWlmf#rz!(E#3|_Anp-^a@C;$kD!?l? z>@(Z8K`8~L6r6K3H#cYf8f$KD2It)2vuztfh?b0n*|uGsu0xD5l$DijxP?1~&*wu~ zS((GPR!)v?X0L1!u@cTXZ2M*XD=seH((%g53IO2v@#C-7Lo_xvIvk^;qt2NWLX>1I zjB_qak_66qI)ZQ9m86db0IYG#*$7K%nx>^vDF8q;8r{?xWGD zBWx|(Ut}zdG4{74NmXg0v9Yl&g_V?)Y#Xt$v9Wc+IOlWO9R87Xmq01S%*+g?rlzvq z#;K_(%*@Oj#6 z9%q$9LqoWA>sH31`uh4XG&JP!n5GHNIoEZ4XtOz+X2tC6EGsT9p7(e>A1^H}fl>+~ z1crx)QC?n-ii(OIiR$m~M=%%!V+`pXC@LxfW9-KL`}ePAEDQh+9y~ZMglIJkBmdd6 zXHMn5efu_Iu^5gXJ?gYOuVQIw3FptBM^{&uvnU}1{C+KL^7*4jj~27;V4Iqn zT(T_pJ%0SSX?AvY-80iPQD0w=`uck8+qVw@@Z`x8L?RJHA`w`Ywa(t_^`gAI{3&DX z^uWMC-y8S*`}gmcG)?<*Zf@>-0RC(f!0-2eF+DwfnJ&Xx7%H$>$+Q#B&@EkuIuv^MS)=$P*oMSZAU1jH7hGCv3L5x zo5snLC)J9Iib_q>E-f!F*ISkaAp~457u;?)2q9M=K79Dyg9i^5CMPFFru diff --git a/astrid/res/values/strings-timers.xml b/astrid/res/values/strings-timers.xml index 1e1c695c5..bbecc35d5 100644 --- a/astrid/res/values/strings-timers.xml +++ b/astrid/res/values/strings-timers.xml @@ -10,8 +10,8 @@ Stop Timer - - Elapsed Time: %s + + Timers Active for %s! Timer Filters diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java index fa863f684..c9e86b9d8 100644 --- a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java @@ -313,16 +313,7 @@ public class FilterListActivity extends ExpandableListActivity { Filter filter = (Filter) item; info.targetView.setTag(filter); menuItem = menu.add(0, CONTEXT_MENU_SHORTCUT, 0, R.string.FLA_context_shortcut); - Intent shortcutIntent = new Intent(this, ShortcutActivity.class); - shortcutIntent.setAction(Intent.ACTION_VIEW); - shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_TITLE, filter.title); - shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_SQL, filter.sqlQuery); - if(filter.valuesForNewTasks != null) { - shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_VALUES, - filter.valuesForNewTasks.toString()); - } - - menuItem.setIntent(shortcutIntent); + menuItem.setIntent(ShortcutActivity.createIntent(filter)); } for(int i = 0; i < item.contextMenuLabels.length; i++) { diff --git a/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java b/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java index e8e528e75..a0db742f7 100644 --- a/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/ShortcutActivity.java @@ -24,6 +24,7 @@ import android.content.ContentValues; import android.content.Intent; import android.os.Bundle; +import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.astrid.api.Filter; @@ -90,4 +91,17 @@ public class ShortcutActivity extends Activity { } finish(); } + + public static Intent createIntent(Filter filter) { + Intent shortcutIntent = new Intent(ContextManager.getContext(), + ShortcutActivity.class); + shortcutIntent.setAction(Intent.ACTION_VIEW); + shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_TITLE, filter.title); + shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_SQL, filter.sqlQuery); + if(filter.valuesForNewTasks != null) { + shortcutIntent.putExtra(ShortcutActivity.TOKEN_FILTER_VALUES, + filter.valuesForNewTasks.toString()); + } + return shortcutIntent; + } } diff --git a/astrid/src/com/todoroo/astrid/utility/Constants.java b/astrid/src/com/todoroo/astrid/utility/Constants.java index b76187437..d7ba910ef 100644 --- a/astrid/src/com/todoroo/astrid/utility/Constants.java +++ b/astrid/src/com/todoroo/astrid/utility/Constants.java @@ -32,7 +32,10 @@ public final class Constants { // --- notification id's - /** Notification Manager id for RMilk notifications */ + /** Notification Manager id for sync notifications */ public static final int NOTIFICATION_SYNC = -1; + /** Notification Manager id for timing */ + public static final int NOTIFICATION_TIMER = -2; + } From 3fa8579c3e0b1007d1aa0aeb3ce4a856b952f432 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 23:26:22 -0700 Subject: [PATCH 5/6] yay! fixed the timer cache thing --- .../com/todoroo/astrid/gcal/Calendars.java | 40 ++++----- .../astrid/timers/TimerActionExposer.java | 2 +- .../todoroo/astrid/timers/TimerPlugin.java | 2 +- astrid/res/layout/timer_decoration.xml | 7 +- .../todoroo/astrid/adapter/TaskAdapter.java | 89 ++++++++++--------- 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java b/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java index e68e141f1..0defec359 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java +++ b/astrid/plugin-src/com/todoroo/astrid/gcal/Calendars.java @@ -83,30 +83,29 @@ public class Calendars { Resources r = context.getResources(); Cursor c = cr.query(getCalendarContentUri(CALENDAR_CONTENT_CALENDARS), CALENDARS_PROJECTION, CALENDARS_WHERE, null, CALENDARS_SORT); + try { + // Fetch the current setting. Invalid calendar id will + // be changed to default value. + String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default); - // Fetch the current setting. Invalid calendar id will - // be changed to default value. - String defaultSetting = Preferences.getStringValue(R.string.gcal_p_default); + CalendarResult result = new CalendarResult(); - CalendarResult result = new CalendarResult(); + if (c == null || c.getCount() == 0) { + // Something went wrong when querying calendars. Only offer them + // the system default choice + result.calendars = new String[] { + r.getString(R.string.gcal_GCP_default) }; + result.calendarIds = new String[] { null }; + result.defaultIndex = 0; + return result; + } - if (c == null || c.getCount() == 0) { - // Something went wrong when querying calendars. Only offer them - // the system default choice - result.calendars = new String[] { - r.getString(R.string.gcal_GCP_default) }; - result.calendarIds = new String[] { null }; - result.defaultIndex = 0; - return result; - } + int calendarCount = c.getCount(); - int calendarCount = c.getCount(); + result.calendars = new String[calendarCount]; + result.calendarIds = new String[calendarCount]; - result.calendars = new String[calendarCount]; - result.calendarIds = new String[calendarCount]; - - // Iterate calendars one by one, and fill up the list preference - try { + // Iterate calendars one by one, and fill up the list preference int row = 0; int idColumn = c.getColumnIndex(ID_COLUMN_NAME); int nameColumn = c.getColumnIndex(DISPLAY_COLUMN_NAME); @@ -130,7 +129,8 @@ public class Calendars { return result; } finally { - c.deactivate(); + if(c != null) + c.close(); } } diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java index fed1e858e..521d58fb9 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java @@ -44,7 +44,7 @@ public class TimerActionExposer extends BroadcastReceiver { Intent newIntent = new Intent(TIMER_ACTION); newIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId); TaskAction action = new TaskAction(label, - PendingIntent.getBroadcast(context, 0, newIntent, 0)); + PendingIntent.getBroadcast(context, (int)taskId, newIntent, 0)); // transmit Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_ACTIONS); diff --git a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java index a2fbe632b..461c38216 100644 --- a/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java @@ -77,7 +77,7 @@ public class TimerPlugin extends BroadcastReceiver { Intent notifyIntent = ShortcutActivity.createIntent(filter); notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(context, - 0, notifyIntent, 0); + Constants.NOTIFICATION_TIMER, notifyIntent, 0); Resources r = context.getResources(); String appName = r.getString(R.string.app_name); diff --git a/astrid/res/layout/timer_decoration.xml b/astrid/res/layout/timer_decoration.xml index c618f2da6..1f568e9c0 100644 --- a/astrid/res/layout/timer_decoration.xml +++ b/astrid/res/layout/timer_decoration.xml @@ -2,20 +2,23 @@ diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java index 5c803eb96..05e782e75 100644 --- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java @@ -1,7 +1,6 @@ package com.todoroo.astrid.adapter; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -15,7 +14,6 @@ import android.database.Cursor; import android.graphics.Color; import android.graphics.Paint; import android.text.Html; -import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -109,6 +107,9 @@ public class TaskAdapter extends CursorAdapter { protected OnCompletedTaskListener onCompletedTaskListener = null; private int fontSize; + // the task that's expanded + private long expanded = -1; + // --- task detail and decoration soft caches public final DetailManager detailManager = new DetailManager(false); @@ -161,6 +162,7 @@ public class TaskAdapter extends CursorAdapter { // create view holder ViewHolder viewHolder = new ViewHolder(); viewHolder.task = new Task(); + viewHolder.view = view; viewHolder.nameView = (TextView)view.findViewById(R.id.title); viewHolder.completeBox = (CheckBox)view.findViewById(R.id.completeBox); viewHolder.dueDate = (TextView)view.findViewById(R.id.dueDate); @@ -189,11 +191,12 @@ public class TaskAdapter extends CursorAdapter { public void bindView(View view, Context context, Cursor c) { TodorooCursor cursor = (TodorooCursor)c; ViewHolder viewHolder = ((ViewHolder)view.getTag()); - Task actionItem = viewHolder.task; - actionItem.readFromCursor(cursor); + + Task task = viewHolder.task; + task.readFromCursor(cursor); setFieldContentsAndVisibility(view); - setTaskAppearance(viewHolder, actionItem.isCompleted()); + setTaskAppearance(viewHolder, task.isCompleted()); } /** Helper method to set the visibility based on if there's stuff inside */ @@ -212,6 +215,7 @@ public class TaskAdapter extends CursorAdapter { */ public class ViewHolder { public Task task; + public View view; public TextView nameView; public CheckBox completeBox; public TextView dueDate; @@ -220,7 +224,6 @@ public class TaskAdapter extends CursorAdapter { public View importance; public LinearLayout actions; public LinearLayout taskRow; - public boolean expanded; public View[] decorations; } @@ -286,10 +289,17 @@ public class TaskAdapter extends CursorAdapter { importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]); } - // details and decorations + // details and decorations, expanded if(!isFling) { detailManager.request(viewHolder); decorationManager.request(viewHolder); + if(expanded == task.getId()) { + extendedDetailManager.request(viewHolder); + taskActionManager.request(viewHolder); + } else { + viewHolder.extendedDetails.setVisibility(View.GONE); + viewHolder.actions.setVisibility(View.GONE); + } } } @@ -365,7 +375,7 @@ public class TaskAdapter extends CursorAdapter { continue; activity.runOnUiThread(new Runnable() { public void run() { - draw(viewHolder, cacheList); + draw(viewHolder, taskId, cacheList); } }); } @@ -379,17 +389,15 @@ public class TaskAdapter extends CursorAdapter { @SuppressWarnings("nls") @Override - void draw(ViewHolder viewHolder, Collection details) { - if(details == null) + void draw(ViewHolder viewHolder, long taskId, Collection details) { + if(details == null || viewHolder.task.getId() != taskId) return; TextView view = extended ? viewHolder.extendedDetails : viewHolder.details; - view.setVisibility(!details.isEmpty() ? View.VISIBLE : View.GONE); - if(details.isEmpty() || (extended && !viewHolder.expanded)) { + if(details.isEmpty() || (extended && expanded != taskId)) { view.setVisibility(View.GONE); return; - } else { - view.setVisibility(View.VISIBLE); } + view.setVisibility(View.VISIBLE); StringBuilder detailText = new StringBuilder(); for(Iterator iterator = details.iterator(); iterator.hasNext(); ) { detailText.append(iterator.next()); @@ -420,16 +428,17 @@ public class TaskAdapter extends CursorAdapter { } @Override - void draw(ViewHolder viewHolder, Collection decorations) { - if(decorations == null || decorations.size() == 0) + void draw(ViewHolder viewHolder, long taskId, Collection decorations) { + if(decorations == null || viewHolder.task.getId() != taskId) return; if(viewHolder.decorations != null) { for(View view : viewHolder.decorations) viewHolder.taskRow.removeView(view); - ((View)viewHolder.taskRow.getParent()).setBackgroundColor(Color.TRANSPARENT); } - + viewHolder.view.setBackgroundColor(Color.TRANSPARENT); + if(decorations.size() == 0) + return; int i = 0; boolean colorSet = false; @@ -437,7 +446,7 @@ public class TaskAdapter extends CursorAdapter { for(TaskDecoration decoration : decorations) { if(decoration.color != 0 && !colorSet) { colorSet = true; - ((View)viewHolder.taskRow.getParent()).setBackgroundColor(decoration.color); + viewHolder.view.setBackgroundColor(decoration.color); } if(decoration.decoration != null) { View view = decoration.decoration.apply(activity, viewHolder.taskRow); @@ -470,11 +479,11 @@ public class TaskAdapter extends CursorAdapter { } @Override - void draw(final ViewHolder viewHolder, Collection actions) { - if(actions == null) + void draw(final ViewHolder viewHolder, final long taskId, Collection actions) { + if(actions == null || viewHolder.task.getId() != taskId) return; - if(!viewHolder.expanded) { + if(expanded != taskId) { viewHolder.actions.setVisibility(View.GONE); return; } @@ -491,7 +500,6 @@ public class TaskAdapter extends CursorAdapter { @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); } @@ -542,13 +550,9 @@ public class TaskAdapter extends CursorAdapter { public void onClick(View v) { try { action.intent.send(); - - // refresh ourselves - getCursor().requery(); - notifyDataSetChanged(); } catch (Exception e) { - // oh too bad. - Log.i("astrid-action-error", "Error launching intent", e); //$NON-NLS-1$ //$NON-NLS-2$ + exceptionService.displayAndReportError(activity, + "Error launching action", e); //$NON-NLS-1$ } } }; @@ -565,15 +569,13 @@ public class TaskAdapter extends CursorAdapter { public void onClick(View v) { // expand view final ViewHolder viewHolder = (ViewHolder)v.getTag(); - viewHolder.expanded = !viewHolder.expanded; - - if(viewHolder.expanded) { - extendedDetailManager.request(viewHolder); - taskActionManager.request(viewHolder); + long taskId = viewHolder.task.getId(); + if(expanded == taskId) { + expanded = -1; } else { - viewHolder.extendedDetails.setVisibility(View.GONE); - viewHolder.actions.setVisibility(View.GONE); + expanded = taskId; } + notifyDataSetInvalidated(); } } @@ -640,8 +642,7 @@ public class TaskAdapter extends CursorAdapter { abstract private class AddOnManager { private final Map> cache = - Collections.synchronizedMap(new SoftHashMap>()); + new SoftHashMap>(); // --- interface @@ -654,14 +655,14 @@ public class TaskAdapter extends CursorAdapter { Collection list = initialize(taskId); if(list != null) { - draw(viewHolder, list); + draw(viewHolder, taskId, list); return false; } // request details + draw(viewHolder, taskId, get(taskId)); Intent broadcastIntent = createBroadcastIntent(taskId); activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); - draw(viewHolder, get(taskId)); return true; } @@ -669,7 +670,7 @@ public class TaskAdapter extends CursorAdapter { abstract Intent createBroadcastIntent(long taskId); /** updates the given view */ - abstract void draw(ViewHolder viewHolder, Collection list); + abstract void draw(ViewHolder viewHolder, long taskId, Collection list); /** on receive an intent */ public void addNew(long taskId, String addOn, TYPE item) { @@ -685,7 +686,7 @@ public class TaskAdapter extends CursorAdapter { ViewHolder viewHolder = (ViewHolder) listView.getChildAt(i).getTag(); if(viewHolder == null || viewHolder.task.getId() != taskId) continue; - draw(viewHolder, cacheList); + draw(viewHolder, taskId, cacheList); break; } } @@ -706,7 +707,7 @@ public class TaskAdapter extends CursorAdapter { * @param taskId * @return list if there was already one */ - protected Collection initialize(long taskId) { + protected synchronized Collection initialize(long taskId) { if(cache.containsKey(taskId)) return get(taskId); cache.put(taskId, new HashMap()); @@ -719,7 +720,7 @@ public class TaskAdapter extends CursorAdapter { * @param item * @return iterator if item was added, null if it already existed */ - protected Collection addIfNotExists(long taskId, String addOn, + protected synchronized Collection addIfNotExists(long taskId, String addOn, TYPE item) { HashMap list = cache.get(taskId); if(list == null) From a84e76d9834321e4a612e224d7e2bb96fb9f0aa4 Mon Sep 17 00:00:00 2001 From: Tim Su Date: Mon, 19 Jul 2010 23:52:39 -0700 Subject: [PATCH 6/6] miiinor tweaks to timers --- astrid/AndroidManifest.xml | 4 ++-- astrid/res/layout/timer_decoration.xml | 22 +++++++++---------- .../todoroo/astrid/adapter/TaskAdapter.java | 4 +++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index e5ca88c42..26679768f 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -151,7 +151,7 @@ - + @@ -223,7 +223,7 @@ - + diff --git a/astrid/res/layout/timer_decoration.xml b/astrid/res/layout/timer_decoration.xml index 1f568e9c0..449a2707c 100644 --- a/astrid/res/layout/timer_decoration.xml +++ b/astrid/res/layout/timer_decoration.xml @@ -7,19 +7,19 @@ android:paddingLeft="3px"> + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="5" + android:gravity="bottom|center_horizontal" + android:scaleType="center" + android:src="@drawable/timers_decoration" /> + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="2" + android:gravity="top" + android:textSize="10sp" /> diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java index 05e782e75..16d54d356 100644 --- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java +++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java @@ -575,7 +575,9 @@ public class TaskAdapter extends CursorAdapter { } else { expanded = taskId; } - notifyDataSetInvalidated(); + notifyDataSetChanged(); + ListView listView = activity.getListView(); + listView.setSelection(listView.indexOfChild(viewHolder.view)); } }