diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index 89957c65d..26679768f 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -151,7 +151,7 @@
-
+
@@ -207,6 +207,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:clearTaskOnLaunch="true" />
diff --git a/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java b/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
index afc1a0f4f..7a284f37b 100644
--- a/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
+++ b/astrid/api-src/com/todoroo/astrid/api/AstridApiConstants.java
@@ -47,6 +47,11 @@ public class AstridApiConstants {
*/
public static final String EXTRAS_ADDON = "addon";
+ /**
+ * Extras name for whether task detail request is extended
+ */
+ public static final String EXTRAS_EXTENDED = "extended";
+
// --- Add-ons API
/**
@@ -89,11 +94,14 @@ public class AstridApiConstants {
*/
public static final String BROADCAST_SEND_EDIT_CONTROLS = PACKAGE + ".SEND_EDIT_CONTROLS";
- // --- Task List Details API
+ // --- Task Details API
/**
- * Action name for broadcast intent requesting task list details for a task
+ * Action name for broadcast intent requesting details for a task.
+ * Extended details are displayed when a user presses on a task.
+ *
* @extra EXTRAS_TASK_ID id of the task
+ * @extra EXTRAS_EXTENDED whether request is for standard or extended details
*/
public static final String BROADCAST_REQUEST_DETAILS = PACKAGE + ".REQUEST_DETAILS";
@@ -101,10 +109,43 @@ public class AstridApiConstants {
* Action name for broadcast intent sending details back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
- * @extra EXTRAS_RESPONSE a {@link TaskDetail} object
+ * @extra EXTRAS_EXTENDED whether request is for standard or extended details
+ * @extra EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_DETAILS = PACKAGE + ".SEND_DETAILS";
+ // --- Task Actions API
+
+ /**
+ * Action name for broadcast intent requesting actions for a task
+ * @extra EXTRAS_TASK_ID id of the task
+ */
+ public static final String BROADCAST_REQUEST_ACTIONS = PACKAGE + ".REQUEST_ACTIONS";
+
+ /**
+ * Action name for broadcast intent sending actions back to Astrid
+ * @extra EXTRAS_ADDON your add-on identifier
+ * @extra EXTRAS_TASK_ID id of the task
+ * @extra EXTRAS_RESPONSE a String
+ */
+ public static final String BROADCAST_SEND_ACTIONS = PACKAGE + ".SEND_ACTIONS";
+
+ // --- Task Decorations API
+
+ /**
+ * Action name for broadcast intent requesting task list decorations for a task
+ * @extra EXTRAS_TASK_ID id of the task
+ */
+ public static final String BROADCAST_REQUEST_DECORATIONS = PACKAGE + ".REQUEST_DECORATIONS";
+
+ /**
+ * Action name for broadcast intent sending decorations back to Astrid
+ * @extra EXTRAS_ADDON your add-on identifier
+ * @extra EXTRAS_TASK_ID id of the task
+ * @extra EXTRAS_RESPONSE a {@link TaskDecoration}
+ */
+ public static final String BROADCAST_SEND_DECORATIONS = PACKAGE + ".SEND_DECORATIONS";
+
// --- Actions API
/**
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/EditOperation.java b/astrid/api-src/com/todoroo/astrid/api/TaskAction.java
similarity index 54%
rename from astrid/api-src/com/todoroo/astrid/api/EditOperation.java
rename to astrid/api-src/com/todoroo/astrid/api/TaskAction.java
index 94fb63c3d..75aed3b1a 100644
--- a/astrid/api-src/com/todoroo/astrid/api/EditOperation.java
+++ b/astrid/api-src/com/todoroo/astrid/api/TaskAction.java
@@ -3,22 +3,17 @@
*/
package com.todoroo.astrid.api;
-import android.content.Intent;
+import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Represents an intent that can be called on a task being edited
+ * Represents an intent that can be called on a task
*
* @author Tim Su
*
*/
-public final class EditOperation implements Parcelable {
-
- /**
- * Plugin Id
- */
- public final String plugin;
+public class TaskAction implements Parcelable {
/**
* Label
@@ -28,20 +23,18 @@ public final class EditOperation implements Parcelable {
/**
* Intent to call when invoking this operation
*/
- public Intent intent = null;
+ public PendingIntent intent = null;
/**
* 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, PendingIntent 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(), (PendingIntent)source.readParcelable(
+ PendingIntent.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..e1cf4115c
--- /dev/null
+++ b/astrid/api-src/com/todoroo/astrid/api/TaskDecoration.java
@@ -0,0 +1,96 @@
+/**
+ * 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 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 e1dcc9b9c..046dd723f 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/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/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/gcal/GCalTaskCompleteListener.java b/astrid/plugin-src/com/todoroo/astrid/gcal/GCalTaskCompleteListener.java
index a9d8b6199..a66d938f5 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 f5ef99e88..2a14ed1e6 100644
--- a/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/notes/NoteDetailExposer.java
@@ -7,27 +7,19 @@ 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.api.TaskDetail;
+import com.todoroo.astrid.core.PluginServices;
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
*
*/
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
@@ -35,7 +27,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,29 +36,29 @@ 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) {
- synchronized(NoteDetailExposer.class) {
- if(staticTaskService == null) {
- DependencyInjectionService.getInstance().inject(this);
- staticTaskService = taskService;
- } else {
- taskService = staticTaskService;
- }
- }
+ public String getTaskDetails(Context context, long id, boolean extended) {
+ if(!extended)
+ return null;
- 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);
if(notes.length() == 0)
return null;
- return new TaskDetail(notes);
+ return notes;
+ }
+
+ @Override
+ public String getPluginIdentifier() {
+ return NotesPlugin.IDENTIFIER;
}
}
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..46d3af6f7 100644
--- a/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/repeats/RepeatDetailExposer.java
@@ -16,27 +16,19 @@ 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.api.TaskDetail;
+import com.todoroo.astrid.core.PluginServices;
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
*
*/
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
@@ -44,7 +36,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,21 +45,16 @@ 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) {
- synchronized(RepeatDetailExposer.class) {
- if(staticTaskService == null) {
- DependencyInjectionService.getInstance().inject(this);
- staticTaskService = taskService;
- } else {
- taskService = staticTaskService;
- }
- }
+ public String getTaskDetails(Context context, long id, boolean extended) {
+ if(extended)
+ return null;
- Task task = taskService.fetchById(id, Task.FLAGS, Task.RECURRENCE);
+ Task task = PluginServices.getTaskService().fetchById(id, Task.FLAGS, Task.RECURRENCE);
if(task == null)
return null;
@@ -126,9 +114,14 @@ 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;
}
+ @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 60d1ee278..f266b69dd 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,59 @@ 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());
+ }
+
+ @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 16a7b4668..d8c4ea6fc 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,26 @@ 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);
+ }
+
+ @Override
+ public String getPluginIdentifier() {
+ return TagsPlugin.IDENTIFIER;
}
}
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/TimerActionExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
new file mode 100644
index 000000000..521d58fb9
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerActionExposer.java
@@ -0,0 +1,63 @@
+/**
+ * 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.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;
+
+/**
+ * Exposes {@link TaskDecoration} for timers
+ *
+ * @author Tim Su
+ *
+ */
+public class TimerActionExposer extends BroadcastReceiver {
+
+ private static final String TIMER_ACTION = "com.todoroo.astrid.TIMER_BUTTON"; //$NON-NLS-1$
+
+ @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.TIMER_START,
+ Task.ELAPSED_SECONDS);
+
+ // 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(TIMER_ACTION);
+ newIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ TaskAction action = new TaskAction(label,
+ PendingIntent.getBroadcast(context, (int)taskId, 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 if(TIMER_ACTION.equals(intent.getAction())) {
+ if(task.getValue(Task.TIMER_START) == 0)
+ TimerPlugin.updateTimer(context, task, true);
+ else
+ TimerPlugin.updateTimer(context, task, false);
+ }
+ }
+
+}
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..b8b980b7c
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerDecorationExposer.java
@@ -0,0 +1,79 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.timers;
+
+import java.util.HashMap;
+
+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.utility.DateUtilities;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.TaskDecoration;
+import com.todoroo.astrid.core.PluginServices;
+import com.todoroo.astrid.model.Task;
+
+/**
+ * Exposes {@link TaskDecoration} for timers
+ *
+ * @author Tim Su
+ *
+ */
+public class TimerDecorationExposer extends BroadcastReceiver {
+
+ private static final int TIMING_BG_COLOR = Color.argb(200, 220, 50, 0);
+
+ private static HashMap decorations =
+ new HashMap();
+
+ public static void removeFromCache(long taskId) {
+ decorations.remove(taskId);
+ }
+
+ @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.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, 0);
+ decorations.put(taskId, decoration);
+ } 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);
+ 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/TimerFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java
new file mode 100644
index 000000000..224dd34fc
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerFilterExposer.java
@@ -0,0 +1,60 @@
+/**
+ * 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.Query;
+import com.todoroo.andlib.sql.QueryTemplate;
+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.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) {
+
+ if(PluginServices.getTaskService().count(Query.select(Task.ID).
+ where(Task.TIMER_START.gt(0))) == 0)
+ return;
+
+ Filter workingOn = createFilter(context);
+
+ // 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);
+ }
+
+ 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
new file mode 100644
index 000000000..461c38216
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerPlugin.java
@@ -0,0 +1,97 @@
+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 {
+
+ 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);
+ }
+
+ /**
+ * 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,
+ Constants.NOTIFICATION_TIMER, 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
new file mode 100644
index 000000000..8da52acaf
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/timers/TimerTaskCompleteListener.java
@@ -0,0 +1,27 @@
+package com.todoroo.astrid.timers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.core.PluginServices;
+import com.todoroo.astrid.model.Task;
+
+public class TimerTaskCompleteListener extends BroadcastReceiver {
+
+ @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;
+
+ TimerPlugin.updateTimer(context, task, false);
+ }
+
+}
diff --git a/astrid/res/drawable/tango_clock.png b/astrid/res/drawable/tango_clock.png
new file mode 100644
index 000000000..d676ffd46
Binary files /dev/null and b/astrid/res/drawable/tango_clock.png differ
diff --git a/astrid/res/drawable/timers_decoration.png b/astrid/res/drawable/timers_decoration.png
new file mode 100644
index 000000000..18b7c6781
Binary files /dev/null and b/astrid/res/drawable/timers_decoration.png differ
diff --git a/astrid/res/drawable/timers_notification.png b/astrid/res/drawable/timers_notification.png
new file mode 100644
index 000000000..6484e6e7d
Binary files /dev/null and b/astrid/res/drawable/timers_notification.png differ
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/res/values/strings-timers.xml b/astrid/res/values/strings-timers.xml
new file mode 100644
index 000000000..bbecc35d5
--- /dev/null
+++ b/astrid/res/values/strings-timers.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+ Start Timer
+
+
+ Stop Timer
+
+
+ Timers Active for %s!
+
+
+ Timer Filters
+
+
+ Tasks Being Timed
+
+
diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
index c74bc8142..41dd55a32 100644
--- a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
@@ -314,16 +314,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/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
index bb35ff5ce..f5730dd7a 100644
--- a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -46,13 +46,15 @@ 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;
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.TaskAction;
+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;
@@ -324,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
@@ -378,6 +382,10 @@ 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));
+ registerReceiver(detailReceiver,
+ new IntentFilter(AstridApiConstants.BROADCAST_SEND_ACTIONS));
}
@Override
@@ -387,7 +395,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 +406,21 @@ 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);
+ String addOn = extras.getString(AstridApiConstants.EXTRAS_ADDON);
+
+ if(AstridApiConstants.BROADCAST_SEND_DECORATIONS.equals(intent.getAction())) {
+ TaskDecoration deco = extras.getParcelable(AstridApiConstants.EXTRAS_RESPONSE);
+ taskAdapter.decorationManager.addNew(taskId, addOn, deco);
+ } else if(AstridApiConstants.BROADCAST_SEND_DETAILS.equals(intent.getAction())) {
+ String detail = extras.getString(AstridApiConstants.EXTRAS_RESPONSE);
+ 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.EXTRAS_RESPONSE);
+ taskAdapter.taskActionManager.addNew(taskId, addOn, action);
+ }
} catch (Exception e) {
exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON), e);
@@ -463,7 +484,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..16d54d356 100644
--- a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
+++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
@@ -1,15 +1,17 @@
package com.todoroo.astrid.adapter;
+import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
+import java.util.Map;
-import android.app.Activity;
+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;
@@ -19,11 +21,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;
@@ -37,10 +39,10 @@ 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.TaskDetail;
+import com.todoroo.astrid.api.TaskAction;
+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 +99,24 @@ 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>();
+
+ // the task that's expanded
+ private long expanded = -1;
+
+ // --- task detail and decoration soft caches
+
+ 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
@@ -120,7 +131,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);
@@ -151,11 +162,14 @@ 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);
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);
@@ -177,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 */
@@ -200,13 +215,17 @@ public class TaskAdapter extends CursorAdapter {
*/
public class ViewHolder {
public Task task;
+ public View view;
public TextView nameView;
public CheckBox completeBox;
public TextView dueDate;
public TextView details;
+ public TextView extendedDetails;
public View importance;
public LinearLayout actions;
- public boolean expanded;
+ public LinearLayout taskRow;
+
+ public View[] decorations;
}
/** Helper method to set the contents and visibility of each field */
@@ -251,7 +270,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,189 +283,301 @@ 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, 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);
+ }
+ }
}
- // --- 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);
- private void retrieveDetails(final ViewHolder viewHolder) {
- final long taskId = viewHolder.task.getId();
+ // context menu listener
+ container.setOnCreateContextMenuListener(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);
- }
+ // tap listener
+ container.setOnClickListener(listener);
+ }
- if(inCache) {
- spanifyAndAdd(viewHolder.details, details);
- return;
- }
+ /* ======================================================================
+ * ============================================================== add-ons
+ * ====================================================================== */
- // request details
- Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
- broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
- activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
-
- // load internal details
- new Thread() {
- @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;
+ /**
+ * Called to tell the cache to be cleared
+ */
+ public void flushCaches() {
+ detailManager.clearCache();
+ extendedDetailManager.clearCache();
+ decorationManager.clearCache();
+ taskActionManager.clearCache();
+ }
- details.add(detail);
- if(taskId != viewHolder.task.getId())
- continue;
+ /**
+ * AddOnManager for Details
+ * @author Tim Su
+ *
+ */
+ public class DetailManager extends AddOnManager {
+
+ private final boolean extended;
+ public DetailManager(boolean extended) {
+ this.extended = extended;
+ }
+
+ @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;
+ }
- activity.runOnUiThread(new Runnable() {
- public void run() {
- spanifyAndAdd(viewHolder.details, details);
+ @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, taskId, cacheList);
+ }
+ });
+ }
}
- });
- }
- };
- }.start();
- }
+ };
+ }.start();
+ return true;
+ }
+ return false;
+ }
- @SuppressWarnings("nls")
- private void spanifyAndAdd(TextView view, LinkedHashSet details) {
- view.setVisibility(details.size() > 0 ? View.VISIBLE : View.GONE);
- if(details.size() == 0)
- return;
- StringBuilder detailText = new StringBuilder();
- for(Iterator iterator = details.iterator(); iterator.hasNext(); ) {
- detailText.append(iterator.next().text);
- if(iterator.hasNext())
- detailText.append(DETAIL_SEPARATOR);
+ @SuppressWarnings("nls")
+ @Override
+ void draw(ViewHolder viewHolder, long taskId, Collection details) {
+ if(details == null || viewHolder.task.getId() != taskId)
+ return;
+ TextView view = extended ? viewHolder.extendedDetails : viewHolder.details;
+ if(details.isEmpty() || (extended && expanded != taskId)) {
+ view.setVisibility(View.GONE);
+ return;
+ }
+ 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());
}
- 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
+ * AddOnManager for TaskDecorations
+ *
+ * @author Tim Su
+ *
*/
- public void flushDetailCache() {
- detailCache.clear();
- }
+ public class DecorationManager extends AddOnManager {
+ @Override
+ Intent createBroadcastIntent(long taskId) {
+ Intent intent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DECORATIONS);
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ return intent;
+ }
- @Override
- public void notifyDataSetChanged() {
- super.notifyDataSetChanged();
- fontSize = Preferences.getIntegerFromString(R.string.p_fontSize);
+ @Override
+ 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);
+ }
+ viewHolder.view.setBackgroundColor(Color.TRANSPARENT);
+ if(decorations.size() == 0)
+ return;
+
+ int i = 0;
+ boolean colorSet = false;
+ viewHolder.decorations = new View[decorations.size()];
+ for(TaskDecoration decoration : decorations) {
+ if(decoration.color != 0 && !colorSet) {
+ colorSet = true;
+ viewHolder.view.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 addDetails(ListView list, long taskId, TaskDetail detail) {
- if(detail == null)
- return;
+ 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;
+ }
+
+ @Override
+ void draw(final ViewHolder viewHolder, final long taskId, Collection actions) {
+ if(actions == null || viewHolder.task.getId() != taskId)
+ return;
+
+ if(expanded != taskId) {
+ 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);
+ 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);
+ }
- 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;
}
}
+ /* ======================================================================
+ * ======================================================= event handlers
+ * ====================================================================== */
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ fontSize = Preferences.getIntegerFromString(R.string.p_fontSize);
+ }
+
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());
}
};
- protected ContextMenuListener listener = new ContextMenuListener();
- /**
- * 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);
-
- // tap listener
- container.setOnClickListener(listener);
- }
+ private final class ActionClickListener implements View.OnClickListener {
+ TaskAction action;
- /* ======================================================================
- * ======================================================= event handlers
- * ====================================================================== */
+ public ActionClickListener(TaskAction action) {
+ this.action = action;
+ }
+ public void onClick(View v) {
+ try {
+ action.intent.send();
+ } catch (Exception e) {
+ exceptionService.displayAndReportError(activity,
+ "Error launching action", e); //$NON-NLS-1$
+ }
+ }
+ };
- class ContextMenuListener implements OnCreateContextMenuListener, OnClickListener {
+ 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
public void onClick(View v) {
+ // expand view
final ViewHolder viewHolder = (ViewHolder)v.getTag();
- viewHolder.expanded = !viewHolder.expanded;
- LinearLayout actions = viewHolder.actions;
- actions.setVisibility(viewHolder.expanded ? View.VISIBLE : View.GONE);
- if(viewHolder.expanded && 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, viewHolder.task.getId());
- activity.startActivityForResult(intent, TaskListActivity.ACTIVITY_EDIT_TASK);
- }
- });
- actions.addView(edit);
+ long taskId = viewHolder.task.getId();
+ if(expanded == taskId) {
+ expanded = -1;
+ } else {
+ expanded = taskId;
}
+ notifyDataSetChanged();
+ ListView listView = activity.getListView();
+ listView.setSelection(listView.indexOfChild(viewHolder.view));
}
}
@@ -507,4 +637,111 @@ public class TaskAdapter extends CursorAdapter {
}
}
+ /* ======================================================================
+ * ========================================================= addon helper
+ * ====================================================================== */
+
+ abstract private class AddOnManager {
+
+ private final Map> cache =
+ 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, taskId, list);
+ return false;
+ }
+
+ // request details
+ draw(viewHolder, taskId, get(taskId));
+ Intent broadcastIntent = createBroadcastIntent(taskId);
+ activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ return true;
+ }
+
+ /** creates a broadcast intent for requesting */
+ abstract Intent createBroadcastIntent(long taskId);
+
+ /** updates the given view */
+ abstract void draw(ViewHolder viewHolder, long taskId, 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, taskId, cacheList);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Clears the cache
+ */
+ public void clearCache() {
+ cache.clear();
+ }
+
+ // --- internal goodies
+
+ /**
+ * 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
+ */
+ protected synchronized Collection initialize(long taskId) {
+ if(cache.containsKey(taskId))
+ return get(taskId);
+ cache.put(taskId, new HashMap());
+ return null;
+ }
+
+ /**
+ * Adds an item to the cache if it doesn't exist
+ * @param taskId
+ * @param item
+ * @return iterator if item was added, null if it already existed
+ */
+ protected synchronized Collection addIfNotExists(long taskId, String addOn,
+ TYPE item) {
+ HashMap list = cache.get(taskId);
+ if(list == null)
+ return null;
+ if(list.containsKey(addOn) && list.get(addOn).equals(item))
+ return null;
+ list.put(addOn, item);
+ return get(taskId);
+ }
+
+ /**
+ * Gets an item at the given index
+ * @param taskId
+ * @return
+ */
+ 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 2a4e84e60..90ef95b9d 100644
--- a/astrid/src/com/todoroo/astrid/api/DetailExposer.java
+++ b/astrid/src/com/todoroo/astrid/api/DetailExposer.java
@@ -12,8 +12,15 @@ 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);
+
+ 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(
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();
+ }
+ }
+
}
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;
+
}