allItemsWithMetadataUri instead of this one.
- */
- public static Uri allItemsUri() {
- return Uri.parse("content://" + PROVIDER + "/items");
- }
-
- /**
- * URI for:
- * allItemsUri.
- *
- * For queries, allItemsUri will be more efficient, but will
- * not work if your selection clause contains columns not mentioned in your
- * projection
- *
- * @param metadata
- * array of metadata columns you wish to select using
- *
- */
- public static Uri allItemsWithMetadataUri(String[] metadata) {
- if(metadata == null || metadata.length == 0)
- throw new IllegalArgumentException("You must provide metadata");
- StringBuilder builder = new StringBuilder();
- for(int i = 0; i < metadata.length; i++)
- builder.append(escapeUriSubComponent(metadata[i])).append(
- SUB_COMPONENT_SEPARATOR);
-
- return Uri.parse("content://" + PROVIDER + "/itemsWith/" +
- escapeUriComponent(builder.toString()));
- }
-
- /**
- * URI for:
- *
Task.URGENCY_*) */
- public static final String URGENCY = "urgency";
-
- /** int: Task Importance setting (see Task.IMPORTANCE_*) */
- public static final String IMPORTANCE = "importance";
-
- /** int: unixtime Task is due, 0 if not set */
- public static final String DUE_DATE = "dueDate";
-
- /** int: unixtime Task should be hidden until, 0 if not set */
- public static final String HIDDEN_UNTIL = "hiddenUntil";
-
- /** int: unixtime Task was created */
- public static final String CREATION_DATE = "creationDate";
-
- /** int: unixtime Task was completed, 0 if task not completed */
- public static final String COMPLETION_DATE = "completionDate";
-
- /** int: unixtime Task was deleted, 0 if task not deleted */
- public static final String DELETION_DATE = "deletionDate";
-
- /** int: unixtime Task was modified */
- public static final String MODIFICATION_DATE = "modificationDate";
-
- // --- urgency settings
-
- public static final int URGENCY_NONE = 0;
- public static final int URGENCY_TODAY = 1;
- public static final int URGENCY_THIS_WEEK = 2;
- public static final int URGENCY_THIS_MONTH = 3;
- public static final int URGENCY_WITHIN_THREE_MONTHS = 4;
- public static final int URGENCY_WITHIN_SIX_MONTHS = 5;
- public static final int URGENCY_WITHIN_A_YEAR = 6;
- public static final int URGENCY_SPECIFIC_DAY = 7;
- public static final int URGENCY_SPECIFIC_DAY_TIME = 8;
-
- // --- importance settings
-
- public static final int IMPORTANCE_DO_OR_DIE = 0;
- public static final int IMPORTANCE_MUST_DO = 1;
- public static final int IMPORTANCE_SHOULD_DO = 2;
- public static final int IMPORTANCE_NONE = 3;
-
-
- }
-
- // --- internal methods
-
- /**
- * Escapes a string for use in a URI. Used internally to pass extra data
- * to the content provider.
- * @param component
- * @return
- */
- private static String escapeUriComponent(String component) {
- return component.replace("%", "%o").replace("/", "%s");
- }
-
- private static final String SUB_COMPONENT_SEPARATOR = "|";
-
- /**
- * Escapes a string for use as part of a URI string. Used internally to pass extra data
- * to the content provider.
- * @param component
- * @return
- */
- private static String escapeUriSubComponent(String component) {
- return component.replace("$", "$o").replace(SUB_COMPONENT_SEPARATOR, "$s");
- }
-
-}
diff --git a/astrid/api-src/com/todoroo/astrid/api/EditOperation.java b/astrid/api-src/com/todoroo/astrid/api/EditOperation.java
index 779b40689..84af715dd 100644
--- a/astrid/api-src/com/todoroo/astrid/api/EditOperation.java
+++ b/astrid/api-src/com/todoroo/astrid/api/EditOperation.java
@@ -13,12 +13,12 @@ import android.os.Parcelable;
* @author Tim Su FilterListFilter allows users to display tasks that have
@@ -19,7 +20,12 @@ import com.todoroo.astrid.api.AstridContentProvider.AstridTask;
* @author Tim Su SELECT fields FROM table %s". Use
- * {@link AstridApiConstants.TASK_TABLE} and
- * {@link AstridApiConstants.METADATA_TABLE} as table names,
- * {@link AstridTask} for field names.
+ * statement after "SELECT fields FROM table %s". It is
+ * recommended that you use a {@link QueryTemplate} to construct your
+ * query.
* * Examples: *
" WHERE completionDate = 0"
- * " INNER JOIN " +
+ * "WHERE completionDate = 0"
+ * "INNER JOIN " +
* Constants.TABLE_METADATA + " ON metadata.task = tasks.id WHERE
* metadata.namespace = " + NAMESPACE + " AND metadata.key = 'a' AND
* metadata.value = 'b' GROUP BY tasks.id ORDER BY tasks.title"
@@ -48,25 +53,18 @@ public class Filter extends FilterListItem {
public String sqlQuery;
/**
- * SQL query to execute on a task when quick-creating a new task while viewing
- * this filter. For example, when a user views tasks tagged 'ABC', the
+ * Values to apply to a task when quick-adding a task from this filter.
+ * For example, when a user views tasks tagged 'ABC', the
* tasks they create should also be tagged 'ABC'. If set to null, no
- * query will be executed. In this string, $ID will be replaced with the
- * task id.
- *
- * Examples:
- *
- * "INSERT INTO " + Constants.TABLE_METADATA + " (task,
- * namespace, key, string) VALUES ($ID, " + ... + ")"
- * "UPDATE " + Constants.TABLE_TASK + " SET urgency = 0
- * WHERE _id = $ID"
- *
+ * additional values will be stored for a task.
*/
- public String sqlForNewTasks = null;
+ public ContentValues valuesForNewTasks = null;
/**
* Utility constructor for creating a TaskList object
*
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param title
@@ -74,22 +72,26 @@ public class Filter extends FilterListItem {
* filter, e.g. Inbox (20 tasks)
* @param sqlQuery
* SQL query for this list (see {@link sqlQuery} for examples).
- * @param sqlForNewTasks
+ * @param valuesForNewTasks
* see {@link sqlForNewTasks}
*/
- public Filter(String listingTitle,
- String title, String sqlQuery, String sqlForNewTasks) {
+ public Filter(String plugin, String listingTitle,
+ String title, QueryTemplate sqlQuery, ContentValues valuesForNewTasks) {
+ this.plugin = plugin;
this.listingTitle = listingTitle;
this.title = title;
- this.sqlQuery = sqlQuery;
- this.sqlForNewTasks = sqlForNewTasks;
+ this.sqlQuery = sqlQuery.toString();
+ this.valuesForNewTasks = valuesForNewTasks;
}
/**
- * Blank constructor
+ * Utility constructor
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
*/
- public Filter() {
- //
+ protected Filter(String plugin) {
+ this.plugin = plugin;
}
// --- parcelable
@@ -106,10 +108,11 @@ public class Filter extends FilterListItem {
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
super.writeToParcel(dest, flags);
dest.writeString(title);
dest.writeString(sqlQuery);
- dest.writeString(sqlForNewTasks);
+ dest.writeParcelable(valuesForNewTasks, 0);
}
/**
@@ -121,11 +124,11 @@ public class Filter extends FilterListItem {
* {@inheritDoc}
*/
public Filter createFromParcel(Parcel source) {
- Filter item = new Filter();
+ Filter item = new Filter(source.readString());
item.readFromParcel(source);
item.title = source.readString();
item.sqlQuery = source.readString();
- item.sqlForNewTasks = source.readString();
+ item.valuesForNewTasks = source.readParcelable(ContentValues.class.getClassLoader());
return item;
}
diff --git a/astrid/api-src/com/todoroo/astrid/api/FilterCategory.java b/astrid/api-src/com/todoroo/astrid/api/FilterCategory.java
index a95ffb802..ffaa9cb35 100644
--- a/astrid/api-src/com/todoroo/astrid/api/FilterCategory.java
+++ b/astrid/api-src/com/todoroo/astrid/api/FilterCategory.java
@@ -15,6 +15,11 @@ import android.os.Parcelable;
*/
public class FilterCategory extends FilterListItem {
+ /**
+ * Plug-in Identifier
+ */
+ public final String plugin;
+
/**
* {@link Filter}s contained by this category
*/
@@ -23,22 +28,28 @@ public class FilterCategory extends FilterListItem {
/**
* Constructor for creating a new FilterCategory
*
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param children
* filters belonging to this category
*/
- public FilterCategory(String listingTitle,
+ public FilterCategory(String plugin, String listingTitle,
Filter[] children) {
+ this.plugin = plugin;
this.listingTitle = listingTitle;
this.children = children;
}
/**
- * Blank constructor
+ * Constructor for creating a new FilterCategory
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
*/
- public FilterCategory() {
- //
+ protected FilterCategory(String plugin) {
+ this.plugin = plugin;
}
// --- parcelable
@@ -55,6 +66,7 @@ public class FilterCategory extends FilterListItem {
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
super.writeToParcel(dest, flags);
dest.writeParcelableArray(children, 0);
}
@@ -68,7 +80,7 @@ public class FilterCategory extends FilterListItem {
* {@inheritDoc}
*/
public FilterCategory createFromParcel(Parcel source) {
- FilterCategory item = new FilterCategory();
+ FilterCategory item = new FilterCategory(source.readString());
item.readFromParcel(source);
Parcelable[] parcelableChildren = source.readParcelableArray(
diff --git a/astrid/api-src/com/todoroo/astrid/api/FilterListHeader.java b/astrid/api-src/com/todoroo/astrid/api/FilterListHeader.java
index b980a49e8..e1d38b87b 100644
--- a/astrid/api-src/com/todoroo/astrid/api/FilterListHeader.java
+++ b/astrid/api-src/com/todoroo/astrid/api/FilterListHeader.java
@@ -8,44 +8,57 @@ import android.os.Parcelable;
/**
* Section Header for Filter List
- *
+ *
* @author Tim Su
*
*/
public class FilterListHeader extends FilterListItem {
+ /**
+ * Plug-in Identifier
+ */
+ public final String plugin;
+
/**
* Constructor for creating a new FilterListHeader
- *
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
* @param listingTitle
* @param listingIconResource
* @param priority
*/
- public FilterListHeader(String listingTitle) {
+ public FilterListHeader(String plugin, String listingTitle) {
+ this.plugin = plugin;
this.listingTitle = listingTitle;
}
-
+
/**
- * Empty constructor
+ * Constructor for creating a new FilterListHeader
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
*/
- public FilterListHeader() {
- //
+ protected FilterListHeader(String plugin) {
+ this.plugin = plugin;
}
-
+
// --- parcelable
-
+
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
super.writeToParcel(dest, flags);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public FilterListHeader createFromParcel(Parcel source) {
- FilterListHeader item = new FilterListHeader();
+ FilterListHeader item = new FilterListHeader(source.readString());
item.readFromParcel(source);
return item;
}
@@ -53,6 +66,6 @@ public class FilterListHeader extends FilterListItem {
public FilterListHeader[] newArray(int size) {
return new FilterListHeader[size];
}
-
+
};
}
diff --git a/astrid/api-src/com/todoroo/astrid/api/FilterListItem.java b/astrid/api-src/com/todoroo/astrid/api/FilterListItem.java
index f0eab9ec8..747a22cf3 100644
--- a/astrid/api-src/com/todoroo/astrid/api/FilterListItem.java
+++ b/astrid/api-src/com/todoroo/astrid/api/FilterListItem.java
@@ -57,6 +57,7 @@ abstract public class FilterListItem implements Parcelable {
/**
* Utility method to read FilterListItem properties from a parcel.
+ *
* @param source
*/
public void readFromParcel(Parcel source) {
diff --git a/astrid/api-src/com/todoroo/astrid/api/Plugin.java b/astrid/api-src/com/todoroo/astrid/api/Plugin.java
new file mode 100644
index 000000000..81f280ff7
--- /dev/null
+++ b/astrid/api-src/com/todoroo/astrid/api/Plugin.java
@@ -0,0 +1,93 @@
+/**
+ * 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 plug-in for Astrid. Users can enable or disable plug-ins,
+ * which affect all other extension points that share the same identifier.
+ *
+ * @author Tim Su
+ *
+ */
+public class Plugin implements Parcelable {
+
+ /**
+ * Plug-in Identifier
+ */
+ public String plugin = null;
+
+ /**
+ * Plug-in Title
+ */
+ public String title = null;
+
+ /**
+ * Plug-in Author
+ */
+ public String author = null;
+
+ /**
+ * Plug-in Description
+ */
+ public String description = null;
+
+ /**
+ * Convenience constructor to generate a plug-in object
+ *
+ * @param plugin
+ * @param title
+ * @param author
+ * @param description
+ */
+ public Plugin(String plugin, String title, String author, String description) {
+ this.plugin = plugin;
+ this.title = title;
+ this.author = author;
+ this.description = description;
+ }
+
+ // --- parcelable helpers
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
+ dest.writeString(title);
+ dest.writeString(author);
+ dest.writeString(description);
+ }
+
+ /**
+ * Parcelable creator
+ */
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ /**
+ * {@inheritDoc}
+ */
+ public Plugin createFromParcel(Parcel source) {
+ return new Plugin(source.readString(), source.readString(),
+ source.readString(), source.readString());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Plugin[] newArray(int size) {
+ return new Plugin[size];
+ };
+ };
+
+}
diff --git a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java b/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java
index 573a7b35c..2fe01fd66 100644
--- a/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java
+++ b/astrid/api-src/com/todoroo/astrid/api/TaskDetail.java
@@ -12,7 +12,12 @@ import android.os.Parcelable;
* @author Tim Su
*
*/
-public class TaskDetail implements Parcelable {
+public final class TaskDetail implements Parcelable {
+
+ /**
+ * Plug-in Identifier
+ */
+ public final String plugin;
/**
* Text of detail
@@ -27,13 +32,15 @@ public class TaskDetail implements Parcelable {
/**
* Creates a TaskDetail object
*
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
* @param text
* text to display
* @param color
* color to use for text. Use 0 for default color
*/
- public TaskDetail(String text, int color) {
- super();
+ public TaskDetail(String plugin, String text, int color) {
+ this.plugin = plugin;
this.text = text;
this.color = color;
}
@@ -44,8 +51,8 @@ public class TaskDetail implements Parcelable {
* @param text
* text to display
*/
- public TaskDetail(String text) {
- this(text, 0);
+ public TaskDetail(String plugin, String text) {
+ this(plugin, text, 0);
}
@@ -63,6 +70,7 @@ public class TaskDetail implements Parcelable {
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
dest.writeString(text);
dest.writeInt(color);
}
@@ -75,7 +83,7 @@ public class TaskDetail implements Parcelable {
* {@inheritDoc}
*/
public TaskDetail createFromParcel(Parcel source) {
- return new TaskDetail(source.readString(), source.readInt());
+ return new TaskDetail(source.readString(), source.readString(), source.readInt());
}
/**
diff --git a/astrid/astrid.launch b/astrid/astrid.launch
new file mode 100644
index 000000000..cc219d422
--- /dev/null
+++ b/astrid/astrid.launch
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/common-src/com/todoroo/andlib/data/AbstractDatabase.java b/astrid/common-src/com/todoroo/andlib/data/AbstractDatabase.java
index e5d268a57..197356893 100644
--- a/astrid/common-src/com/todoroo/andlib/data/AbstractDatabase.java
+++ b/astrid/common-src/com/todoroo/andlib/data/AbstractDatabase.java
@@ -148,11 +148,12 @@ abstract public class AbstractDatabase {
}
/**
- * @return sql database. throws error if database was not opened
+ * @return sql database. opens database if not yet open
*/
public final SQLiteDatabase getDatabase() {
+ // open database if requested
if(database == null)
- throw new IllegalStateException("Database was not opened!");
+ openForWriting();
return database;
}
diff --git a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
index b28e296d1..b70d86d1b 100644
--- a/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
+++ b/astrid/common-src/com/todoroo/andlib/data/AbstractModel.java
@@ -99,6 +99,18 @@ public abstract class AbstractModel implements Parcelable {
setValues = null;
}
+ /**
+ * Transfers all set values into values. This occurs when a task is
+ * saved - future saves will not need to write all the data as before.
+ */
+ public void markSaved() {
+ if(values == null)
+ values = setValues;
+ else if(setValues != null)
+ values.putAll(setValues);
+ setValues = null;
+ }
+
/**
* Use merged values to compare two models to each other. Must be of
* exactly the same class.
@@ -176,6 +188,13 @@ public abstract class AbstractModel implements Parcelable {
setValues.put(ID_PROPERTY_NAME, id);
}
+ /**
+ * @return true if this model has found Jesus (i.e. the database)
+ */
+ public boolean isSaved() {
+ return getId() != NO_ID;
+ }
+
/**
* @param property
* @return true if setValues or values contains this property
@@ -228,6 +247,16 @@ public abstract class AbstractModel implements Parcelable {
saver.save(property, setValues, value);
}
+ /**
+ * Merges content values with those coming from another source
+ */
+ public synchronized void mergeWith(ContentValues other) {
+ if (setValues == null)
+ setValues = other;
+ else
+ setValues.putAll(other);
+ }
+
/**
* Clear the key for the given property
* @param property
@@ -332,7 +361,7 @@ public abstract class AbstractModel implements Parcelable {
protected static final class ModelCreator
implements Parcelable.Creator {
- private Class cls;
+ private final Class cls;
public ModelCreator(Class cls) {
super();
diff --git a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
index 7fe7b38f5..f2d3a53c2 100644
--- a/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
+++ b/astrid/common-src/com/todoroo/andlib/data/GenericDao.java
@@ -11,8 +11,8 @@ import java.lang.reflect.InvocationTargetException;
import android.content.ContentValues;
import android.database.Cursor;
-import com.todoroo.andlib.data.sql.Criterion;
-import com.todoroo.andlib.data.sql.Query;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Query;
@@ -24,7 +24,7 @@ import com.todoroo.andlib.data.sql.Query;
*/
public class GenericDao {
- private Class modelClass;
+ private final Class modelClass;
private Table table;
@@ -76,7 +76,7 @@ public class GenericDao {
* properties to read
* @param id
* id of item
- * @return
+ * @return null if no item found
*/
public TYPE fetch(long id, Property>... properties) {
TodorooCursor cursor = fetchItem(id, properties);
@@ -133,14 +133,14 @@ public class GenericDao {
*/
public boolean persist(AbstractModel item) {
if (item.getId() == AbstractModel.NO_ID) {
- return createItem(item);
+ return createNew(item);
} else {
ContentValues values = item.getSetValues();
if (values.size() == 0) // nothing changed
return true;
- return saveItem(item);
+ return saveExisting(item);
}
}
@@ -154,7 +154,7 @@ public class GenericDao {
* item model
* @return returns true on success.
*/
- public boolean createItem(AbstractModel item) {
+ public boolean createNew(AbstractModel item) {
long newRow = database.getDatabase().insert(table.name,
AbstractModel.ID_PROPERTY.name, item.getMergedValues());
item.setId(newRow);
@@ -162,7 +162,7 @@ public class GenericDao {
}
/**
- * Saves the given item.
+ * Saves the given item. Will not create a new item!
*
* @param database
* @param table
@@ -171,7 +171,7 @@ public class GenericDao {
* item model
* @return returns true on success.
*/
- public boolean saveItem(AbstractModel item) {
+ public boolean saveExisting(AbstractModel item) {
ContentValues values = item.getSetValues();
if(values.size() == 0) // nothing changed
return true;
diff --git a/astrid/common-src/com/todoroo/andlib/data/Property.java b/astrid/common-src/com/todoroo/andlib/data/Property.java
index ad51e5353..9a4665bf2 100644
--- a/astrid/common-src/com/todoroo/andlib/data/Property.java
+++ b/astrid/common-src/com/todoroo/andlib/data/Property.java
@@ -5,7 +5,7 @@
*/
package com.todoroo.andlib.data;
-import com.todoroo.andlib.data.sql.Field;
+import com.todoroo.andlib.sql.Field;
/**
* Property represents a typed column in a database.
diff --git a/astrid/common-src/com/todoroo/andlib/data/Table.java b/astrid/common-src/com/todoroo/andlib/data/Table.java
index ae8d59274..3d2cb8880 100644
--- a/astrid/common-src/com/todoroo/andlib/data/Table.java
+++ b/astrid/common-src/com/todoroo/andlib/data/Table.java
@@ -1,6 +1,7 @@
package com.todoroo.andlib.data;
-import com.todoroo.andlib.data.sql.Field;
+import com.todoroo.andlib.sql.Field;
+import com.todoroo.andlib.sql.SqlTable;
/**
* Table class. Most fields are final, so methods such as as will
@@ -9,7 +10,7 @@ import com.todoroo.andlib.data.sql.Field;
* @author Tim Su
*
*/
-public final class Table extends com.todoroo.andlib.data.sql.Table {
+public final class Table extends SqlTable {
public final String name;
public final Class extends AbstractModel> modelClass;
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/OrderType.java b/astrid/common-src/com/todoroo/andlib/data/sql/OrderType.java
deleted file mode 100644
index b3b8fa5af..000000000
--- a/astrid/common-src/com/todoroo/andlib/data/sql/OrderType.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.todoroo.andlib.data.sql;
-
-public enum OrderType {
- DESC, ASC
-}
diff --git a/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java b/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java
index f347bd371..feaa2a4c8 100644
--- a/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java
+++ b/astrid/common-src/com/todoroo/andlib/service/DependencyInjectionService.java
@@ -3,8 +3,6 @@ package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.Arrays;
-import android.util.Log;
-
/**
* Simple Dependency Injection Service for Android.
*
@@ -138,6 +136,5 @@ public class DependencyInjectionService {
*/
public synchronized void setInjectors(AbstractDependencyInjector[] injectors) {
this.injectors = injectors;
- Log.e("INJECTION SETTING", "Set Injector List to: " + Arrays.asList(injectors)); // (debug)
}
}
diff --git a/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java b/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java
index cb9ff8371..56e229eb7 100644
--- a/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java
+++ b/astrid/common-src/com/todoroo/andlib/service/NotificationManager.java
@@ -28,7 +28,7 @@ public interface NotificationManager {
*
*/
public static class AndroidNotificationManager implements NotificationManager {
- final android.app.NotificationManager nm;
+ private final android.app.NotificationManager nm;
public AndroidNotificationManager(Context context) {
nm = (android.app.NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Constants.java b/astrid/common-src/com/todoroo/andlib/sql/Constants.java
similarity index 85%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Constants.java
rename to astrid/common-src/com/todoroo/andlib/sql/Constants.java
index 4596aa34b..36e425eae 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Constants.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Constants.java
@@ -1,7 +1,7 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
@SuppressWarnings("nls")
-public class Constants {
+public final class Constants {
static final String SELECT = "SELECT";
static final String SPACE = " ";
static final String AS = "AS";
@@ -21,4 +21,5 @@ public class Constants {
static final String WHERE = "WHERE";
public static final String EXISTS = "EXISTS";
public static final String NOT = "NOT";
+ public static final String LIMIT = "LIMIT";
}
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Criterion.java b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java
similarity index 73%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Criterion.java
rename to astrid/common-src/com/todoroo/andlib/sql/Criterion.java
index bae70f44d..9de7d828b 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Criterion.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Criterion.java
@@ -1,12 +1,12 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.AND;
-import static com.todoroo.andlib.data.sql.Constants.EXISTS;
-import static com.todoroo.andlib.data.sql.Constants.LEFT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.NOT;
-import static com.todoroo.andlib.data.sql.Constants.OR;
-import static com.todoroo.andlib.data.sql.Constants.RIGHT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.AND;
+import static com.todoroo.andlib.sql.Constants.EXISTS;
+import static com.todoroo.andlib.sql.Constants.LEFT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.NOT;
+import static com.todoroo.andlib.sql.Constants.OR;
+import static com.todoroo.andlib.sql.Constants.RIGHT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public abstract class Criterion {
protected final Operator operator;
@@ -15,6 +15,13 @@ public abstract class Criterion {
this.operator = operator;
}
+ public static Criterion all = new Criterion(Operator.exists) {
+ @Override
+ protected void populate(StringBuilder sb) {
+ sb.append(true);
+ }
+ };
+
public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) {
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/DBObject.java b/astrid/common-src/com/todoroo/andlib/sql/DBObject.java
similarity index 88%
rename from astrid/common-src/com/todoroo/andlib/data/sql/DBObject.java
rename to astrid/common-src/com/todoroo/andlib/sql/DBObject.java
index 7837eb77f..7d27d70a6 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/DBObject.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/DBObject.java
@@ -1,7 +1,7 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.AS;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.AS;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public abstract class DBObject> implements Cloneable {
protected String alias;
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/EqCriterion.java b/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java
similarity index 76%
rename from astrid/common-src/com/todoroo/andlib/data/sql/EqCriterion.java
rename to astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java
index b2ec34cb8..99fc74ad2 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/EqCriterion.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/EqCriterion.java
@@ -1,4 +1,4 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
public class EqCriterion extends UnaryCriterion {
EqCriterion(Field field, Object value) {
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Field.java b/astrid/common-src/com/todoroo/andlib/sql/Field.java
similarity index 78%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Field.java
rename to astrid/common-src/com/todoroo/andlib/sql/Field.java
index 9111d9c25..5131ba000 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Field.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Field.java
@@ -1,11 +1,11 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.AND;
-import static com.todoroo.andlib.data.sql.Constants.BETWEEN;
-import static com.todoroo.andlib.data.sql.Constants.COMMA;
-import static com.todoroo.andlib.data.sql.Constants.LEFT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.RIGHT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.AND;
+import static com.todoroo.andlib.sql.Constants.BETWEEN;
+import static com.todoroo.andlib.sql.Constants.COMMA;
+import static com.todoroo.andlib.sql.Constants.LEFT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.RIGHT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public class Field extends DBObject {
@@ -18,10 +18,14 @@ public class Field extends DBObject {
}
public Criterion eq(Object value) {
+ if(value == null)
+ return UnaryCriterion.isNull(this);
return UnaryCriterion.eq(this, value);
}
public Criterion neq(Object value) {
+ if(value == null)
+ return UnaryCriterion.isNotNull(this);
return UnaryCriterion.neq(this, value);
}
diff --git a/astrid/common-src/com/todoroo/andlib/sql/Functions.java b/astrid/common-src/com/todoroo/andlib/sql/Functions.java
new file mode 100644
index 000000000..ac994dad8
--- /dev/null
+++ b/astrid/common-src/com/todoroo/andlib/sql/Functions.java
@@ -0,0 +1,15 @@
+package com.todoroo.andlib.sql;
+
+@SuppressWarnings("nls")
+public final class Functions {
+
+ public static String caseStatement(Criterion when, Object ifTrue, Object ifFalse) {
+ return new StringBuilder("CASE WHEN ").
+ append(when.toString()).append(" THEN ").append(value(ifTrue)).
+ append(" ELSE ").append(value(ifFalse)).append(" END").toString();
+ }
+
+ private static String value(Object value) {
+ return value.toString();
+ }
+}
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/GroupBy.java b/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java
similarity index 85%
rename from astrid/common-src/com/todoroo/andlib/data/sql/GroupBy.java
rename to astrid/common-src/com/todoroo/andlib/sql/GroupBy.java
index 103128411..c0d1c4290 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/GroupBy.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/GroupBy.java
@@ -1,4 +1,4 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
import java.util.List;
import java.util.ArrayList;
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Join.java b/astrid/common-src/com/todoroo/andlib/sql/Join.java
similarity index 56%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Join.java
rename to astrid/common-src/com/todoroo/andlib/sql/Join.java
index f3809dba9..382c1253f 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Join.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Join.java
@@ -1,33 +1,33 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.JOIN;
-import static com.todoroo.andlib.data.sql.Constants.ON;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.JOIN;
+import static com.todoroo.andlib.sql.Constants.ON;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public class Join {
- private final Table joinTable;
+ private final SqlTable joinTable;
private final JoinType joinType;
private final Criterion[] criterions;
- private Join(Table table, JoinType joinType, Criterion... criterions) {
+ private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table;
this.joinType = joinType;
this.criterions = criterions;
}
- public static Join inner(Table expression, Criterion... criterions) {
+ public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions);
}
- public static Join left(Table table, Criterion... criterions) {
+ public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions);
}
- public static Join right(Table table, Criterion... criterions) {
+ public static Join right(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.RIGHT, criterions);
}
- public static Join out(Table table, Criterion... criterions) {
+ public static Join out(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.OUT, criterions);
}
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/JoinType.java b/astrid/common-src/com/todoroo/andlib/sql/JoinType.java
similarity index 56%
rename from astrid/common-src/com/todoroo/andlib/data/sql/JoinType.java
rename to astrid/common-src/com/todoroo/andlib/sql/JoinType.java
index 86cd8f2d3..c1cb7389b 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/JoinType.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/JoinType.java
@@ -1,4 +1,4 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
public enum JoinType {
INNER, LEFT, RIGHT, OUT
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Operator.java b/astrid/common-src/com/todoroo/andlib/sql/Operator.java
similarity index 92%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Operator.java
rename to astrid/common-src/com/todoroo/andlib/sql/Operator.java
index 55981e71a..5f97301c0 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Operator.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Operator.java
@@ -1,6 +1,6 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.SPACE;
import java.util.HashMap;
import java.util.Map;
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Order.java b/astrid/common-src/com/todoroo/andlib/sql/Order.java
similarity index 52%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Order.java
rename to astrid/common-src/com/todoroo/andlib/sql/Order.java
index 5bff6bf49..fc1049045 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Order.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Order.java
@@ -1,25 +1,25 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public class Order {
- private final Field expression;
+ private final Object expression;
private final OrderType orderType;
- private Order(Field expression) {
+ private Order(Object expression) {
this(expression, OrderType.ASC);
}
- private Order(Field expression, OrderType orderType) {
+ private Order(Object expression, OrderType orderType) {
this.expression = expression;
this.orderType = orderType;
}
- public static Order asc(Field expression) {
+ public static Order asc(Object expression) {
return new Order(expression);
}
- public static Order desc(Field expression) {
+ public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC);
}
diff --git a/astrid/common-src/com/todoroo/andlib/sql/OrderType.java b/astrid/common-src/com/todoroo/andlib/sql/OrderType.java
new file mode 100644
index 000000000..299c670c0
--- /dev/null
+++ b/astrid/common-src/com/todoroo/andlib/sql/OrderType.java
@@ -0,0 +1,5 @@
+package com.todoroo.andlib.sql;
+
+public enum OrderType {
+ DESC, ASC
+}
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Query.java b/astrid/common-src/com/todoroo/andlib/sql/Query.java
similarity index 63%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Query.java
rename to astrid/common-src/com/todoroo/andlib/sql/Query.java
index 7a3d4f7e6..9d4398911 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Query.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/Query.java
@@ -1,32 +1,32 @@
-package com.todoroo.andlib.data.sql;
-
-import static com.todoroo.andlib.data.sql.Constants.ALL;
-import static com.todoroo.andlib.data.sql.Constants.COMMA;
-import static com.todoroo.andlib.data.sql.Constants.FROM;
-import static com.todoroo.andlib.data.sql.Constants.GROUP_BY;
-import static com.todoroo.andlib.data.sql.Constants.LEFT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.ORDER_BY;
-import static com.todoroo.andlib.data.sql.Constants.RIGHT_PARENTHESIS;
-import static com.todoroo.andlib.data.sql.Constants.SELECT;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
-import static com.todoroo.andlib.data.sql.Constants.WHERE;
-import static com.todoroo.andlib.data.sql.Table.table;
+package com.todoroo.andlib.sql;
+
+import static com.todoroo.andlib.sql.Constants.ALL;
+import static com.todoroo.andlib.sql.Constants.COMMA;
+import static com.todoroo.andlib.sql.Constants.FROM;
+import static com.todoroo.andlib.sql.Constants.GROUP_BY;
+import static com.todoroo.andlib.sql.Constants.LEFT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.ORDER_BY;
+import static com.todoroo.andlib.sql.Constants.RIGHT_PARENTHESIS;
+import static com.todoroo.andlib.sql.Constants.SELECT;
+import static com.todoroo.andlib.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.WHERE;
+import static com.todoroo.andlib.sql.SqlTable.table;
import static java.util.Arrays.asList;
import java.util.ArrayList;
-import java.util.List;
import com.todoroo.andlib.data.Property;
-public class Query {
+public final class Query {
- private Table table;
- private List criterions = new ArrayList();
- private List fields = new ArrayList();
- private List joins = new ArrayList();
- private List groupBies = new ArrayList();
- private List orders = new ArrayList();
- private List havings = new ArrayList();
+ private SqlTable table;
+ private String queryTemplate = null;
+ private final ArrayList criterions = new ArrayList();
+ private final ArrayList fields = new ArrayList();
+ private final ArrayList joins = new ArrayList();
+ private final ArrayList groupBies = new ArrayList();
+ private final ArrayList orders = new ArrayList();
+ private final ArrayList havings = new ArrayList();
private Query(Field... fields) {
this.fields.addAll(asList(fields));
@@ -36,7 +36,7 @@ public class Query {
return new Query(fields);
}
- public Query from(Table fromTable) {
+ public Query from(SqlTable fromTable) {
this.table = fromTable;
return this;
}
@@ -81,10 +81,19 @@ public class Query {
StringBuilder sql = new StringBuilder();
visitSelectClause(sql);
visitFromClause(sql);
- visitJoinClause(sql);
- visitWhereClause(sql);
- visitGroupByClause(sql);
- visitOrderByClause(sql);
+
+ if(queryTemplate == null) {
+ visitJoinClause(sql);
+ visitWhereClause(sql);
+ visitGroupByClause(sql);
+ visitOrderByClause(sql);
+ } else {
+ if(joins.size() > 0 || groupBies.size() > 0 || orders.size() > 0 ||
+ havings.size() > 0)
+ throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
+ sql.append(queryTemplate);
+ }
+
return sql.toString();
}
@@ -154,7 +163,7 @@ public class Query {
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
- public Table as(String alias) {
+ public SqlTable as(String alias) {
return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias);
}
@@ -170,4 +179,14 @@ public class Query {
public Property>[] getFields() {
return fields.toArray(new Property>[fields.size()]);
}
+
+ /**
+ * Add the SQL query template (comes after the "from")
+ * @param sqlQuery
+ * @return
+ */
+ public Query withQueryTemplate(String template) {
+ queryTemplate = template;
+ return this;
+ }
}
diff --git a/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java b/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java
new file mode 100644
index 000000000..6251101a4
--- /dev/null
+++ b/astrid/common-src/com/todoroo/andlib/sql/QueryTemplate.java
@@ -0,0 +1,117 @@
+package com.todoroo.andlib.sql;
+
+import static com.todoroo.andlib.sql.Constants.COMMA;
+import static com.todoroo.andlib.sql.Constants.GROUP_BY;
+import static com.todoroo.andlib.sql.Constants.LIMIT;
+import static com.todoroo.andlib.sql.Constants.ORDER_BY;
+import static com.todoroo.andlib.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.WHERE;
+import static java.util.Arrays.asList;
+
+import java.util.ArrayList;
+
+/**
+ * Query Template returns a bunch of criteria that allows a query to be
+ * constructed
+ *
+ * @author Tim Su
+ *
+ */
+public final class QueryTemplate {
+
+ private final ArrayList criterions = new ArrayList();
+ private final ArrayList joins = new ArrayList();
+ private final ArrayList groupBies = new ArrayList();
+ private final ArrayList orders = new ArrayList();
+ private final ArrayList havings = new ArrayList();
+ private Integer limit = null;
+
+ public QueryTemplate join(Join... join) {
+ joins.addAll(asList(join));
+ return this;
+ }
+
+ public QueryTemplate where(Criterion criterion) {
+ criterions.add(criterion);
+ return this;
+ }
+
+ public QueryTemplate groupBy(Field... groupBy) {
+ groupBies.addAll(asList(groupBy));
+ return this;
+ }
+
+ public QueryTemplate orderBy(Order... order) {
+ orders.addAll(asList(order));
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sql = new StringBuilder();
+ visitJoinClause(sql);
+ visitWhereClause(sql);
+ visitGroupByClause(sql);
+ visitOrderByClause(sql);
+ if(limit != null)
+ sql.append(LIMIT).append(SPACE).append(limit);
+ return sql.toString();
+ }
+
+ private void visitOrderByClause(StringBuilder sql) {
+ if (orders.isEmpty()) {
+ return;
+ }
+ sql.append(ORDER_BY);
+ for (Order order : orders) {
+ sql.append(SPACE).append(order).append(COMMA);
+ }
+ sql.deleteCharAt(sql.length() - 1).append(SPACE);
+ }
+
+ @SuppressWarnings("nls")
+ private void visitGroupByClause(StringBuilder sql) {
+ if (groupBies.isEmpty()) {
+ return;
+ }
+ sql.append(GROUP_BY);
+ for (Field groupBy : groupBies) {
+ sql.append(SPACE).append(groupBy).append(COMMA);
+ }
+ sql.deleteCharAt(sql.length() - 1).append(SPACE);
+ if (havings.isEmpty()) {
+ return;
+ }
+ sql.append("HAVING");
+ for (Criterion havingCriterion : havings) {
+ sql.append(SPACE).append(havingCriterion).append(COMMA);
+ }
+ sql.deleteCharAt(sql.length() - 1).append(SPACE);
+ }
+
+ private void visitWhereClause(StringBuilder sql) {
+ if (criterions.isEmpty()) {
+ return;
+ }
+ sql.append(WHERE);
+ for (Criterion criterion : criterions) {
+ sql.append(SPACE).append(criterion).append(SPACE);
+ }
+ }
+
+ private void visitJoinClause(StringBuilder sql) {
+ for (Join join : joins) {
+ sql.append(join).append(SPACE);
+ }
+ }
+
+ public QueryTemplate having(Criterion criterion) {
+ this.havings.add(criterion);
+ return this;
+ }
+
+ public QueryTemplate limit(int limitValue) {
+ this.limit = limitValue;
+ return this;
+ }
+}
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/Table.java b/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java
similarity index 52%
rename from astrid/common-src/com/todoroo/andlib/data/sql/Table.java
rename to astrid/common-src/com/todoroo/andlib/sql/SqlTable.java
index ee04ae0df..07bc2e998 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/Table.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/SqlTable.java
@@ -1,13 +1,13 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-public class Table extends DBObject {
+public class SqlTable extends DBObject {
- protected Table(String expression) {
+ protected SqlTable(String expression) {
super(expression);
}
- public static Table table(String table) {
- return new Table(table);
+ public static SqlTable table(String table) {
+ return new SqlTable(table);
}
@SuppressWarnings("nls")
diff --git a/astrid/common-src/com/todoroo/andlib/data/sql/UnaryCriterion.java b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java
similarity index 93%
rename from astrid/common-src/com/todoroo/andlib/data/sql/UnaryCriterion.java
rename to astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java
index 387c8f36d..e76f90d03 100644
--- a/astrid/common-src/com/todoroo/andlib/data/sql/UnaryCriterion.java
+++ b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java
@@ -1,6 +1,6 @@
-package com.todoroo.andlib.data.sql;
+package com.todoroo.andlib.sql;
-import static com.todoroo.andlib.data.sql.Constants.SPACE;
+import static com.todoroo.andlib.sql.Constants.SPACE;
public class UnaryCriterion extends Criterion {
protected final Field expression;
diff --git a/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java b/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java
index c19380b5c..974977776 100644
--- a/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java
+++ b/astrid/common-src/com/todoroo/andlib/utility/AndroidUtilities.java
@@ -7,14 +7,11 @@ import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
@@ -110,4 +107,37 @@ public class AndroidUtilities {
}
}
+ /**
+ * Put an arbitrary object into a {@link ContentValues}
+ * @param target
+ * @param key
+ * @param value
+ */
+ public static void putInto(ContentValues target, String key, Object value) {
+ if(value instanceof String)
+ target.put(key, (String) value);
+ else if(value instanceof Long)
+ target.put(key, (Long) value);
+ else if(value instanceof Integer)
+ target.put(key, (Integer) value);
+ else if(value instanceof Double)
+ target.put(key, (Double) value);
+ else
+ throw new UnsupportedOperationException("Could not handle type " + //$NON-NLS-1$
+ value.getClass());
+ }
+
+ /**
+ * Return index of value in array
+ * @param array array to search
+ * @param value value to look for
+ * @return
+ */
+ public static int indexOf(TYPE[] array, TYPE value) {
+ for(int i = 0; i < array.length; i++)
+ if(array[i].equals(value))
+ return i;
+ return -1;
+ }
+
}
diff --git a/astrid/common-src/com/todoroo/andlib/utility/DateUtilities.java b/astrid/common-src/com/todoroo/andlib/utility/DateUtilities.java
index 39685bf13..bb370bca1 100644
--- a/astrid/common-src/com/todoroo/andlib/utility/DateUtilities.java
+++ b/astrid/common-src/com/todoroo/andlib/utility/DateUtilities.java
@@ -5,11 +5,15 @@
*/
package com.todoroo.andlib.utility;
+import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.Locale;
+import android.content.Context;
import android.content.res.Resources;
import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
@@ -21,6 +25,9 @@ public class DateUtilities {
@Autowired
public Integer monthsResource;
+ @Autowired
+ public Integer weeksResource;
+
@Autowired
public Integer daysResource;
@@ -50,81 +57,209 @@ public class DateUtilities {
}
/* ======================================================================
- * ============================================================ unix time
+ * ============================================================ long time
* ====================================================================== */
/** Convert unixtime into date */
- public static final Date unixtimeToDate(int seconds) {
- if(seconds == 0)
+ public static final Date unixtimeToDate(long millis) {
+ if(millis == 0)
return null;
- return new Date(seconds * 1000L);
+ return new Date(millis);
}
/** Convert date into unixtime */
- public static final int dateToUnixtime(Date date) {
+ public static final long dateToUnixtime(Date date) {
if(date == null)
return 0;
- return (int)(date.getTime() / 1000);
+ return date.getTime();
}
/** Returns unixtime for current time */
- public static final int now() {
- return (int) (System.currentTimeMillis() / 1000L);
+ public static final long now() {
+ return System.currentTimeMillis();
}
+ /** Represents a single hour */
+ public static long ONE_HOUR = 3600000L;
+
+ /** Represents a single day */
+ public static long ONE_DAY = 24 * ONE_HOUR;
+
+ /** Represents a single week */
+ public static long ONE_WEEK = 7 * ONE_DAY;
+
/* ======================================================================
* =========================================================== formatters
* ====================================================================== */
+ @SuppressWarnings("nls")
+ public static boolean is24HourFormat(Context context) {
+ String value = android.provider.Settings.System.getString(context.getContentResolver(),
+ android.provider.Settings.System.TIME_12_24);
+ boolean b24 = !(value == null || value.equals("12"));
+ return b24;
+ }
+
+ /**
+ * @return time format (hours and minutes)
+ */
+ public static SimpleDateFormat getTimeFormat(Context context) {
+ String value = getTimeFormatString(context);
+ return new SimpleDateFormat(value);
+ }
+
+ /**
+ * @return string used for time formatting
+ */
+ @SuppressWarnings("nls")
+ private static String getTimeFormatString(Context context) {
+ String value;
+ if (is24HourFormat(context)) {
+ value = "H:mm";
+ } else {
+ value = "h:mm a";
+ }
+ return value;
+ }
+
+ /**
+ * @return string used for date formatting
+ */
+ @SuppressWarnings("nls")
+ private static String getDateFormatString(Context context) {
+ String value = android.provider.Settings.System.getString(context.getContentResolver(),
+ android.provider.Settings.System.DATE_FORMAT);
+ if (value == null) {
+ // united states, you are special
+ if (Locale.US.equals(Locale.getDefault())
+ || Locale.CANADA.equals(Locale.getDefault()))
+ value = "MMM d yyyy";
+ else
+ value = "d MMM yyyy";
+ }
+ return value;
+ }
+
+ /**
+ * @return date format (month, day, year)
+ */
+ public static SimpleDateFormat getDateFormat(Context context) {
+ return new SimpleDateFormat(getDateFormatString(context));
+ }
+
+ /**
+ * @return date format as getDateFormat with weekday
+ */
+ @SuppressWarnings("nls")
+ public static SimpleDateFormat getDateFormatWithWeekday(Context context) {
+ return new SimpleDateFormat("EEE, " + getDateFormatString(context));
+
+ }
+
+ /**
+ * @return date with time at the end
+ */
+ @SuppressWarnings("nls")
+ public static SimpleDateFormat getDateWithTimeFormat(Context context) {
+ return new SimpleDateFormat(getDateFormatString(context) + " " +
+ getTimeFormatString(context));
+
+ }
+
+ /**
+ * @return formatted date (will contain month, day, year)
+ */
+ public static String getFormattedDate(Context context, Date date) {
+ return getDateFormat(context).format(date);
+ }
+
+
+ /* ======================================================================
+ * ============================================================= duration
+ * ====================================================================== */
+
/**
* Convenience method for dropping the preposition argument.
+ * @param duration in millis
+ * @param unitsToShow number of units to show (i.e. if 2, then 5 hours
+ * 3 minutes 2 seconds is truncated to 5 hours 3 minutes)
*/
- public String getDurationString(Resources r, int timeInSeconds,
- int unitsToShow) {
- return getDurationString(r, timeInSeconds, unitsToShow, false);
+ public String getDurationString(long duration, int unitsToShow) {
+ return getDurationString(duration, unitsToShow, false);
}
/**
* Format a time into the format: 5 days, 3 hours, 2 minutes
*
- * @param r Resources to get strings from
- * @param timeInSeconds
+ * @param duration in millis
* @param unitsToShow number of units to show (i.e. if 2, then 5 hours
* 3 minutes 2 seconds is truncated to 5 hours 3 minutes)
* @param withPreposition whether there is a preceding preposition
* @return
*/
- public String getDurationString(Resources r, int timeInSeconds,
- int unitsToShow, boolean withPreposition) {
- int years, months, days, hours, minutes, seconds;
+ public String getDurationString(long duration, int unitsToShow, boolean withPreposition) {
+ Resources r = ContextManager.getContext().getResources();
short unitsDisplayed = 0;
- timeInSeconds = Math.abs(timeInSeconds);
+ duration = Math.abs(duration);
- if(timeInSeconds == 0)
+ if(duration == 0)
return r.getQuantityString(secondsResource, 0, 0);
- Date now = new Date(80, 0, 1);
- Date then = unixtimeToDate((int)(now.getTime() / 1000L) + timeInSeconds);
+ Date now = new Date(80, 1, 1);
+ Date then = new Date(now.getTime() + duration);
+
+ int[] values = new int[] {
+ then.getYear() - now.getYear(),
+ then.getMonth() - now.getMonth(),
+ (then.getDate() - now.getDate())/7,
+ (then.getDate() - now.getDate()) - (then.getDate() - now.getDate())/7*7,
+ then.getHours() - now.getHours(),
+ then.getMinutes() - now.getMinutes(),
+ then.getSeconds() - now.getSeconds(),
+ };
+ int[] maxValues = new int[] {
+ Integer.MAX_VALUE,
+ 12,
+ 5,
+ 7,
+ 24,
+ 60,
+ 60
+ };
+
+ // perform rounding (this is definitely magic... trust the unit tests)
+ int cursor = 0;
+ while(values[cursor] == 0 && ++cursor < values.length)
+ ;
+ int postCursor = cursor + unitsToShow;
+ for(int i = values.length - 1; i >= postCursor; i--) {
+ if(values[i] >= maxValues[i]/2) {
+ values[i-1]++;
+ }
+ }
+ for(int i = Math.min(values.length, postCursor) - 1; i >= 1; i--) {
+ if(values[i] == maxValues[i]) {
+ values[i-1]++;
+ for(int j = i; j < values.length; j++)
+ values[j] = 0;
+ }
+ }
- years = then.getYear() - now.getYear();
- months = then.getMonth() - now.getMonth();
- days = then.getDate() - now.getDate();
- hours = then.getHours() - now.getHours();
- minutes = then.getMinutes() - now.getMinutes();
- seconds = then.getSeconds() - now.getSeconds();
StringBuilder result = new StringBuilder();
- unitsDisplayed = displayUnits(r, yearsResource, unitsToShow, years, months >= 6,
+ unitsDisplayed = displayUnits(r, yearsResource, unitsToShow, values[0],
+ unitsDisplayed, result);
+ unitsDisplayed = displayUnits(r, monthsResource, unitsToShow, values[1],
unitsDisplayed, result);
- unitsDisplayed = displayUnits(r, monthsResource, unitsToShow, months, days >= 15,
+ unitsDisplayed = displayUnits(r, weeksResource, unitsToShow, values[2],
unitsDisplayed, result);
- unitsDisplayed = displayUnits(r, daysResource, unitsToShow, days, hours >= 12,
+ unitsDisplayed = displayUnits(r, daysResource, unitsToShow, values[3],
unitsDisplayed, result);
- unitsDisplayed = displayUnits(r, hoursResource, unitsToShow, hours, minutes >= 30,
+ unitsDisplayed = displayUnits(r, hoursResource, unitsToShow, values[4],
unitsDisplayed, result);
- unitsDisplayed = displayUnits(r, minutesResource, unitsToShow, minutes, seconds >= 30,
+ unitsDisplayed = displayUnits(r, minutesResource, unitsToShow, values[5],
unitsDisplayed, result);
- unitsDisplayed = displayUnits(r, secondsResource, unitsToShow, seconds, false,
+ unitsDisplayed = displayUnits(r, secondsResource, unitsToShow, values[6],
unitsDisplayed, result);
return result.toString().trim();
@@ -132,11 +267,8 @@ public class DateUtilities {
/** Display units, rounding up if necessary. Returns units to show */
private short displayUnits(Resources r, int resource, int unitsToShow, int value,
- boolean shouldRound, short unitsDisplayed, StringBuilder result) {
+ short unitsDisplayed, StringBuilder result) {
if(unitsDisplayed < unitsToShow && value > 0) {
- // round up if needed
- if(unitsDisplayed + 1 == unitsToShow && shouldRound)
- value++;
result.append(r.getQuantityString(resource, value, value)).
append(' ');
unitsDisplayed++;
diff --git a/astrid/common-src/com/todoroo/andlib/utility/DialogUtilities.java b/astrid/common-src/com/todoroo/andlib/utility/DialogUtilities.java
index 32d5a94a5..8d6f3e4f6 100644
--- a/astrid/common-src/com/todoroo/andlib/utility/DialogUtilities.java
+++ b/astrid/common-src/com/todoroo/andlib/utility/DialogUtilities.java
@@ -15,6 +15,9 @@ public class DialogUtilities {
@Autowired
public Integer informationDialogTitleResource;
+ @Autowired
+ public Integer confirmDialogTitleResource;
+
public DialogUtilities() {
DependencyInjectionService.getInstance().inject(this);
}
@@ -32,7 +35,7 @@ public class DialogUtilities {
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
- .setTitle(informationDialogTitleResource)
+ .setTitle(confirmDialogTitleResource)
.setMessage(text)
.setView(view)
.setIcon(android.R.drawable.ic_dialog_alert)
diff --git a/astrid/default.properties b/astrid/default.properties
index c5d5335ee..a72fa071c 100644
--- a/astrid/default.properties
+++ b/astrid/default.properties
@@ -10,5 +10,5 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
-target=android-8
+target=Google Inc.:Google APIs:8
apk-configurations=
diff --git a/astrid/plugin-src/com/todoroo/astrid/alarms/Alarm.java b/astrid/plugin-src/com/todoroo/astrid/alarms/Alarm.java
index e9a39ab33..77f732acc 100644
--- a/astrid/plugin-src/com/todoroo/astrid/alarms/Alarm.java
+++ b/astrid/plugin-src/com/todoroo/astrid/alarms/Alarm.java
@@ -38,8 +38,8 @@ public class Alarm extends AbstractModel {
public static final LongProperty TASK = new LongProperty(
TABLE, "task");
- /** Alarm Time (seconds since epoch) */
- public static final IntegerProperty TIME = new IntegerProperty(
+ /** Alarm Time */
+ public static final LongProperty TIME = new LongProperty(
TABLE, "time");
/** Alarm Type (see constants) */
diff --git a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmsDatabase.java b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDatabase.java
similarity index 97%
rename from astrid/plugin-src/com/todoroo/astrid/alarms/AlarmsDatabase.java
rename to astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDatabase.java
index 667dcdb0e..9600358f1 100644
--- a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmsDatabase.java
+++ b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmDatabase.java
@@ -16,7 +16,7 @@ import com.todoroo.andlib.data.Table;
*
*/
@SuppressWarnings("nls")
-public class AlarmsDatabase extends AbstractDatabase {
+public class AlarmDatabase extends AbstractDatabase {
// --- constants
diff --git a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmService.java b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmService.java
index 015c7ba48..c90c13cf2 100644
--- a/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/alarms/AlarmService.java
@@ -1,8 +1,11 @@
package com.todoroo.astrid.alarms;
-import android.content.Context;
+import java.util.ArrayList;
+import com.todoroo.andlib.data.GenericDao;
+import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Query;
/**
* Provides operations for working with alerts
@@ -13,13 +16,44 @@ import com.todoroo.andlib.service.DependencyInjectionService;
@SuppressWarnings("nls")
public class AlarmService {
+ AlarmDatabase database = new AlarmDatabase();
+
+ GenericDao dao = new GenericDao(Alarm.class, database);
+
/**
* Metadata key for # of alarms
*/
public static final String ALARM_COUNT = "alarms-count";
- public AlarmService(@SuppressWarnings("unused") Context context) {
+ public AlarmService() {
DependencyInjectionService.getInstance().inject(this);
}
+
+ /**
+ * Return alarms for the given task
+ *
+ * @param taskId
+ */
+ public TodorooCursor getAlarms(long taskId) {
+ database.openForReading();
+ Query query = Query.select(Alarm.PROPERTIES).where(Alarm.TASK.eq(taskId));
+ return dao.query(query);
+ }
+
+ /**
+ * Save the given array of tags into the database
+ * @param taskId
+ * @param tags
+ */
+ public void synchronizeAlarms(long taskId, ArrayList alarms) {
+ database.openForWriting();
+ dao.deleteWhere(Alarm.TASK.eq(taskId));
+
+ for(Alarm alarm : alarms) {
+ alarm.setId(Alarm.NO_ID);
+ alarm.setValue(Alarm.TASK, taskId);
+ dao.saveExisting(alarm);
+ }
+ }
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/filters/CoreFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/filters/CoreFilterExposer.java
new file mode 100644
index 000000000..2b6e3e8e0
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/filters/CoreFilterExposer.java
@@ -0,0 +1,80 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.filters;
+
+import android.content.BroadcastReceiver;
+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.Functions;
+import com.todoroo.andlib.sql.Order;
+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.api.SearchFilter;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
+import com.todoroo.astrid.model.Task;
+
+/**
+ * Exposes Astrid's built in filters to the {@link FilterListActivity}
+ *
+ * @author Tim Su
+ *
+ */
+public final class CoreFilterExposer extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Resources r = context.getResources();
+
+ // build filters
+ Filter inbox = buildInboxFilter(r);
+
+ Filter all = new Filter(CorePlugin.IDENTIFIER, r.getString(R.string.BFE_All),
+ r.getString(R.string.BFE_All),
+ new QueryTemplate().where(Criterion.not(TaskCriteria.isDeleted())).
+ orderBy(Order.desc(Task.MODIFICATION_DATE)),
+ null);
+ all.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_all)).getBitmap();
+
+ SearchFilter searchFilter = new SearchFilter(CorePlugin.IDENTIFIER,
+ "Search");
+ searchFilter.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_search)).getBitmap();
+
+ // transmit filter list
+ FilterListItem[] list = new FilterListItem[3];
+ list[0] = inbox;
+ list[1] = all;
+ list[2] = searchFilter;
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+ /**
+ * Build inbox filter
+ * @return
+ */
+ @SuppressWarnings("nls")
+ public static Filter buildInboxFilter(Resources r) {
+ Filter inbox = new Filter(CorePlugin.IDENTIFIER, r.getString(R.string.BFE_Inbox),
+ r.getString(R.string.BFE_Inbox_title),
+ new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
+ TaskCriteria.isVisible(DateUtilities.now()))).orderBy(
+ Order.asc(Functions.caseStatement(Task.DUE_DATE.eq(0),
+ String.format("(%d + 1000000 * %s)", DateUtilities.now(), Task.IMPORTANCE),
+ String.format("(%s + 1000000 * %s)", Task.DUE_DATE, Task.IMPORTANCE)))),
+ null);
+ inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_inbox)).getBitmap();
+ return inbox;
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/filters/CorePlugin.java b/astrid/plugin-src/com/todoroo/astrid/filters/CorePlugin.java
new file mode 100644
index 000000000..8981f42c6
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/filters/CorePlugin.java
@@ -0,0 +1,24 @@
+package com.todoroo.astrid.filters;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Plugin;
+
+public class CorePlugin extends BroadcastReceiver {
+
+ static final String IDENTIFIER = "core";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Plugin plugin = new Plugin(IDENTIFIER, "Core Filters", "Todoroo",
+ "Provides 'Inbox' and 'All Tasks' Filters");
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedFilterExposer.java
new file mode 100644
index 000000000..d4f2ba567
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedFilterExposer.java
@@ -0,0 +1,89 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.filters;
+
+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.Order;
+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.FilterListHeader;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
+import com.todoroo.astrid.model.Task;
+
+/**
+ * Exposes Astrid's built in filters to the {@link FilterListActivity}
+ *
+ * @author Tim Su
+ *
+ */
+public final class ExtendedFilterExposer extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Resources r = context.getResources();
+
+ // build filters
+ FilterListHeader header = new FilterListHeader(ExtendedPlugin.IDENTIFIER,
+ "Extended");
+
+ Filter alphabetical = new Filter(ExtendedPlugin.IDENTIFIER,
+ "Alphabetical",
+ "Alphabetical",
+ new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
+ TaskCriteria.isVisible(DateUtilities.now()))).
+ orderBy(Order.asc(Task.TITLE)),
+ null);
+ alphabetical.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_alpha)).getBitmap();
+
+ Filter recent = new Filter(ExtendedPlugin.IDENTIFIER,
+ "Recently Modified",
+ "Recently Modified",
+ new QueryTemplate().orderBy(Order.desc(Task.MODIFICATION_DATE)).limit(15),
+ null);
+ recent.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_recent)).getBitmap();
+
+ ContentValues hiddenValues = new ContentValues();
+ hiddenValues.put(Task.HIDE_UNTIL.name, DateUtilities.now() + DateUtilities.ONE_DAY);
+ Filter hidden = new Filter(ExtendedPlugin.IDENTIFIER,
+ "Hidden Tasks",
+ "Hidden Tasks",
+ new QueryTemplate().where(Criterion.and(TaskCriteria.isActive(),
+ Criterion.not(TaskCriteria.isVisible(DateUtilities.now())))).
+ orderBy(Order.asc(Task.HIDE_UNTIL)),
+ hiddenValues);
+ hidden.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_hidden)).getBitmap();
+
+ Filter deleted = new Filter(ExtendedPlugin.IDENTIFIER,
+ "Deleted Tasks",
+ "Deleted Tasks",
+ new QueryTemplate().where(TaskCriteria.isDeleted()).
+ orderBy(Order.desc(Task.DELETION_DATE)),
+ null);
+ deleted.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_deleted)).getBitmap();
+
+ // transmit filter list
+ FilterListItem[] list = new FilterListItem[5];
+ list[0] = header;
+ list[1] = alphabetical;
+ list[2] = recent;
+ list[3] = hidden;
+ list[4] = deleted;
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedPlugin.java b/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedPlugin.java
new file mode 100644
index 000000000..b942550ed
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/filters/ExtendedPlugin.java
@@ -0,0 +1,24 @@
+package com.todoroo.astrid.filters;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Plugin;
+
+public class ExtendedPlugin extends BroadcastReceiver {
+
+ static final String IDENTIFIER = "extended";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Plugin plugin = new Plugin(IDENTIFIER, "Extended Filters", "Todoroo",
+ "Provides extended filters for viewing subsets of your tasks");
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskListNotify.java b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java
similarity index 54%
rename from astrid/src-legacy/com/timsu/astrid/activities/TaskListNotify.java
rename to astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java
index 75fbb2b6b..1320eb72d 100644
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskListNotify.java
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/NotificationActivity.java
@@ -17,11 +17,18 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-package com.timsu.astrid.activities;
+package com.todoroo.astrid.reminders;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.widget.Toast;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.sql.QueryTemplate;
+import com.todoroo.astrid.activity.TaskListActivity;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
/**
* This activity is launched when a user opens up a notification from the
@@ -30,7 +37,14 @@ import android.os.Bundle;
* @author timsu
*
*/
-public class TaskListNotify extends Activity {
+public class NotificationActivity extends Activity {
+
+ // --- constants
+
+ /** task id from notification */
+ public static final String TOKEN_ID = "id"; //$NON-NLS-1$
+
+ // --- implementation
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -47,10 +61,23 @@ public class TaskListNotify extends Activity {
}
private void launchTaskList(Intent intent) {
- Intent taskListIntent = new Intent(this, TaskList.class);
- taskListIntent.putExtra(TaskList.VARIABLES_TAG, intent.getExtras());
+ long id = intent.getLongExtra(TOKEN_ID, -1);
+ if(id == -1)
+ return;
+
+ Intent taskListIntent = new Intent(this, TaskListActivity.class);
+ Filter itemFilter = new Filter(ReminderPlugin.IDENTIFIER,
+ getString(R.string.rmd_NoA_filter),
+ getString(R.string.rmd_NoA_filter),
+ new QueryTemplate().where(TaskCriteria.byId(id)),
+ null);
+
+ taskListIntent.putExtra(TaskListActivity.TOKEN_FILTER, itemFilter);
startActivity(taskListIntent);
+ String reminder = Notifications.getRandomReminder(getResources().getStringArray(R.array.responses));
+ Toast.makeText(this, reminder, Toast.LENGTH_LONG).show();
+
finish();
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java
new file mode 100644
index 000000000..329e55b75
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java
@@ -0,0 +1,249 @@
+package com.todoroo.astrid.reminders;
+
+import java.util.Date;
+
+import android.app.Activity;
+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 android.graphics.Color;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.util.Log;
+
+import com.timsu.astrid.R;
+import com.timsu.astrid.data.task.TaskIdentifier;
+import com.timsu.astrid.utilities.Constants;
+import com.timsu.astrid.utilities.Preferences;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.service.NotificationManager;
+import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.AstridDependencyInjector;
+
+public class Notifications extends BroadcastReceiver {
+
+ // --- constants
+
+ /** task id extra */
+ static final String ID_KEY = "id"; //$NON-NLS-1$
+
+ /** notification type extra */
+ static final String TYPE_KEY = "type"; //$NON-NLS-1$
+
+ // --- instance variables
+
+ @Autowired
+ private TaskDao taskDao;
+
+ @Autowired
+ private ExceptionService exceptionService;
+
+ public static NotificationManager notificationManager = null;
+
+ // --- alarm handling
+
+ static {
+ AstridDependencyInjector.initialize();
+ }
+
+ public Notifications() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ @Override
+ /** Alarm intent */
+ public void onReceive(Context context, Intent intent) {
+ ContextManager.setContext(context);
+
+ long id = intent.getLongExtra(ID_KEY, 0);
+ int type = intent.getIntExtra(TYPE_KEY, (byte) 0);
+
+ Resources r = context.getResources();
+ String reminder;
+ if(type == ReminderService.TYPE_DUE || type == ReminderService.TYPE_OVERDUE)
+ reminder = getRandomReminder(r.getStringArray(R.array.reminders_due));
+ else if(type == ReminderService.TYPE_SNOOZE)
+ reminder = getRandomReminder(r.getStringArray(R.array.reminders_snooze));
+ else
+ reminder = getRandomReminder(r.getStringArray(R.array.reminders));
+
+ if(!showNotification(id, type, reminder)) {
+ notificationManager.cancel((int)id);
+ }
+ }
+
+ // --- notification creation
+
+ /** Clear notifications associated with this application */
+ public static void clearAllNotifications(Context context, TaskIdentifier taskId) {
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Activity.NOTIFICATION_SERVICE);
+ nm.cancel((int)taskId.getId());
+ }
+
+ /** @return a random reminder string */
+ static String getRandomReminder(String[] reminders) {
+ int next = ReminderService.random.nextInt(reminders.length);
+ String reminder = reminders[next];
+ return reminder;
+ }
+
+ /**
+ * Schedule a new notification about the given task. Returns false if there was
+ * some sort of error or the alarm should be disabled.
+ */
+ public boolean showNotification(long id, int type, String reminder) {
+ Context context = ContextManager.getContext();
+ if(notificationManager == null)
+ notificationManager = new AndroidNotificationManager(context);
+
+ Task task;
+ try {
+ task = taskDao.fetch(id, Task.TITLE, Task.HIDE_UNTIL, Task.COMPLETION_DATE,
+ Task.DELETION_DATE, Task.REMINDER_FLAGS);
+ if(task == null)
+ throw new IllegalArgumentException("cound not find item with id"); //$NON-NLS-1$
+
+ } catch (Exception e) {
+ exceptionService.reportError("show-notif", e); //$NON-NLS-1$
+ return false;
+ }
+
+ // you're done - don't sound, do delete
+ if(task.isCompleted() || task.isDeleted())
+ return false;
+
+ // it's hidden - don't sound, don't delete
+ if(task.isHidden() && type == ReminderService.TYPE_RANDOM)
+ return true;
+
+ // read properties
+ String taskTitle = task.getValue(Task.TITLE);
+ boolean nonstopMode = task.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_NONSTOP);
+
+ // update last reminder time
+ task.setValue(Task.REMINDER_LAST, DateUtilities.now());
+ taskDao.save(task, false);
+
+ // quiet hours? unless alarm clock
+ boolean quietHours = false;
+ Integer quietHoursStart = Preferences.getQuietHourStart(context);
+ Integer quietHoursEnd = Preferences.getQuietHourEnd(context);
+ if(quietHoursStart != null && quietHoursEnd != null && !nonstopMode) {
+ int hour = new Date().getHours();
+ if(quietHoursStart < quietHoursEnd) {
+ if(hour >= quietHoursStart && hour < quietHoursEnd)
+ quietHours = true;
+ } else { // wrap across 24/hour boundary
+ if(hour >= quietHoursStart || hour < quietHoursEnd)
+ quietHours = true;
+ }
+ }
+
+ Resources r = context.getResources();
+
+ Intent notifyIntent = new Intent(context, NotificationActivity.class);
+ notifyIntent.putExtra(NotificationActivity.TOKEN_ID, id);
+ notifyIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context,
+ (int)id, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
+
+ // set up properties (name and icon) for the notification
+ String appName = r.getString(R.string.app_name);
+ int icon;
+ switch(Preferences.getNotificationIconTheme(context)) {
+ case Preferences.ICON_SET_PINK:
+ icon = R.drawable.notif_pink_alarm;
+ break;
+ case Preferences.ICON_SET_BORING:
+ icon = R.drawable.notif_boring_alarm;
+ break;
+ default:
+ icon = R.drawable.notif_astrid;
+ }
+
+ // create notification object
+ Notification notification = new Notification(
+ icon, reminder, System.currentTimeMillis());
+ notification.setLatestEventInfo(context,
+ appName,
+ reminder + " " + taskTitle, //$NON-NLS-1$
+ pendingIntent);
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+ if(Preferences.isPersistenceMode(context)) {
+ notification.flags |= Notification.FLAG_NO_CLEAR |
+ Notification.FLAG_SHOW_LIGHTS;
+ notification.ledOffMS = 5000;
+ notification.ledOnMS = 700;
+ notification.ledARGB = Color.YELLOW;
+ }
+ else
+ notification.defaults = Notification.DEFAULT_LIGHTS;
+
+ AudioManager audioManager = (AudioManager)context.getSystemService(
+ Context.AUDIO_SERVICE);
+
+ // if non-stop mode is activated, set up the flags for insistent
+ // notification, and increase the volume to full volume, so the user
+ // will actually pay attention to the alarm
+ if(nonstopMode && (type != ReminderService.TYPE_RANDOM)) {
+ notification.flags |= Notification.FLAG_INSISTENT;
+ notification.audioStreamType = AudioManager.STREAM_ALARM;
+ audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
+ audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
+ } else {
+ notification.audioStreamType = AudioManager.STREAM_NOTIFICATION;
+ }
+
+ // quiet hours = no sound
+ if(quietHours) {
+ notification.sound = null;
+ } else {
+ Uri notificationSound = Preferences.getNotificationRingtone(context);
+ if(audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
+ notification.sound = null;
+ } else if(notificationSound != null &&
+ !notificationSound.toString().equals("")) { //$NON-NLS-1$
+ notification.sound = notificationSound;
+ } else {
+ notification.defaults |= Notification.DEFAULT_SOUND;
+ }
+ }
+
+ // quiet hours + periodic = no vibrate
+ if(quietHours && (type == ReminderService.TYPE_RANDOM)) {
+ notification.vibrate = null;
+ } else {
+ if (Preferences.shouldVibrate(context)
+ && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
+ notification.vibrate = new long[] {0, 1000, 500, 1000, 500, 1000};
+ } else {
+ notification.vibrate = null;
+ }
+ }
+
+ if(Constants.DEBUG)
+ Log.w("Astrid", "Logging notification: " + reminder); //$NON-NLS-1$ //$NON-NLS-2$
+
+ notificationManager.notify((int)id, notification);
+
+ return true;
+ }
+
+ // --- notification manager
+
+ public static void setNotificationManager(
+ NotificationManager notificationManager) {
+ Notifications.notificationManager = notificationManager;
+ }
+
+}
\ No newline at end of file
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPlugin.java b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPlugin.java
new file mode 100644
index 000000000..d4c1edd9e
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPlugin.java
@@ -0,0 +1,21 @@
+package com.todoroo.astrid.reminders;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class ReminderPlugin extends BroadcastReceiver {
+
+ static final String IDENTIFIER = "reminders"; //$NON-NLS-1$
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ /*Plugin plugin = new Plugin(IDENTIFIER, "Reminders", "Todoroo",
+ "Provides notification reminders for tasks");
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);*/
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPreferences.java b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPreferences.java
new file mode 100644
index 000000000..e16abe301
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderPreferences.java
@@ -0,0 +1,119 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.reminders;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.preference.Preference.OnPreferenceChangeListener;
+
+import com.timsu.astrid.R;
+
+/**
+ * Displays the preference screen for users to edit their preferences
+ *
+ * @author Tim Su
+ *
+ */
+public class ReminderPreferences extends PreferenceActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.preferences_reminders);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ initializePreference(screen);
+
+ }
+
+ private void initializePreference(Preference preference) {
+ if(preference instanceof PreferenceGroup) {
+ PreferenceGroup group = (PreferenceGroup)preference;
+ for(int i = 0; i < group.getPreferenceCount(); i++) {
+ initializePreference(group.getPreference(i));
+ }
+ } else {
+ Object value = null;
+ if(preference instanceof ListPreference)
+ value = ((ListPreference)preference).getValue();
+ else if(preference instanceof CheckBoxPreference)
+ value = ((CheckBoxPreference)preference).isChecked();
+ else if(preference instanceof EditTextPreference)
+ value = ((EditTextPreference)preference).getText();
+
+ if(value != null)
+ updatePreferences(preference, value);
+
+ preference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference myPreference, Object newValue) {
+ updatePreferences(myPreference, newValue);
+ return true;
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ protected int valueToIndex(String value, String[] array) {
+ for(int i = 0; i < array.length; i++)
+ if(array[i].equals(value))
+ return i;
+ return -1;
+ }
+
+ /**
+ *
+ * @param resource if null, updates all resources
+ */
+ protected void updatePreferences(Preference preference, Object value) {
+ Resources r = getResources();
+
+ if(r.getString(R.string.p_rmd_quietStart).equals(preference.getKey())) {
+ int index = valueToIndex((String)value, r.getStringArray(R.array.EPr_quiet_hours_start_values));
+ if(index == -1)
+ preference.setSummary(r.getString(R.string.rmd_EPr_quiet_hours_desc_none));
+ else {
+ String duration = r.getStringArray(R.array.EPr_quiet_hours_start)[index];
+ preference.setSummary(r.getString(R.string.rmd_EPr_quiet_hours_start_desc, duration));
+ }
+ } else if(r.getString(R.string.p_rmd_quietEnd).equals(preference.getKey())) {
+ int index = valueToIndex((String)value, r.getStringArray(R.array.EPr_quiet_hours_end_values));
+ if(index == -1)
+ preference.setSummary(r.getString(R.string.rmd_EPr_quiet_hours_desc_none));
+ else {
+ String duration = r.getStringArray(R.array.EPr_quiet_hours_end)[index];
+ preference.setSummary(r.getString(R.string.rmd_EPr_quiet_hours_end_desc, duration));
+ }
+ } else if(r.getString(R.string.p_rmd_ringtone).equals(preference.getKey())) {
+ if(value == null)
+ preference.setSummary(r.getString(R.string.rmd_EPr_ringtone_desc_default));
+ else
+ preference.setSummary(r.getString(R.string.rmd_EPr_ringtone_desc_custom));
+ } else if(r.getString(R.string.p_rmd_persistent).equals(preference.getKey())) {
+ if((Boolean)value)
+ preference.setSummary(r.getString(R.string.rmd_EPr_persistent_desc_true));
+ else
+ preference.setSummary(r.getString(R.string.rmd_EPr_persistent_desc_false));
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java
new file mode 100644
index 000000000..07b0566e7
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderService.java
@@ -0,0 +1,301 @@
+package com.todoroo.astrid.reminders;
+
+import java.util.Date;
+import java.util.Random;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.Property;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Query;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.utility.Constants;
+import com.todoroo.astrid.utility.Preferences;
+
+
+/**
+ * Data service for reminders
+ *
+ * @author Tim Su
+ *
+ */
+public final class ReminderService {
+
+ // --- constants
+
+ private static final Property>[] PROPERTIES = new Property>[] {
+ Task.DUE_DATE,
+ Task.REMINDER_FLAGS,
+ Task.REMINDER_PERIOD,
+ Task.REMINDER_LAST
+ };
+
+ /** flag for due date reminder */
+ static final int TYPE_DUE = 0;
+ /** flag for overdue reminder */
+ static final int TYPE_OVERDUE = 1;
+ /** flag for random reminder */
+ static final int TYPE_RANDOM = 2;
+ /** flag for a snoozed reminder */
+ static final int TYPE_SNOOZE = 3;
+
+ static final Random random = new Random();
+
+ // --- instance variables
+
+ @Autowired
+ private TaskDao taskDao;
+
+ private AlarmScheduler scheduler = new ReminderAlarmScheduler();
+
+ public ReminderService() {
+ DependencyInjectionService.getInstance().inject(this);
+ setPreferenceDefaults();
+ }
+
+ // --- preference handling
+
+ private static boolean preferencesInitialized = false;
+
+ /** Set preference defaults, if unset. called at startup */
+ public void setPreferenceDefaults() {
+ if(preferencesInitialized)
+ return;
+
+ Context context = ContextManager.getContext();
+ SharedPreferences prefs = Preferences.getPrefs(context);
+ Editor editor = prefs.edit();
+ Resources r = context.getResources();
+
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_quietStart, 22);
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_quietEnd, 10);
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_default_random_hours, 0);
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_time, 12);
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_nagging, true);
+ Preferences.setIfUnset(prefs, editor, r, R.string.p_rmd_persistent, true);
+
+ editor.commit();
+ preferencesInitialized = true;
+ }
+
+ // --- reminder scheduling logic
+
+ /**
+ * Schedules all alarms
+ */
+ public void scheduleAllAlarms() {
+
+ TodorooCursor cursor = getTasksWithReminders(PROPERTIES);
+ try {
+ Task task = new Task();
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ task.readFromCursor(cursor);
+ scheduleAlarm(task, false);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private static final long NO_ALARM = Long.MAX_VALUE;
+
+ /**
+ * Schedules alarms for a single task
+ * @param task
+ */
+ public void scheduleAlarm(Task task) {
+ scheduleAlarm(task, true);
+ }
+
+ /**
+ * Schedules alarms for a single task
+ *
+ * @param shouldPerformPropertyCheck
+ * whether to check if task has requisite properties
+ */
+ private void scheduleAlarm(Task task, boolean shouldPerformPropertyCheck) {
+ if(task == null || !task.isSaved())
+ return;
+
+ // read data if necessary
+ if(shouldPerformPropertyCheck) {
+ for(Property> property : PROPERTIES) {
+ if(!task.containsValue(property)) {
+ task = taskDao.fetch(task.getId(), PROPERTIES);
+ if(task == null)
+ return;
+ break;
+ }
+ }
+ }
+
+ // random reminders
+ long whenRandom = calculateNextRandomReminder(task);
+
+ // notifications at due date
+ long whenDueDate = calculateNextDueDateReminder(task);
+
+ // notifications after due date
+ long whenOverdue = calculateNextOverdueReminder(task);
+
+ if(whenRandom < whenDueDate && whenRandom < whenOverdue)
+ scheduler.createAlarm(task, whenRandom, TYPE_RANDOM);
+ else if(whenDueDate < whenOverdue)
+ scheduler.createAlarm(task, whenDueDate, TYPE_DUE);
+ else if(whenOverdue != NO_ALARM)
+ scheduler.createAlarm(task, whenOverdue, TYPE_OVERDUE);
+ }
+
+ /**
+ * Calculate the next alarm time for overdue reminders. If the due date
+ * has passed, we schedule a reminder some time in the next 4 - 24 hours.
+ *
+ * @param task
+ * @return
+ */
+ private long calculateNextOverdueReminder(Task task) {
+ if(task.hasDueDate() && task.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE)) {
+ long dueDate = task.getValue(Task.DUE_DATE);
+ if(dueDate > DateUtilities.now())
+ return NO_ALARM;
+ return DateUtilities.now() + (long)((4 + 20 * random.nextFloat()) * DateUtilities.ONE_HOUR);
+ }
+ return NO_ALARM;
+ }
+
+ /**
+ * Calculate the next alarm time for due date reminders. If the due date
+ * has not already passed, we return the due date, altering the time
+ * if the date was indicated to not have a due time
+ *
+ * @param task
+ * @return
+ */
+ private long calculateNextDueDateReminder(Task task) {
+ if(task.hasDueDate() && task.getFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AT_DEADLINE)) {
+ long dueDate = task.getValue(Task.DUE_DATE);
+ if(dueDate < DateUtilities.now())
+ return NO_ALARM;
+ else if(task.hasDueTime())
+ // return due date straight up
+ return dueDate;
+ else {
+ // return notification time on this day
+ Date date = new Date(dueDate);
+ date.setHours(Preferences.getIntegerFromString(R.string.p_rmd_time));
+ date.setMinutes(0);
+ return date.getTime();
+ }
+ }
+ return NO_ALARM;
+ }
+
+ /**
+ * Calculate the next alarm time for random reminders. We take the last
+ * random reminder time and add approximately the reminder period, until
+ * we get a time that's in the future.
+ *
+ * @param task
+ * @return
+ */
+ private long calculateNextRandomReminder(Task task) {
+ long reminderPeriod = task.getValue(Task.REMINDER_PERIOD);
+ if((reminderPeriod) > 0) {
+ long when = task.getValue(Task.REMINDER_LAST);
+
+ // get or make up a last notification time
+ if(when == 0) {
+ when = DateUtilities.now();
+ task.setValue(Task.REMINDER_LAST, when);
+ taskDao.save(task, false);
+ }
+
+ when += (long)(reminderPeriod * (0.85f + 0.3f * random.nextFloat()));
+ return when;
+ }
+ return NO_ALARM;
+ }
+
+ // --- alarm manager alarm creation
+
+ /**
+ * Interface for testing
+ */
+ public interface AlarmScheduler {
+ public void createAlarm(Task task, long time, int type);
+ }
+
+ public void setScheduler(AlarmScheduler scheduler) {
+ this.scheduler = scheduler;
+ }
+
+ public AlarmScheduler getScheduler() {
+ return scheduler;
+ }
+
+ private class ReminderAlarmScheduler implements AlarmScheduler {
+ /**
+ * Create an alarm for the given task at the given type
+ *
+ * @param task
+ * @param time
+ * @param type
+ * @param flags
+ */
+ @SuppressWarnings("nls")
+ public void createAlarm(Task task, long time, int type) {
+ if(time == 0 || time == NO_ALARM)
+ return;
+
+ if(time < DateUtilities.now()) {
+ time = DateUtilities.now() + (long)((0.5f +
+ 4 * random.nextFloat()) * DateUtilities.ONE_HOUR);
+ }
+
+ Context context = ContextManager.getContext();
+ Intent intent = new Intent(context, Notifications.class);
+ intent.setType(Long.toString(task.getId()));
+ intent.setAction(Integer.toString(type));
+ intent.putExtra(Notifications.ID_KEY, task.getId());
+ intent.putExtra(Notifications.TYPE_KEY, type);
+
+ AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
+ intent, 0);
+
+ if(Constants.DEBUG)
+ Log.e("Astrid", "Alarm (" + task.getId() + ", " + type +
+ ") set for " + new Date(time));
+ am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ // --- data fetching classes
+
+ /**
+ * Gets a listing of all tasks that are active &
+ * @param properties
+ * @return todoroo cursor. PLEASE CLOSE THIS CURSOR!
+ */
+ private TodorooCursor getTasksWithReminders(Property>... properties) {
+ return taskDao.query(Query.select(properties).where(Criterion.and(TaskCriteria.isActive(),
+ Task.REMINDER_FLAGS.gt(0))));
+ }
+
+
+}
\ No newline at end of file
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderStartupService.java b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderStartupService.java
new file mode 100644
index 000000000..6cdf78fe8
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/ReminderStartupService.java
@@ -0,0 +1,30 @@
+package com.todoroo.astrid.reminders;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.astrid.service.AstridDependencyInjector;
+
+/**
+ * Service which handles jobs that need to be run when phone boots
+ *
+ * @author Tim Su
+ *
+ */
+public class ReminderStartupService extends BroadcastReceiver {
+
+ static {
+ AstridDependencyInjector.initialize();
+ }
+
+ // --- system startup
+
+ @Override
+ /** Called when the system is started up */
+ public void onReceive(Context context, Intent intent) {
+ ContextManager.setContext(context);
+ new ReminderService().scheduleAllAlarms();
+ }
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
new file mode 100644
index 000000000..dcd08bcbd
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagDetailExposer.java
@@ -0,0 +1,50 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.tags;
+
+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.TaskDetail;
+
+/**
+ * Exposes {@link TaskDetail} for tags, i.e. "Tags: frogs, animals"
+ *
+ * @author Tim Su
+ *
+ */
+public class TagDetailExposer extends BroadcastReceiver {
+
+ private static TagService tagService = null;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // get tags associated with this task
+ long taskId = intent.getLongExtra(AstridApiConstants.EXTRAS_TASK_ID, -1);
+ if(taskId == -1)
+ return;
+
+ if(tagService == null)
+ tagService = new TagService();
+ String tagList = tagService.getTagsAsString(taskId);
+
+ if(tagList.length() == 0)
+ return;
+
+ TaskDetail taskDetail = new TaskDetail(TagsPlugin.IDENTIFIER,
+ context.getString(R.string.tag_TLA_detail, tagList));
+
+ // transmit
+ TaskDetail[] details = new TaskDetail[1];
+ details[0] = taskDetail;
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_DETAILS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, details);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java
new file mode 100644
index 000000000..ca0a96c24
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java
@@ -0,0 +1,104 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.tags;
+
+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.QueryTemplate;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterCategory;
+import com.todoroo.astrid.api.FilterListHeader;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.tags.TagService.Tag;
+
+/**
+ * Exposes filters based on tags
+ *
+ * @author Tim Su
+ *
+ */
+public class TagFilterExposer extends BroadcastReceiver {
+
+ private TagService tagService;
+
+ @SuppressWarnings("nls")
+ private Filter filterFromTag(Context context, Tag tag) {
+ String listTitle = context.getString(R.string.tag_FEx_tag_w_size).
+ replace("$T", tag.tag).replace("$C", Integer.toString(tag.count));
+ String title = context.getString(R.string.tag_FEx_name, tag.tag);
+ QueryTemplate tagTemplate = tag.queryTemplate();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(TagService.KEY, tag.tag);
+
+ Filter filter = new Filter(TagsPlugin.IDENTIFIER,
+ listTitle, title,
+ tagTemplate,
+ contentValues);
+
+// filters[0].contextMenuLabels = new String[] {
+// "Rename Tag",
+// "Delete Tag"
+// };
+// filters[0].contextMenuIntents = new Intent[] {
+// new Intent(),
+// new Intent()
+// };
+
+ return filter;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ tagService = new TagService();
+ Tag[] tagsByAlpha = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA);
+
+ // If user does not have any tags, don't show this section at all
+ if(tagsByAlpha.length == 0)
+ return;
+
+ Resources r = context.getResources();
+
+ Tag[] tagsBySize = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
+ Filter[] filtersByAlpha = new Filter[tagsByAlpha.length];
+ for(int i = 0; i < tagsByAlpha.length; i++)
+ filtersByAlpha[i] = filterFromTag(context, tagsByAlpha[i]);
+
+ Filter[] filtersBySize = new Filter[tagsBySize.length];
+ for(int i = 0; i < tagsBySize.length; i++)
+ filtersBySize[i] = filterFromTag(context, tagsBySize[i]);
+
+ FilterListHeader tagsHeader = new FilterListHeader(TagsPlugin.IDENTIFIER,
+ context.getString(R.string.tag_FEx_header));
+ Filter untagged = new Filter(TagsPlugin.IDENTIFIER,
+ "Untagged",
+ "Untagged",
+ tagService.untaggedTemplate(),
+ null);
+ untagged.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_untagged)).getBitmap();
+ FilterCategory tagsCategoryBySize = new FilterCategory(TagsPlugin.IDENTIFIER,
+ context.getString(R.string.tag_FEx_by_size), filtersBySize);
+ tagsCategoryBySize.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap();
+ FilterCategory tagsCategoryByAlpha = new FilterCategory(TagsPlugin.IDENTIFIER,
+ context.getString(R.string.tag_FEx_alpha), filtersByAlpha);
+ tagsCategoryByAlpha.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap();
+
+ // transmit filter list
+ FilterListItem[] list = new FilterListItem[4];
+ list[0] = tagsHeader;
+ list[1] = untagged;
+ list[2] = tagsCategoryBySize;
+ list[3] = tagsCategoryByAlpha;
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ITEMS, list);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
index 5ec0969a3..b4d8149bd 100644
--- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
@@ -2,19 +2,18 @@ package com.todoroo.astrid.tags;
import java.util.ArrayList;
-import android.content.Context;
-
-import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.CountProperty;
-import com.todoroo.andlib.data.sql.Criterion;
-import com.todoroo.andlib.data.sql.Join;
-import com.todoroo.andlib.data.sql.Order;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Join;
+import com.todoroo.andlib.sql.Order;
+import com.todoroo.andlib.sql.Query;
+import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
+import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.MetadataService;
@@ -39,7 +38,7 @@ public class TagService {
@Autowired
private MetadataService metadataService;
- public TagService(@SuppressWarnings("unused") Context context) {
+ public TagService() {
DependencyInjectionService.getInstance().inject(this);
}
@@ -57,7 +56,7 @@ public class TagService {
* @author Tim Su
*
*/
- public class Tag {
+ public final class Tag {
public String tag;
int count;
@@ -65,6 +64,25 @@ public class TagService {
public String toString() {
return tag;
}
+
+ /**
+ * Return SQL selector query for getting tasks with a given tag
+ *
+ * @param tag
+ * @return
+ */
+ public QueryTemplate queryTemplate() {
+ return new QueryTemplate().join(Join.inner(Metadata.TABLE,
+ Task.ID.eq(Metadata.TASK))).where(Criterion.and(
+ MetadataCriteria.withKey(KEY), Metadata.VALUE.eq(tag)));
+ }
+ }
+
+ public QueryTemplate untaggedTemplate() {
+ return new QueryTemplate().join(Join.left(Metadata.TABLE,
+ Task.ID.eq(Metadata.TASK))).where(Criterion.and(
+ TaskCriteria.isActive(), MetadataCriteria.withKey(KEY),
+ Metadata.VALUE.isNull()));
}
/**
@@ -94,7 +112,7 @@ public class TagService {
* Return tags on the given task
*
* @param taskId
- * @return empty array if no tags, otherwise array
+ * @return cursor. PLEASE CLOSE THE CURSOR!
*/
public TodorooCursor getTags(long taskId) {
Query query = Query.select(Metadata.VALUE).where(Criterion.and(MetadataCriteria.withKey(KEY),
@@ -111,30 +129,22 @@ public class TagService {
public String getTagsAsString(long taskId) {
StringBuilder tagBuilder = new StringBuilder();
TodorooCursor tags = getTags(taskId);
- int length = tags.getCount();
- Metadata metadata = new Metadata();
- for (int i = 0; i < length; i++) {
- tags.moveToNext();
- metadata.readFromCursor(tags);
- tagBuilder.append(metadata.getValue(Metadata.VALUE));
- if (i < length - 1)
- tagBuilder.append(", ");
+ try {
+ int length = tags.getCount();
+ Metadata metadata = new Metadata();
+ for (int i = 0; i < length; i++) {
+ tags.moveToNext();
+ metadata.readFromCursor(tags);
+ tagBuilder.append(metadata.getValue(Metadata.VALUE));
+ if (i < length - 1)
+ tagBuilder.append(", ");
+ }
+ } finally {
+ tags.close();
}
return tagBuilder.toString();
}
- /**
- * Return SQL selector query for getting tasks with a given tag
- *
- * @param tag
- * @return
- */
- public Query tasksWithTag(String tag, Property>... properties) {
- return Query.select(properties).join(Join.inner(Metadata.TABLE,
- Task.ID.eq(Metadata.TASK))).where(Criterion.and(
- MetadataCriteria.withKey(KEY), Metadata.VALUE.eq(tag)));
- }
-
/**
* Save the given array of tags into the database
* @param taskId
@@ -149,7 +159,7 @@ public class TagService {
metadata.setValue(Metadata.TASK, taskId);
for(String tag : tags) {
metadata.setValue(Metadata.VALUE, tag.trim());
- metadataDao.createItem(metadata);
+ metadataDao.createNew(metadata);
}
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java
new file mode 100644
index 000000000..b29a045c1
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java
@@ -0,0 +1,24 @@
+package com.todoroo.astrid.tags;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Plugin;
+
+public class TagsPlugin extends BroadcastReceiver {
+
+ static final String IDENTIFIER = "tags";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Plugin plugin = new Plugin(IDENTIFIER, "Tags", "Todoroo",
+ "Provides tagging support for tasks.");
+
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_PLUGINS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_PLUGIN, plugin);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+}
diff --git a/astrid/res/drawable/background_gradient.xml b/astrid/res/drawable/background_gradient.xml
new file mode 100644
index 000000000..21ad1b2eb
--- /dev/null
+++ b/astrid/res/drawable/background_gradient.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/astrid/res/drawable/black_white_gradient.xml b/astrid/res/drawable/black_white_gradient.xml
new file mode 100644
index 000000000..c468a2135
--- /dev/null
+++ b/astrid/res/drawable/black_white_gradient.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/astrid/res/drawable/btn_add.png b/astrid/res/drawable/btn_add.png
new file mode 100644
index 000000000..6332fefea
Binary files /dev/null and b/astrid/res/drawable/btn_add.png differ
diff --git a/astrid/res/drawable/btn_add_extended.png b/astrid/res/drawable/btn_add_extended.png
new file mode 100644
index 000000000..937bded9d
Binary files /dev/null and b/astrid/res/drawable/btn_add_extended.png differ
diff --git a/astrid/res/drawable/btn_check0.xml b/astrid/res/drawable/btn_check.xml
similarity index 96%
rename from astrid/res/drawable/btn_check0.xml
rename to astrid/res/drawable/btn_check.xml
index ec1956320..a6dcbb432 100644
--- a/astrid/res/drawable/btn_check0.xml
+++ b/astrid/res/drawable/btn_check.xml
@@ -27,7 +27,7 @@
+ android:drawable="@drawable/btn_check_off" />
diff --git a/astrid/res/drawable/btn_check50.xml b/astrid/res/drawable/btn_check50.xml
deleted file mode 100644
index a3bce13c9..000000000
--- a/astrid/res/drawable/btn_check50.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/drawable/btn_check75.xml b/astrid/res/drawable/btn_check75.xml
deleted file mode 100644
index f18054182..000000000
--- a/astrid/res/drawable/btn_check75.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/drawable/btn_check_0.png b/astrid/res/drawable/btn_check_0.png
deleted file mode 100644
index 8bd6db1f1..000000000
Binary files a/astrid/res/drawable/btn_check_0.png and /dev/null differ
diff --git a/astrid/res/drawable/btn_check_25.png b/astrid/res/drawable/btn_check_25.png
deleted file mode 100644
index 4108bfa3e..000000000
Binary files a/astrid/res/drawable/btn_check_25.png and /dev/null differ
diff --git a/astrid/res/drawable/btn_check_50.png b/astrid/res/drawable/btn_check_50.png
deleted file mode 100644
index 60363ca74..000000000
Binary files a/astrid/res/drawable/btn_check_50.png and /dev/null differ
diff --git a/astrid/res/drawable/btn_check_75.png b/astrid/res/drawable/btn_check_75.png
deleted file mode 100644
index 55795fc2f..000000000
Binary files a/astrid/res/drawable/btn_check_75.png and /dev/null differ
diff --git a/astrid/res/drawable/edit_header.png b/astrid/res/drawable/edit_header.png
new file mode 100644
index 000000000..91962ba25
Binary files /dev/null and b/astrid/res/drawable/edit_header.png differ
diff --git a/astrid/res/drawable/edit_header_red.png b/astrid/res/drawable/edit_header_red.png
new file mode 100644
index 000000000..8a13f3e4a
Binary files /dev/null and b/astrid/res/drawable/edit_header_red.png differ
diff --git a/astrid/res/drawable/edit_titlebar.png b/astrid/res/drawable/edit_titlebar.png
new file mode 100644
index 000000000..c3c971588
Binary files /dev/null and b/astrid/res/drawable/edit_titlebar.png differ
diff --git a/astrid/res/layout/edit_importance_item.xml b/astrid/res/drawable/expander_group.xml
similarity index 61%
rename from astrid/res/layout/edit_importance_item.xml
rename to astrid/res/drawable/expander_group.xml
index 9d25a7832..ef665d3ff 100644
--- a/astrid/res/layout/edit_importance_item.xml
+++ b/astrid/res/drawable/expander_group.xml
@@ -4,23 +4,17 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
-
-
\ No newline at end of file
+
+
+
+
diff --git a/astrid/res/drawable/expander_ic_maximized.9.png b/astrid/res/drawable/expander_ic_maximized.9.png
new file mode 100644
index 000000000..eb461e9d3
Binary files /dev/null and b/astrid/res/drawable/expander_ic_maximized.9.png differ
diff --git a/astrid/res/drawable/expander_ic_minimized.9.png b/astrid/res/drawable/expander_ic_minimized.9.png
new file mode 100644
index 000000000..e3cec8d1b
Binary files /dev/null and b/astrid/res/drawable/expander_ic_minimized.9.png differ
diff --git a/astrid/res/drawable/filter_all.png b/astrid/res/drawable/filter_all.png
new file mode 100644
index 000000000..3d09261a2
Binary files /dev/null and b/astrid/res/drawable/filter_all.png differ
diff --git a/astrid/res/drawable/filter_alpha.png b/astrid/res/drawable/filter_alpha.png
new file mode 100644
index 000000000..b7960db9d
Binary files /dev/null and b/astrid/res/drawable/filter_alpha.png differ
diff --git a/astrid/res/drawable/filter_deleted.png b/astrid/res/drawable/filter_deleted.png
new file mode 100644
index 000000000..1514d51a3
Binary files /dev/null and b/astrid/res/drawable/filter_deleted.png differ
diff --git a/astrid/res/drawable/filter_hidden.png b/astrid/res/drawable/filter_hidden.png
new file mode 100644
index 000000000..5bf8313c6
Binary files /dev/null and b/astrid/res/drawable/filter_hidden.png differ
diff --git a/astrid/res/drawable/filter_inbox.png b/astrid/res/drawable/filter_inbox.png
new file mode 100644
index 000000000..fed62219f
Binary files /dev/null and b/astrid/res/drawable/filter_inbox.png differ
diff --git a/astrid/res/drawable/filter_recent.png b/astrid/res/drawable/filter_recent.png
new file mode 100644
index 000000000..6a9bf0370
Binary files /dev/null and b/astrid/res/drawable/filter_recent.png differ
diff --git a/astrid/res/drawable/filter_search.png b/astrid/res/drawable/filter_search.png
new file mode 100644
index 000000000..cf3d97f75
Binary files /dev/null and b/astrid/res/drawable/filter_search.png differ
diff --git a/astrid/res/drawable/filter_tags1.png b/astrid/res/drawable/filter_tags1.png
new file mode 100644
index 000000000..454a59f30
Binary files /dev/null and b/astrid/res/drawable/filter_tags1.png differ
diff --git a/astrid/res/drawable/filter_tags2.png b/astrid/res/drawable/filter_tags2.png
new file mode 100644
index 000000000..ebaf0e874
Binary files /dev/null and b/astrid/res/drawable/filter_tags2.png differ
diff --git a/astrid/res/drawable/filter_untagged.png b/astrid/res/drawable/filter_untagged.png
new file mode 100644
index 000000000..c726d6794
Binary files /dev/null and b/astrid/res/drawable/filter_untagged.png differ
diff --git a/astrid/res/drawable/ic_dialog_alert_c.png b/astrid/res/drawable/ic_dialog_alert_c.png
deleted file mode 100644
index d0c6f2f39..000000000
Binary files a/astrid/res/drawable/ic_dialog_alert_c.png and /dev/null differ
diff --git a/astrid/res/drawable/ic_dialog_info_c.png b/astrid/res/drawable/ic_dialog_info_c.png
deleted file mode 100644
index a11458967..000000000
Binary files a/astrid/res/drawable/ic_dialog_info_c.png and /dev/null differ
diff --git a/astrid/res/drawable/ic_dialog_time_c.png b/astrid/res/drawable/ic_dialog_time_c.png
deleted file mode 100644
index 20b805ad1..000000000
Binary files a/astrid/res/drawable/ic_dialog_time_c.png and /dev/null differ
diff --git a/astrid/res/drawable/ic_tasklist_back.png b/astrid/res/drawable/ic_tasklist_back.png
new file mode 100644
index 000000000..0581857c2
Binary files /dev/null and b/astrid/res/drawable/ic_tasklist_back.png differ
diff --git a/astrid/res/drawable/ic_tasklist_back_pressed.png b/astrid/res/drawable/ic_tasklist_back_pressed.png
new file mode 100644
index 000000000..3f052d99f
Binary files /dev/null and b/astrid/res/drawable/ic_tasklist_back_pressed.png differ
diff --git a/astrid/res/drawable/ic_tasklist_back_selected.png b/astrid/res/drawable/ic_tasklist_back_selected.png
new file mode 100644
index 000000000..c9f2bebab
Binary files /dev/null and b/astrid/res/drawable/ic_tasklist_back_selected.png differ
diff --git a/astrid/res/drawable/icon.png b/astrid/res/drawable/icon.png
index fee7992e4..25402e7f6 100644
Binary files a/astrid/res/drawable/icon.png and b/astrid/res/drawable/icon.png differ
diff --git a/astrid/res/drawable/icon_add.png b/astrid/res/drawable/icon_add.png
deleted file mode 100644
index 98e065838..000000000
Binary files a/astrid/res/drawable/icon_add.png and /dev/null differ
diff --git a/astrid/res/drawable/icon_blank.png b/astrid/res/drawable/icon_blank.png
new file mode 100644
index 000000000..d490f6130
Binary files /dev/null and b/astrid/res/drawable/icon_blank.png differ
diff --git a/astrid/res/drawable/icon_tag.png b/astrid/res/drawable/icon_tag.png
deleted file mode 100644
index bf3e1969b..000000000
Binary files a/astrid/res/drawable/icon_tag.png and /dev/null differ
diff --git a/astrid/res/drawable/none.png b/astrid/res/drawable/none.png
new file mode 100644
index 000000000..3871ebb96
Binary files /dev/null and b/astrid/res/drawable/none.png differ
diff --git a/astrid/res/drawable/btn_check25.xml b/astrid/res/drawable/tasklist_back.xml
similarity index 54%
rename from astrid/res/drawable/btn_check25.xml
rename to astrid/res/drawable/tasklist_back.xml
index 3891015c0..3883b385f 100644
--- a/astrid/res/drawable/btn_check25.xml
+++ b/astrid/res/drawable/tasklist_back.xml
@@ -16,20 +16,11 @@
-
-
-
-
-
-
-
+
+
+
diff --git a/astrid/res/drawable/tea_tab_basic.png b/astrid/res/drawable/tea_tab_basic.png
new file mode 100644
index 000000000..8ef006005
Binary files /dev/null and b/astrid/res/drawable/tea_tab_basic.png differ
diff --git a/astrid/res/drawable/tea_tab_extensions.png b/astrid/res/drawable/tea_tab_extensions.png
new file mode 100644
index 000000000..8bad05dfc
Binary files /dev/null and b/astrid/res/drawable/tea_tab_extensions.png differ
diff --git a/astrid/res/drawable/tea_tab_extra.png b/astrid/res/drawable/tea_tab_extra.png
new file mode 100644
index 000000000..de1e16e4c
Binary files /dev/null and b/astrid/res/drawable/tea_tab_extra.png differ
diff --git a/astrid/res/layout/edit_alert_item.xml b/astrid/res/layout/edit_alert_item.xml
deleted file mode 100644
index 920e79fee..000000000
--- a/astrid/res/layout/edit_alert_item.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/layout/filter_list_activity.xml b/astrid/res/layout/filter_list_activity.xml
new file mode 100644
index 000000000..55d99b19f
--- /dev/null
+++ b/astrid/res/layout/filter_list_activity.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/layout/importance_spinner_dropdown.xml b/astrid/res/layout/importance_spinner_dropdown.xml
deleted file mode 100644
index 1d9e63458..000000000
--- a/astrid/res/layout/importance_spinner_dropdown.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
\ No newline at end of file
diff --git a/astrid/res/layout/main.xml b/astrid/res/layout/main.xml
deleted file mode 100644
index 2786e139a..000000000
--- a/astrid/res/layout/main.xml
+++ /dev/null
@@ -1,139 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/astrid/res/layout/notification_dialog.xml b/astrid/res/layout/notification_dialog.xml
deleted file mode 100644
index 68c8ae4bb..000000000
--- a/astrid/res/layout/notification_dialog.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/layout/edit_tag_item.xml b/astrid/res/layout/tag_edit_row.xml
similarity index 100%
rename from astrid/res/layout/edit_tag_item.xml
rename to astrid/res/layout/tag_edit_row.xml
diff --git a/astrid/res/layout/task_adapter_row.xml b/astrid/res/layout/task_adapter_row.xml
new file mode 100644
index 000000000..cd118e7f5
--- /dev/null
+++ b/astrid/res/layout/task_adapter_row.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/layout/task_edit.xml b/astrid/res/layout/task_edit.xml
deleted file mode 100644
index be3a3afde..000000000
--- a/astrid/res/layout/task_edit.xml
+++ /dev/null
@@ -1,410 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/astrid/res/layout/task_edit_activity.xml b/astrid/res/layout/task_edit_activity.xml
new file mode 100644
index 000000000..0046a5d4a
--- /dev/null
+++ b/astrid/res/layout/task_edit_activity.xml
@@ -0,0 +1,360 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/task_list_activity.xml b/astrid/res/layout/task_list_activity.xml
new file mode 100644
index 000000000..d587ec717
--- /dev/null
+++ b/astrid/res/layout/task_list_activity.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/layout/task_list_row.xml b/astrid/res/layout/task_list_row.xml
deleted file mode 100644
index bdce1ad58..000000000
--- a/astrid/res/layout/task_list_row.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/layout/task_notify.xml b/astrid/res/layout/task_notify.xml
deleted file mode 100644
index 14537c357..000000000
--- a/astrid/res/layout/task_notify.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/astrid/res/values/arrays.xml b/astrid/res/values/arrays.xml
index 48344f12d..0e8246a35 100644
--- a/astrid/res/values/arrays.xml
+++ b/astrid/res/values/arrays.xml
@@ -25,62 +25,58 @@
-->
-
-
-
- - Hi there! Have a sec?
- - Can I see you for a sec?
- - Have a few minutes?
- - Did you forget?
- - Excuse me!
- - When you have a minute:
- - On your agenda:
- - Free for a moment?
- - Astrid here!
- - Hi! Can I bug you?
- - A minute of your time?
- - It\'s a great day to
+
+
+
+
+
+ - No Urgency
+ - Today
+ - Tomorrow
+
+ - Next Week
+ - Next Month
+ - Specific Day
+ - Specific Day & Time
-
-
- - I\'ve got something for you!
- - Ready to put this in the past?
- - Why don\'t you get this done?
- - How about it? Ready tiger?
- - Ready to do this?
- - Can you handle this?
- - You can be happy! Just finish this!
- - I promise you\'ll feel better if you finish this!
- - Won\'t you do this today?
- - Please finish this, I\'m sick of it!
- - Can you finish this? Yes you can!
- - Are you ever going to do this?
- - Feel good about yourself! Let\'s go!
- - I\'m so proud of you! Lets get it done!
- - A little snack after you finish this?
- - Just this one task? Please?
- - Time to shorten your todo list!
+
+
+ - Don\'t hide
+ - Task is due
+ - Day before due
+ - Week before due
+ - Specific Day
-
-
- - Please tell me it isn\'t true that you\'re a procrastinator!
- - Doesn\'t being lazy get old sometimes?
- - Somewhere, someone is depending on you to finish this!
- - When you said postpone, you really meant \'I\'m doing this\', right?
- - This is the last time you postpone this, right?
- - Just finish this today, I won\'t tell anyone!
- - Why postpone when you can um... not postpone!
- - You\'ll finish this eventually, I presume?
- - I think you\'re really great! How about not putting this off?
- - Will you be able to achieve your goals if you do that?
- - Postpone, postpone, postpone. When will you change!
- - I\'ve had enough with your excuses! Just do it already!
- - Didn\'t you make that excuse last time?
- - I can\'t help you organize your life if you do that...
+
+
+
+
+ - !!!! (Highest)
+ - !!!
+ - !!
+ - ! (Lowest)
+
+
+ - No Urgency
+ - Today
+ - Tomorrow
+ - Day After Tomorrow
+ - Next Week
+ - Next Month
+
+
+
+
+ - Don\'t hide
+ - Task is due
+ - Day before due
+ - Week before due
+
+
- Pink
@@ -155,5 +151,5 @@
- 07:00
- 08:00
-
+
diff --git a/astrid/res/values/colors-legacy.xml b/astrid/res/values/colors.xml
similarity index 58%
rename from astrid/res/values/colors-legacy.xml
rename to astrid/res/values/colors.xml
index 9cf351f88..42220ddd1 100644
--- a/astrid/res/values/colors-legacy.xml
+++ b/astrid/res/values/colors.xml
@@ -1,24 +1,4 @@
-
#FFFB6666
#FFFFFFFF
diff --git a/astrid/res/values/keys.xml b/astrid/res/values/keys.xml
index 820f1269c..0bfccd1af 100644
--- a/astrid/res/values/keys.xml
+++ b/astrid/res/values/keys.xml
@@ -1,168 +1,185 @@
-
-
-
- sync_rtm
- sync_every
- sync_freq
- sync_button
- sync_dialogs
- sync_bgwifi
-
- notif_qstart
- notif_qend
-
- notif_default_reminder
-
- notif_annoy
-
- notif_vibrate
-
- notification_ringtone
-
- notif_theme
-
- colorize
-
- font_size
-
- nagging
-
- deadline_time
-
- backup
-
- default_calendar_id
- 1
-
- titleVisible
- true
- deadlineVisible
- true
- timeVisible
- true
- importanceVisible
- true
- tagsVisible
- true
- repeatVisible
- true
- reminderVisible
- false
- notesVisible
- false
-
-
-
- - 0
- - 1
- - 2
-
-
-
-
- - 0
- - 1800
- - 3600
- - 43200
- - 86400
- - 302400
- - 604800
-
-
-
-
-
- - 20
- - 21
- - 22
- - 23
- - 00
- - 01
- - 02
- - 03
- - 04
- - 05
- - 06
- - 07
- - 08
- - 09
- - 10
- - 11
- - 12
- - 13
- - 14
- - 15
- - 16
- - 17
- - 18
- - 19
-
-
-
-
- - 09
- - 10
- - 11
- - 12
- - 13
- - 14
- - 15
- - 16
- - 17
- - 18
- - 19
- - 20
- - 21
- - 22
- - 23
- - 00
- - 01
- - 02
- - 03
- - 04
- - 05
- - 06
- - 07
- - 08
-
-
-
-
- - 14
- - 16
- - 18
- - 20
- - 22
- - 24
- - 26
- - 28
- - 30
- - 32
- - 36
- - 48
-
+
+
+
+ sync_rtm
+ sync_freq
+ sync_button
+ sync_dialogs
+ sync_bgwifi
+
+
+
+ - 0
+ - 1800
+ - 3600
+ - 43200
+ - 86400
+ - 302400
+ - 604800
+
+
+
+
+
+ notif_qstart
+
+
+ notif_qend
+
+
+ notif_annoy
+
+
+ notif_vibrate
+
+
+ notification_ringtone
+
+
+ notif_theme
+
+
+ nagging
+
+
+ reminder_time
+
+
+ notif_default_reminder
+
+
+
+ - 0
+ - 1
+ - 2
+
+
+
+
+
+ - 20
+ - 21
+ - 22
+ - 23
+ - 00
+ - 01
+ - 02
+ - 03
+ - 04
+ - 05
+ - 06
+ - 07
+ - 08
+ - 09
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+ - 17
+ - 18
+ - 19
+
+
+
+
+ - 09
+ - 10
+ - 11
+ - 12
+ - 13
+ - 14
+ - 15
+ - 16
+ - 17
+ - 18
+ - 19
+ - 20
+ - 21
+ - 22
+ - 23
+ - 00
+ - 01
+ - 02
+ - 03
+ - 04
+ - 05
+ - 06
+ - 07
+ - 08
+
+
+
+
+ colorize
+ font_size
+
+
+
+ - 14
+ - 16
+ - 18
+ - 20
+ - 22
+ - 24
+ - 26
+ - 28
+ - 30
+ - 32
+ - 36
+ - 48
+
+
+
+
+
+ p_def_urg
+
+
+
+ - 0
+ - 1
+ - 2
+ - 3
+
+
+
+
+ - 0
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+
+
+
+
+ - 0
+ - 1
+ - 2
+ - 3
+
+
+
+ p_def_imp
+
+
+ p_def_hide
+
+
+
+ backup
+ default_calendar_id
+ 1
diff --git a/astrid/res/values/strings-3.0.xml b/astrid/res/values/strings-3.0.xml
index 0acdf14a0..3fd9ad16e 100644
--- a/astrid/res/values/strings-3.0.xml
+++ b/astrid/res/values/strings-3.0.xml
@@ -2,221 +2,222 @@
-
+
- Astrid Tasks
-
-
- Astrid Permission
-
-
- read tasks, display task filters
-
-
- Astrid Permission
-
-
- create new tasks, edit existing tasks
-
-
-
-
-
- - 1 Year
- - %d Years
-
-
-
- - 1 Month
- - %d Months
-
-
-
- - 1 Day
- - %d Days
-
-
-
- - 1 Hour
- - %d Hours
-
-
-
- - 1 Minute
- - %d Minutes
-
-
-
- - 1 Second
- - %d Seconds
-
-
-
- - 1 Hr
- - %d Hrs
-
-
-
- - 1 Min
- - %d Min
-
-
-
- - 1 Sec
- - %d Sec
-
-
-
-
-
- Confirm?
-
-
- Question:
-
-
- Information
-
-
- Yes
-
-
- No
-
-
- Close
+ Astrid Tasks
-
- Communicating with Server...
-
-
- Oops, looks like some trouble occurred! Here\'s what happened:\n\n%s
-
-
- Delete this task?
-
-
- Next
-
-
- Previous
-
-
- Done
-
-
- Cancel
-
-
-
-
- Click To Set
-
-
- $D $T
-
-
- Disable
-
-
-
-
- Astrid: %s
-
-
- You Have No Tasks!
-
-
- Add Task
-
-
- Plugins
-
-
- Settings
-
-
- Help
-
-
- Search Within This Filter
-
-
- Search Within This Filter
-
-
- Add to this list
-
-
- Help: Tasks Page
-This page displays all of your tasks belonging to the filter you have chosen.
-The back button at the top of the screen lets you pick a different filter
-to display.
-
-You can use the quick-add widget at the bottom of the screen to add tasks
-without going to the edit page.
-
-uh... that\'s it for help for today.
- ]]>
-
-
-
-
- %s [hidden]
-
-
- loading
-
-
- $D
-
-
- $D at $T
-
-
- Finished %s
-
-
- ... loading ...
-
-
- Edit Task
+
+ Astrid Permission
+
+
+ read tasks, display task filters
+
+
+ Astrid Permission
+
+
+ create new tasks, edit existing tasks
+
+
+
+
+
+ - 1 Year
+ - %d Years
+
+
+
+ - 1 Month
+ - %d Months
+
+
+
+ - 1 Week
+ - %d Weeks
+
+
+
+ - 1 Day
+ - %d Days
+
+
+
+ - 1 Hour
+ - %d Hours
+
+
+
+ - 1 Minute
+ - %d Minutes
+
+
+
+ - 1 Second
+ - %d Seconds
+
+
+
+ - 1 Hr
+ - %d Hrs
+
+
+
+ - 1 Min
+ - %d Min
+
+
+
+ - 1 Sec
+ - %d Sec
+
+
+
+
+
+ Confirm?
+
+
+ Question:
+
+
+ Information
+
+
+ Yes
+
+
+ No
+
+
+ Close
+
+
+ Communicating with Server...
+
+
+ Oops, looks like some trouble occurred! Here\'s what happened:\n\n%s
+
+
+ Delete this task?
+
+
+ Next
+
+
+ Previous
+
+
+ Done
+
+
+ Cancel
+
+
+ Please wait...
+
+
+
+
+ Click To Set
+
+
+ $D $T
+
+
+ Disable
+
+
+
+
+ No Tasks!
+
+
+ Add-ons
+
+
+ Settings
+
+
+ Help
+
+
+ Search Within This Filter
+
+
+ Search Within This Filter
+
+
+ Add to this list...
+
+
+ Help: Task Listing
+bar at the top: shows the name of this filter, and a button to go back
+to your list of filters.
+
+middle: shows all your tasks.
+
+bottom: quick add tasks. left button: add straight to the list. right
+button: add task & go to the edit page.
+ ]]>
+
+
+
+
+ %s [hidden]
+
+
+ %s [deleted]
+
+
+
+
+
+ Finished %s
+
+
+ Loading...
+
+
+ Edit Task
-
- Delete Task
-
-
+
+ Delete Task
+
+
-
- Astrid: Filters
-
-
- Please Wait...
-
-
- Create Shortcut On Desktop
-
-
- Search...
-
-
- Help
-
-
- Create Shortcut:
-
-
- Search For Tasks
-
-
- Search For Tasks
-
-
-
+ Astrid: Filters
+
+
+ Loading Filters...
+
+
+ Create Shortcut On Desktop
+
+
+ Search Tasks...
+
+
+ Help
+
+
+ Create Shortcut
+
+
+ Name of shortcut:
+
+
+ Search For Tasks
+
+
+ Matching \'%s\'
+
+
+ Help: Filters Page
On the Filters page you can see all of the filters that you can use to
-organize your tasks. Some Astrid Plugins will add new filters to this
+organize your tasks. Some Astrid Add-ons will add new filters to this
list.
You can turn any filter into a shortcut on your desktop by long-pressing
@@ -227,113 +228,167 @@ are using.
If you are having trouble with a particular plugin, please report errors
to the plugin creator for fastest service.
- ]]>
-
-
- Created Shortcut: %s
-
-
-
-
- Astrid: Editing
-
-
- Astrid: New Task
-
-
- Summary
-
-
- Task Name
-
-
- How important is it?
-
-
- When must you finish it?
-
-
- Hide Until This Date
-
-
- Plugins
-
-
- Save
-
-
- Don\'t Save
+ ]]>
+
+
+ Created Shortcut: %s
+
+
+
+
+ Astrid: Editing \'%s\'
+
+
+ Astrid: New Task
+
+
+ Basic
+
+
+ Extra
+
+
+ Add-ons
+
+
+ What to do?
+
+
+ Task Summary
+
+
+ How Important?
+
+
+ When to Start?
+
+
+ Hide Until?
+
+
+ Notes:
+
+
+ Enter Task Notes...
+
+
+ How Long Will it Take?
+
+
+ Time Already Spent on Task
+
+
+ Calendar Integration:
+
+
+ Create Calendar Event
+
+
+ Open Calendar Event
+
+
+ Repeat Every
+
+
+ No Repeat Set
-
- Save Changes
-
-
- Don\'t Save
-
-
- Delete Task
-
-
- Task Saved: due in %s
-
-
- Task Saved: due %s ago
-
-
- Task Saved
-
-
- Task Editing Was Abandoned
-
-
+
+ Tags:
+
+
+ Tag Name
-
- Astrid: New Reminder
-
-
-
-
- Welcome to Astrid!
-
-
- I Agree
-
-
- I Disagree
-
-
- Start Using Todoroo
-
-
-
-
- E-mail Todoroo
-
-
-
-
- What\'s New In Astrid?
-
-
-
-
- Astrid: Preferences
+
+ Repeat Every (0 to disable)
+ Help: Astrid Repeats
+
+To use repeats, set at least one of the deadlines above. When you complete this task, the deadline will be automatically advanced.
+\n\n
+If you don\'t want to see the new task right after you complete the old one, you should use the \"Hide Until\" field, which will also be advanced automatically.
+\n
+
+ Don\'t Show Help Anymore
+
+
+ Save
+
+
+ Cancel
+
+
+ Delete
-
- New Task Defaults
+
+ Save Changes
+
+
+ Don\'t Save
+
+
+ Delete Task
+
+
+ Task Saved: due in %s
+
+
+ Task Saved: due %s ago
+
+
+ Task Saved
+
+
+ Task Editing Was Canceled
+
+
+ Task Deleted!
+
+
+
+
+ Astrid: New Reminder
+
+
+
+
+ Welcome to Astrid!
+
+
+ I Agree!!
+
+
+ I Disagree
+
+
+
+
+ E-mail Todoroo
-
- Default Urgency
-
- Currently Set To: %s
-
- p_durg
+
+
+
+ What\'s New In Astrid?
+
+
-
- Default Importance
-
- Currently Set To: %s
-
- p_dimp
-
+
+ Astrid: Preferences
+
+
+ New Task Defaults
+
+
+ Default Urgency
+
+ Currently Set To: %s
+
+
+ Default Importance
+
+ Currently Set To: %s
+
+
+ Default Hide Until
+
+ Currently Set To: %s
+
diff --git a/astrid/res/values/strings-filters.xml b/astrid/res/values/strings-filters.xml
new file mode 100644
index 000000000..d8921a5e0
--- /dev/null
+++ b/astrid/res/values/strings-filters.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Inbox
+
+
+ Astrid: Inbox
+
+
+ Completed
+
+
+ All Tasks
+
+
diff --git a/astrid/res/values/strings.xml b/astrid/res/values/strings-legacy.xml
similarity index 98%
rename from astrid/res/values/strings.xml
rename to astrid/res/values/strings-legacy.xml
index 1f40a6937..2603d4245 100644
--- a/astrid/res/values/strings.xml
+++ b/astrid/res/values/strings-legacy.xml
@@ -399,15 +399,6 @@ Skipped %d tasks.\n
when you have uncompleted tasks with the following criteria:
Tagged with:
-
-
-
- Absolute Deadline!
- Goal Deadline!
- Working on:
-
- You have $NUM tagged $TAG!
-
diff --git a/astrid/res/values/strings-reminders.xml b/astrid/res/values/strings-reminders.xml
new file mode 100644
index 000000000..853d1c878
--- /dev/null
+++ b/astrid/res/values/strings-reminders.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+ Remind me...
+
+
+ ... when it\'s time to start the task
+
+
+ ... when task is overdue
+
+
+ ... randomly every
+
+
+ Ring/Vibrate Type:
+
+
+ Ring Once
+
+
+ Ring Until I Dismiss Alarm
+
+
+
+ - hour
+ - day
+ - week
+ - two weeks
+ - month
+ - two months
+
+
+
+
+ - 1
+ - 24
+ - 168
+ - 336
+ - 730
+ - 1460
+
+
+
+
+
+ Reminder!
+
+
+
+
+ Reminder Settings
+
+
+ Quiet Hours Start
+
+ No notifications will appear after %s
+
+ Please set both start and end times
+
+
+ Quiet Hours End
+
+ Notifications will begin appearing starting at %s
+
+
+ Notification Ringtone
+
+ Custom ringtone has been set
+
+ Default ringtone will be used
+
+
+ Notification Persistence
+
+ Notifications must be viewed individually to be cleared
+
+ Notifications can be cleared with \"Clear All\" button
+
+
+ New Task Defaults
+
+
+
+ - disabled
+ - hourly
+ - daily
+ - weekly
+ - bi-weekly
+ - monthly
+ - bi-monthly
+
+
+
+
+ - 0
+ - 1
+ - 24
+ - 168
+ - 336
+ - 730
+ - 1460
+
+
+
+
+
+
+ - Hi there! Have a sec?
+ - Can I see you for a sec?
+ - Have a few minutes?
+ - Did you forget?
+ - Excuse me!
+ - When you have a minute:
+ - On your agenda:
+ - Free for a moment?
+ - Astrid here!
+ - Hi! Can I bug you?
+ - A minute of your time?
+ - It\'s a great day to
+
+
+
+
+ - Time to work!
+ - Due date is here!
+ - Ready to start?
+ - You said you would do:
+ - You\'re supposed to start:
+ - Time to start:
+ - It\'s time!
+ - Excuse me! Time for
+ - You free? Time to
+
+
+
+
+ - Don\'t be lazy now!
+ - Snooze time is up!
+ - No more snoozing!
+ - Now are you ready?
+ - No more postponing!
+
+
+
+
+ - I\'ve got something for you!
+ - Ready to put this in the past?
+ - Why don\'t you get this done?
+ - How about it? Ready tiger?
+ - Ready to do this?
+ - Can you handle this?
+ - You can be happy! Just finish this!
+ - I promise you\'ll feel better if you finish this!
+ - Won\'t you do this today?
+ - Please finish this, I\'m sick of it!
+ - Can you finish this? Yes you can!
+ - Are you ever going to do this?
+ - Feel good about yourself! Let\'s go!
+ - I\'m so proud of you! Lets get it done!
+ - A little snack after you finish this?
+ - Just this one task? Please?
+ - Time to shorten your todo list!
+
+
+
+
+ - Please tell me it isn\'t true that you\'re a procrastinator!
+ - Doesn\'t being lazy get old sometimes?
+ - Somewhere, someone is depending on you to finish this!
+ - When you said postpone, you really meant \'I\'m doing this\', right?
+ - This is the last time you postpone this, right?
+ - Just finish this today, I won\'t tell anyone!
+ - Why postpone when you can um... not postpone!
+ - You\'ll finish this eventually, I presume?
+ - I think you\'re really great! How about not putting this off?
+ - Will you be able to achieve your goals if you do that?
+ - Postpone, postpone, postpone. When will you change!
+ - I\'ve had enough with your excuses! Just do it already!
+ - Didn\'t you make that excuse last time?
+ - I can\'t help you organize your life if you do that...
+
+
+
diff --git a/astrid/res/values/strings-tags.xml b/astrid/res/values/strings-tags.xml
new file mode 100644
index 000000000..cf7ba787b
--- /dev/null
+++ b/astrid/res/values/strings-tags.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ Add Tags
+ Tags: %s
+
+ Tags: %s
+
+ Tags
+ By Size
+ Alphabetical
+
+
+ $T ($C)
+
+ Tagged \'%s\'
+
+ Type In a Tag
+ Edit Tags
+
+
diff --git a/astrid/res/values/styles-3.0.xml b/astrid/res/values/styles-3.0.xml
new file mode 100644
index 000000000..6a89ffe4a
--- /dev/null
+++ b/astrid/res/values/styles-3.0.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/res/xml/filter_list_searchable.xml b/astrid/res/xml/filter_list_searchable.xml
new file mode 100644
index 000000000..e032d2713
--- /dev/null
+++ b/astrid/res/xml/filter_list_searchable.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/astrid/res/xml/preferences.xml b/astrid/res/xml/preferences.xml
index cad90874b..e4934152f 100644
--- a/astrid/res/xml/preferences.xml
+++ b/astrid/res/xml/preferences.xml
@@ -2,112 +2,37 @@
-
+
+ android:key="@string/p_default_importance_key"
+ android:entries="@array/EPr_default_importance"
+ android:entryValues="@array/EPr_default_importance_values"
+ android:title="@string/EPr_default_importance_title" />
-
-
-
-
+ android:key="@string/p_default_urgency_key"
+ android:entries="@array/EPr_default_urgency"
+ android:entryValues="@array/EPr_default_urgency_values"
+ android:title="@string/EPr_default_urgency_title" />
-
-
+ android:key="@string/p_default_hideUntil_key"
+ android:entries="@array/EPr_default_hideUntil"
+ android:entryValues="@array/EPr_default_hideUntil_values"
+ android:title="@string/EPr_default_hideUntil_title" />
+
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/astrid/res/xml/preferences_reminders.xml b/astrid/res/xml/preferences_reminders.xml
new file mode 100644
index 000000000..ec209916c
--- /dev/null
+++ b/astrid/res/xml/preferences_reminders.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/EditPreferences.java b/astrid/src-legacy/com/timsu/astrid/activities/EditPreferences.java
deleted file mode 100644
index 938d0c108..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/EditPreferences.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.utilities.Calendars;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.Preferences;
-
-/**
- * Displays the preference screen for users to edit their preferences
- *
- * @author timsu
- *
- */
-public class EditPreferences extends PreferenceActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.preferences);
-
- Preference backupPreference = findPreference(getString(R.string.p_backup));
- String backupSummary = Preferences.getBackupSummary(this);
- if(backupSummary != null && backupPreference != null)
- backupPreference.setSummary(backupSummary);
-
- ListPreference defaultCalendarPreference = (ListPreference) findPreference(getString(R.string.prefs_defaultCalendar));
- Calendars.initCalendarsPreference(this, defaultCalendarPreference);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- // set up flurry
- FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- FlurryAgent.onEndSession(this);
- }
-}
\ No newline at end of file
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/SubActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/SubActivity.java
deleted file mode 100644
index 435d728c1..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/SubActivity.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.timsu.astrid.data.tag.TagController;
-import com.timsu.astrid.data.task.TaskController;
-
-/**
- * Interface for views that are displayed from the main view page.
- *
- * @author timsu
- */
-abstract public class SubActivity {
- private TaskList parent;
- int code;
- private View view;
-
- public SubActivity(TaskList parent, int code, View view) {
- this.parent = parent;
- this.code = code;
- this.view = view;
- view.setTag(this);
- }
-
- // --- pass-through to activity listeners
-
- /** Called when this subactivity is displayed to the user */
- void onDisplay(Bundle variables) {
- //
- }
-
- boolean onPrepareOptionsMenu(Menu menu) {
- return false;
- }
-
- void onActivityResult(int requestCode, int resultCode, Intent data) {
- //
- }
-
- boolean onMenuItemSelected(int featureId, MenuItem item) {
- return false;
- }
-
-
- void onWindowFocusChanged(boolean hasFocus) {
- //
- }
-
- boolean onKeyDown(int keyCode, KeyEvent event) {
- return false;
- }
-
- void onSaveInstanceState(Bundle outState) {
- //
- }
-
- Object onRetainNonConfigurationInstance() {
- return null;
- }
-
- // --- pass-through to activity methods
-
- public Resources getResources() {
- return parent.getResources();
- }
-
- public View findViewById(int id) {
- return view.findViewById(id);
- }
-
- public void startManagingCursor(Cursor c) {
- if(c != null)
- parent.startManagingCursor(c);
- }
-
- public void setTitle(CharSequence title) {
- parent.setTitle(title);
- }
-
- public void closeActivity() {
- parent.finish();
- }
-
- public void launchActivity(Intent intent, int requestCode) {
- parent.startActivityForResult(intent, requestCode);
- }
-
- public Object getLastNonConfigurationInstance() {
- return parent.getLastNonConfigurationInstance();
- }
-
- // --- helper methods
-
- public Activity getParent() {
- return parent;
- }
-
- public TaskController getTaskController() {
- return parent.taskController;
- }
-
- public TagController getTagController() {
- return parent.tagController;
- }
-
- public View.OnTouchListener getGestureListener() {
- return parent.gestureListener;
- }
-
- public void switchToActivity(int activity, Bundle state) {
- parent.switchToActivity(activity, state);
- }
-
- // --- internal methods
-
- protected int getActivityCode() {
- return code;
- }
-
- protected View getView() {
- return view;
- }
-}
\ No newline at end of file
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/SyncLoginActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/SyncLoginActivity.java
index 6eaa8de02..65e98dd38 100644
--- a/astrid/src-legacy/com/timsu/astrid/activities/SyncLoginActivity.java
+++ b/astrid/src-legacy/com/timsu/astrid/activities/SyncLoginActivity.java
@@ -119,9 +119,9 @@ public class SyncLoginActivity extends Activity {
@Override
public void run() {
final String result = callback.verifyLogin(handler);
- TaskListSubActivity.syncPreferencesOpened = true;
+// TaskListSubActivity.syncPreferencesOpened = true;
if(result == null) {
- TaskList.synchronizeNow = true;
+// TaskList.synchronizeNow = true;
finish();
} else {
// display the error
@@ -131,7 +131,7 @@ public class SyncLoginActivity extends Activity {
DialogUtilities.okDialog(SyncLoginActivity.this, result,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
- TaskListSubActivity.shouldRefreshTaskList = true;
+// TaskListSubActivity.shouldRefreshTaskList = true;
finish();
}
});
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TagListSubActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/TagListSubActivity.java
deleted file mode 100644
index 856c2542c..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TagListSubActivity.java
+++ /dev/null
@@ -1,492 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.StaleDataException;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.data.tag.TagIdentifier;
-import com.timsu.astrid.data.tag.TagModelForView;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.utilities.DialogUtilities;
-import com.timsu.astrid.utilities.Preferences;
-
-
-/**
- * List all tags and allows a user to see all tasks for a given tag
- *
- * @author timsu
- *
- */
-public class TagListSubActivity extends SubActivity {
- private static final int ACTIVITY_CREATE = 0;
-
- private static final int MENU_SORT_ALPHA_ID = Menu.FIRST;
- private static final int MENU_SORT_SIZE_ID = Menu.FIRST + 1;
- private static final int CONTEXT_CREATE_ID = Menu.FIRST + 10;
- private static final int CONTEXT_DELETE_ID = Menu.FIRST + 11;
- private static final int CONTEXT_SHOWHIDE_ID = Menu.FIRST + 12;
- private static final int CONTEXT_SHORTCUT_ID = Menu.FIRST + 13;
-
- protected ListView listView;
- protected LinkedList tagArray;
- HashMap tagToTaskCount;
- protected Handler handler;
- protected TextView loadingText;
- protected boolean untaggedTagDisplayed;
-
- protected static SortMode sortMode = SortMode.SIZE;
- protected static boolean sortReverse = false;
-
- public TagListSubActivity(TaskList parent, int code, View view) {
- super(parent, code, view);
- }
-
- @Override
- public void onDisplay(Bundle variables) {
- listView = (ListView)findViewById(R.id.taglist);
- handler = new Handler();
- loadingText = (TextView)findViewById(R.id.loading);
-
- // time to go!
- new Thread(new Runnable() {
- public void run() {
- loadTagListSort();
- fillData();
- }
- }).start();
-
- FlurryAgent.onEvent("view-tags"); //$NON-NLS-1$
- }
-
- // --- stuff for sorting
-
- private enum SortMode {
- ALPHA {
- @Override
- int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1) {
- return arg0.getName().compareTo(arg1.getName());
- }
- },
- SIZE {
- @Override
- int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1) {
- synchronized(self) {
- return self.tagToTaskCount.get(arg1) - self.tagToTaskCount.get(arg0);
- }
- }
- };
-
- abstract int compareTo(TagListSubActivity self, TagModelForView arg0, TagModelForView arg1);
- };
-
- /** Counts how many tasks appear in active task list */
- public static int countActiveTasks(HashSet activeTasks, LinkedList tasks) {
- int count = 0;
- if(tasks != null) {
- for(TaskIdentifier task : tasks)
- if(activeTasks.contains(task))
- count++;
- }
- return count;
- }
-
- private synchronized void sortTagArray() {
- // get all tasks
- HashSet activeTasks =
- getTaskController().getActiveTaskIdentifiers();
- if(activeTasks == null)
- activeTasks = new HashSet();
-
- // get task count for each tag
- tagToTaskCount = new HashMap();
-
- for(TagModelForView tag : tagArray) {
- LinkedList tasks;
- tasks = getTagController().getTaggedTasks(tag.getTagIdentifier());
- int count = countActiveTasks(activeTasks, tasks);
- tagToTaskCount.put(tag, count);
- }
-
- // do sort
- Collections.sort(tagArray, new Comparator() {
- public int compare(TagModelForView arg0, TagModelForView arg1) {
- return sortMode.compareTo(TagListSubActivity.this, arg0, arg1);
- }
- });
-
- // show "untagged" as a category at the top, in the proper language/localization
- String untaggedLabel = getResources().getString(R.string.tagList_untagged);
- TagModelForView untaggedModel = TagModelForView.getUntaggedModel(untaggedLabel);
- int count = countActiveTasks(activeTasks, getTagController().getUntaggedTasks());
- if(count > 0) {
- untaggedTagDisplayed = true;
- tagArray.addFirst(untaggedModel);
- tagToTaskCount.put(untaggedModel, count);
- }
-
- if(sortReverse)
- Collections.reverse(tagArray);
- }
-
- /** Save the sorting mode to the preferences */
- private void saveTagListSort() {
- int sortId = sortMode.ordinal() + 1;
-
- if (sortReverse)
- sortId *= -1;
-
- Preferences.setTagListSort(getParent(), sortId);
- }
-
- /** Save the sorting mode to the preferences */
- protected void loadTagListSort() {
- try {
- int sortId = Preferences.getTagListSort(getParent());
- if (sortId == 0)
- return;
- sortReverse = sortId < 0;
- sortId = Math.abs(sortId) - 1;
-
- sortMode = SortMode.values()[sortId];
- } catch (Exception e) {
- // do nothing
- }
- }
-
-
- // --- fill data
-
- /** Fill in the Tag List with our tags */
- protected synchronized void fillData() {
- try {
- tagArray = getTagController().getAllTags();
-
- sortTagArray(); // count and sort each tag
- } catch (StaleDataException e) {
- // happens when you rotate the screen while the thread is
- // still running. i don't think it's avoidable?
- Log.w("astrid", "StaleDataException", e); //$NON-NLS-1$ //$NON-NLS-2$
- return;
- } catch (Exception e) {
- Log.e("astrid", "Error loading list", e); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- handler.post(new Runnable() {
- public void run() {
- synchronized(TagListSubActivity.this) {
- // set up our adapter
- final TagListAdapter tagAdapter = new TagListAdapter(getParent(),
- android.R.layout.simple_list_item_1, tagArray,
- tagToTaskCount);
- // set up ui components
- setUpListUI(tagAdapter);
- }
- loadingText.setVisibility(View.GONE);
- }
- });
- }
-
- /** Set up list handlers and adapter. run on the UI thread */
- protected void setUpListUI(ListAdapter adapter) {
- // set up the title
- Resources r = getResources();
- int tags = tagArray.size();
- if(untaggedTagDisplayed && tags > 0)
- tags--;
- StringBuilder title = new StringBuilder().
- append(r.getString(R.string.tagList_titlePrefix)).
- append(" ").append(r.getQuantityString(R.plurals.Ntags,
- tags, tags));
- final CharSequence finalTitle = title;
- handler.post(new Runnable() {
- public void run() {
- setTitle(finalTitle);
- }
- });
-
- listView.setAdapter(adapter);
-
- // list view listener
- listView.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> parent, View view,
- int position, long id) {
- TagModelForView tag = (TagModelForView)view.getTag();
-
- Bundle bundle = new Bundle();
- bundle.putLong(TaskListSubActivity.TAG_TOKEN, tag.getTagIdentifier().getId());
- switchToActivity(TaskList.AC_TASK_LIST_W_TAG, bundle);
- }
- });
-
- listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- AdapterContextMenuInfo adapterMenuInfo =
- (AdapterContextMenuInfo)menuInfo;
- int position = adapterMenuInfo.position;
-
- menu.add(position, CONTEXT_CREATE_ID, Menu.NONE,
- R.string.tagList_context_create);
- menu.add(position, CONTEXT_DELETE_ID, Menu.NONE,
- R.string.tagList_context_delete);
-
- int showHideLabel = R.string.tagList_context_hideTag;
- if(tagArray.get(position).shouldHideFromMainList())
- showHideLabel = R.string.tagList_context_showTag;
- menu.add(position, CONTEXT_SHOWHIDE_ID, Menu.NONE,
- showHideLabel);
-
- menu.add(position, CONTEXT_SHORTCUT_ID, Menu.NONE,
- R.string.tagList_context_shortcut);
-
- menu.setHeaderTitle(tagArray.get(position).getName());
- }
- });
-
-
- listView.setOnTouchListener(getGestureListener());
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode,
- Intent intent) {
- fillData();
- }
-
- // --- list adapter
-
- private void createTask(TagModelForView tag) {
- Intent intent = new Intent(getParent(), TaskEdit.class);
- intent.putExtra(TaskEdit.TAG_NAME_TOKEN, tag.getName());
- launchActivity(intent, ACTIVITY_CREATE);
- }
-
- private void deleteTag(final TagIdentifier tagId) {
- new AlertDialog.Builder(getParent())
- .setTitle(R.string.delete_title)
- .setMessage(R.string.delete_this_tag_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- getTagController().deleteTag(tagId);
- fillData();
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .show();
- }
-
- @Override
- /** Handle back button by moving to task list */
- protected boolean onKeyDown(int keyCode, KeyEvent event) {
- if(keyCode == KeyEvent.KEYCODE_BACK) {
- switchToActivity(TaskList.AC_TASK_LIST, null);
- return true;
- }
-
- return false;
- }
-
- @Override
- /** Picked item in the options list */
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- switch(item.getItemId()) {
- case MENU_SORT_ALPHA_ID:
- if(sortMode == SortMode.ALPHA)
- sortReverse = !sortReverse;
- else {
- sortMode = SortMode.ALPHA;
- sortReverse = false;
- }
- saveTagListSort();
- fillData();
- return true;
- case MENU_SORT_SIZE_ID:
- if(sortMode == SortMode.SIZE)
- sortReverse = !sortReverse;
- else {
- sortMode = SortMode.SIZE;
- sortReverse = false;
- }
- saveTagListSort();
- fillData();
- return true;
- case CONTEXT_CREATE_ID:
- TagModelForView tag = tagArray.get(item.getGroupId());
- createTask(tag);
- return true;
- case CONTEXT_DELETE_ID:
- tag = tagArray.get(item.getGroupId());
- deleteTag(tag.getTagIdentifier());
- return true;
- case CONTEXT_SHOWHIDE_ID:
- tag = tagArray.get(item.getGroupId());
- tag.toggleHideFromMainList();
- try {
- getTagController().saveTag(tag);
- } catch (Exception e) {
- DialogUtilities.okDialog(getParent(), "Error: You probably " +
- "already have a tag named '" + tag.getName() + "'!",
- null);
- }
- fillData();
- return true;
- case CONTEXT_SHORTCUT_ID:
- tag = tagArray.get(item.getGroupId());
- Resources r = getResources();
- Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
- shortcutIntent.setComponent(new ComponentName(
- getParent().getApplicationContext(), TagView.class));
- shortcutIntent.setData(Uri.parse("tag:" + tag.getTagIdentifier().getId())); //$NON-NLS-1$
-
- Intent createShortcutIntent = new Intent();
- createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
- String label = tag.getName();
- if(tag.shouldHideFromMainList())
- label = label.substring(1);
-
- // add the @ sign if the task starts with a letter, for clarity
- if(Character.isLetterOrDigit(label.charAt(0)))
- label = "@" + label; //$NON-NLS-1$
-
- createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
- createShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON,
- ((BitmapDrawable)r.getDrawable(R.drawable.icon_tag)).getBitmap());
- createShortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$
-
- getParent().sendBroadcast(createShortcutIntent);
- Toast.makeText(getParent(), R.string.tagList_shortcut_created, Toast.LENGTH_SHORT).show();
- return true;
- }
-
- return false;
- }
-
- // --- creating stuff
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem item;
-
- item = menu.add(Menu.NONE, MENU_SORT_ALPHA_ID, Menu.NONE,
- R.string.tagList_menu_sortAlpha);
- item.setIcon(android.R.drawable.ic_menu_sort_alphabetically);
- item.setAlphabeticShortcut('a');
-
- item = menu.add(Menu.NONE, MENU_SORT_SIZE_ID, Menu.NONE,
- R.string.tagList_menu_sortSize);
- item.setIcon(android.R.drawable.ic_menu_sort_by_size);
- item.setAlphabeticShortcut('s');
-
- return true;
- }
-
- // --------------------------------------------------- tag list adapter
-
- class TagListAdapter extends ArrayAdapter {
-
- private List objects;
- private int resource;
- private LayoutInflater inflater;
- private HashMap tagCount;
-
- public TagListAdapter(Context context, int resource,
- List objects, HashMap
- tagToTaskCount) {
- super(context, resource, objects);
-
- inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- this.objects = objects;
- this.resource = resource;
- this.tagCount = tagToTaskCount;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = convertView;
-
- if(view == null) {
- view = inflater.inflate(resource, parent, false);
- }
- setupView(view, objects.get(position), false);
-
- return view;
- }
-
- public void setupView(View view, final TagModelForView tag, boolean retry) {
- Resources r = getResources();
- view.setTag(tag);
-
- final TextView name = ((TextView)view.findViewById(android.R.id.text1));
- try {
- if(tagCount == null)
- tagCount = tagToTaskCount;
-
- name.setText(new StringBuilder(tag.getName()).
- append(" (").append(tagCount.get(tag)).append(")"));
-
- if(tagCount == null || tagCount.get(tag) == null || tagCount.get(tag) == 0)
- name.setTextColor(r.getColor(R.color.task_list_done));
- else
- name.setTextColor(r.getColor(android.R.color.white));
- } catch (Exception e) {
- Log.e("astrid", "Error loading tag list", e); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TagView.java b/astrid/src-legacy/com/timsu/astrid/activities/TagView.java
deleted file mode 100644
index fdde8bd8d..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TagView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * This activity is launched from a desktop shortcut and takes the user to
- * view a specific tag
- *
- * @author timsu
- *
- */
-public class TagView extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- launchTaskList(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
-
- launchTaskList(intent);
- }
-
- private void launchTaskList(Intent intent) {
- String tag = intent.getData().toString();
- long tagId = Long.parseLong(tag.substring(tag.indexOf(":")+1));
-
- Bundle variables = new Bundle();
- variables.putLong(TaskListSubActivity.TAG_TOKEN, tagId);
-
- Intent taskListIntent = new Intent(this, TaskList.class);
- taskListIntent.putExtra(TaskList.VARIABLES_TAG, variables);
- startActivity(taskListIntent);
-
- finish();
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskEdit.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskEdit.java
deleted file mode 100644
index 6f6c57aed..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskEdit.java
+++ /dev/null
@@ -1,970 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import android.app.AlertDialog;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.SimpleCursorAdapter;
-import android.widget.Spinner;
-import android.widget.TabHost;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.ToggleButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.data.alerts.AlertController;
-import com.timsu.astrid.data.enums.Importance;
-import com.timsu.astrid.data.enums.RepeatInterval;
-import com.timsu.astrid.data.sync.SyncDataController;
-import com.timsu.astrid.data.tag.TagController;
-import com.timsu.astrid.data.tag.TagIdentifier;
-import com.timsu.astrid.data.tag.TagModelForView;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.data.task.TaskModelForEdit;
-import com.timsu.astrid.data.task.TaskModelForList;
-import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
-import com.timsu.astrid.utilities.AstridUtilities;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.DateUtilities;
-import com.timsu.astrid.utilities.Notifications;
-import com.timsu.astrid.utilities.Preferences;
-import com.timsu.astrid.widget.DateControlSet;
-import com.timsu.astrid.widget.DateWithNullControlSet;
-import com.timsu.astrid.widget.NumberPicker;
-import com.timsu.astrid.widget.NumberPickerDialog;
-import com.timsu.astrid.widget.TimeDurationControlSet;
-import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
-import com.timsu.astrid.widget.TimeDurationControlSet.TimeDurationType;
-
-/**
- * This activity is responsible for creating new tasks and editing existing
- * ones. It saves a task when it is paused (screen rotated, back button
- * pressed) as long as the task has a title.
- *
- * @author timsu
- *
- */
-public class TaskEdit extends TaskModificationTabbedActivity {
-
- // bundle arguments
- public static final String TAG_NAME_TOKEN = "t";
- public static final String START_CHAR_TOKEN = "s";
-
- // menu items
- private static final int SAVE_ID = Menu.FIRST;
- private static final int DISCARD_ID = Menu.FIRST + 1;
- private static final int DELETE_ID = Menu.FIRST + 2;
-
- // other constants
- private static final int MAX_TAGS = 5;
- private static final int MAX_ALERTS = 5;
- private static final String TAB_BASIC = "basic";
- private static final String TAB_DATES = "dates";
- private static final String TAB_ALERTS = "alerts";
- private static final int DEFAULT_CAL_TIME = 3600;
-
- // UI components
- private EditText name;
- private ImportanceControlSet importance;
- private TimeDurationControlSet estimatedDuration;
- private TimeDurationControlSet elapsedDuration;
- private TimeDurationControlSet notification;
- private DateControlSet definiteDueDate;
- private DateControlSet preferredDueDate;
- private DateControlSet hiddenUntil;
- private EditText notes;
- private LinearLayout tagsContainer;
- private NotifyFlagControlSet flags;
- private LinearLayout alertsContainer;
- private Button repeatValue;
- private Spinner repeatInterval;
- private CheckBox addToCalendar;
-
- // other instance variables
- private boolean shouldSaveState = true;
- private boolean repeatHelpShown = false;
- private TagController tagController;
- private AlertController alertController;
- private List tags;
- private List taskTags;
-
- // OnClickListeners for save, discard and delete
- private View.OnClickListener mSaveListener = new View.OnClickListener() {
- public void onClick(View v) {
- saveButtonClick();
- }
- };
- private View.OnClickListener mDiscardListener = new View.OnClickListener() {
- public void onClick(View v) {
- discardButtonClick();
- }
- };
- private View.OnClickListener mDeleteListener = new View.OnClickListener() {
- public void onClick(View v) {
- deleteButtonClick();
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- tagController = new TagController(this);
- tagController.open();
- alertController = new AlertController(this);
- alertController.open();
-
- TabHost tabHost = getTabHost();
- tabHost.setPadding(0, 4, 0, 0);
- Resources r = getResources();
-
- LayoutInflater.from(this).inflate(R.layout.task_edit, tabHost.getTabContentView(), true);
-
- tabHost.addTab(tabHost.newTabSpec(TAB_BASIC).setIndicator(r.getString(R.string.taskEdit_tab_basic),
- r.getDrawable(R.drawable.ic_dialog_info_c)).setContent(R.id.tab_basic));
- tabHost.addTab(tabHost.newTabSpec(TAB_DATES).setIndicator(r.getString(R.string.taskEdit_tab_dates),
- r.getDrawable(R.drawable.ic_dialog_time_c)).setContent(R.id.tab_dates));
- tabHost.addTab(tabHost.newTabSpec(TAB_ALERTS).setIndicator(r.getString(R.string.taskEdit_tab_alerts),
- r.getDrawable(R.drawable.ic_dialog_alert_c)).setContent(R.id.tab_notification));
-
- setUpUIComponents();
- setUpListeners();
-
- // disable name input box until user requests it
- AstridUtilities.suppressVirtualKeyboard(name);
- }
-
- @Override
- protected TaskModelForEdit getModel(TaskIdentifier identifier) {
- if (identifier != null) {
- TaskModelForEdit task = controller.fetchTaskForEdit(this, identifier);
- if(task.getCursor().getCount() > 0)
- return task;
- }
-
- return controller.createNewTaskForEdit();
- }
-
- /* ======================================================================
- * =============================================== model reading / saving
- * ====================================================================== */
-
- /** Populate UI component values from the model */
- private void populateFields() {
- Resources r = getResources();
-
- // set UI components based on model variables
- if(model.getCursor() != null)
- startManagingCursor(model.getCursor());
- if(model.getTaskIdentifier() == null) {
- FlurryAgent.onEvent("create-task");
- Bundle extras = getIntent().getExtras();
- if(extras != null && extras.containsKey(START_CHAR_TOKEN))
- name.setText("" + extras.getChar(START_CHAR_TOKEN));
- } else {
- FlurryAgent.onEvent("edit-task");
- name.setText(model.getName());
- }
-
- if(model.getName().length() > 0)
- setTitle(new StringBuilder().
- append(r.getString(R.string.taskEdit_titlePrefix)).
- append(" ").
- append(model.getName()));
- estimatedDuration.setTimeDuration(model.getEstimatedSeconds());
- elapsedDuration.setTimeDuration(model.getElapsedSeconds());
- importance.setImportance(model.getImportance());
- definiteDueDate.setDate(model.getDefiniteDueDate());
- preferredDueDate.setDate(model.getPreferredDueDate());
- hiddenUntil.setDate(model.getHiddenUntil());
- notification.setTimeDuration(model.getNotificationIntervalSeconds());
- flags.setValue(model.getNotificationFlags());
- notes.setText(model.getNotes());
- if(model.getTaskIdentifier() == null) {
- Integer reminder = Preferences.getDefaultReminder(this);
- if(reminder != null)
- notification.setTimeDuration(24*3600*reminder);
- }
- if(model.getCalendarUri() != null)
- addToCalendar.setText(r.getString(R.string.showCalendar_label));
-
- // tags (only configure if not already set)
- if(tagsContainer.getChildCount() == 0) {
- tags = tagController.getAllTags();
- if(model.getTaskIdentifier() != null) {
- taskTags = tagController.getTaskTags(model.getTaskIdentifier());
- if(taskTags.size() > 0) {
- Map tagsMap =
- new HashMap();
- for(TagModelForView tag : tags)
- tagsMap.put(tag.getTagIdentifier(), tag);
- for(TagIdentifier id : taskTags) {
- if(!tagsMap.containsKey(id))
- continue;
-
- TagModelForView tag = tagsMap.get(id);
- addTag(tag.getName());
- }
- }
- } else {
- taskTags = new LinkedList();
-
- Bundle extras = getIntent().getExtras();
- if(extras != null && extras.containsKey(TAG_NAME_TOKEN)) {
- addTag(extras.getString(TAG_NAME_TOKEN));
- }
- }
- addTag("");
- }
-
- // alerts
- if(model.getTaskIdentifier() != null) {
- List alerts = alertController.getTaskAlerts(model.getTaskIdentifier());
- for(Date alert : alerts) {
- addAlert(alert);
- }
- }
-
- // repeats
- RepeatInfo repeatInfo = model.getRepeat();
- if(repeatInfo != null) {
- setRepeatValue(repeatInfo.getValue());
- repeatInterval.setSelection(repeatInfo.getInterval().ordinal());
- } else
- setRepeatValue(0);
-
- }
-
- /** Save task model from values in UI components */
- private void save() {
- // don't save if user accidentally created a new task
- if(name.getText().length() == 0)
- return;
-
- // tell the task list to update itself
- TaskListSubActivity.shouldRefreshTaskList = true;
-
- model.setName(name.getText().toString());
- model.setEstimatedSeconds(estimatedDuration.getTimeDurationInSeconds());
- model.setElapsedSeconds(elapsedDuration.getTimeDurationInSeconds());
- model.setImportance(importance.getImportance());
- model.setDefiniteDueDate(definiteDueDate.getDate());
- model.setPreferredDueDate(preferredDueDate.getDate());
- model.setHiddenUntil(hiddenUntil.getDate());
- model.setNotificationFlags(flags.getValue());
- model.setNotes(notes.getText().toString());
- model.setNotificationIntervalSeconds(notification.getTimeDurationInSeconds());
- model.setRepeat(getRepeatValue());
-
- try {
- // write out to database
- controller.saveTask(model, false);
- saveTags();
- saveAlerts();
- Notifications.updateAlarm(this, controller, alertController, model);
-
- Date dueDate = model.getPreferredDueDate();
- if (dueDate == null) {
- dueDate = model.getDefiniteDueDate();
- }
- if (dueDate != null && model.getProgressPercentage() != TaskModelForEdit.COMPLETE_PERCENTAGE) {
- showSaveToast(dueDate);
- } else {
- showSaveToast();
- }
-
- } catch (Exception e) {
- Log.e("astrid", "Error saving", e);
- }
- }
-
- /**
- * Displays a Toast reporting that the selected task has been saved and is
- * due in 'x' amount of time, to 2 time-units of precision (e.g. Days + Hours).
- * @param dueDate the Date when the task is due
- */
- private void showSaveToast(Date dueDate) {
- int stringResource;
-
- int timeInSeconds = (int)((dueDate.getTime() - System.currentTimeMillis())/1000L);
-
- if (timeInSeconds < 0) {
- timeInSeconds *= -1; // DateUtilities.getDurationString() requires positive integer
- stringResource = R.string.taskEdit_onTaskSave_Overdue;
- } else {
- stringResource = R.string.taskEdit_onTaskSave_Due;
- }
- String formattedDate = DateUtilities.getDurationString(getResources(), timeInSeconds, 2);
- Toast.makeText(this,
- getResources().getString(stringResource, formattedDate),
- Toast.LENGTH_SHORT).show();
- }
-
- /**
- * Displays a Toast reporting that the selected task has been saved.
- * Use this version when no due Date has been set.
- */
- private void showSaveToast() {
- Toast.makeText(this, R.string.taskEdit_onTaskSave_notDue, Toast.LENGTH_SHORT).show();
- }
-
- /** Save task tags. Must be called after task already has an ID */
- private void saveTags() {
- Set tagsToDelete;
- Set tagsToAdd;
-
- HashSet tagNames = new HashSet();
- for(int i = 0; i < tagsContainer.getChildCount(); i++) {
- TextView tagName = (TextView)tagsContainer.getChildAt(i).findViewById(R.id.text1);
- if(tagName.getText().length() == 0)
- continue;
- tagNames.add(tagName.getText().toString());
- }
-
- // map names to tag identifiers, creating them if necessary
- HashSet tagIds = new HashSet();
- HashMap tagsByName = new HashMap();
- for(TagModelForView tag : tags)
- tagsByName.put(tag.getName(), tag.getTagIdentifier());
- for(String tagName : tagNames) {
- if(tagsByName.containsKey(tagName))
- tagIds.add(tagsByName.get(tagName));
- else {
- TagIdentifier newTagId = tagController.createTag(tagName);
- tagIds.add(newTagId);
- }
- }
-
- // intersect tags to figure out which we need to add / remove
- tagsToDelete = new HashSet(taskTags);
- tagsToDelete.removeAll(tagIds);
- tagsToAdd = tagIds;
- tagsToAdd.removeAll(taskTags);
-
- // perform the database updates
- for(TagIdentifier tagId : tagsToDelete)
- tagController.removeTag(model.getTaskIdentifier(), tagId);
- for(TagIdentifier tagId : tagsToAdd)
- tagController.addTag(model.getTaskIdentifier(), tagId);
-
- if(tagsToDelete.size() > 0 || tagsToAdd.size() > 0)
- SyncDataController.taskUpdated(this, model);
- }
-
- /** Helper method to save alerts for this task */
- private void saveAlerts() {
- alertController.removeAlerts(model.getTaskIdentifier());
-
- for(int i = 0; i < alertsContainer.getChildCount(); i++) {
- DateControlSet dateControlSet = (DateControlSet)alertsContainer.
- getChildAt(i).getTag();
- Date date = dateControlSet.getDate();
- alertController.addAlert(model.getTaskIdentifier(), date);
- }
- }
-
- /* ======================================================================
- * ==================================================== UI initialization
- * ====================================================================== */
-
- /** Initialize UI components */
- private void setUpUIComponents() {
- Resources r = getResources();
- setTitle(new StringBuilder()
- .append(r.getString(R.string.taskEdit_titleGeneric)));
-
- // populate instance variables
- name = (EditText)findViewById(R.id.name);
- importance = new ImportanceControlSet(R.id.importance_container);
- tagsContainer = (LinearLayout)findViewById(R.id.tags_container);
- estimatedDuration = new TimeDurationControlSet(this,
- R.id.estimatedDuration, 0, R.string.hour_minutes_dialog,
- TimeDurationType.HOURS_MINUTES);
- elapsedDuration = new TimeDurationControlSet(this, R.id.elapsedDuration,
- 0, R.string.hour_minutes_dialog,
- TimeDurationType.HOURS_MINUTES);
- notification = new TimeDurationControlSet(this, R.id.notification,
- R.string.notification_prefix, R.string.notification_dialog,
- TimeDurationType.DAYS_HOURS);
- definiteDueDate = new DateWithNullControlSet(this, R.id.definiteDueDate_notnull,
- R.id.definiteDueDate_date, R.id.definiteDueDate_time);
- preferredDueDate = new DateWithNullControlSet(this, R.id.preferredDueDate_notnull,
- R.id.preferredDueDate_date, R.id.preferredDueDate_time);
- hiddenUntil = new DateWithNullControlSet(this, R.id.hiddenUntil_notnull,
- R.id.hiddenUntil_date, R.id.hiddenUntil_time);
- notes = (EditText)findViewById(R.id.notes);
- flags = new NotifyFlagControlSet(R.id.flag_before,
- R.id.flag_during, R.id.flag_after, R.id.flag_nonstop);
- alertsContainer = (LinearLayout)findViewById(R.id.alert_container);
- repeatInterval = (Spinner)findViewById(R.id.repeat_interval);
- repeatValue = (Button)findViewById(R.id.repeat_value);
- addToCalendar = (CheckBox)findViewById(R.id.add_to_calendar);
-
- // individual ui component initialization
- ArrayAdapter repeatAdapter = new ArrayAdapter(
- this, android.R.layout.simple_spinner_item,
- RepeatInterval.getLabels(getResources()));
- repeatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- repeatInterval.setAdapter(repeatAdapter);
- }
-
- /** Set up button listeners */
- private void setUpListeners() {
- Button saveButtonGeneral = (Button) findViewById(R.id.save_general);
- saveButtonGeneral.setOnClickListener(mSaveListener);
-
- Button saveButtonDates = (Button) findViewById(R.id.save_dates);
- saveButtonDates.setOnClickListener(mSaveListener);
-
- Button saveButtonNotify = (Button) findViewById(R.id.save_notify);
- saveButtonNotify.setOnClickListener(mSaveListener);
-
- Button discardButtonGeneral = (Button) findViewById(R.id.discard_general);
- discardButtonGeneral.setOnClickListener(mDiscardListener);
-
- Button discardButtonDates = (Button) findViewById(R.id.discard_dates);
- discardButtonDates.setOnClickListener(mDiscardListener);
-
- Button discardButtonNotify = (Button) findViewById(R.id.discard_notify);
- discardButtonNotify.setOnClickListener(mDiscardListener);
-
- Button deleteButtonGeneral = (Button) findViewById(R.id.delete_general);
- Button deleteButtonDates = (Button) findViewById(R.id.delete_dates);
- Button deleteButtonNotify = (Button) findViewById(R.id.delete_notify);
- if(model.getTaskIdentifier() == null) {
- deleteButtonGeneral.setVisibility(View.GONE);
- deleteButtonDates.setVisibility(View.GONE);
- deleteButtonNotify.setVisibility(View.GONE);
- } else {
- deleteButtonGeneral.setOnClickListener(mDeleteListener);
- deleteButtonDates.setOnClickListener(mDeleteListener);
- deleteButtonNotify.setOnClickListener(mDeleteListener);
- }
-
- Button addAlertButton = (Button) findViewById(R.id.addAlert);
- addAlertButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- addAlert(null);
- }
- });
-
- repeatValue.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- repeatValueClick();
- }
- });
- }
-
- /** Set up the repeat value button */
- private void setRepeatValue(int value) {
- if(value == 0)
- repeatValue.setText(R.string.repeat_value_unset);
- else
- repeatValue.setText(Integer.toString(value));
- repeatValue.setTag(value);
- }
-
- private RepeatInfo getRepeatValue() {
- if(repeatValue.getTag().equals(0))
- return null;
- return new RepeatInfo(RepeatInterval.values()
- [repeatInterval.getSelectedItemPosition()],
- (Integer)repeatValue.getTag());
- }
-
- /** Adds an alert to the alert field */
- private boolean addAlert(Date alert) {
- if(alertsContainer.getChildCount() >= MAX_ALERTS)
- return false;
-
- LayoutInflater inflater = getLayoutInflater();
- final View alertItem = inflater.inflate(R.layout.edit_alert_item, null);
- alertsContainer.addView(alertItem);
-
- DateControlSet dcs = new DateControlSet(this,
- (Button)alertItem.findViewById(R.id.date),
- (Button)alertItem.findViewById(R.id.time));
- dcs.setDate(alert);
- alertItem.setTag(dcs);
-
- ImageButton reminderRemoveButton;
- reminderRemoveButton = (ImageButton)alertItem.findViewById(R.id.button1);
- reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- alertsContainer.removeView(alertItem);
- }
- });
-
- return true;
- }
-
- /** Adds a tag to the tag field */
- private boolean addTag(String tagName) {
- if (tagsContainer.getChildCount() >= MAX_TAGS) {
- return false;
- }
-
- LayoutInflater inflater = getLayoutInflater();
- final View tagItem = inflater.inflate(R.layout.edit_tag_item, null);
- tagsContainer.addView(tagItem);
-
- AutoCompleteTextView textView = (AutoCompleteTextView)tagItem.
- findViewById(R.id.text1);
- textView.setText(tagName);
- ArrayAdapter tagsAdapter =
- new ArrayAdapter(this,
- android.R.layout.simple_dropdown_item_1line, tags);
- textView.setAdapter(tagsAdapter);
- textView.addTextChangedListener(new TextWatcher() {
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- if(start == 0 && tagsContainer.getChildAt(
- tagsContainer.getChildCount()-1) == tagItem) {
- addTag("");
- }
- }
-
- public void afterTextChanged(Editable s) {
- //
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- //
- }
- });
-
- ImageButton reminderRemoveButton;
- reminderRemoveButton = (ImageButton)tagItem.findViewById(R.id.button1);
- reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- tagsContainer.removeView(tagItem);
- }
- });
-
- return true;
- }
-
- /* ======================================================================
- * ======================================================= event handlers
- * ====================================================================== */
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- if(hasFocus && TaskList.shouldCloseInstance) { // user wants to quit
- finish();
- }
- }
-
- private void saveButtonClick() {
- setResult(RESULT_OK);
- finish();
- }
-
- private void discardButtonClick() {
- shouldSaveState = false;
- setResult(Constants.RESULT_DISCARD);
- finish();
- }
-
- private void deleteButtonClick() {
- new AlertDialog.Builder(this)
- .setTitle(R.string.delete_title)
- .setMessage(R.string.delete_this_task_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // tell the task list to update itself
- TaskListSubActivity.shouldRefreshTaskList = true;
-
- controller.deleteTask(model.getTaskIdentifier());
- shouldSaveState = false;
- setResult(Constants.RESULT_GO_HOME);
- finish();
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .show();
- }
-
- private void repeatValueClick() {
- final int tagValue = (Integer)repeatValue.getTag();
- if(tagValue > 0)
- repeatHelpShown = true;
-
- final Runnable openDialogRunnable = new Runnable() {
- public void run() {
- repeatHelpShown = true;
-
- int dialogValue = tagValue;
- if(dialogValue == 0)
- dialogValue = 1;
-
- new NumberPickerDialog(TaskEdit.this, new OnNumberPickedListener() {
- public void onNumberPicked(NumberPicker view, int number) {
- setRepeatValue(number);
- }
- }, getResources().getString(R.string.repeat_picker_title),
- dialogValue, 1, 0, 31).show();
- }
- };
-
- if(repeatHelpShown || !Preferences.shouldShowRepeatHelp(this)) {
- openDialogRunnable.run();
- return;
- }
-
- new AlertDialog.Builder(this)
- .setTitle(R.string.repeat_help_dialog_title)
- .setMessage(R.string.repeat_help_dialog)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- openDialogRunnable.run();
- }
- })
- .setNeutralButton(R.string.repeat_help_hide,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- Preferences.setShowRepeatHelp(TaskEdit.this, false);
- openDialogRunnable.run();
- }
- })
- .show();
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- switch(item.getItemId()) {
- case SAVE_ID:
- saveButtonClick();
- return true;
- case DISCARD_ID:
- discardButtonClick();
- return true;
- case DELETE_ID:
- deleteButtonClick();
- return true;
- }
-
- return super.onMenuItemSelected(featureId, item);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuItem item;
-
- item = menu.add(Menu.NONE, SAVE_ID, 0, R.string.save_label);
- item.setIcon(android.R.drawable.ic_menu_save);
- item.setAlphabeticShortcut('s');
-
- item = menu.add(Menu.NONE, DISCARD_ID, 0, R.string.discard_label);
- item.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
- item.setAlphabeticShortcut('c');
-
- if (model.getTaskIdentifier() != null) {
- item = menu.add(Menu.NONE, DELETE_ID, 0, R.string.delete_label);
- item.setIcon(android.R.drawable.ic_menu_delete);
- item.setAlphabeticShortcut('d');
- }
-
- return true;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- // save the tag name token for when we rotate the screen
- Bundle extras = getIntent().getExtras();
- if(extras != null && extras.containsKey(TAG_NAME_TOKEN))
- outState.putString(TAG_NAME_TOKEN,
- extras.getString(TAG_NAME_TOKEN));
- }
-
- /** Take the values from the model and set the calendar start and end times
- * based on these. Sets keys 'dtstart' and 'dtend'.
- *
- * @param preferred preferred due date or null
- * @param definite definite due date or null
- * @param estimatedSeconds estimated duration or null
- * @param values
- */
- public static void createCalendarStartEndTimes(Date preferred, Date definite,
- Integer estimatedSeconds, ContentValues values) {
- FlurryAgent.onEvent("create-calendar-event");
-
- Long deadlineDate = null;
- if (preferred != null && preferred.after(new Date()))
- deadlineDate = preferred.getTime();
- else if (definite != null)
- deadlineDate = definite.getTime();
- else
- deadlineDate = System.currentTimeMillis() + 24*3600*1000L;
-
- int estimatedTime = DEFAULT_CAL_TIME;
- if(estimatedSeconds != null && estimatedSeconds > 0) {
- estimatedTime = estimatedSeconds;
- }
- values.put("dtstart", deadlineDate - estimatedTime * 1000L);
- values.put("dtend", deadlineDate);
- }
-
- @Override
- protected void onPause() {
- // create calendar event
- if(addToCalendar.isChecked() && model.getCalendarUri() == null) {
-
- Uri uri = Uri.parse("content://calendar/events");
- ContentResolver cr = getContentResolver();
-
- ContentValues values = new ContentValues();
- values.put("title", name.getText().toString());
- values.put("calendar_id", Preferences.getDefaultCalendarIDSafe(this));
- values.put("description", notes.getText().toString());
- values.put("hasAlarm", 0);
- values.put("transparency", 0);
- values.put("visibility", 0);
-
- createCalendarStartEndTimes(model.getPreferredDueDate(),
- model.getDefiniteDueDate(), model.getEstimatedSeconds(),
- values);
-
- Uri result = null;
- try{
- result = cr.insert(uri, values);
- model.setCalendarUri(result.toString());
- } catch (IllegalArgumentException e) {
- Log.e("astrid", "Error creating calendar event!", e);
- }
- }
-
- if(shouldSaveState)
- save();
-
- if(addToCalendar.isChecked() && model.getCalendarUri() != null) {
- Uri result = Uri.parse(model.getCalendarUri());
- Intent intent = new Intent(Intent.ACTION_EDIT, result);
-
- ContentValues values = new ContentValues();
- createCalendarStartEndTimes(model.getPreferredDueDate(),
- model.getDefiniteDueDate(), model.getEstimatedSeconds(),
- values);
-
- intent.putExtra("beginTime", values.getAsLong("dtstart"));
- intent.putExtra("endTime", values.getAsLong("dtend"));
-
- startActivity(intent);
- }
-
- super.onPause();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- populateFields();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- tagController.close();
- alertController.close();
- }
-
- /* ======================================================================
- * ========================================== UI component helper classes
- * ====================================================================== */
-
- /** Control set dealing with notification flags */
- public class NotifyFlagControlSet {
- private CheckBox before, during, after, nonstop;
-
- public NotifyFlagControlSet(int beforeId, int duringId,
- int afterId, int nonstopId) {
- before = (CheckBox)findViewById(beforeId);
- during = (CheckBox)findViewById(duringId);
- after = (CheckBox)findViewById(afterId);
- nonstop = (CheckBox)findViewById(nonstopId);
- }
-
- public void setValue(int flags) {
- before.setChecked((flags &
- TaskModelForEdit.NOTIFY_BEFORE_DEADLINE) > 0);
- during.setChecked((flags &
- TaskModelForEdit.NOTIFY_AT_DEADLINE) > 0);
- after.setChecked((flags &
- TaskModelForEdit.NOTIFY_AFTER_DEADLINE) > 0);
- nonstop.setChecked((flags &
- TaskModelForEdit.NOTIFY_NONSTOP) > 0);
- }
-
- public int getValue() {
- int value = 0;
- if(before.isChecked())
- value |= TaskModelForEdit.NOTIFY_BEFORE_DEADLINE;
- if(during.isChecked())
- value |= TaskModelForEdit.NOTIFY_AT_DEADLINE;
- if(after.isChecked())
- value |= TaskModelForEdit.NOTIFY_AFTER_DEADLINE;
- if(nonstop.isChecked())
- value |= TaskModelForEdit.NOTIFY_NONSTOP;
- return value;
- }
- }
-
- /** Control set dealing with importance */
- public class ImportanceControlSet {
- private List buttons = new LinkedList();
-
- public ImportanceControlSet(int containerId) {
- LinearLayout layout = (LinearLayout)findViewById(containerId);
- Resources r = getResources();
-
- for(Importance i : Importance.values()) {
- final ToggleButton button = new ToggleButton(TaskEdit.this);
- button.setLayoutParams(new LinearLayout.LayoutParams(
- LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1));
- button.setTextColor(r.getColor(i.getColorResource()));
- button.setTextOff(r.getString(i.getLabelResource()));
- button.setTextOn(r.getString(i.getLabelResource()));
- button.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- setImportance((Importance)button.getTag());
- }
- });
- button.setTag(i);
-
- buttons.add(button);
- layout.addView(button);
- }
- }
-
- public void setImportance(Importance i) {
- for(CompoundButton b : buttons) {
- if(b.getTag() == i) {
- b.setTextSize(24);
- b.setChecked(true);
- } else {
- b.setTextSize(16);
- b.setChecked(false);
- }
- }
- }
-
- public Importance getImportance() {
- for(CompoundButton b : buttons)
- if(b.isChecked())
- return (Importance)b.getTag();
- return Importance.DEFAULT;
- }
- }
-
- /** Control set dealing with "blocking on" */
- public class BlockingOnControlSet {
-
- private CheckBox activatedCheckBox;
- private Spinner taskBox;
-
- public BlockingOnControlSet(int checkBoxId, int taskBoxId) {
- activatedCheckBox = (CheckBox)findViewById(checkBoxId);
- taskBox = (Spinner)findViewById(taskBoxId);
-
- Cursor tasks = controller.getActiveTaskListCursor();
- startManagingCursor(tasks);
- SimpleCursorAdapter tasksAdapter = new SimpleCursorAdapter(TaskEdit.this,
- android.R.layout.simple_list_item_1, tasks,
- new String[] { TaskModelForList.getNameField() },
- new int[] { android.R.id.text1 });
- taskBox.setAdapter(tasksAdapter);
-
- activatedCheckBox.setOnCheckedChangeListener(
- new OnCheckedChangeListener() {
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- taskBox.setEnabled(isChecked);
- }
- });
-
- }
-
- public void setBlockingOn(TaskIdentifier value) {
- activatedCheckBox.setChecked(value != null);
- if(value == null) {
- return;
- }
-
- for(int i = 0; i < taskBox.getCount(); i++)
- if(taskBox.getItemIdAtPosition(i) == value.getId()) {
- taskBox.setSelection(i);
- return;
- }
-
- // not found
- activatedCheckBox.setChecked(false);
- }
-
- public TaskIdentifier getBlockingOn() {
- if(!activatedCheckBox.isChecked())
- return null;
-
- return new TaskIdentifier(taskBox.getSelectedItemId());
- }
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskList.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskList.java
deleted file mode 100644
index 42b56b21f..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskList.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.app.Activity;
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.RemoteViews;
-import android.widget.ViewFlipper;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.appwidget.AstridAppWidgetProvider;
-import com.timsu.astrid.data.tag.TagController;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.sync.Synchronizer;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.StartupReceiver;
-import com.timsu.astrid.utilities.AstridUtilities.AstridUncaughtExceptionHandler;
-
-/**
- * TaskList is the main launched activity for Astrid. It uses a ViewFlipper
- * to flip between child views, which in this case are the TaskListSubActivity
- * and the TagListSubActivity.
- *
- * @author timsu
- */
-public class TaskList extends Activity {
-
- // constants for the different pages that we can display
- public static final int AC_TASK_LIST = 0;
- public static final int AC_TAG_LIST = 1;
- public static final int AC_TASK_LIST_W_TAG = 2;
-
- /** Bundle Key: activity code id of current activity */
- private static final String LAST_ACTIVITY_TAG = "l";
-
- /** Bundle Key: variables of current activity */
- private static final String LAST_BUNDLE_TAG = "b";
-
- /** Bundle Key: variables to pass to the sub-activity */
- public static final String VARIABLES_TAG = "v";
-
- /** Minimum distance a fling must cover to trigger motion */
- private static final int FLING_DIST_THRESHOLD = 120;
-
- /** Maximum distance in the other axis for a fling */
- private static final int MAX_FLING_OTHER_AXIS = 300;
-
- /** Minimum velocity a fling must have to trigger motion */
- private static final int FLING_VEL_THRESHOLD = 200;
-
- // view components
- private ViewFlipper viewFlipper;
- private GestureDetector gestureDetector;
- View.OnTouchListener gestureListener;
- private SubActivity taskList;
- private SubActivity tagList;
- private SubActivity taskListWTag;
- private Bundle lastActivityBundle;
-
- // animations
- private Animation mFadeInAnim;
- private Animation mFadeOutAnim;
-
- // data controllers
- TaskController taskController;
- TagController tagController;
-
- // static variables
- public static boolean synchronizeNow = false;
-
- /** If set, the application will close when this activity gets focus */
- static boolean shouldCloseInstance = false;
-
- @Override
- /** Called when loading up the activity for the first time */
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- // set uncaught exception handler
- Thread.setDefaultUncaughtExceptionHandler(new AstridUncaughtExceptionHandler());
-
- // open controllers & perform application startup rituals
- StartupReceiver.onStartupApplication(this);
- shouldCloseInstance = false;
- taskController = new TaskController(this);
- taskController.open();
- tagController = new TagController(this);
- tagController.open();
-
- setupUIComponents();
-
- Bundle variables = new Bundle();
-
- if(savedInstanceState != null && savedInstanceState.containsKey(LAST_ACTIVITY_TAG)) {
- viewFlipper.setDisplayedChild(savedInstanceState.getInt(LAST_ACTIVITY_TAG));
- Bundle lastBundle = savedInstanceState.getBundle(LAST_BUNDLE_TAG);
- if(lastBundle != null)
- variables.putAll(lastBundle);
- }
- if(getIntent().hasExtra(VARIABLES_TAG))
- variables.putAll(getIntent().getBundleExtra(VARIABLES_TAG));
-
- getCurrentSubActivity().onDisplay(variables);
-
- // sync now if requested
- if(synchronizeNow) {
- synchronizeNow = false;
- Synchronizer sync = new Synchronizer(false);
- sync.setTagController(tagController);
- sync.setTaskController(taskController);
- sync.synchronize(this, null);
- }
-
- // if we have no filter tag, we're not on the last task
- if(getCurrentSubActivity() == taskListWTag &&
- ((TaskListSubActivity)taskListWTag).getFilterTag() == null) {
- switchToActivity(AC_TASK_LIST, null);
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- // set up flurry
- FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- updateWidget();
-
- FlurryAgent.onEndSession(this);
- }
-
- private void updateWidget()
- {
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
-
- RemoteViews views = AstridAppWidgetProvider.UpdateService.buildUpdate(this);
- ComponentName widgetName = new ComponentName(this, AstridAppWidgetProvider.class);
- appWidgetManager.updateAppWidget(widgetName, views);
- }
-
- /** Set up user interface components */
- private void setupUIComponents() {
- gestureDetector = new GestureDetector(new AstridGestureDetector());
- viewFlipper = (ViewFlipper)findViewById(R.id.main);
- taskList = new TaskListSubActivity(this, AC_TASK_LIST,
- findViewById(R.id.tasklist_layout));
- tagList = new TagListSubActivity(this, AC_TAG_LIST,
- findViewById(R.id.taglist_layout));
- taskListWTag = new TaskListSubActivity(this, AC_TASK_LIST_W_TAG,
- findViewById(R.id.tasklistwtag_layout));
-
- mFadeInAnim = AnimationUtils.loadAnimation(this, R.anim.fade_in);
- mFadeOutAnim = AnimationUtils.loadAnimation(this, R.anim.fade_out);
- viewFlipper.setInAnimation(mFadeInAnim);
- viewFlipper.setOutAnimation(mFadeOutAnim);
-
- gestureListener = new View.OnTouchListener() {
- public boolean onTouch(View v, MotionEvent event) {
- if (gestureDetector.onTouchEvent(event)) {
- return true;
- }
- return false;
- }
- };
- }
-
- /** Gesture detector switches between sub-activities */
- class AstridGestureDetector extends SimpleOnGestureListener {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- try {
- if(Math.abs(e1.getY() - e2.getY()) > MAX_FLING_OTHER_AXIS)
- return false;
-
- Log.i("astrid", "Got fling. X: " + (e2.getX() - e1.getX()) +
- ", vel: " + velocityX + " Y: " + (e2.getY() - e1.getY()));
-
- // flick R to L
- if(e1.getX() - e2.getX() > FLING_DIST_THRESHOLD &&
- Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
-
- switch(getCurrentSubActivity().getActivityCode()) {
- case AC_TASK_LIST:
- switchToActivity(AC_TAG_LIST, null);
- return true;
- default:
- return false;
- }
- }
-
- // flick L to R
- else if(e2.getX() - e1.getX() > FLING_DIST_THRESHOLD &&
- Math.abs(velocityX) > FLING_VEL_THRESHOLD) {
-
- switch(getCurrentSubActivity().getActivityCode()) {
- case AC_TASK_LIST_W_TAG:
- switchToActivity(AC_TAG_LIST, null);
- return true;
- case AC_TAG_LIST:
- switchToActivity(AC_TASK_LIST, null);
- return true;
- default:
- return false;
- }
- }
- } catch (Exception e) {
- // ignore!
- }
-
- return false;
- }
- }
-
- /* ======================================================================
- * ==================================================== subactivity stuff
- * ====================================================================== */
-
- /** Switches to another activity, with appropriate animation */
- void switchToActivity(int activity, Bundle variables) {
- closeOptionsMenu();
-
- // and flip to them
- switch(getCurrentSubActivity().getActivityCode()) {
- case AC_TASK_LIST:
- switch(activity) {
- case AC_TAG_LIST:
- viewFlipper.showNext();
- break;
- case AC_TASK_LIST_W_TAG:
- viewFlipper.setDisplayedChild(taskListWTag.code);
- }
- break;
-
- case AC_TAG_LIST:
- switch(activity) {
- case AC_TASK_LIST:
- viewFlipper.showPrevious();
- break;
- case AC_TASK_LIST_W_TAG:
- viewFlipper.showNext();
- break;
- }
- break;
-
- case AC_TASK_LIST_W_TAG:
- switch(activity) {
- case AC_TAG_LIST:
- viewFlipper.showPrevious();
- break;
- case AC_TASK_LIST:
- viewFlipper.setDisplayedChild(taskList.code);
- }
- break;
- }
-
- // initialize the components
- switch(activity) {
- case AC_TASK_LIST:
- taskList.onDisplay(variables);
- break;
- case AC_TAG_LIST:
- tagList.onDisplay(variables);
- break;
- case AC_TASK_LIST_W_TAG:
- taskListWTag.onDisplay(variables);
- }
-
- lastActivityBundle = variables;
- }
-
- /** Helper method gets the currently visible subactivity */
- private SubActivity getCurrentSubActivity() {
- return (SubActivity)viewFlipper.getCurrentView().getTag();
- }
-
- /* ======================================================================
- * ======================================================= event handling
- * ====================================================================== */
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(LAST_ACTIVITY_TAG, getCurrentSubActivity().code);
- outState.putBundle(LAST_BUNDLE_TAG, lastActivityBundle);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if(getCurrentSubActivity().onKeyDown(keyCode, event))
- return true;
- else
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.clear();
- return getCurrentSubActivity().onPrepareOptionsMenu(menu);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if(resultCode == Constants.RESULT_GO_HOME) {
- switchToActivity(AC_TASK_LIST, null);
- } else
- getCurrentSubActivity().onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- if(hasFocus && shouldCloseInstance) { // user wants to quit
- finish();
- } else
- getCurrentSubActivity().onWindowFocusChanged(hasFocus);
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- if(getCurrentSubActivity().onMenuItemSelected(featureId, item))
- return true;
- else
- return super.onMenuItemSelected(featureId, item);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (gestureDetector.onTouchEvent(event))
- return true;
- else
- return false;
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- return getCurrentSubActivity().onRetainNonConfigurationInstance();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- taskController.close();
- tagController.close();
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskListAdapter.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskListAdapter.java
deleted file mode 100644
index 188f6773f..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskListAdapter.java
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import java.text.Format;
-import java.util.Date;
-import java.util.List;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.View.OnKeyListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.data.alerts.AlertController;
-import com.timsu.astrid.data.enums.Importance;
-import com.timsu.astrid.data.tag.TagController;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.data.task.TaskModelForList;
-import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
-import com.timsu.astrid.utilities.AstridUtilities;
-import com.timsu.astrid.utilities.DateUtilities;
-import com.timsu.astrid.utilities.Preferences;
-import com.timsu.astrid.utilities.TaskFieldsVisibility;
-
-/**
- * Adapter for displaying a list of TaskModelForList entities
- *
- * @author timsu
- *
- */
-public class TaskListAdapter extends ArrayAdapter {
-
- public static final int CONTEXT_EDIT_ID = Menu.FIRST + 50;
- public static final int CONTEXT_DELETE_ID = Menu.FIRST + 51;
- public static final int CONTEXT_TIMER_ID = Menu.FIRST + 52;
- public static final int CONTEXT_POSTPONE_ID = Menu.FIRST + 53;
-
- // keys for caching task properties
- private static final int KEY_NAME = 0;
- private static final int KEY_DEADLINE = 1;
- private static final int KEY_OVERDUE = 2;
- private static final int KEY_REPEAT = 3;
- private static final int KEY_REMINDERS = 4;
- private static final int KEY_TIMES = 5;
- private static final int KEY_TAGS = 6;
- private static final int KEY_HIDDEN = 7;
- private static final int KEY_EXPANDED = 8;
- private static final int KEY_CREATION = 9;
-
- private static final String CACHE_TRUE = "y";
-
- /** Number of seconds after which we display the full date deadline */
- private static final int FULL_DATE_THRESHOLD = 7*24*3600;
-
- // alarm date formatter
- private static Format alarmFormat = null;
-
- private final Activity activity;
- private List objects;
- private int resource;
- private LayoutInflater inflater;
- private TaskListAdapterHooks hooks;
- private TextView deletedItemView = new TextView(getContext());
-
- private Integer fontSizePreference;
- private AlertController alarmController;
-
- private TaskModelForList recentlyCompleted = null;
-
- /**
- * Call-back interface for interacting with parent activity
- *
- * @author timsu
- *
- */
- public interface TaskListAdapterHooks {
- List getTaskArray();
- String getTagsFor(TaskModelForList task);
- TaskController taskController();
- TagController tagController();
- void performItemClick(View v, int position);
- void onCreatedTaskListView(View v, TaskModelForList task);
-
- void editItem(TaskModelForList task);
- void toggleTimerOnItem(TaskModelForList task);
- void setSelectedItem(TaskIdentifier taskId);
- }
-
- /**
- * Constructor
- *
- * @param activity
- * @param context
- * @param resource
- * @param objects
- * @param hooks
- */
- public TaskListAdapter(Activity activity, int resource,
- List objects, TaskListAdapterHooks hooks) {
- super(activity, resource, objects);
-
- inflater = (LayoutInflater)activity.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- this.objects = objects;
- this.resource = resource;
- this.activity = activity;
- this.hooks = hooks;
-
- fontSizePreference = Preferences.getTaskListFontSize(getContext());
- alarmController = new AlertController(activity);
- }
-
- /** Sets the expanded state as desired */
- public void setExpanded(View view, TaskModelForList task, boolean state) {
- try {
- if(state) {
- task.clearCache();
- task.putCachedLabel(KEY_EXPANDED, CACHE_TRUE);
- hooks.setSelectedItem(task.getTaskIdentifier());
- } else {
- task.putCachedLabel(KEY_EXPANDED, null);
- hooks.setSelectedItem(null);
- }
-
- if(view != null) {
- setFieldContentsAndVisibility(view, task);
-
- // if the item is near the bottom of the list, we need to give
- // it focus so that the list knows there's new stuff down there
- int position = objects.indexOf(task);
- if(objects.size() - position < 2 && view.getParent() != null)
- ((ListView)view.getParent()).setSelection(position);
- view.requestFocus();
- }
- } catch (Exception e) {
- AstridUtilities.reportFlurryError("task-adapter-set-expanded", e);
- Log.e("astrid", "Error in setExpanded", e);
- }
- }
-
- /** Toggle the expanded state of this task */
- public void toggleExpanded(View view, TaskModelForList task) {
- if(CACHE_TRUE.equals(task.getCachedLabel(KEY_EXPANDED))) {
- setExpanded(view, task, false);
- } else {
- setExpanded(view, task, true);
- }
- }
-
- // ----------------------------------------------------------------------
- // --- code for setting up each view
- // ----------------------------------------------------------------------
-
- @Override
- /** Creates or reuses the view for a row in the list */
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = convertView;
-
- if(objects.size() <= position || objects.get(position) == null)
- return deletedItemView;
-
- if(view == null) {
- view = inflater.inflate(resource, parent, false);
- initializeView(view);
- addListeners(view);
- }
-
- setupView(view, objects.get(position));
-
- return view;
- }
-
- /**
- * Perform initial setup on the row. Called once for the whole list
- *
- * @param view
- */
- private void initializeView(View view) {
- final TextView name = ((TextView)view.findViewById(R.id.task_name));
- if(fontSizePreference != null && fontSizePreference > 0)
- name.setTextSize(fontSizePreference);
- deletedItemView.setText(getContext().getResources().getString(R.string.taskList_deleted));
- }
-
- /**
- * Setup the given view for the specified task. Called every row
- *
- * @param view
- * @param task
- */
- private void setupView(View view, final TaskModelForList task) {
- Resources r = activity.getResources();
-
- view.setTag(task);
- setFieldContentsAndVisibility(view, task);
-
- if(task == null) {
- return;
- }
-
- final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
- progress.setChecked(task.isTaskCompleted());
-
- final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
- if(task.getTimerStart() != null) {
- timer.setImageDrawable(r.getDrawable(R.drawable.icon_timer));
- view.setMinimumHeight(90);
- } else {
- timer.setImageDrawable(null);
- view.setMinimumHeight(45);
- }
-
- final TextView name = ((TextView)view.findViewById(R.id.task_name));
- setTaskAppearance(task, name, progress);
-
- hooks.onCreatedTaskListView(view, task);
- }
-
- /** Helper method to set the visibility based on if there's stuff inside */
- private static void setVisibility(TextView v) {
- if(v.getText().length() > 0)
- v.setVisibility(View.VISIBLE);
- else
- v.setVisibility(View.GONE);
- }
-
- /** Helper method to add a line and maybe a newline */
- private static void appendLine(StringBuilder sb, String line) {
- if(line.length() == 0)
- return;
-
- if(sb.length() > 0)
- sb.append("\n");
- sb.append(line);
- }
-
- /** Helper method to set the contents and visibility of each field */
- private void setFieldContentsAndVisibility(View view, TaskModelForList task) {
- if(task == null) {
- view.setVisibility(View.GONE);
- return;
- }
-
- Resources r = getContext().getResources();
- TaskFieldsVisibility visibleFields = Preferences.getTaskFieldsVisibility(activity);
- boolean isExpanded = CACHE_TRUE.equals(task.getCachedLabel(KEY_EXPANDED));
- StringBuilder details = new StringBuilder();
- StringBuilder expandedDetails = new StringBuilder();
-
- // expanded container
- final View expandedContainer = view.findViewById(R.id.expanded_layout);
- if(expandedContainer != null)
- expandedContainer.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
-
- // name
- final TextView name = ((TextView)view.findViewById(R.id.task_name)); {
- String cachedResult = task.getCachedLabel(KEY_NAME);
- if(cachedResult == null) {
- String nameValue = task.getName();
- if(task.getHiddenUntil() != null && task.getHiddenUntil().getTime() > System.currentTimeMillis()) {
- nameValue = "(" + r.getString(R.string.taskList_hiddenPrefix) + ") " + nameValue;
- task.putCachedLabel(KEY_HIDDEN, CACHE_TRUE);
- }
- cachedResult = nameValue;
- task.putCachedLabel(KEY_NAME, cachedResult);
- }
- name.setText(cachedResult);
- if(CACHE_TRUE.equals(task.getCachedLabel(KEY_HIDDEN)))
- name.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
- else
- name.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
- }
-
- // importance
- final View importance = view.findViewById(R.id.importance);
- if(visibleFields.IMPORTANCE) {
- importance.setBackgroundColor(r.getColor(
- task.getImportance().getColorResource()));
- } else
- importance.setVisibility(View.GONE);
-
- // due date / completion date
- final TextView deadlines = ((TextView)view.findViewById(R.id.text_deadlines));
- if(visibleFields.DEADLINE || isExpanded) {
- String cachedResult = task.getCachedLabel(KEY_DEADLINE);
- if(cachedResult == null) {
- StringBuilder label = new StringBuilder();
- if(task.isTaskCompleted()) {
- if(task.getCompletionDate() != null) {
- int secondsLeft = (int)((task.getCompletionDate().getTime() -
- System.currentTimeMillis()) / 1000);
- String finishedTime;
- if(Math.abs(secondsLeft) < FULL_DATE_THRESHOLD)
- finishedTime = r.getString(R.string.ago_string,
- DateUtilities.getDurationString(r,
- Math.abs(secondsLeft), 1));
- else
- finishedTime = DateUtilities.getFormattedDate(activity,
- task.getCompletionDate());
- label.append(r.getString(R.string.taskList_completedPrefix,
- finishedTime));
- }
- } else {
- boolean taskOverdue = false;
- if(task.getDefiniteDueDate() != null) {
- long timeLeft = (task.getDefiniteDueDate().getTime() -
- System.currentTimeMillis())/1000;
- if(timeLeft > 0) {
- if(timeLeft < FULL_DATE_THRESHOLD)
- label.append(r.getString(R.string.taskList_dueRelativeTime)).append(" ");
- else
- label.append(r.getString(R.string.taskList_dueAbsoluteDate)).append(" ");
- } else {
- taskOverdue = true;
- label.append(r.getString(R.string.taskList_overdueBy)).append(" ");
- task.putCachedLabel(KEY_OVERDUE, CACHE_TRUE);
- }
-
- if(timeLeft < FULL_DATE_THRESHOLD)
- label.append(DateUtilities.getDurationString(r,
- (int)Math.abs(timeLeft), 1, true));
- else
- label.append(DateUtilities.getFormattedDate(activity,
- task.getDefiniteDueDate()));
- }
- if(!taskOverdue && task.getPreferredDueDate() != null) {
- if(task.getDefiniteDueDate() != null)
- label.append(" / ");
- long timeLeft = (task.getPreferredDueDate().getTime() -
- System.currentTimeMillis())/1000;
- label.append(r.getString(R.string.taskList_goalPrefix)).append(" ");
- if(timeLeft > 0) {
- if(timeLeft < FULL_DATE_THRESHOLD)
- label.append(r.getString(R.string.taskList_dueRelativeTime)).append(" ");
- else
- label.append(r.getString(R.string.taskList_dueAbsoluteDate)).append(" ");
- } else {
- label.append(r.getString(R.string.taskList_overdueBy)).append(" ");
- task.putCachedLabel(KEY_OVERDUE, CACHE_TRUE);
- }
-
- if(timeLeft < FULL_DATE_THRESHOLD)
- label.append(DateUtilities.getDurationString(r,
- (int)Math.abs(timeLeft), 1, true));
- else
- label.append(DateUtilities.getFormattedDate(activity,
- task.getPreferredDueDate()));
- }
- }
- cachedResult = label.toString();
- task.putCachedLabel(KEY_DEADLINE, cachedResult);
- }
-
- if(visibleFields.DEADLINE) {
- deadlines.setText(cachedResult);
- if(CACHE_TRUE.equals(task.getCachedLabel(KEY_OVERDUE)))
- deadlines.setTextColor(r.getColor(R.color.taskList_dueDateOverdue));
- else
- deadlines.setTextColor(r.getColor(R.color.taskList_details));
- } else {
- expandedDetails.append(cachedResult);
- }
- }
- setVisibility(deadlines);
-
- // estimated / elapsed time
- if(visibleFields.TIMES || isExpanded) {
- String cachedResult = task.getCachedLabel(KEY_TIMES);
- if(cachedResult == null) {
- Integer elapsed = task.getElapsedSeconds();
- if(task.getTimerStart() != null)
- elapsed += (int)((System.currentTimeMillis() - task.getTimerStart().getTime())/1000L);
- Integer estimated = task.getEstimatedSeconds();
- StringBuilder label = new StringBuilder();
- if(estimated > 0) {
- label.append(r.getString(R.string.taskList_estimatedTimePrefix)).
- append(" ").
- append(DateUtilities.getDurationString(r, estimated, 2));
- if(elapsed > 0)
- label.append(" / ");
- }
- if(elapsed > 0) {
- label.append(r.getString(R.string.taskList_elapsedTimePrefix)).
- append(" ").
- append(DateUtilities.getAbbreviatedDurationString(r, elapsed, 2));
- }
- cachedResult = label.toString();
- task.putCachedLabel(KEY_TIMES, cachedResult);
- }
- if(visibleFields.TIMES)
- appendLine(details, cachedResult);
- else
- appendLine(expandedDetails, cachedResult);
- }
-
- // reminders
- if(visibleFields.REMINDERS || isExpanded) {
- String cachedResult = task.getCachedLabel(KEY_REMINDERS);
- if(cachedResult == null) {
- Integer notifyEvery = task.getNotificationIntervalSeconds();
- StringBuilder label = new StringBuilder();
- if(notifyEvery != null && notifyEvery > 0) {
- label.append(r.getString(R.string.taskList_periodicReminderPrefix)).
- append(" ").append(DateUtilities.getDurationString(r, notifyEvery, 1, true));
- }
-
- try {
- alarmController.open();
- List alerts = alarmController.getTaskAlerts(task.getTaskIdentifier());
- if(alerts.size() > 0) {
- Date nextAlarm = null;
- Date now = new Date();
- for(Date alert : alerts) {
- if(alert.after(now) && (nextAlarm == null ||
- alert.before(nextAlarm)))
- nextAlarm = alert;
- }
-
- if(nextAlarm != null) {
- if(label.length() > 0)
- label.append(". ");
- if(alarmFormat == null)
- alarmFormat = Preferences.getDateWithTimeFormat(activity);
- String alarmString = alarmFormat.format(nextAlarm);
- label.append(r.getString(R.string.taskList_alarmPrefix) +
- " " + alarmString);
- }
- }
- } finally {
- alarmController.close();
- }
- cachedResult = label.toString();
- task.putCachedLabel(KEY_REMINDERS, cachedResult);
- }
- if(visibleFields.REMINDERS)
- appendLine(details, cachedResult);
- else
- appendLine(expandedDetails, cachedResult);
- }
-
- // repeats
- if(visibleFields.REPEATS || isExpanded) {
- String cachedResult = task.getCachedLabel(KEY_REPEAT);
- if(cachedResult == null) {
- RepeatInfo repeatInfo = task.getRepeat();
- if((task.getFlags() & TaskModelForList.FLAG_SYNC_ON_COMPLETE) > 0) {
- cachedResult = r.getString(R.string.taskList_repeatsRemotely);
- } else if(repeatInfo != null) {
- cachedResult = r.getString(R.string.taskList_repeatPrefix) +
- " " + repeatInfo.getValue() + " " +
- r.getString(repeatInfo.getInterval().getLabelResource());
- } else {
- cachedResult = "";
- }
- task.putCachedLabel(KEY_REPEAT, cachedResult);
- }
- if(visibleFields.REPEATS)
- appendLine(details, cachedResult);
- else
- appendLine(expandedDetails, cachedResult);
- }
-
- // tags
- if(visibleFields.TAGS || isExpanded) {
- String cachedResult = task.getCachedLabel(KEY_TAGS);
- if(cachedResult == null) {
- String tagString = hooks.getTagsFor(task);
- if(tagString != null && !tagString.equals(""))
- cachedResult = r.getString(R.string.taskList_tagsPrefix) +
- " " + tagString;
- else
- cachedResult = "";
- task.putCachedLabel(KEY_TAGS, cachedResult);
- }
- if(visibleFields.TAGS)
- appendLine(details, cachedResult);
- else
- appendLine(expandedDetails, cachedResult);
- }
-
- // notes
- if(visibleFields.NOTES || isExpanded) {
- if(task.getNotes() != null && task.getNotes().length() > 0) {
- String notes = r.getString(R.string.taskList_notesPrefix) +
- " " + task.getNotes();
- if(visibleFields.NOTES)
- appendLine(details, notes);
- else
- appendLine(expandedDetails, notes);
- }
- }
-
- final TextView detailsView = ((TextView)view.findViewById(R.id.details));
- detailsView.setText(details.toString());
- setVisibility(detailsView);
-
-
- // expanded-only fields: creation date, ...
- if(isExpanded) {
- if(task.getCreationDate() != null) {
- String cachedResult = task.getCachedLabel(KEY_CREATION);
- if(cachedResult == null) {
- int secondsAgo = (int) ((System.currentTimeMillis() -
- task.getCreationDate().getTime())/1000);
- cachedResult = r.getString(R.string.taskList_createdPrefix) + " " +
- r.getString(R.string.ago_string, DateUtilities.getDurationString(r, Math.abs(secondsAgo), 1));
- task.putCachedLabel(KEY_CREATION, cachedResult);
- }
- appendLine(expandedDetails, cachedResult);
- }
-
- final Button timerButton =
- ((Button)view.findViewById(R.id.timer));
- if(task.getTimerStart() == null)
- timerButton.setText(r.getString(R.string.startTimer_label));
- else
- timerButton.setText(r.getString(R.string.stopTimer_label));
-
- final TextView expandedDetailsView =
- ((TextView)view.findViewById(R.id.expanded_details));
- expandedDetailsView.setText(expandedDetails.toString());
- }
-
- }
-
- /**
- * Refresh the item given at the specified position in the list (not
- * related to task identifier number)
- *
- * @param listView the parent view to refresh
- * @param position the index of the item
- */
- public void refreshItem(ListView listView, int position) {
- TaskModelForList task = hooks.getTaskArray().get(position);
- task.clearCache();
- listView.invalidateViews();
- }
-
- /** Set listeners for this view. This is called once total */
- private void addListeners(View view) {
- final CheckBox progress = ((CheckBox)view.findViewById(R.id.cb1));
-
- // clicking the check box
- progress.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- View parent = (View)v.getParent().getParent();
- TaskModelForList task = (TaskModelForList)parent.getTag();
-
- int newProgressPercentage;
- if(progress.isChecked())
- newProgressPercentage =
- TaskModelForList.getCompletedPercentage();
- else
- newProgressPercentage = 0;
-
- if(newProgressPercentage != task.getProgressPercentage()) {
- setTaskProgress(task, parent, newProgressPercentage);
- setupView(parent, task);
- }
- }
-
- });
-
- // clicking the text field
- view.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- TaskModelForList task = (TaskModelForList)v.getTag();
- toggleExpanded(v, task);
- }
- });
-
- // typing while selected something
- view.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if(event.getAction() != KeyEvent.ACTION_UP)
- return false;
-
- // hot-key to set task priority - 1-4 or ALT + Q-R
- Importance importance = null;
- if(event.getNumber() >= '1' && event.getNumber() <= '4')
- importance = Importance.values()[event.getNumber() - '1'];
-
- if(importance != null) {
- TaskModelForList task = (TaskModelForList)v.getTag();
- task.setImportance(importance);
- hooks.taskController().saveTask(task, false);
- setFieldContentsAndVisibility(v, task);
- return true;
- }
-
- return false;
- }
- });
-
- // long-clicking the text field
- view.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- TaskModelForList task = (TaskModelForList)v.getTag();
- int id = (int)task.getTaskIdentifier().getId();
-
- menu.add(id, CONTEXT_EDIT_ID, Menu.NONE,
- R.string.taskList_context_edit);
- menu.add(id, CONTEXT_DELETE_ID, Menu.NONE,
- R.string.taskList_context_delete);
-
- int timerTitle;
- if(task.getTimerStart() == null)
- timerTitle = R.string.taskList_context_startTimer;
- else
- timerTitle = R.string.taskList_context_stopTimer;
- menu.add(id, CONTEXT_TIMER_ID, Menu.NONE, timerTitle);
-
- if(task.getDefiniteDueDate() != null ||
- task.getPreferredDueDate() != null)
- menu.add(id, CONTEXT_POSTPONE_ID, Menu.NONE,
- R.string.taskList_context_postpone);
-
- menu.setHeaderTitle(task.getName());
- }
- });
-
- // clicking one of the expanded buttons
- Button editButton = (Button)view.findViewById(R.id.edit);
- editButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- View parent = (View)v.getParent().getParent().getParent().getParent();
- TaskModelForList task = (TaskModelForList)parent.getTag();
- hooks.editItem(task);
- }
- });
-
- Button toggleTimerButton = (Button)view.findViewById(R.id.timer);
- toggleTimerButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- View parent = (View)v.getParent().getParent().getParent().getParent();
- TaskModelForList task = (TaskModelForList)parent.getTag();
- hooks.toggleTimerOnItem(task);
- }
- });
- }
-
- /** Helper method to set a task's progress and then adjust its appearance
- *
- * @param task
- * @param view
- * @param progress
- */
- private void setTaskProgress(final TaskModelForList task, View view, int progress) {
- final ImageView timer = ((ImageView)view.findViewById(R.id.imageLeft));
- task.setProgressPercentage(progress);
- hooks.taskController().saveTask(task, false);
-
- // show this task as completed even if it has repeats
- if(progress == 100) {
- recentlyCompleted = task;
- FlurryAgent.onEvent("complete-task");
- } else {
- FlurryAgent.onEvent("uncomplete-task");
- recentlyCompleted = null;
- }
-
- // if our timer is on, ask if we want to stop
- if(progress == 100 && task.getTimerStart() != null) {
- new AlertDialog.Builder(activity)
- .setTitle(R.string.question_title)
- .setMessage(R.string.stop_timer_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- task.stopTimerAndUpdateElapsedTime();
- hooks.taskController().saveTask(task, false);
- timer.setVisibility(View.GONE);
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .show();
- }
- }
-
- /** Helper method to adjust a tasks' apperance if the task is completed or
- * uncompleted.
- *
- * @param task
- * @param name
- * @param progress
- */
- private void setTaskAppearance(TaskModelForList task, TextView name, CheckBox progress) {
- Resources r = activity.getResources();
-
- if(task.isTaskCompleted() || task == recentlyCompleted) {
- name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
- name.setTextColor(r.getColor(R.color.task_list_done));
- progress.setButtonDrawable(R.drawable.btn_check0);
- progress.setChecked(true);
- } else {
- name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
- name.setTextColor(r.getColor(task.getTaskColorResource(getContext())));
-
- float completedPercentage = 0;
- if(task.getEstimatedSeconds() > 0) {
- completedPercentage = 1.0f * task.getElapsedSeconds() /
- task.getEstimatedSeconds();
- }
-
- if(completedPercentage < 0.25f)
- progress.setButtonDrawable(R.drawable.btn_check0);
- else if(completedPercentage < 0.5f)
- progress.setButtonDrawable(R.drawable.btn_check25);
- else if(completedPercentage < 0.75f)
- progress.setButtonDrawable(R.drawable.btn_check50);
- else
- progress.setButtonDrawable(R.drawable.btn_check75);
- }
- }
-
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskListSubActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskListSubActivity.java
deleted file mode 100644
index 6b975a3ad..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskListSubActivity.java
+++ /dev/null
@@ -1,1369 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.StaleDataException;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnClickListener;
-import android.view.View.OnCreateContextMenuListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.AdapterView.OnItemClickListener;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.activities.TaskListAdapter.TaskListAdapterHooks;
-import com.timsu.astrid.data.tag.TagController;
-import com.timsu.astrid.data.tag.TagIdentifier;
-import com.timsu.astrid.data.tag.TagModelForView;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.data.task.TaskModelForEdit;
-import com.timsu.astrid.data.task.TaskModelForList;
-import com.timsu.astrid.sync.SynchronizationService;
-import com.timsu.astrid.sync.Synchronizer;
-import com.timsu.astrid.sync.Synchronizer.SynchronizerListener;
-import com.timsu.astrid.utilities.AstridUtilities;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.DialogUtilities;
-import com.timsu.astrid.utilities.Notifications;
-import com.timsu.astrid.utilities.Preferences;
-import com.timsu.astrid.utilities.TasksXmlExporter;
-import com.timsu.astrid.utilities.TasksXmlImporter;
-import com.timsu.astrid.widget.FilePickerBuilder;
-import com.timsu.astrid.widget.NumberPicker;
-import com.timsu.astrid.widget.NumberPickerDialog;
-import com.timsu.astrid.widget.NNumberPickerDialog.OnNNumberPickedListener;
-import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
-
-/**
- * Primary view for the Astrid Application. Lists all of the tasks in the
- * system, and allows users to interact with them.
- *
- * @author timsu
- *
- */
-public class TaskListSubActivity extends SubActivity {
-
- // bundle tokens
- public static final String TAG_TOKEN = "tag";
- public static final String FROM_NOTIFICATION_TOKEN = "notify";
- public static final String NOTIF_FLAGS_TOKEN = "notif_flags";
- public static final String NOTIF_REPEAT_TOKEN = "notif_repeat";
- public static final String LOAD_INSTANCE_TOKEN = "id";
-
- // activities
- private static final int ACTIVITY_CREATE = 0;
- private static final int ACTIVITY_EDIT = 1;
- private static final int ACTIVITY_TAGS = 2;
- private static final int ACTIVITY_PREFERENCES = 3;
- private static final int ACTIVITY_SYNCHRONIZE = 4;
-
- // menu codes
- private static final int INSERT_ID = Menu.FIRST;
- private static final int FILTERS_ID = Menu.FIRST + 1;
- private static final int TAGS_ID = Menu.FIRST + 2;
- private static final int SYNC_ID = Menu.FIRST + 3;
- private static final int MORE_ID = Menu.FIRST + 4;
-
- private static final int OPTIONS_SYNC_ID = Menu.FIRST + 10;
- private static final int OPTIONS_SETTINGS_ID = Menu.FIRST + 11;
- private static final int OPTIONS_HELP_ID = Menu.FIRST + 12;
- private static final int OPTIONS_CLEANUP_ID = Menu.FIRST + 13;
- private static final int OPTIONS_QUICK_TIPS = Menu.FIRST + 14;
- private static final int OPTIONS_EXPORT = Menu.FIRST + 15;
- private static final int OPTIONS_IMPORT = Menu.FIRST + 16;
-
- private static final int CONTEXT_FILTER_HIDDEN = Menu.FIRST + 20;
- private static final int CONTEXT_FILTER_DONE = Menu.FIRST + 21;
- private static final int CONTEXT_FILTER_TAG = Menu.FIRST + 22;
- private static final int CONTEXT_SORT_AUTO = Menu.FIRST + 23;
- private static final int CONTEXT_SORT_ALPHA = Menu.FIRST + 24;
- private static final int CONTEXT_SORT_DUEDATE = Menu.FIRST + 25;
- private static final int CONTEXT_SORT_REVERSE = Menu.FIRST + 26;
- private static final int CONTEXT_SORT_GROUP = Menu.FIRST;
-
- // other constants
- private static final int SORTFLAG_FILTERDONE = (1 << 5);
- private static final int SORTFLAG_FILTERHIDDEN = (1 << 6);
- private static final float POSTPONE_STAT_PCT = 0.4f;
- private static final int AUTO_REFRESH_MAX_LIST_SIZE = 50;
-
- // UI components
- private ListView listView;
- private ImageButton addButton;
- private View layout;
- private TextView loadingText;
-
- // indicator flag set if task list should be refreshed (something changed
- // in another activity)
- public static boolean shouldRefreshTaskList = false;
-
- // indicator flag set if synchronization window has been opened & closed
- static boolean syncPreferencesOpened = false;
-
- // other instance variables
- static class TaskListContext {
- HashMap tagMap;
- List taskArray;
- HashMap tasksById;
- HashMap taskTags;
- TaskModelForList selectedTask = null;
- Thread loadingThread = null;
- TaskListAdapter listAdapter = null;
- TagModelForView filterTag = null;
- CharSequence windowTitle;
- }
-
- Handler handler = null;
- Long selectedTaskId = null;
- Runnable reLoadRunnable = null;
- private TaskListContext context;
-
- // display filters
- private static boolean filterShowHidden = false;
- private static boolean filterShowDone = false;
- private static SortMode sortMode = SortMode.AUTO;
- private static boolean sortReverse = false;
-
- /*
- * ======================================================================
- * ======================================================= initialization
- * ======================================================================
- */
-
- public TaskListSubActivity(TaskList parent, int code, View view) {
- super(parent, code, view);
- }
-
- @Override
- /* Called when loading up the activity */
- public void onDisplay(final Bundle variables) {
- // process task that's selected, if any
- if (variables != null && variables.containsKey(LOAD_INSTANCE_TOKEN)) {
- selectedTaskId = variables.getLong(LOAD_INSTANCE_TOKEN);
- } else {
- selectedTaskId = null;
- }
- setupUIComponents();
-
- // declare the reload runnable, which is called when the task list
- // wants to reload itself
- reLoadRunnable = new Runnable() {
- public void run() {
- handler.post(new Runnable() {
- public void run() {
- loadingText.setText(getParent().getResources()
- .getString(R.string.updating));
- }
- });
-
- fillData();
- }
- };
-
- // if we have a non-configuration instance (i.e. the screen was just
- // rotated), use that instead of loading the whole task list again.
- // this makes screen rotation an inexpensive operation
- if (getLastNonConfigurationInstance() != null) {
- context = (TaskListContext) getLastNonConfigurationInstance();
- listView.setAdapter(context.listAdapter);
- onTaskListLoaded();
- return;
- }
-
- context = new TaskListContext();
- if (selectedTaskId == null)
- context.selectedTask = null;
-
- // process tag to filter, if any (intercept UNTAGGED identifier, if
- // applicable)
- if (variables != null && variables.containsKey(TAG_TOKEN)) {
- TagIdentifier identifier = new TagIdentifier(variables
- .getLong(TAG_TOKEN));
- context.tagMap = getTagController().getAllTagsAsMap();
- if (context.tagMap.containsKey(identifier))
- context.filterTag = context.tagMap.get(identifier);
- else if (identifier.equals(TagModelForView.UNTAGGED_IDENTIFIER))
- context.filterTag = TagModelForView.getUntaggedModel();
- else
- Toast.makeText(getParent(), R.string.missing_tag,
- Toast.LENGTH_SHORT).show();
-
- FlurryAgent.onEvent("filter-by-tag");
- }
-
- // time to go! creates a thread that loads the task list, then
- // displays the reminder box if it is requested
- context.loadingThread = new Thread(new Runnable() {
- public void run() {
- handler.post(new Runnable() {
- public void run() {
- loadingText.setVisibility(View.VISIBLE);
- }
- });
-
- loadTaskListSort();
- fillData();
-
- // open up reminder box
- if (variables != null
- && variables.containsKey(NOTIF_FLAGS_TOKEN)
- && context.selectedTask != null) {
- FlurryAgent.onEvent("open-notification");
- handler.post(new Runnable() {
- public void run() {
- long repeatInterval = 0;
- int flags = 0;
-
- if (variables.containsKey(NOTIF_REPEAT_TOKEN))
- repeatInterval = variables
- .getLong(NOTIF_REPEAT_TOKEN);
- flags = variables.getInt(NOTIF_FLAGS_TOKEN);
- showNotificationAlert(context.selectedTask,
- repeatInterval, flags);
- }
- });
- }
- }
- });
- context.loadingThread.start();
- }
-
- /** Initialize UI components */
- public void setupUIComponents() {
- handler = new Handler();
-
- listView = (ListView) findViewById(R.id.tasklist);
- loadingText = (TextView) findViewById(R.id.loading);
- addButton = (ImageButton) findViewById(R.id.quickAddButton);
- addButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- TextView quickAdd = (TextView)findViewById(R.id.quickAddText);
- if(quickAdd.getText().length() > 0) {
- quickAddTask(quickAdd.getText().toString());
- quickAdd.setText(""); //$NON-NLS-1$
- reloadList();
- } else {
- createTask(null);
- }
- }
- });
-
- layout = getView();
- layout.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- if (menu.hasVisibleItems())
- return;
- onCreateMoreOptionsMenu(menu);
- }
- });
-
- // disable quick-add keyboard until user requests it
- EditText quickAdd = (EditText)findViewById(R.id.quickAddText);
- AstridUtilities.suppressVirtualKeyboard(quickAdd);
- }
-
- /**
- * Quick-add a new task
- * @param title
- * @return
- */
- @SuppressWarnings("nls")
- protected TaskModelForEdit quickAddTask(String title) {
- TaskModelForEdit task = getTaskController().createNewTaskForEdit();
- try {
- task.setName(title);
- getTaskController().saveTask(task, false);
- if (context.filterTag != null) {
- getTagController().addTag(task.getTaskIdentifier(),
- context.filterTag.getTagIdentifier());
- }
- return task;
- } catch (Exception e) {
- AstridUtilities.reportFlurryError("quick-add-task", e);
- return task;
- }
-
- }
-
- @Override
- /* Create options menu (displayed when user presses menu key) */
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem item;
-
- item = menu.add(Menu.NONE, INSERT_ID, Menu.NONE,
- R.string.taskList_menu_insert);
- item.setIcon(android.R.drawable.ic_menu_add);
- item.setAlphabeticShortcut('n');
-
- item = menu.add(Menu.NONE, FILTERS_ID, Menu.NONE,
- R.string.taskList_menu_filters);
- item.setIcon(android.R.drawable.ic_menu_view);
- item.setAlphabeticShortcut('f');
-
- item = menu.add(Menu.NONE, TAGS_ID, Menu.NONE,
- R.string.taskList_menu_tags);
- item.setIcon(android.R.drawable.ic_menu_myplaces);
- item.setAlphabeticShortcut('t');
-
- if (Constants.SYNCHRONIZE && Preferences.shouldDisplaySyncButton(getParent())) {
- item = menu.add(Menu.NONE, SYNC_ID, Menu.NONE,
- R.string.taskList_menu_syncshortcut);
- item.setIcon(android.R.drawable.ic_menu_upload);
- item.setAlphabeticShortcut('s');
- }
-
- item = menu.add(Menu.NONE, MORE_ID, Menu.NONE,
- R.string.taskList_menu_more);
- item.setIcon(android.R.drawable.ic_menu_more);
- item.setAlphabeticShortcut('m');
-
- return true;
- }
-
- /** Create 'more options' menu */
- public boolean onCreateMoreOptionsMenu(Menu menu) {
- MenuItem item;
-
- if(Constants.SYNCHRONIZE) {
- item = menu.add(Menu.NONE, OPTIONS_SYNC_ID, Menu.NONE,
- R.string.taskList_menu_sync);
- item.setAlphabeticShortcut('s');
- }
-
- item = menu.add(Menu.NONE, OPTIONS_SETTINGS_ID, Menu.NONE,
- R.string.taskList_menu_settings);
- item.setAlphabeticShortcut('p');
-
- item = menu.add(Menu.NONE, OPTIONS_CLEANUP_ID, Menu.NONE,
- R.string.taskList_menu_cleanup);
-
- item = menu.add(Menu.NONE, OPTIONS_QUICK_TIPS, Menu.NONE,
- R.string.taskList_menu_tips);
-
- item = menu.add(Menu.NONE, OPTIONS_EXPORT, Menu.NONE,
- R.string.taskList_menu_export);
-
- item = menu.add(Menu.NONE, OPTIONS_IMPORT, Menu.NONE,
- R.string.taskList_menu_import);
-
- item = menu.add(Menu.NONE, OPTIONS_HELP_ID, Menu.NONE,
- R.string.taskList_menu_help);
- item.setAlphabeticShortcut('h');
-
- return true;
- }
-
- /**
- * Enum that determines how the task list is sorted. Contains a comparison
- * method that determines sorting order.
- *
- * @author timsu
- *
- */
- private enum SortMode {
- ALPHA {
- @Override
- int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
- return arg0.getName().toLowerCase().compareTo(
- arg1.getName().toLowerCase());
- }
- },
- DUEDATE {
- long getDueDate(TaskModelForList task) {
- Date definite = task.getDefiniteDueDate();
- Date preferred = task.getPreferredDueDate();
- if (definite != null && preferred != null) {
- if (preferred.getTime() < System.currentTimeMillis())
- return definite.getTime();
- return preferred.getTime();
- } else if (definite != null)
- return definite.getTime();
- else if (preferred != null)
- return preferred.getTime();
- else
- return new Date(2020, 1, 1).getTime();
- }
-
- @Override
- int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
- return (int) ((getDueDate(arg0) - getDueDate(arg1)) / 1000);
- }
- },
- AUTO {
- @Override
- int compareTo(TaskModelForList arg0, TaskModelForList arg1) {
- return arg0.getTaskWeight() - arg1.getTaskWeight();
- }
- };
-
- abstract int compareTo(TaskModelForList arg0, TaskModelForList arg1);
- };
-
- /*
- * ======================================================================
- * ======================================================== notifications
- * ======================================================================
- */
-
- /** Called when user clicks on a notification to get here */
- private void showNotificationAlert(final TaskModelForList task,
- final long repeatInterval, final int flags) {
- Resources r = getResources();
-
- // clear notifications
- Notifications.clearAllNotifications(getParent(), task
- .getTaskIdentifier());
-
- String[] strings = new String[] {
- r.getString(R.string.notify_yes),
- r.getString(R.string.notify_done),
- r.getString(R.string.notify_snooze),
- r.getString(R.string.notify_no)
- };
-
- String response;
- if (Preferences.shouldShowNags(getParent())) {
- String[] responses = r.getStringArray(R.array.reminder_responses);
- response = responses[new Random().nextInt(responses.length)];
- } else
- response = r.getString(R.string.taskList_nonag_reminder);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getParent());
- final AlertDialog dialog;
-
- LayoutInflater inflater = (LayoutInflater) getParent().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View dialogView = inflater.inflate(R.layout.notification_dialog, null);
-
- builder.setTitle(task.getName());
- builder.setIcon(android.R.drawable.ic_dialog_info);
- builder.setCancelable(true);
- builder.setView(dialogView);
- dialog = builder.create();
-
- TextView message = (TextView)dialogView.findViewById(R.id.message);
- message.setText(response);
- message.setTextSize(18);
-
- ListView items = (ListView)dialogView.findViewById(R.id.items);
- items.setAdapter(new ArrayAdapter(getParent(),
- android.R.layout.simple_list_item_checked, strings));
- items.setFocusableInTouchMode(true);
- items.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> arg0, View arg1, int which,
- long id) {
- switch(which) {
- case 0:
- break;
- case 1:
- task.setProgressPercentage(TaskModelForList.COMPLETE_PERCENTAGE);
- getTaskController().saveTask(task, false);
- break;
- case 2:
- snoozeAlert(task, repeatInterval, flags);
- break;
- case 3:
- TaskList.shouldCloseInstance = true;
- closeActivity();
- break;
- }
- dialog.dismiss();
- }
- });
-
- dialog.show();
- }
-
- /**
- * Helper method to "snooze" an alert (i.e. set a new one for some time from
- * now.
- *
- * @param task
- * @param repeatInterval
- * @param flags
- */
- private void snoozeAlert(final TaskModelForList task,
- final long repeatInterval, final int flags) {
- DialogUtilities.hourMinutePicker(getParent(), getResources().getString(
- R.string.notify_snooze_title), new OnNNumberPickedListener() {
- public void onNumbersPicked(int[] values) {
- int snoozeSeconds = values[0] * 3600 + values[1] * 60;
- Notifications.createSnoozeAlarm(getParent(), task
- .getTaskIdentifier(), snoozeSeconds, flags,
- repeatInterval);
-
- TaskList.shouldCloseInstance = true;
- closeActivity();
- }
- });
- }
-
- /*
- * ======================================================================
- * ====================================================== populating list
- * ======================================================================
- */
-
- /** Helper method returns true if the task is considered 'hidden' */
- private boolean isTaskHidden(TaskModelForList task) {
- if (task == context.selectedTask)
- return false;
-
- if (task.isHidden())
- return true;
-
- if (context.filterTag == null) {
- if (context.taskTags.get(task).contains(
- TagModelForView.HIDDEN_FROM_MAIN_LIST_PREFIX))
- return true;
- }
-
- return false;
- }
-
- /** Fill in the Task List with our tasks */
- private synchronized void fillData() {
- int hiddenTasks = 0; // # of tasks hidden
- int completedTasks = 0; // # of tasks on list that are done
-
- handler.post(new Runnable() {
- public void run() {
- loadingText.setVisibility(View.VISIBLE);
- }
- });
-
- try {
- // get a cursor to the task list
- Cursor tasksCursor;
- if (context.filterTag != null) { // Filter by TAG
- LinkedList tasks;
-
- // Check "named" Tag vs. "Untagged"
- TagIdentifier tagId = context.filterTag.getTagIdentifier();
- if (!tagId.equals(TagModelForView.UNTAGGED_IDENTIFIER)) {
- tasks = getTagController().getTaggedTasks(tagId);
- } else {
- tasks = getTagController().getUntaggedTasks();
- }
- tasksCursor = getTaskController().getTaskListCursorById(tasks);
-
- } else {
- if (filterShowDone)
- tasksCursor = getTaskController().getAllTaskListCursor();
- else
- tasksCursor = getTaskController().getActiveTaskListCursor();
- }
- // if internal state is compromised, bail out
- if(tasksCursor == null)
- return;
- startManagingCursor(tasksCursor);
- context.taskArray = Collections
- .synchronizedList(getTaskController()
- .createTaskListFromCursor(tasksCursor));
-
- // read tags and apply filters
- context.tagMap = getTagController().getAllTagsAsMap();
- context.taskTags = new HashMap();
- StringBuilder tagBuilder = new StringBuilder();
- context.tasksById = new HashMap();
-
- // null may occur when extremely low memory(?)
- // tsu: i'm not sure why, but we get NPE's from the for loop
- if(context.taskArray == null)
- return;
-
- for (Iterator i = context.taskArray.iterator(); i
- .hasNext();) {
- if (Thread.interrupted())
- return;
-
- final TaskModelForList task = i.next();
-
- if (!filterShowDone) {
- if (task.isTaskCompleted()) {
- i.remove();
- continue;
- }
- }
-
- if (selectedTaskId != null
- && task.getTaskIdentifier().getId() == selectedTaskId) {
- context.selectedTask = task;
- }
-
- // get list of tags
- LinkedList tagIds = getTagController()
- .getTaskTags(task.getTaskIdentifier());
- tagBuilder.delete(0, tagBuilder.length());
- for (Iterator j = tagIds.iterator(); j.hasNext();) {
- TagIdentifier id = j.next();
- if(!context.tagMap.containsKey(id)) {
- // tag identifier does not exist anymore
- Log.w("task-list", "WARNING: tag id was deleted - " + id);
- getTagController().removeTag(task.getTaskIdentifier(), id);
- continue;
- }
-
- TagModelForView tag = context.tagMap.get(id);
-
- // bad entry from map (can this ever happen? at least we
- // won't crash
- if(tag == null) {
- Log.w("task-list", "WARNING: tag id was null: " + id);
- continue;
- }
-
- tagBuilder.append(tag.getName());
- if (j.hasNext())
- tagBuilder.append(", ");
- }
- context.taskTags.put(task, tagBuilder.toString());
-
- // hide hidden
- if (!filterShowHidden) {
- if (isTaskHidden(task)) {
- hiddenTasks++;
- i.remove();
- continue;
- }
- }
-
- context.tasksById.put(task.getTaskIdentifier().getId(), task);
-
- if (task.isTaskCompleted())
- completedTasks++;
- }
-
- HashMap args = new HashMap();
- args.put("tasks", Integer.toString(context.taskArray.size()));
- FlurryAgent.onEvent("loaded-tasks", args);
-
- } catch (StaleDataException e) {
- // happens when you rotate the screen while the thread is
- // still running. i don't think it's avoidable?
- Log.w("astrid", "StaleDataException", e);
- return;
- } catch (final IllegalStateException e) {
-
- // activity has been closed. suppress error
- if(e.getMessage().contains("attempt to acquire a reference on a close SQLiteClosable")) {
- Log.w("astrid", "Caught error", e);
- AstridUtilities.reportFlurryError("task-list-error-caught", e);
-
-
- // may happen when you run out of memory usually
- } else {
- AstridUtilities.reportFlurryError("task-list-error", e);
- Log.e("astrid", "Error loading task list", e);
- handler.post(new Runnable() {
- public void run() {
- if (!e.getMessage().contains("Couldn't init cursor window"))
- return;
- DialogUtilities.okDialog(getParent(), "Ran out of memory! "
- + "Try restarting Astrid...", null);
- }
- });
- return;
- }
- } catch (SQLiteException e) {
- // log it but don't throw it
- Log.e("astrid", "Error loading task list", e);
- // DialogUtilities.okDialog(getParent(), "Error loading task list: " + e.getMessage() + ".\n\nOffending line: " + e.getStackTrace()[0], null);
-
- } catch (final Exception e) {
- AstridUtilities.reportFlurryError("task-list-error", e);
- Log.e("astrid", "Error loading task list", e);
- DialogUtilities.okDialog(getParent(), "Error loading task list: " + e.getMessage() + ".\n\nOffending line: " + e.getStackTrace()[0], null);
- onTaskListLoaded();
- return;
- }
-
- try {
- int activeTasks = context.taskArray.size() - completedTasks;
-
- // sort task list
- Collections.sort(context.taskArray, new Comparator() {
- public int compare(TaskModelForList arg0, TaskModelForList arg1) {
- return sortMode.compareTo(arg0, arg1);
- }
- });
- if (sortReverse)
- Collections.reverse(context.taskArray);
-
- final int finalCompleted = completedTasks;
- final int finalActive = activeTasks;
- final int finalHidden = hiddenTasks;
-
- handler.post(new Runnable() {
- public void run() {
- Resources r = getResources();
- StringBuilder title = new StringBuilder().append(
- r.getString(R.string.taskList_titlePrefix)).append(" ");
- if (context.filterTag != null) {
- if (TagModelForView.UNTAGGED_IDENTIFIER.equals(context.filterTag.getTagIdentifier())) {
- title.append(
- r.getString(R.string.taskList_titleUntagged)).append(
- " ");
- } else {
- title.append(
- r.getString(R.string.taskList_titleTagPrefix,
- context.filterTag.getName())).append(" ");
- }
- }
-
- if (finalCompleted > 0)
- title.append(r.getQuantityString(R.plurals.NactiveTasks,
- finalActive, finalActive, context.taskArray.size()));
- else
- title.append(r.getQuantityString(R.plurals.Ntasks,
- context.taskArray.size(), context.taskArray.size()));
- if (finalHidden > 0)
- title.append(" (+").append(finalHidden).append(" ").append(
- r.getString(R.string.taskList_hiddenSuffix)).append(")");
- context.windowTitle = title;
- }
- });
- } catch (Exception e) {
- AstridUtilities.reportFlurryError("task-list-error-block2", e);
- Log.e("astrid", "Error loading task list block 2", e);
- }
-
- onTaskListLoaded();
- }
-
- /** Sets up the interface after everything has been loaded */
- private void onTaskListLoaded() {
- // set up the title
- handler.post(new Runnable() {
- public void run() {
- setTitle(context.windowTitle);
- setUpListUI();
- loadingText.setVisibility(View.GONE);
- }
- });
- }
-
- class TaskListHooks implements TaskListAdapterHooks {
-
- private HashMap myTaskTags;
- private List myTaskArray;
-
- public TaskListHooks() {
- this.myTaskTags = context.taskTags;
- this.myTaskArray = context.taskArray;
- }
-
- public TagController tagController() {
- return getTagController();
- }
-
- public String getTagsFor(TaskModelForList task) {
- return myTaskTags.get(task);
- }
-
- public List getTaskArray() {
- return myTaskArray;
- }
-
- public TaskController taskController() {
- return getTaskController();
- }
-
- public void performItemClick(View v, int position) {
- listView.performItemClick(v, position, 0);
- }
-
- public void onCreatedTaskListView(View v, TaskModelForList task) {
- v.setOnTouchListener(getGestureListener());
- }
-
- public void editItem(TaskModelForList task) {
- editTask(task);
- }
-
- public void toggleTimerOnItem(TaskModelForList task) {
- toggleTimer(task);
- }
-
- public void setSelectedItem(TaskIdentifier taskId) {
- if (taskId == null) {
- selectedTaskId = null;
- context.selectedTask = null;
- } else
- selectedTaskId = taskId.getId();
- }
- }
-
- /** Set up the adapter for our task list */
- private void setUpListUI() {
- // if something happened, don't finish setting up the list
- if(context.taskArray == null)
- return;
-
- // set up our adapter
- context.listAdapter = new TaskListAdapter(getParent(),
- R.layout.task_list_row, context.taskArray, new TaskListHooks());
- listView.setAdapter(context.listAdapter);
- listView.setItemsCanFocus(true);
-
- if (context.selectedTask != null) {
- try {
- int selectedPosition = context.listAdapter.getPosition(context.selectedTask);
- View v = listView.getChildAt(selectedPosition);
- context.listAdapter.setExpanded(v, context.selectedTask, true);
- listView.setSelection(selectedPosition);
- } catch (Exception e) {
- AstridUtilities.reportFlurryError("task-list-selected", e);
- Log.e("astrid", "error with selected task", e);
- }
- }
-
- // filters context menu
- listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
-
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- if (menu.hasVisibleItems())
- return;
- Resources r = getResources();
- menu.setHeaderTitle(R.string.taskList_filter_title);
-
- MenuItem item = menu.add(Menu.NONE, CONTEXT_FILTER_HIDDEN,
- Menu.NONE, R.string.taskList_filter_hidden);
- item.setCheckable(true);
- item.setChecked(filterShowHidden);
-
- item = menu.add(Menu.NONE, CONTEXT_FILTER_DONE, Menu.NONE,
- R.string.taskList_filter_done);
- item.setCheckable(true);
- item.setChecked(filterShowDone);
-
- if (context.filterTag != null) {
- item = menu.add(Menu.NONE, CONTEXT_FILTER_TAG, Menu.NONE,
- r.getString(R.string.taskList_filter_tagged,
- context.filterTag.getName()));
- item.setCheckable(true);
- item.setChecked(true);
- }
-
- item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_AUTO,
- Menu.NONE, R.string.taskList_sort_auto);
- item.setChecked(sortMode == SortMode.AUTO);
- item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_ALPHA,
- Menu.NONE, R.string.taskList_sort_alpha);
- item.setChecked(sortMode == SortMode.ALPHA);
- item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_DUEDATE,
- Menu.NONE, R.string.taskList_sort_duedate);
- item.setChecked(sortMode == SortMode.DUEDATE);
- menu.setGroupCheckable(CONTEXT_SORT_GROUP, true, true);
-
- item = menu.add(CONTEXT_SORT_GROUP, CONTEXT_SORT_REVERSE,
- Menu.NONE, R.string.taskList_sort_reverse);
- item.setCheckable(true);
- item.setChecked(sortReverse);
- }
- });
-
- listView.setOnTouchListener(getGestureListener());
- }
-
- private void reloadList() {
- if (context.loadingThread != null && context.loadingThread.isAlive()) {
- context.loadingThread.interrupt();
- }
- context.loadingThread = new Thread(reLoadRunnable);
- context.loadingThread.start();
- }
-
- /*
- * ======================================================================
- * ======================================================= event handlers
- * ======================================================================
- */
-
- @Override
- protected Object onRetainNonConfigurationInstance() {
- return context;
- }
-
- @Override
- protected boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (context.filterTag != null) {
- showTagsView();
- return true;
- } else {
- // close the app
- getParent().finish();
- }
- }
-
- // if it's a printable character that's not 1..4
- char character = event.getNumber();
- if (character >= '!' && (character < '1' || character > '4') &&
- character <= '~') {
- createTask(event.getNumber());
- return true;
- }
-
- return false;
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- if (context.loadingThread != null && context.loadingThread.isAlive())
- context.loadingThread.stop();
- }
-
- @Override
- void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- if (hasFocus) {
- if (shouldRefreshTaskList)
- reloadList();
- else if (syncPreferencesOpened) {
- syncPreferencesOpened = false;
-
- if (TaskList.synchronizeNow) {
- TaskList.synchronizeNow = false;
- synchronize();
- }
-
- // schedule synchronization service
- if(Constants.SYNCHRONIZE)
- SynchronizationService.scheduleService(getParent());
-
- } else if (context.taskArray != null
- && context.taskArray.size() > 0
- && context.taskArray.size() < AUTO_REFRESH_MAX_LIST_SIZE) {
-
- // invalidate caches
- for (TaskModelForList task : context.taskArray)
- task.clearCache();
- listView.invalidateViews();
- }
- }
-
- shouldRefreshTaskList = false;
- }
-
- /** Invoke synchronizer */
- private void synchronize() {
- if(!Constants.SYNCHRONIZE)
- return;
-
- Synchronizer sync = new Synchronizer(false);
- sync.setTagController(getTagController());
- sync.setTaskController(getTaskController());
- sync.synchronize(getParent(), new SynchronizerListener() {
- public void onSynchronizerFinished(int numServicesSynced) {
- if (numServicesSynced == 0) {
- DialogUtilities.okDialog(getParent(), getResources()
- .getString(R.string.sync_no_synchronizers), null);
- return;
- }
- reloadList();
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Constants.RESULT_SYNCHRONIZE) {
- synchronize();
- } else if (requestCode == ACTIVITY_TAGS) {
- switchToActivity(TaskList.AC_TAG_LIST, null);
- } else if(requestCode == ACTIVITY_PREFERENCES) {
- reloadList();
- }
- }
-
- /** Call an activity to create the given task */
- private void createTask(Character startCharacter) {
- Intent intent = new Intent(getParent(), TaskEdit.class);
- if (context.filterTag != null)
- intent.putExtra(TaskEdit.TAG_NAME_TOKEN, context.filterTag
- .getName());
- if (startCharacter != null)
- intent.putExtra(TaskEdit.START_CHAR_TOKEN, startCharacter);
- launchActivity(intent, ACTIVITY_CREATE);
- }
-
- /** Show a dialog box and delete the task specified */
- private void deleteTask(final TaskModelForList task) {
- new AlertDialog.Builder(getParent()).setTitle(R.string.delete_title)
- .setMessage(R.string.delete_this_task_title).setIcon(
- android.R.drawable.ic_dialog_alert).setPositiveButton(
- android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- context.listAdapter.remove(task);
- context.taskArray.remove(task);
- getTaskController().deleteTask(
- task.getTaskIdentifier());
- }
- }).setNegativeButton(android.R.string.cancel, null)
- .show();
- }
-
- /** Take you to the task edit page */
- private void editTask(TaskModelForList task) {
- Intent intent = new Intent(getParent(), TaskEdit.class);
- intent.putExtra(TaskEdit.LOAD_INSTANCE_TOKEN, task.getTaskIdentifier()
- .getId());
- launchActivity(intent, ACTIVITY_EDIT);
- }
-
- /** Toggle the timer */
- private void toggleTimer(TaskModelForList task) {
- if(task == null)
- return;
-
- if (task.getTimerStart() == null) {
- FlurryAgent.onEvent("start-timer");
- task.setTimerStart(new Date());
- } else {
- FlurryAgent.onEvent("stop-timer");
- task.stopTimerAndUpdateElapsedTime();
- }
- getTaskController().saveTask(task, false);
- context.listAdapter.refreshItem(listView, context.taskArray
- .indexOf(task));
- }
-
- /** Show the tags view */
- public void showTagsView() {
- switchToActivity(TaskList.AC_TAG_LIST, null);
- }
-
- @Override
- public void launchActivity(Intent intent, int requestCode) {
- super.launchActivity(intent, requestCode);
- }
-
- /** Save the sorting mode to the preferences */
- private void saveTaskListSort() {
- int sortId = sortMode.ordinal() + 1;
-
- if (filterShowDone)
- sortId |= SORTFLAG_FILTERDONE;
- if (filterShowHidden)
- sortId |= SORTFLAG_FILTERHIDDEN;
-
- if (sortReverse)
- sortId *= -1;
-
- Preferences.setTaskListSort(getParent(), sortId);
- }
-
- /** Save the sorting mode to the preferences */
- private void loadTaskListSort() {
- int sortId = Preferences.getTaskListSort(getParent());
- if (sortId == 0)
- return;
- sortReverse = sortId < 0;
- sortId = Math.abs(sortId);
-
- filterShowDone = (sortId & SORTFLAG_FILTERDONE) > 0;
- filterShowHidden = (sortId & SORTFLAG_FILTERHIDDEN) > 0;
-
- sortId = sortId & ~(SORTFLAG_FILTERDONE | SORTFLAG_FILTERHIDDEN);
-
- sortMode = SortMode.values()[sortId - 1];
- }
-
- /** Compute date after postponing tasks */
- private Date computePostponeDate(Date input, long postponeMillis,
- boolean shiftFromTodayIfPast) {
- if (input != null) {
- if (shiftFromTodayIfPast
- && input.getTime() < System.currentTimeMillis())
- input = new Date();
- input = new Date(input.getTime() + postponeMillis);
- }
-
- return input;
- }
-
- /** Show a dialog box and delete old tasks as requested */
- private void cleanOldTasks() {
- final Resources r = getResources();
- new NumberPickerDialog(getParent(), new OnNumberPickedListener() {
- public void onNumberPicked(NumberPicker view, int number) {
- Date date = new Date(System.currentTimeMillis() - 24L * 3600
- * 1000 * number);
- int deleted = getTaskController()
- .deleteCompletedTasksOlderThan(date);
- DialogUtilities.okDialog(getParent(), r.getQuantityString(
- R.plurals.Ntasks, deleted, deleted)
- + " " + r.getString(R.string.taskList_deleted), null);
- if (TaskListSubActivity.filterShowDone)
- reloadList();
- }
- }, r.getString(R.string.taskList_cleanup_dialog), 30, 5, 0, 999).show();
- }
-
- /** Show a dialog box to postpone your tasks */
- private void postponeTask(final TaskModelForList task) {
- FlurryAgent.onEvent("postpone-task");
-
- final Resources r = getResources();
- DialogUtilities.dayHourPicker(getParent(), r
- .getString(R.string.taskList_postpone_dialog),
- new OnNNumberPickedListener() {
- public void onNumbersPicked(int[] values) {
- long postponeMillis = (values[0] * 24 + values[1]) * 3600L * 1000;
- if (postponeMillis <= 0)
- return;
-
- task.setPreferredDueDate(computePostponeDate(task
- .getPreferredDueDate(), postponeMillis, true));
- task.setDefiniteDueDate(computePostponeDate(task
- .getDefiniteDueDate(), postponeMillis, true));
- task.setHiddenUntil(computePostponeDate(task
- .getHiddenUntil(), postponeMillis, false));
-
- // show nag
- int postponeCount = getTaskController()
- .fetchTaskPostponeCount(
- task.getTaskIdentifier()) + 1;
- if (Preferences.shouldShowNags(getParent())) {
- Random random = new Random();
- final String nagText;
- if (postponeCount > 1
- && random.nextFloat() < POSTPONE_STAT_PCT) {
- nagText = r.getString(
- R.string.taskList_postpone_count,
- postponeCount);
- } else {
- String[] nags = r
- .getStringArray(R.array.postpone_nags);
- nagText = nags[random.nextInt(nags.length)];
- }
-
- handler.post(new Runnable() {
- public void run() {
- Toast.makeText(getParent(), nagText,
- Toast.LENGTH_LONG).show();
- }
- });
- }
- task.setPostponeCount(postponeCount);
-
- getTaskController().saveTask(task, false);
- getTaskController().updateAlarmForTask(
- task.getTaskIdentifier());
- context.listAdapter.refreshItem(listView,
- context.taskArray.indexOf(task));
- }
- });
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, final MenuItem item) {
- final TaskModelForList task;
-
- switch (item.getItemId()) {
- // --- options menu items
- case INSERT_ID:
- createTask(null);
- return true;
- case FILTERS_ID:
- listView.showContextMenu();
- return true;
- case TAGS_ID:
- showTagsView();
- return true;
- case SYNC_ID:
- synchronize();
- return true;
- case MORE_ID:
- layout.showContextMenu();
- return true;
-
- // --- more options menu items
- case OPTIONS_SYNC_ID:
- syncPreferencesOpened = true;
- launchActivity(new Intent(getParent(), SyncPreferences.class),
- ACTIVITY_SYNCHRONIZE);
- return true;
- case OPTIONS_SETTINGS_ID:
- launchActivity(new Intent(getParent(), EditPreferences.class),
- ACTIVITY_PREFERENCES);
- return true;
- case OPTIONS_HELP_ID:
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri
- .parse(Constants.HELP_URL));
- launchActivity(browserIntent, 0);
- return true;
- case OPTIONS_QUICK_TIPS:
- DialogUtilities.okDialog(getParent(), getResources().getString(
- R.string.quick_tips), null);
- return true;
- case OPTIONS_CLEANUP_ID:
- cleanOldTasks();
- return true;
- case OPTIONS_EXPORT:
- exportTasks();
- return true;
- case OPTIONS_IMPORT:
- importTasks();
- return true;
-
- // --- list context menu items
- case TaskListAdapter.CONTEXT_EDIT_ID:
- task = context.tasksById.get((long) item.getGroupId());
- editTask(task);
- return true;
- case TaskListAdapter.CONTEXT_DELETE_ID:
- task = context.tasksById.get((long) item.getGroupId());
- deleteTask(task);
- return true;
- case TaskListAdapter.CONTEXT_TIMER_ID:
- task = context.tasksById.get((long) item.getGroupId());
- toggleTimer(task);
- return true;
- case TaskListAdapter.CONTEXT_POSTPONE_ID:
- task = context.tasksById.get((long) item.getGroupId());
- postponeTask(task);
- return true;
-
- // --- display context menu items
- case CONTEXT_FILTER_HIDDEN:
- TaskListSubActivity.filterShowHidden = !filterShowHidden;
- saveTaskListSort();
- reloadList();
- return true;
- case CONTEXT_FILTER_DONE:
- TaskListSubActivity.filterShowDone = !filterShowDone;
- saveTaskListSort();
- reloadList();
- return true;
- case CONTEXT_FILTER_TAG:
- switchToActivity(TaskList.AC_TASK_LIST, null);
- return true;
- case CONTEXT_SORT_AUTO:
- if (sortMode == SortMode.AUTO)
- return true;
- TaskListSubActivity.sortReverse = false;
- TaskListSubActivity.sortMode = SortMode.AUTO;
- saveTaskListSort();
- reloadList();
- return true;
- case CONTEXT_SORT_ALPHA:
- if (sortMode == SortMode.ALPHA)
- return true;
- TaskListSubActivity.sortReverse = false;
- TaskListSubActivity.sortMode = SortMode.ALPHA;
- saveTaskListSort();
- reloadList();
- return true;
- case CONTEXT_SORT_DUEDATE:
- if (sortMode == SortMode.DUEDATE)
- return true;
- TaskListSubActivity.sortReverse = false;
- TaskListSubActivity.sortMode = SortMode.DUEDATE;
- saveTaskListSort();
- reloadList();
- return true;
- case CONTEXT_SORT_REVERSE:
- TaskListSubActivity.sortReverse = !sortReverse;
- saveTaskListSort();
- reloadList();
- return true;
- }
-
- return false;
- }
-
- private void importTasks() {
- final Runnable reloadList = new Runnable() {
- public void run() {
- reloadList();
- }
- };
- final Context ctx = this.getParent();
- FilePickerBuilder.OnFilePickedListener listener = new FilePickerBuilder.OnFilePickedListener() {
- @Override
- public void onFilePicked(String filePath) {
- TasksXmlImporter importer = new TasksXmlImporter(ctx);
- importer.setInput(filePath);
- importer.importTasks(reloadList);
- }
- };
- DialogUtilities.filePicker(ctx,
- ctx.getString(R.string.import_file_prompt),
- TasksXmlExporter.getExportDirectory(),
- listener);
- }
-
- private void exportTasks() {
- TasksXmlExporter exporter = new TasksXmlExporter(false);
- exporter.setContext(getParent());
- exporter.exportTasks(TasksXmlExporter.getExportDirectory());
- }
-
- /*
- * ======================================================================
- * ===================================================== getters / setters
- * ======================================================================
- */
-
- public TagModelForView getFilterTag() {
- return context.filterTag;
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationActivity.java
deleted file mode 100644
index 9d1a19176..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationActivity.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.os.Bundle;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.data.task.AbstractTaskModel;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.DialogUtilities;
-
-/** Abstract activity that operates on a single task. Use the generic parameter
- * to pass in the model class you are working with.
- *
- * @author timsu
- */
-public abstract class TaskModificationActivity extends Activity {
- public static final String LOAD_INSTANCE_TOKEN = "id";
- protected TaskController controller;
- protected MODEL_TYPE model;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- try {
- controller = new TaskController(this);
- controller.open();
-
- // check if we have a TaskIdentifier
- TaskIdentifier identifier = null;
- Bundle extras = getIntent().getExtras();
- if(savedInstanceState != null && savedInstanceState.containsKey(LOAD_INSTANCE_TOKEN)) {
- identifier = new TaskIdentifier(savedInstanceState.getLong(
- LOAD_INSTANCE_TOKEN));
- } else if(extras != null && extras.containsKey(LOAD_INSTANCE_TOKEN))
- identifier = new TaskIdentifier(extras.getLong(
- LOAD_INSTANCE_TOKEN));
-
- model = getModel(identifier);
- } catch (Exception e) {
- showErrorAndFinish(R.string.error_opening, e);
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- // set up flurry
- FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- FlurryAgent.onEndSession(this);
- }
-
- protected void showErrorAndFinish(int prefix, Throwable e) {
- Resources r = getResources();
- DialogUtilities.okDialog(this,
- r.getString(prefix) + " " +
- e.getLocalizedMessage(), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- }
-
- abstract protected MODEL_TYPE getModel(TaskIdentifier identifier);
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- controller.close();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if(model.getTaskIdentifier() != null)
- outState.putLong(LOAD_INSTANCE_TOKEN, model.getTaskIdentifier().getId());
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationTabbedActivity.java b/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationTabbedActivity.java
deleted file mode 100644
index b85d40786..000000000
--- a/astrid/src-legacy/com/timsu/astrid/activities/TaskModificationTabbedActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * ASTRID: Android's Simple Task Recording Dashboard
- *
- * Copyright (c) 2009 Tim Su
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-package com.timsu.astrid.activities;
-
-import android.app.TabActivity;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.os.Bundle;
-
-import com.flurry.android.FlurryAgent;
-import com.timsu.astrid.R;
-import com.timsu.astrid.data.task.AbstractTaskModel;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.utilities.AstridUtilities;
-import com.timsu.astrid.utilities.Constants;
-import com.timsu.astrid.utilities.DialogUtilities;
-
-/** Hack hack for tabbed activity. Should provide better interaction
- *
- * @author timsu
- */
-public abstract class TaskModificationTabbedActivity extends TabActivity {
- protected static final String LOAD_INSTANCE_TOKEN = TaskModificationActivity.LOAD_INSTANCE_TOKEN;
-
- protected TaskController controller;
- protected MODEL_TYPE model;
-
- protected class Context {
- protected MODEL_TYPE cModel;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- controller = new TaskController(this);
- controller.open();
-
- // if we've saved our model, use that instead
- if(getLastNonConfigurationInstance() != null) {
- Context context = (Context)getLastNonConfigurationInstance();
- model = context.cModel;
- } else {
- try {
- // check if we have a TaskIdentifier
- TaskIdentifier identifier = null;
- Bundle extras = getIntent().getExtras();
- if(savedInstanceState != null && savedInstanceState.containsKey(LOAD_INSTANCE_TOKEN)) {
- identifier = new TaskIdentifier(savedInstanceState.getLong(
- LOAD_INSTANCE_TOKEN));
- } else if(extras != null && extras.containsKey(LOAD_INSTANCE_TOKEN))
- identifier = new TaskIdentifier(extras.getLong(
- LOAD_INSTANCE_TOKEN));
-
- model = getModel(identifier);
- } catch (Exception e) {
- showErrorAndFinish(R.string.error_opening, e);
- }
- }
- }
-
- /**
- * Called when screen is rotated. We return the model we are editing so that
- * we will have access to it instead of having to re-read from the database
- */
- @Override
- public Object onRetainNonConfigurationInstance() {
- Context context = new Context();
- context.cModel = model;
- return context;
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- FlurryAgent.onEndSession(this);
- }
-
- protected void showErrorAndFinish(int prefix, Throwable e) {
- AstridUtilities.reportFlurryError("taskedit", e);
- Resources r = getResources();
- DialogUtilities.okDialog(this,
- r.getString(prefix) + " " +
- e.getLocalizedMessage(), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- }
-
- abstract protected MODEL_TYPE getModel(TaskIdentifier identifier);
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- controller.close();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if(model.getTaskIdentifier() != null)
- outState.putLong(LOAD_INSTANCE_TOKEN, model.getTaskIdentifier().getId());
- }
-}
diff --git a/astrid/src-legacy/com/timsu/astrid/appwidget/AstridAppWidgetProvider.java b/astrid/src-legacy/com/timsu/astrid/appwidget/AstridAppWidgetProvider.java
index 2701ef70f..1af8186d5 100644
--- a/astrid/src-legacy/com/timsu/astrid/appwidget/AstridAppWidgetProvider.java
+++ b/astrid/src-legacy/com/timsu/astrid/appwidget/AstridAppWidgetProvider.java
@@ -16,10 +16,10 @@ import android.view.View;
import android.widget.RemoteViews;
import com.timsu.astrid.R;
-import com.timsu.astrid.activities.TaskEdit;
-import com.timsu.astrid.activities.TaskList;
import com.timsu.astrid.data.task.TaskController;
import com.timsu.astrid.data.task.TaskModelForWidget;
+import com.todoroo.astrid.activity.TaskEditActivity;
+import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.service.AstridDependencyInjector;
public class AstridAppWidgetProvider extends AppWidgetProvider {
@@ -81,7 +81,7 @@ public class AstridAppWidgetProvider extends AppWidgetProvider {
.getTasksForWidget(Integer.toString(numberOfTasks));
taskController.close();
- Intent listIntent = new Intent(context, TaskList.class);
+ Intent listIntent = new Intent(context, TaskListActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
listIntent, 0);
views.setOnClickPendingIntent(R.id.taskbody, pendingIntent);
@@ -114,7 +114,7 @@ public class AstridAppWidgetProvider extends AppWidgetProvider {
views.setTextColor(textIDs[i], textColor);
}
- Intent editIntent = new Intent(context, TaskEdit.class);
+ Intent editIntent = new Intent(context, TaskEditActivity.class);
pendingIntent = PendingIntent.getActivity(context, 0,
editIntent, 0);
views.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/AbstractTaskModel.java b/astrid/src-legacy/com/timsu/astrid/data/task/AbstractTaskModel.java
index 438c79009..e3b206038 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/AbstractTaskModel.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/AbstractTaskModel.java
@@ -340,8 +340,8 @@ public abstract class AbstractTaskModel extends AbstractModel {
// --- helper classes
public static class RepeatInfo {
- private RepeatInterval interval;
- private int value;
+ private final RepeatInterval interval;
+ private final int value;
public RepeatInfo(RepeatInterval repeatInterval, int value) {
this.interval = repeatInterval;
@@ -362,6 +362,26 @@ public abstract class AbstractTaskModel extends AbstractModel {
return value;
}
+ public static int toSingleField(RepeatInfo repeatInfo) {
+ int repeat;
+ if(repeatInfo == null)
+ repeat = 0;
+ else
+ repeat = (repeatInfo.value << REPEAT_VALUE_OFFSET) +
+ repeatInfo.interval.ordinal();
+ return repeat;
+ }
+
+ public static RepeatInfo fromSingleField(int repeat) {
+ if(repeat == 0)
+ return null;
+ int value = repeat >> REPEAT_VALUE_OFFSET;
+ RepeatInterval interval = RepeatInterval.values()
+ [repeat - (value << REPEAT_VALUE_OFFSET)];
+
+ return new RepeatInfo(interval, value);
+ }
+
}
// --- task identifier
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskController.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskController.java
index fb691d2ec..38bdf766e 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskController.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskController.java
@@ -36,8 +36,6 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
-import com.timsu.astrid.activities.TaskEdit;
-import com.timsu.astrid.activities.TaskListSubActivity;
import com.timsu.astrid.appwidget.AstridAppWidgetProvider.UpdateService;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController;
@@ -47,7 +45,6 @@ import com.timsu.astrid.data.task.AbstractTaskModel.TaskModelDatabaseHelper;
import com.timsu.astrid.provider.TasksProvider;
import com.timsu.astrid.sync.Synchronizer;
import com.timsu.astrid.sync.Synchronizer.SynchronizerListener;
-import com.timsu.astrid.utilities.Notifications;
/**
* Controller for task-related operations
@@ -308,12 +305,12 @@ public class TaskController extends AbstractController {
// task timer was updated, update notification bar
if(values.containsKey(AbstractTaskModel.TIMER_START)) {
// show notification bar if timer was started
- if(values.getAsLong(AbstractTaskModel.TIMER_START) != 0) {
- Notifications.showTimingNotification(context,
- task.getTaskIdentifier(), task.getName());
- } else {
- Notifications.clearAllNotifications(context, task.getTaskIdentifier());
- }
+// if(values.getAsLong(AbstractTaskModel.TIMER_START) != 0) {
+// ReminderService.showTimingNotification(context,
+// task.getTaskIdentifier(), task.getName());
+// } else {
+// ReminderService.clearAllNotifications(context, task.getTaskIdentifier());
+// }
}
// due date was updated, update calendar event
@@ -342,8 +339,8 @@ public class TaskController extends AbstractController {
// create new start and end date for this event
ContentValues newValues = new ContentValues();
- TaskEdit.createCalendarStartEndTimes(task.getPreferredDueDate(),
- task.getDefiniteDueDate(), estimated, newValues);
+ /*TaskEditActivity.createCalendarStartEndTimes(task.getPreferredDueDate(),
+ task.getDefiniteDueDate(), estimated, newValues); TODO */
cr.update(uri, newValues, null, null);
}
} catch (Exception e) {
@@ -379,7 +376,7 @@ public class TaskController extends AbstractController {
Synchronizer synchronizer = new Synchronizer(model.getTaskIdentifier());
synchronizer.synchronize(context, new SynchronizerListener() {
public void onSynchronizerFinished(int numServicesSynced) {
- TaskListSubActivity.shouldRefreshTaskList = true;
+// TaskListSubActivity.shouldRefreshTaskList = true;
}
});
}
@@ -391,7 +388,7 @@ public class TaskController extends AbstractController {
/** Clean up state from a task. Called when deleting or completing it */
private void cleanupTask(TaskIdentifier taskId, boolean isRepeating) {
// delete notifications & alarms
- Notifications.deleteAlarm(context, null, taskId.getId());
+// ReminderService.deleteAlarm(context, null, taskId.getId());
// delete calendar event if not repeating
if(!isRepeating) {
@@ -579,7 +576,7 @@ public class TaskController extends AbstractController {
TaskModelForNotify task = fetchTaskForNotify(taskId);
AlertController alertController = new AlertController(context);
alertController.open();
- Notifications.updateAlarm(context, this, alertController, task);
+// ReminderService.updateAlarm(context, this, alertController, task);
alertController.close();
}
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForEdit.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForEdit.java
index f96943335..914c70e97 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForEdit.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForEdit.java
@@ -24,12 +24,11 @@ import java.util.Date;
import android.database.Cursor;
import com.timsu.astrid.data.enums.Importance;
-import com.timsu.astrid.utilities.Notifications.Notifiable;
/** Fields that you would want to edit in the TaskModel */
-public class TaskModelForEdit extends AbstractTaskModel implements Notifiable {
+public class TaskModelForEdit extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
NAME,
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForHandlers.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForHandlers.java
index 58cb3b5cf..b0a88756a 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForHandlers.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForHandlers.java
@@ -28,14 +28,12 @@ import android.database.Cursor;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController;
-import com.timsu.astrid.utilities.Notifications;
-import com.timsu.astrid.utilities.Notifications.Notifiable;
/** Fields that you would want to read or edit in the onTaskSave and onTaskComplete
* event handlers */
-public class TaskModelForHandlers extends AbstractTaskModel implements Notifiable {
+public class TaskModelForHandlers extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
@@ -92,7 +90,7 @@ public class TaskModelForHandlers extends AbstractTaskModel implements Notifiabl
// reset periodic alerts
setLastNotificationTime(null);
- Notifications.updateAlarm(context, taskController, alertController, this);
+// ReminderService.updateAlarm(context, taskController, alertController, this);
alertController.close();
}
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForList.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForList.java
index 07d04fa59..6ad03d20a 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForList.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForList.java
@@ -108,7 +108,7 @@ public class TaskModelForList extends AbstractTaskModel {
}
/** map of cached display labels */
- private HashMap displayLabels = new HashMap();
+ private final HashMap displayLabels = new HashMap();
public String getCachedLabel(int key) {
return displayLabels.get(key);
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForNotify.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForNotify.java
index a7213878e..54b40c64c 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForNotify.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForNotify.java
@@ -24,12 +24,11 @@ import java.util.Date;
import android.database.Cursor;
import com.timsu.astrid.data.AbstractController;
-import com.timsu.astrid.utilities.Notifications.Notifiable;
/** Fields that you would want to see in the TaskView activity */
-public class TaskModelForNotify extends AbstractTaskModel implements Notifiable {
+public class TaskModelForNotify extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
diff --git a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForSync.java b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForSync.java
index 59320bbe9..3a3c54d9b 100644
--- a/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForSync.java
+++ b/astrid/src-legacy/com/timsu/astrid/data/task/TaskModelForSync.java
@@ -25,12 +25,11 @@ import android.database.Cursor;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.enums.Importance;
-import com.timsu.astrid.utilities.Notifications.Notifiable;
/** Fields that you would want to synchronize in the TaskModel */
-public class TaskModelForSync extends AbstractTaskModel implements Notifiable {
+public class TaskModelForSync extends AbstractTaskModel {
static String[] FIELD_LIST = new String[] {
AbstractController.KEY_ROWID,
diff --git a/astrid/src-legacy/com/timsu/astrid/sync/SynchronizationProvider.java b/astrid/src-legacy/com/timsu/astrid/sync/SynchronizationProvider.java
index e975312c6..21b77f869 100644
--- a/astrid/src-legacy/com/timsu/astrid/sync/SynchronizationProvider.java
+++ b/astrid/src-legacy/com/timsu/astrid/sync/SynchronizationProvider.java
@@ -47,7 +47,6 @@ import com.timsu.astrid.data.task.TaskIdentifier;
import com.timsu.astrid.data.task.TaskModelForSync;
import com.timsu.astrid.utilities.AstridUtilities;
import com.timsu.astrid.utilities.DialogUtilities;
-import com.timsu.astrid.utilities.Notifications;
import com.timsu.astrid.utilities.Preferences;
/** A service that synchronizes with Astrid
@@ -57,7 +56,7 @@ import com.timsu.astrid.utilities.Preferences;
*/
public abstract class SynchronizationProvider {
- private int id;
+ private final int id;
static ProgressDialog progressDialog;
private Handler syncHandler;
protected Synchronizer synchronizer;
@@ -478,8 +477,8 @@ public abstract class SynchronizationProvider {
stats.localCreatedTasks++;
}
- Notifications.updateAlarm(context, taskController, alertController,
- task);
+// ReminderService.updateAlarm(context, taskController, alertController,
+// task);
postUpdate(new ProgressUpdater(stats.localUpdatedTasks,
remoteTasks.size()));
}
diff --git a/astrid/src-legacy/com/timsu/astrid/sync/Synchronizer.java b/astrid/src-legacy/com/timsu/astrid/sync/Synchronizer.java
index 936c2cc50..38bf96f9c 100644
--- a/astrid/src-legacy/com/timsu/astrid/sync/Synchronizer.java
+++ b/astrid/src-legacy/com/timsu/astrid/sync/Synchronizer.java
@@ -26,7 +26,6 @@ import android.app.Activity;
import android.content.Context;
import android.util.Log;
-import com.timsu.astrid.activities.TaskListSubActivity;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.AlertController;
import com.timsu.astrid.data.sync.SyncDataController;
@@ -195,7 +194,7 @@ public class Synchronizer {
if(getSingleTaskForSync() == null)
Preferences.setSyncLastSync(context, new Date());
if(!isService) {
- TaskListSubActivity.shouldRefreshTaskList = true;
+// TaskListSubActivity.shouldRefreshTaskList = true;
}
Log.i("sync", "Synchronization Service Finished");
@@ -203,13 +202,13 @@ public class Synchronizer {
// --- controller stuff
- private ControllerWrapper syncController =
+ private final ControllerWrapper syncController =
new ControllerWrapper(SyncDataController.class);
- private ControllerWrapper taskController =
+ private final ControllerWrapper taskController =
new ControllerWrapper(TaskController.class);
- private ControllerWrapper tagController =
+ private final ControllerWrapper tagController =
new ControllerWrapper(TagController.class);
- private ControllerWrapper alertController =
+ private final ControllerWrapper alertController =
new ControllerWrapper(AlertController.class);
private static class ControllerWrapper {
diff --git a/astrid/src-legacy/com/timsu/astrid/utilities/LocaleReceiver.java b/astrid/src-legacy/com/timsu/astrid/utilities/LocaleReceiver.java
index 1e8429e26..cc965bec6 100644
--- a/astrid/src-legacy/com/timsu/astrid/utilities/LocaleReceiver.java
+++ b/astrid/src-legacy/com/timsu/astrid/utilities/LocaleReceiver.java
@@ -11,7 +11,6 @@ import android.util.Log;
import com.timsu.astrid.R;
import com.timsu.astrid.activities.LocaleEditAlerts;
-import com.timsu.astrid.activities.TagListSubActivity;
import com.timsu.astrid.data.tag.TagController;
import com.timsu.astrid.data.tag.TagIdentifier;
import com.timsu.astrid.data.task.TaskController;
@@ -57,13 +56,14 @@ public class LocaleReceiver extends BroadcastReceiver {
HashSet activeTasks = taskController.getActiveVisibleTaskIdentifiers();
LinkedList tasks = tagController.getTaggedTasks(
new TagIdentifier(tagId));
- int count = TagListSubActivity.countActiveTasks(activeTasks, tasks);
+// int count = TagListSubActivity.countActiveTasks(activeTasks, tasks);
+ int count = 0;
if(count > 0) {
Resources r = context.getResources();
String reminder = r.getString(R.string.notif_tagNotification).
replace("$NUM", r.getQuantityString(R.plurals.Ntasks, count, count)).
replace("$TAG", tagName);
- Notifications.showTagNotification(context, tagId, reminder);
+// ReminderService.showTagNotification(context, tagId, reminder);
Preferences.setLocaleLastAlertTime(context, tagId,
System.currentTimeMillis());
diff --git a/astrid/src-legacy/com/timsu/astrid/utilities/Notifications.java b/astrid/src-legacy/com/timsu/astrid/utilities/Notifications.java
deleted file mode 100644
index 8457fef42..000000000
--- a/astrid/src-legacy/com/timsu/astrid/utilities/Notifications.java
+++ /dev/null
@@ -1,605 +0,0 @@
-package com.timsu.astrid.utilities;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.util.Log;
-
-import com.timsu.astrid.R;
-import com.timsu.astrid.activities.TaskListNotify;
-import com.timsu.astrid.activities.TaskListSubActivity;
-import com.timsu.astrid.data.alerts.AlertController;
-import com.timsu.astrid.data.task.TaskController;
-import com.timsu.astrid.data.task.TaskIdentifier;
-import com.timsu.astrid.data.task.TaskModelForList;
-import com.timsu.astrid.data.task.TaskModelForNotify;
-import com.timsu.astrid.data.task.TaskModelForReminder;
-
-public class Notifications extends BroadcastReceiver {
-
- private static final String ID_KEY = "id";
- private static final String FLAGS_KEY = "flags";
- private static final String REPEAT_KEY = "repeat";
- private static final int TAG_ID_OFFSET = 100000;
-
- // stuff for scheduling
- /** minimum # of seconds before a deadline to notify */
- private static final int DEADLINE_NOTIFY_SECS = 60 * 60;
- /** # of seconds after deadline to repeat reminder*/
- private static final int DEADLINE_REPEAT = 10 * 60;
-
- // flags
- public static final int FLAG_DEFINITE_DEADLINE = 1 << 0;
- public static final int FLAG_PREFERRED_DEADLINE = 1 << 1;
- public static final int FLAG_OVERDUE = 1 << 2;
- public static final int FLAG_PERIODIC = 1 << 3;
- public static final int FLAG_FIXED = 1 << 4;
- public static final int FLAG_SNOOZE = 1 << 5;
- /** # of bits to shift the fixed alert ID */
- public static final int FIXED_ID_SHIFT = 6;
-
- private static Random random = new Random();
-
- /** Something we can create a notification for */
- public interface Notifiable {
- public TaskIdentifier getTaskIdentifier();
- public Integer getNotificationIntervalSeconds();
- public boolean isTaskCompleted();
- public Date getHiddenUntil();
- public Date getDefiniteDueDate();
- public Date getPreferredDueDate();
- public Date getLastNotificationDate();
- public int getNotificationFlags();
- public Integer getEstimatedSeconds();
- }
-
- @Override
- /** Alarm intent */
- public void onReceive(Context context, Intent intent) {
- long id = intent.getLongExtra(ID_KEY, 0);
- int flags = intent.getIntExtra(FLAGS_KEY, 0);
-
- Resources r = context.getResources();
- String reminder;
- if((flags & FLAG_DEFINITE_DEADLINE) > 0)
- reminder = r.getString(R.string.notif_definiteDueDate);
- else if((flags & FLAG_PREFERRED_DEADLINE) > 0)
- reminder = r.getString(R.string.notif_preferredDueDate);
- else
- reminder = getRandomReminder(r);
-
- long repeatInterval = intent.getLongExtra(REPEAT_KEY, 0);
- if(Constants.DEBUG)
- Log.e("ALARM", "Alarm triggered id " + id +", flags " + flags +
- ", repeat " + repeatInterval);
-
- if(!showNotification(context, id, flags, repeatInterval, reminder)) {
- deleteAlarm(context, intent, id);
- NotificationManager nm = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel((int)id);
- }
- }
-
- // --- alarm manager stuff
-
- private static boolean shouldDeleteAlarm(Notifiable task) {
- if(task.isTaskCompleted())
- return true;
-
- return false;
- }
-
- public static void scheduleAllAlarms(Context context) {
- try {
- TaskController taskController = new TaskController(context);
- taskController.open();
- AlertController alertController = new AlertController(context);
- alertController.open();
-
- Set tasks = taskController.getTasksWithNotifications();
-
- Set tasksWithAlerts = alertController.getTasksWithActiveAlerts();
- for(TaskIdentifier taskId : tasksWithAlerts) {
- try {
- tasks.add(taskController.fetchTaskForNotify(taskId));
- } catch (Exception e) {
- // task was deleted or something
- }
- }
-
- for(TaskModelForNotify task : tasks)
- updateAlarm(context, taskController, alertController, task);
-
- alertController.close();
- taskController.close();
- } catch (Exception e) {
- Log.e("astrid", "Error scheduling alarms", e);
- }
- }
-
- /** Schedules the next notification for this task */
- public static void updateAlarm(Context context, TaskController taskController,
- AlertController alertController, Notifiable task) {
- if(task.getTaskIdentifier() == null)
- return;
-
- // return if we don't need to go any further
- if(shouldDeleteAlarm(task)) {
- deleteAlarm(context, null, task.getTaskIdentifier().getId());
- return;
- }
-
- // periodic reminders
- if(task.getNotificationIntervalSeconds() > 0) {
- long interval = task.getNotificationIntervalSeconds() * 1000;
-
- long when;
- // get or make up a last notification time
- if(task.getLastNotificationDate() == null) {
- when = System.currentTimeMillis() -
- (long)(interval * (0.7f * random.nextFloat()));
- taskController.setLastNotificationTime(task.getTaskIdentifier(),
- new Date(when));
- } else {
- when = task.getLastNotificationDate().getTime();
- }
-
- if(when < System.currentTimeMillis())
- when += ((System.currentTimeMillis() - when)/interval + 1) * interval;
- scheduleRepeatingAlarm(context, task.getTaskIdentifier().getId(),
- when, FLAG_PERIODIC, interval);
- }
-
- // notifications at deadlines
- int estimatedDuration = DEADLINE_NOTIFY_SECS;
- if(task.getEstimatedSeconds() != null && task.getEstimatedSeconds() > DEADLINE_NOTIFY_SECS)
- estimatedDuration = (int)(task.getEstimatedSeconds() * 1.5f);
-
- // we need to clear all alarms in case users removed a deadline
- clearAlarm(context, task.getTaskIdentifier().getId(), FLAG_DEFINITE_DEADLINE);
- clearAlarm(context, task.getTaskIdentifier().getId(), FLAG_PREFERRED_DEADLINE);
- clearAlarm(context, task.getTaskIdentifier().getId(), FLAG_DEFINITE_DEADLINE | FLAG_OVERDUE);
- clearAlarm(context, task.getTaskIdentifier().getId(), FLAG_PREFERRED_DEADLINE | FLAG_OVERDUE);
-
- // before, during, and after deadlines
- if((task.getNotificationFlags() & TaskModelForList.NOTIFY_BEFORE_DEADLINE) > 0) {
- scheduleDeadline(context, task.getDefiniteDueDate(), -estimatedDuration,
- 0, FLAG_DEFINITE_DEADLINE, task);
- scheduleDeadline(context, task.getPreferredDueDate(), -estimatedDuration,
- 0, FLAG_PREFERRED_DEADLINE, task);
- }
- if((task.getNotificationFlags() & TaskModelForList.NOTIFY_AT_DEADLINE) > 0) {
- if((task.getNotificationFlags() & TaskModelForList.NOTIFY_AFTER_DEADLINE) == 0)
- scheduleDeadline(context, task.getDefiniteDueDate(), 0,
- 0, FLAG_DEFINITE_DEADLINE | FLAG_OVERDUE, task);
- scheduleDeadline(context, task.getPreferredDueDate(), 0,
- 0, FLAG_PREFERRED_DEADLINE | FLAG_OVERDUE, task);
- }
- if((task.getNotificationFlags() & TaskModelForList.NOTIFY_AFTER_DEADLINE) > 0) {
- scheduleDeadline(context, task.getDefiniteDueDate(), 0,
- DEADLINE_REPEAT, FLAG_DEFINITE_DEADLINE | FLAG_OVERDUE, task);
- }
-
- // fixed alerts
- List alerts = alertController.getTaskAlerts(task.getTaskIdentifier());
- scheduleFixedAlerts(context, task.getTaskIdentifier(), alerts);
- }
-
- /** Schedule a list of alerts for a task */
- public static void scheduleFixedAlerts(Context context, TaskIdentifier taskId,
- List alerts) {
- int alertId = 0;
- Date currentDate = new Date();
- for(Date alert : alerts) {
- if(alert.before(currentDate))
- continue;
-
- scheduleAlarm(context, taskId.getId(),
- alert.getTime(), FLAG_FIXED | (alertId++ << FIXED_ID_SHIFT));
- }
- }
-
- /** Schedule an alert around a deadline
- *
- * @param context
- * @param deadline The deadline date. If null, does nothing.
- * @param offsetSeconds Offset from deadline to schedule
- * @param intervalSeconds How often to repeat, or zero
- * @param flags Flags for the alarm
- * @param task
- */
- private static void scheduleDeadline(Context context, Date deadline, int
- offsetSeconds, int intervalSeconds, int flags, Notifiable task) {
- long id = task.getTaskIdentifier().getId();
- if(deadline == null)
- return;
- long when = deadline.getTime() + offsetSeconds * 1000;
- if(when < System.currentTimeMillis() && intervalSeconds == 0)
- return;
-
- if (intervalSeconds == 0)
- scheduleAlarm(context, id, when,
- flags);
- else
- scheduleRepeatingAlarm(context, id,
- when, flags, intervalSeconds * 1000);
- }
-
- /** Create a 'snooze' reminder for this task */
- public static void createSnoozeAlarm(Context context, TaskIdentifier id,
- int secondsToSnooze, int flags, long repeatInterval) {
- // if this is a one-off alarm, just schedule a snooze-type alarm
- if(repeatInterval == 0)
- scheduleAlarm(context, id.getId(), System.currentTimeMillis() +
- secondsToSnooze * 1000, FLAG_SNOOZE);
-
- // else, reschedule our normal alarm
- else
- scheduleRepeatingAlarm(context, id.getId(), System.currentTimeMillis() +
- secondsToSnooze * 1000, flags, repeatInterval);
- }
-
- /** Helper method to create a Intent for alarm from an ID & flags */
- private static Intent createAlarmIntent(Context context, long id, int flags) {
- Intent intent = new Intent(context, Notifications.class);
- intent.setType(Long.toString(id));
- intent.setAction(Integer.toString(flags));
- intent.putExtra(ID_KEY, id);
- intent.putExtra(FLAGS_KEY, flags);
-
- return intent;
- }
-
- /** Delete the given alarm */
- public static void deleteAlarm(Context context, Intent trigger, long id) {
- AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
-
- if(trigger != null) {
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- trigger, 0);
- am.cancel(pendingIntent);
- }
-
- // clear current notifications too
- clearAllNotifications(context, new TaskIdentifier(id));
- }
-
- /** Clear the alarm given by the id and flags */
- public static void clearAlarm(Context context, long id, int flags) {
- AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- createAlarmIntent(context, id, flags), 0);
- am.cancel(pendingIntent);
- }
-
- /** Schedules a single alarm for a single task */
- public static void scheduleAlarm(Context context, long id, long when,
- int flags) {
-
- // if alarm occurs in the past, don't trigger it
- if(when < System.currentTimeMillis())
- return;
-
- AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- createAlarmIntent(context, id, flags), 0);
-
- if(Constants.DEBUG)
- Log.e("Astrid", "Alarm (" + id + ", " + flags + ") set for " + new Date(when));
- am.set(AlarmManager.RTC_WAKEUP, when, pendingIntent);
- }
-
- /** Schedules a recurring alarm for a single task */
- public static void scheduleRepeatingAlarm(Context context, long id, long when,
- int flags, long interval) {
-
- // if alarm occurs in the past, trigger it in the future
- if(when < System.currentTimeMillis())
- when = (long)(System.currentTimeMillis() + when * (0.8 + 0.3 * random.nextDouble()));
-
- AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
- Intent alarmIntent = createAlarmIntent(context, id, flags);
- alarmIntent.putExtra(REPEAT_KEY, interval);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
- alarmIntent, 0);
-
- if(Constants.DEBUG)
- Log.e("Astrid", "Alarm (" + id + ", " + flags + ") set for " +
- new Date(when) + " every " + interval/1000 + " s");
- am.setRepeating(AlarmManager.RTC_WAKEUP, when, interval, pendingIntent);
- }
-
- // --- notification manager stuff
-
- /** Clear notifications associated with this application */
- public static void clearAllNotifications(Context context, TaskIdentifier taskId) {
- NotificationManager nm = (NotificationManager)
- context.getSystemService(Activity.NOTIFICATION_SERVICE);
- nm.cancel((int)taskId.getId());
- }
-
- private static String getRandomReminder(Resources r) {
- String[] reminders = r.getStringArray(R.array.reminders);
- int next = random.nextInt(reminders.length);
- String reminder = reminders[next];
- return reminder;
- }
-
- /** Schedule a new notification about the given task. Returns false if there was
- * some sort of error or the alarm should be disabled. */
- public static boolean showNotification(Context context, long id,
- int flags, long repeatInterval, String reminder) {
-
- String taskName;
- TaskController controller = new TaskController(context);
- boolean nonstopMode = false;
- try {
- controller.open();
- TaskModelForReminder task = controller.fetchTaskForReminder(new TaskIdentifier(id));
-
- // you're working on it - don't sound, don't delete
- if(task.getTimerStart() != null)
- return true;
-
- // you're done - don't sound, do delete
- if(task.isTaskCompleted())
- return false;
-
- // it's hidden - don't sound, don't delete
- if(task.getHiddenUntil() != null &&
- task.getHiddenUntil().after(new Date()) &&
- (flags & FLAG_PERIODIC) > 0)
- return true;
-
- taskName = task.getName();
- if((flags & FLAG_PERIODIC) > 0)
- controller.setLastNotificationTime(task.getTaskIdentifier(),
- new Date());
-
- if((task.getNotificationFlags() & TaskModelForReminder.NOTIFY_NONSTOP) > 0)
- nonstopMode = true;
-
- } catch (Exception e) {
- // task might have been deleted
- Log.e(Notifications.class.getSimpleName(),
- "Error loading task for notification", e);
- return false;
- } finally {
- controller.close();
- }
-
- // quiet hours? disabled if alarm clock
- boolean quietHours = false;
- Integer quietHoursStart = Preferences.getQuietHourStart(context);
- Integer quietHoursEnd = Preferences.getQuietHourEnd(context);
- if(quietHoursStart != null && quietHoursEnd != null && nonstopMode) {
- int hour = new Date().getHours();
- if(quietHoursStart < quietHoursEnd) {
- if(hour >= quietHoursStart && hour < quietHoursEnd)
- quietHours = true;
- } else { // wrap across 24/hour boundary
- if(hour >= quietHoursStart || hour < quietHoursEnd)
- quietHours = true;
- }
- }
-
- NotificationManager nm = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- Resources r = context.getResources();
-
- Intent notifyIntent = new Intent(context, TaskListNotify.class);
- notifyIntent.putExtra(TaskListSubActivity.LOAD_INSTANCE_TOKEN, id);
- notifyIntent.putExtra(TaskListSubActivity.FROM_NOTIFICATION_TOKEN, true);
- notifyIntent.putExtra(TaskListSubActivity.NOTIF_FLAGS_TOKEN, flags);
- notifyIntent.putExtra(TaskListSubActivity.NOTIF_REPEAT_TOKEN, repeatInterval);
- PendingIntent pendingIntent = PendingIntent.getActivity(context,
- (int)id, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
-
- // set up properties (name and icon) for the notification
- String appName = r.getString(R.string.app_name);
- int icon;
- switch(Preferences.getNotificationIconTheme(context)) {
- case Preferences.ICON_SET_PINK:
- icon = R.drawable.notif_pink_alarm;
- break;
- case Preferences.ICON_SET_BORING:
- icon = R.drawable.notif_boring_alarm;
- break;
- default:
- icon = R.drawable.notif_astrid;
- }
-
- // create notification object
- Notification notification = new Notification(
- icon, reminder, System.currentTimeMillis());
- notification.setLatestEventInfo(context,
- appName,
- reminder + " " + taskName,
- pendingIntent);
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- if(Preferences.isPersistenceMode(context)) {
- notification.flags |= Notification.FLAG_NO_CLEAR |
- Notification.FLAG_SHOW_LIGHTS;
- notification.ledOffMS = 5000;
- notification.ledOnMS = 700;
- notification.ledARGB = Color.YELLOW;
- }
- else
- notification.defaults = Notification.DEFAULT_LIGHTS;
-
- AudioManager audioManager = (AudioManager)context.getSystemService(
- Context.AUDIO_SERVICE);
-
- // if nonstop mode is activated, set up the flags for insistent
- // notification, and increase the volume to full volume, so the user
- // will actually pay attention to the alarm
- if(nonstopMode && (flags & FLAG_PERIODIC) == 0) {
- notification.flags |= Notification.FLAG_INSISTENT;
- notification.audioStreamType = AudioManager.STREAM_ALARM;
- audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
- audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
- } else {
- notification.audioStreamType = AudioManager.STREAM_NOTIFICATION;
- }
-
- // quiet hours = no sound
- if(quietHours) {
- notification.sound = null;
- } else {
- Uri notificationSound = Preferences.getNotificationRingtone(context);
- if(audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
- notification.sound = null;
- } else if(notificationSound != null &&
- !notificationSound.toString().equals("")) {
- notification.sound = notificationSound;
- } else {
- notification.defaults |= Notification.DEFAULT_SOUND;
- }
- }
-
- // quiet hours + periodic = no vibrate
- if(quietHours && (flags & FLAG_PERIODIC) > 0) {
- notification.vibrate = null;
- } else {
- if (Preferences.shouldVibrate(context)
- && audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_NOTIFICATION)) {
- notification.vibrate = new long[] {0, 1000, 500, 1000, 500, 1000};
- } else {
- notification.vibrate = null;
- }
- }
-
- if(Constants.DEBUG)
- Log.w("Astrid", "Logging notification: " + reminder);
- nm.notify((int)id, notification);
-
- return true;
- }
-
- /** Show a notification when a user is "on-the-clock" for a given task */
- public static boolean showTimingNotification(Context context,
- TaskIdentifier taskId, String taskName) {
-
- String text = context.getResources().getString(R.string.notif_timerStarted) +
- " " + taskName;
- NotificationManager nm = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- Resources r = context.getResources();
-
- Intent notifyIntent = new Intent(context, TaskListNotify.class);
- notifyIntent.putExtra(TaskListSubActivity.LOAD_INSTANCE_TOKEN, taskId.getId());
- notifyIntent.putExtra(TaskListSubActivity.FROM_NOTIFICATION_TOKEN, true);
- PendingIntent pendingIntent = PendingIntent.getActivity(context,
- (int)taskId.getId(), notifyIntent, 0);
-
- // create notification object
- int icon;
- switch(Preferences.getNotificationIconTheme(context)) {
- case Preferences.ICON_SET_PINK:
- icon = R.drawable.notif_pink_working;
- break;
- case Preferences.ICON_SET_BORING:
- icon = R.drawable.notif_boring_working;
- break;
- default:
- icon = R.drawable.notif_astrid;
- }
-
- String appName = r.getString(R.string.app_name);
- Notification notification = new Notification(
- icon, 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;
-
- if(Constants.DEBUG)
- Log.w("Astrid", "Logging timing notification: " + text);
- nm.notify((int)taskId.getId(), notification);
-
- return true;
- }
-
- /** Schedule a new notification about the given tag. */
- public static boolean showTagNotification(Context context, long tagId,
- String reminder) {
-
- // quiet hours? only for periodic reminders
- boolean quietHours = false;
- Integer quietHoursStart = Preferences.getQuietHourStart(context);
- Integer quietHoursEnd = Preferences.getQuietHourEnd(context);
- if(quietHoursStart != null && quietHoursEnd != null) {
- int hour = new Date().getHours();
- if(quietHoursStart < quietHoursEnd) {
- if(hour >= quietHoursStart && hour < quietHoursEnd)
- quietHours = true;
- } else { // wrap across 24/hour boundary
- if(hour >= quietHoursStart || hour < quietHoursEnd)
- quietHours = true;
- }
- }
-
- NotificationManager nm = (NotificationManager) context
- .getSystemService(Context.NOTIFICATION_SERVICE);
- Resources r = context.getResources();
-
- Intent notifyIntent = new Intent(context, TaskListNotify.class);
- notifyIntent.putExtra(TaskListSubActivity.TAG_TOKEN, tagId);
- notifyIntent.putExtra(TaskListSubActivity.FROM_NOTIFICATION_TOKEN, true);
- PendingIntent pendingIntent = PendingIntent.getActivity(context,
- TAG_ID_OFFSET + (int)tagId, notifyIntent, PendingIntent.FLAG_ONE_SHOT);
-
- // set up properties (name and icon) for the notification
- String appName = r.getString(R.string.app_name);
- int icon = R.drawable.notif_tag;
-
- // create notification object
- Notification notification = new Notification(
- icon, reminder, System.currentTimeMillis());
- notification.setLatestEventInfo(context,
- appName,
- reminder,
- pendingIntent);
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- notification.ledARGB = Color.BLUE;
- notification.defaults = Notification.DEFAULT_LIGHTS;
-
- if(quietHours) {
- notification.vibrate = null;
- notification.sound = null;
- } else {
- notification.defaults |= Notification.DEFAULT_VIBRATE;
- Uri notificationSound = Preferences.getNotificationRingtone(context);
- if(notificationSound != null &&
- !notificationSound.toString().equals("")) {
- notification.sound = notificationSound;
- } else {
- notification.defaults |= Notification.DEFAULT_SOUND;
- }
- }
-
- if(Constants.DEBUG)
- Log.w("Astrid", "Logging tag notification: " + reminder);
- nm.notify(TAG_ID_OFFSET + (int)tagId, notification);
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/astrid/src-legacy/com/timsu/astrid/utilities/Preferences.java b/astrid/src-legacy/com/timsu/astrid/utilities/Preferences.java
index f7074184c..af29d2b85 100644
--- a/astrid/src-legacy/com/timsu/astrid/utilities/Preferences.java
+++ b/astrid/src-legacy/com/timsu/astrid/utilities/Preferences.java
@@ -43,25 +43,25 @@ public class Preferences {
Resources r = context.getResources();
Editor editor = prefs.edit();
- if(!prefs.contains(r.getString(R.string.p_notif_annoy))) {
- editor.putBoolean(r.getString(R.string.p_notif_annoy),
- DEFAULT_PERSISTENCE_MODE);
- }
+// if(!prefs.contains(r.getString(R.string.p_notif_annoy))) {
+// editor.putBoolean(r.getString(R.string.p_notif_annoy),
+// DEFAULT_PERSISTENCE_MODE);
+// }
if(!prefs.contains(r.getString(R.string.p_fontSize))) {
editor.putString(r.getString(R.string.p_fontSize), "20");
}
- if(!prefs.contains(r.getString(R.string.p_deadlineTime))) {
- editor.putString(r.getString(R.string.p_deadlineTime), "1");
- }
- if(!prefs.contains(r.getString(R.string.p_notif_defaultRemind))) {
- editor.putString(r.getString(R.string.p_notif_defaultRemind), "0");
- }
+// if(!prefs.contains(r.getString(R.string.p_deadlineTime))) {
+// editor.putString(r.getString(R.string.p_deadlineTime), "1");
+// }
+// if(!prefs.contains(r.getString(R.string.p_notif_defaultRemind))) {
+// editor.putString(r.getString(R.string.p_notif_defaultRemind), "0");
+// }
if(!prefs.contains(r.getString(R.string.p_colorize))) {
editor.putBoolean(r.getString(R.string.p_colorize), DEFAULT_COLORIZE);
}
- if(!prefs.contains(r.getString(R.string.p_notif_vibrate))) {
- editor.putBoolean(r.getString(R.string.p_notif_vibrate), true);
- }
+// if(!prefs.contains(r.getString(R.string.p_notif_vibrate))) {
+// editor.putBoolean(r.getString(R.string.p_notif_vibrate), true);
+// }
if (!prefs.contains(r.getString(R.string.p_backup))) {
editor.putBoolean(r.getString(R.string.p_backup), true);
}
@@ -77,38 +77,38 @@ public class Preferences {
}
private static void setVisibilityPreferences(SharedPreferences p, Editor e, Resources r) {
- if(!p.contains(r.getString(R.string.prefs_titleVisible))) {
- e.putBoolean(r.getString(R.string.prefs_titleVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_titleVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_timeVisible))) {
- e.putBoolean(r.getString(R.string.prefs_timeVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_timeVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_deadlineVisible))) {
- e.putBoolean(r.getString(R.string.prefs_deadlineVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_deadlineVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_importanceVisible))) {
- e.putBoolean(r.getString(R.string.prefs_importanceVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_importanceVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_reminderVisible))) {
- e.putBoolean(r.getString(R.string.prefs_reminderVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_reminderVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_repeatVisible))) {
- e.putBoolean(r.getString(R.string.prefs_repeatVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_repeatVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_tagsVisible))) {
- e.putBoolean(r.getString(R.string.prefs_tagsVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_tagsVisible_default)));
- }
- if(!p.contains(r.getString(R.string.prefs_notesVisible))) {
- e.putBoolean(r.getString(R.string.prefs_notesVisible),
- Boolean.parseBoolean(r.getString(R.string.prefs_notesVisible_default)));
- }
+// if(!p.contains(r.getString(R.string.prefs_titleVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_titleVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_titleVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_timeVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_timeVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_timeVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_deadlineVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_deadlineVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_deadlineVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_importanceVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_importanceVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_importanceVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_reminderVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_reminderVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_reminderVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_repeatVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_repeatVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_repeatVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_tagsVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_tagsVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_tagsVisible_default)));
+// }
+// if(!p.contains(r.getString(R.string.prefs_notesVisible))) {
+// e.putBoolean(r.getString(R.string.prefs_notesVisible),
+// Boolean.parseBoolean(r.getString(R.string.prefs_notesVisible_default)));
+// }
}
// --- system preferences
@@ -230,60 +230,68 @@ public class Preferences {
/** returns hour at which quiet hours start, or null if not set */
public static Integer getQuietHourStart(Context context) {
- return getIntegerValue(context, R.string.p_notif_quietStart);
+// return getIntegerValue(context, R.string.p_notif_quietStart);
+ return 0;
}
/** returns hour at which quiet hours start, or null if not set */
public static Integer getQuietHourEnd(Context context) {
- return getIntegerValue(context, R.string.p_notif_quietEnd);
+// return getIntegerValue(context, R.string.p_notif_quietEnd);
+ return 0;
}
/** returns hour at which quiet hours start, or null if not set */
public static int getNotificationIconTheme(Context context) {
- Integer index = getIntegerValue(context, R.string.p_notif_icon);
- if(index == null)
- index = 0;
- return index;
+// Integer index = getIntegerValue(context, R.string.p_notif_icon);
+// if(index == null)
+// index = 0;
+// return index;
+ return 0;
}
/** Get notification ring tone, or null if not set */
public static Uri getNotificationRingtone(Context context) {
Resources r = context.getResources();
- String value = getPrefs(context).getString(r.getString(
- R.string.p_notification_ringtone), "");
-
- try {
- return Uri.parse(value);
- } catch (RuntimeException e) {
- return null;
- }
+// String value = getPrefs(context).getString(r.getString(
+// R.string.p_notification_ringtone), "");
+//
+// try {
+// return Uri.parse(value);
+// } catch (RuntimeException e) {
+// return null;
+// }
+ return null;
}
/** Get perstence mode setting */
public static boolean isPersistenceMode(Context context) {
Resources r = context.getResources();
- return getPrefs(context).getBoolean(r.getString(
- R.string.p_notif_annoy), DEFAULT_PERSISTENCE_MODE);
+// return getPrefs(context).getBoolean(r.getString(
+// R.string.p_notif_annoy), DEFAULT_PERSISTENCE_MODE);
+ return false;
}
/** Get vibration mode setting */
public static boolean shouldVibrate(Context context) {
Resources r = context.getResources();
- return getPrefs(context).getBoolean(r.getString(
- R.string.p_notif_vibrate), true);
+// return getPrefs(context).getBoolean(r.getString(
+// R.string.p_notif_vibrate), true);
+ return false;
}
/** Return # of days to remind by default */
public static Integer getDefaultReminder(Context context) {
- return getIntegerValue(context, R.string.p_notif_defaultRemind);
+ // return getIntegerValue(context, R.string.p_notif_defaultRemind);
+ return 0;
}
// --- postpone count & settings
/** whether nags for postponing and other things should be shown */
public static boolean shouldShowNags(Context context) {
- return getPrefs(context).getBoolean(context.getResources().
- getString(R.string.p_nagging), true);
+// return getPrefs(context).getBoolean(context.getResources().
+// getString(R.string.p_nagging), true);
+ return false;
}
// --- appearance settings
@@ -295,7 +303,8 @@ public class Preferences {
/** Return # of days from now to set deadlines by default */
public static Integer getDefaultDeadlineDays(Context context) {
- return getIntegerValue(context, R.string.p_deadlineTime);
+// return getIntegerValue(context, R.string.p_deadlineTime);
+ return 0;
}
/** Get perstence mode setting */
@@ -421,11 +430,6 @@ public class Preferences {
return time;
}
- /** Reads the old auto */
- public static Float getSyncOldAutoSyncFrequency(Context context) {
- return getFloatValue(context, R.string.p_sync_every_old);
- }
-
/** Sets the auto-sync frequency to the desired value */
public static void setSyncAutoSyncFrequency(Context context, int value) {
Editor editor = getPrefs(context).edit();
diff --git a/astrid/src-legacy/com/timsu/astrid/utilities/TaskFieldsVisibility.java b/astrid/src-legacy/com/timsu/astrid/utilities/TaskFieldsVisibility.java
index 9e3a278c1..fb015fcf6 100644
--- a/astrid/src-legacy/com/timsu/astrid/utilities/TaskFieldsVisibility.java
+++ b/astrid/src-legacy/com/timsu/astrid/utilities/TaskFieldsVisibility.java
@@ -4,8 +4,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
-import com.timsu.astrid.R;
-
/** Helper struct to store preferences for field visibility */
public class TaskFieldsVisibility {
@@ -41,14 +39,14 @@ public class TaskFieldsVisibility {
Resources r = context.getResources();
PrefReader pr = new PrefReader(prefs, r);
- tf.TITLE = pr.get(R.string.prefs_titleVisible, R.string.prefs_titleVisible_default);
- tf.TIMES = pr.get(R.string.prefs_timeVisible, R.string.prefs_timeVisible_default);
- tf.IMPORTANCE = pr.get(R.string.prefs_importanceVisible, R.string.prefs_importanceVisible_default);
- tf.DEADLINE = pr.get(R.string.prefs_deadlineVisible, R.string.prefs_deadlineVisible_default);
- tf.REMINDERS = pr.get(R.string.prefs_reminderVisible, R.string.prefs_reminderVisible_default);
- tf.REPEATS = pr.get(R.string.prefs_repeatVisible, R.string.prefs_repeatVisible_default);
- tf.TAGS = pr.get(R.string.prefs_tagsVisible, R.string.prefs_tagsVisible_default);
- tf.NOTES = pr.get(R.string.prefs_notesVisible, R.string.prefs_notesVisible_default);
+ tf.TITLE = true;
+ tf.TIMES = true;
+ tf.IMPORTANCE = true;
+ tf.DEADLINE = true;
+ tf.REMINDERS = true;
+ tf.REPEATS = true;
+ tf.TAGS = true;
+ tf.NOTES = true;
return tf;
}
diff --git a/astrid/src-legacy/com/timsu/astrid/widget/DateControlSet.java b/astrid/src-legacy/com/timsu/astrid/widget/DateControlSet.java
index aaa5b4d7d..9e49e9269 100644
--- a/astrid/src-legacy/com/timsu/astrid/widget/DateControlSet.java
+++ b/astrid/src-legacy/com/timsu/astrid/widget/DateControlSet.java
@@ -66,6 +66,12 @@ public class DateControlSet implements OnTimeSetListener,
return date;
}
+ public long getMillis() {
+ if(date == null)
+ return 0;
+ return date.getTime();
+ }
+
/** Initialize the components for the given date field */
public void setDate(Date newDate) {
if(newDate == null) {
@@ -82,6 +88,13 @@ public class DateControlSet implements OnTimeSetListener,
updateTime();
}
+ public void setDate(long newDate) {
+ if(newDate == 0L)
+ setDate(null);
+ else
+ setDate(new Date(newDate));
+ }
+
public void onDateSet(DatePicker view, int year, int month, int monthDay) {
date.setYear(year - 1900);
date.setMonth(month);
diff --git a/astrid/src/com/todoroo/astrid/activity/EditPreferences.java b/astrid/src/com/todoroo/astrid/activity/EditPreferences.java
new file mode 100644
index 000000000..b1a586d21
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/activity/EditPreferences.java
@@ -0,0 +1,236 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.activity;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map.Entry;
+
+import android.content.Intent;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.dao.Database;
+import com.todoroo.astrid.dao.TaskDao;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.StartupService;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.utility.Constants;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * Displays the preference screen for users to edit their preferences
+ *
+ * @author Tim Su
+ *
+ */
+public class EditPreferences extends PreferenceActivity {
+
+ // --- constants
+
+
+
+ // --- instance variables
+
+ @Autowired
+ TaskService taskService; // for debugging
+
+ @Autowired
+ TaskDao taskDao;
+
+ @Autowired
+ Database database;
+
+ @Autowired
+ DialogUtilities dialogUtilities;
+
+ public EditPreferences() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ new StartupService().onStartupApplication(this);
+ ContextManager.setContext(this);
+
+ setTitle(R.string.EPr_title);
+ addPreferencesFromResource(R.xml.preferences);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ initializePreference(screen);
+
+ // load plug-ins
+ Intent queryIntent = new Intent(AstridApiConstants.ACTION_SETTINGS);
+ PackageManager pm = getPackageManager();
+ List resolveInfoList = pm.queryIntentActivities(queryIntent, 0);
+ int length = resolveInfoList.size();
+ LinkedHashMap> applicationPreferences =
+ new LinkedHashMap>();
+ for(int i = 0; i < length; i++) {
+ ResolveInfo resolveInfo = resolveInfoList.get(i);
+ Intent intent = new Intent(AstridApiConstants.ACTION_SETTINGS);
+ intent.setClassName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+
+ Preference preference = new Preference(this);
+ preference.setTitle(resolveInfo.activityInfo.loadLabel(pm));
+ preference.setIntent(intent);
+
+ String application = resolveInfo.activityInfo.applicationInfo.loadLabel(pm).toString();
+ if(!applicationPreferences.containsKey(application))
+ applicationPreferences.put(application, new ArrayList());
+ ArrayList arrayList = applicationPreferences.get(application);
+ arrayList.add(preference);
+ }
+
+ for(Entry> entry : applicationPreferences.entrySet()) {
+ Preference header = new Preference(this);
+ header.setLayoutResource(android.R.layout.preference_category);
+ header.setTitle(entry.getKey());
+ screen.addPreference(header);
+
+ for(Preference preference : entry.getValue())
+ screen.addPreference(preference);
+ }
+
+ // debugging preferences
+ addDebugPreferences();
+ }
+
+ @SuppressWarnings("nls")
+ private void addDebugPreferences() {
+ if(!Constants.DEBUG)
+ return;
+
+ PreferenceCategory group = new PreferenceCategory(this);
+ group.setTitle("DEBUG");
+ getPreferenceScreen().addPreference(group);
+
+ Preference preference = new Preference(this);
+ preference.setTitle("Make Lots of Tasks");
+ preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference p) {
+ database.openForWriting();
+ Task task = new Task();
+ for(int i = 0; i < 100; i++) {
+ task.setId(Task.NO_ID);
+ task.setValue(Task.TITLE, Integer.toString(i));
+ taskService.save(task, false);
+ }
+ dialogUtilities.okDialog(EditPreferences.this, "done", null);
+ return false;
+ }
+ });
+ group.addPreference(preference);
+
+ preference = new Preference(this);
+ preference.setTitle("Delete all tasks");
+ preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference p) {
+ database.openForWriting();
+ taskDao.deleteWhere(Criterion.all);
+ dialogUtilities.okDialog(EditPreferences.this, "done", null);
+ return false;
+ }
+ });
+ group.addPreference(preference);
+ }
+
+ private void initializePreference(Preference preference) {
+ if(preference instanceof PreferenceGroup) {
+ PreferenceGroup group = (PreferenceGroup)preference;
+ for(int i = 0; i < group.getPreferenceCount(); i++) {
+ initializePreference(group.getPreference(i));
+ }
+ } else {
+ Object value = null;
+ if(preference instanceof ListPreference)
+ value = ((ListPreference)preference).getValue();
+ else if(preference instanceof CheckBoxPreference)
+ value = ((CheckBoxPreference)preference).isChecked();
+ else if(preference instanceof EditTextPreference)
+ value = ((EditTextPreference)preference).getText();
+
+ if(value != null)
+ updatePreferences(preference, value);
+
+ preference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference myPreference, Object newValue) {
+ return updatePreferences(myPreference, newValue);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ /**
+ *
+ * @param resource if null, updates all resources
+ */
+ protected boolean updatePreferences(Preference preference, Object value) {
+ Resources r = getResources();
+
+ // defaults options
+ if(r.getString(R.string.p_default_urgency_key).equals(preference.getKey())) {
+ updateTaskListPreference(preference, value, r, R.array.EPr_default_urgency,
+ R.array.EPr_default_urgency_values, R.string.EPr_default_urgency_desc);
+ } else if(r.getString(R.string.p_default_importance_key).equals(preference.getKey())) {
+ updateTaskListPreference(preference, value, r, R.array.EPr_default_importance,
+ R.array.EPr_default_importance_values, R.string.EPr_default_importance_desc);
+ } else if(r.getString(R.string.p_default_hideUntil_key).equals(preference.getKey())) {
+ updateTaskListPreference(preference, value, r, R.array.EPr_default_hideUntil,
+ R.array.EPr_default_hideUntil_values, R.string.EPr_default_hideUntil_desc);
+ }
+
+ return true;
+ }
+
+ private void updateTaskListPreference(Preference preference, Object value,
+ Resources r, int keyArray, int valueArray, int summaryResource) {
+ int index = AndroidUtilities.indexOf(r.getStringArray(valueArray), (String)value);
+ String setting = r.getStringArray(keyArray)[index];
+ preference.setSummary(r.getString(summaryResource,
+ setting));
+
+ // if user changed the value, refresh task defaults
+ if(!value.equals(Preferences.getStringValue(preference.getKey()))) {
+ Editor editor = Preferences.getPrefs(this).edit();
+ editor.putString(preference.getKey(), (String)value);
+ editor.commit();
+ Task.refreshDefaultValues();
+ }
+ }
+}
\ No newline at end of file
diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
new file mode 100644
index 000000000..9867efb97
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java
@@ -0,0 +1,399 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.activity;
+
+import android.app.AlertDialog;
+import android.app.ExpandableListActivity;
+import android.app.SearchManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.EditText;
+import android.widget.ExpandableListView;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+
+import com.flurry.android.FlurryAgent;
+import com.timsu.astrid.R;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.sql.QueryTemplate;
+import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.astrid.adapter.FilterAdapter;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.api.SearchFilter;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.StartupService;
+import com.todoroo.astrid.utility.Constants;
+
+/**
+ * Activity that displays a user's task lists and allows users
+ * to filter their task list.
+ *
+ * @author Tim Su
+ *
+ */
+public class FilterListActivity extends ExpandableListActivity {
+
+ // --- menu codes
+
+ private static final int MENU_SEARCH_ID = Menu.FIRST + 0;
+ private static final int MENU_HELP_ID = Menu.FIRST + 1;
+
+ private static final int CONTEXT_MENU_SHORTCUT = Menu.FIRST + 2;
+ private static final int CONTEXT_MENU_INTENT = Menu.FIRST + 3;
+
+ // --- instance variables
+
+ @Autowired
+ protected ExceptionService exceptionService;
+
+ @Autowired
+ protected DialogUtilities dialogUtilities;
+
+ FilterAdapter adapter = null;
+ FilterReceiver filterReceiver = new FilterReceiver();
+
+ /* ======================================================================
+ * ======================================================= initialization
+ * ====================================================================== */
+
+ public FilterListActivity() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ /** Called when loading up the activity */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ new StartupService().onStartupApplication(this);
+
+ setContentView(R.layout.filter_list_activity);
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ setTitle(R.string.FLA_title);
+ setUpList();
+ getLists();
+
+ onNewIntent(getIntent());
+ }
+
+ /**
+ * Called when receiving a new intent. Intents this class handles:
+ *
+ * - ACTION_SEARCH - displays a search bar
+ *
- ACTION_ADD_LIST - adds new lists to the merge adapter
+ *
+ */
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ final String intentAction = intent.getAction();
+ if (Intent.ACTION_SEARCH.equals(intentAction)) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ Filter filter = new Filter(null, null,
+ getString(R.string.FLA_search_filter, query),
+ new QueryTemplate().where(Task.TITLE.like("%" + query + "%")), //$NON-NLS-1$ //$NON-NLS-2$
+ null);
+ intent = new Intent(FilterListActivity.this, TaskListActivity.class);
+ intent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
+ startActivity(intent);
+ }
+ }
+
+ /**
+ * Create options menu (displayed when user presses menu key)
+ *
+ * @return true if menu should be displayed
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if(menu.size() > 0)
+ return true;
+
+ MenuItem item;
+
+ item = menu.add(Menu.NONE, MENU_SEARCH_ID, Menu.NONE,
+ R.string.FLA_menu_search);
+ item.setIcon(android.R.drawable.ic_menu_search);
+
+ item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
+ R.string.FLA_menu_help);
+ item.setIcon(android.R.drawable.ic_menu_help);
+
+ return true;
+ }
+
+ /* ======================================================================
+ * ============================================================ lifecycle
+ * ====================================================================== */
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ FlurryAgent.onEndSession(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(filterReceiver,
+ new IntentFilter(AstridApiConstants.BROADCAST_SEND_FILTERS));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(filterReceiver);
+ }
+
+ /**
+ * Receiver which receives intents to add items to the filter list
+ *
+ * @author Tim Su
+ *
+ */
+ protected class FilterReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ Parcelable[] filters = intent.getExtras().
+ getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
+ for (Parcelable item : filters) {
+ adapter.add((FilterListItem)item);
+ }
+ adapter.notifyDataSetChanged();
+ } catch (Exception e) {
+ exceptionService.reportError("receive-filter-" + //$NON-NLS-1$
+ intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
+ }
+ }
+ }
+
+ /* ======================================================================
+ * ===================================================== populating lists
+ * ====================================================================== */
+
+ /** Sets up the coach list adapter */
+ protected void setUpList() {
+ adapter = new FilterAdapter(this);
+ setListAdapter(adapter);
+
+ registerForContextMenu(getExpandableListView());
+ getExpandableListView().setGroupIndicator(
+ getResources().getDrawable(R.drawable.expander_group));
+ }
+
+ /**
+ * Broadcast a request for lists. The request is sent to every
+ * application registered to listen for this broadcast. Each application
+ * can then add lists to the
+ */
+ protected void getLists() {
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_FILTERS);
+ sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+ /* ======================================================================
+ * ============================================================== actions
+ * ====================================================================== */
+
+ /**
+ * Handles items being clicked. Return true if item is handled.
+ */
+ protected boolean onItemClicked(FilterListItem item) {
+ if(item instanceof Filter) {
+ Filter filter = (Filter)item;
+ Intent intent = new Intent(FilterListActivity.this, TaskListActivity.class);
+ intent.putExtra(TaskListActivity.TOKEN_FILTER, filter);
+ startActivity(intent);
+ return true;
+ } else if(item instanceof SearchFilter) {
+ onSearchRequested();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View v,
+ int groupPosition, int childPosition, long id) {
+ FilterListItem item = (FilterListItem) adapter.getChild(groupPosition,
+ childPosition);
+ return onItemClicked(item);
+ }
+
+ @Override
+ public void onGroupExpand(int groupPosition) {
+ FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
+ onItemClicked(item);
+ }
+
+ @Override
+ public void onGroupCollapse(int groupPosition) {
+ FilterListItem item = (FilterListItem) adapter.getGroup(groupPosition);
+ onItemClicked(item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
+
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+ FilterListItem item;
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
+ int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+ int childPos = ExpandableListView.getPackedPositionChild(info.packedPosition);
+ item = (FilterListItem) adapter.getChild(groupPos, childPos);
+ } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ int groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+ item = (FilterListItem) adapter.getGroup(groupPos);
+ } else {
+ return;
+ }
+
+ MenuItem menuItem;
+
+ if(item instanceof Filter) {
+ 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, TaskListActivity.class);
+ shortcutIntent.setAction(Intent.ACTION_VIEW);
+ shortcutIntent.putExtra(TaskListActivity.TOKEN_FILTER_TITLE, filter.title);
+ shortcutIntent.putExtra(TaskListActivity.TOKEN_FILTER_SQL, filter.sqlQuery);
+ shortcutIntent.putExtra(TaskListActivity.TOKEN_FILTER_VALUES, filter.valuesForNewTasks);
+ menuItem.setIntent(shortcutIntent);
+ }
+
+ for(int i = 0; i < item.contextMenuLabels.length; i++) {
+ if(item.contextMenuIntents.length <= i)
+ break;
+ menuItem = menu.add(0, CONTEXT_MENU_INTENT, 0, item.contextMenuLabels[i]);
+ menuItem.setIntent(item.contextMenuIntents[i]);
+ }
+
+ if(menu.size() > 0)
+ menu.setHeaderTitle(item.listingTitle);
+ }
+
+ /**
+ * Creates a shortcut on the user's home screen
+ *
+ * @param shortcutIntent
+ * @param label
+ */
+ private void createShortcut(Filter filter, Intent shortcutIntent, String label) {
+ Bitmap emblem = filter.listingIcon;
+ if(emblem == null)
+ emblem = ((BitmapDrawable) getResources().getDrawable(
+ R.drawable.filter_tags1)).getBitmap();
+ // create icon by superimposing astrid w/ icon
+ Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
+ R.drawable.icon_blank)).getBitmap();
+ bitmap = bitmap.copy(bitmap.getConfig(), true);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawBitmap(emblem, new Rect(0, 0, emblem.getWidth(), emblem.getHeight()),
+ new Rect(bitmap.getWidth() - 32, bitmap.getHeight() - 32,
+ bitmap.getWidth(), bitmap.getHeight()), null);
+
+ Intent createShortcutIntent = new Intent();
+ createShortcutIntent.putExtra(
+ Intent.EXTRA_SHORTCUT_INTENT,
+ shortcutIntent);
+ createShortcutIntent.putExtra(
+ Intent.EXTRA_SHORTCUT_NAME, label);
+ createShortcutIntent.putExtra(
+ Intent.EXTRA_SHORTCUT_ICON, bitmap);
+ createShortcutIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); //$NON-NLS-1$
+
+ sendBroadcast(createShortcutIntent);
+ Toast.makeText(
+ FilterListActivity.this,
+ getString(
+ R.string.FLA_toast_onCreateShortcut,
+ label), Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, final MenuItem item) {
+
+ // handle my own menus
+ switch (item.getItemId()) {
+ case MENU_SEARCH_ID: {
+ onSearchRequested();
+ return true;
+ }
+
+ case MENU_HELP_ID: {
+ // TODO
+ return true;
+ }
+
+ case CONTEXT_MENU_SHORTCUT: {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo)item.getMenuInfo();
+
+ final Intent shortcutIntent = item.getIntent();
+ final Filter filter = (Filter)info.targetView.getTag();
+
+ FrameLayout frameLayout = new FrameLayout(this);
+ frameLayout.setPadding(10, 0, 10, 0);
+ final EditText editText = new EditText(this);
+ editText.setText(filter.listingTitle);
+ frameLayout.addView(editText, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.FILL_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT));
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.FLA_shortcut_dialog_title)
+ .setMessage(R.string.FLA_shortcut_dialog)
+ .setView(frameLayout)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ String label = editText.getText().toString();
+ createShortcut(filter, shortcutIntent, label);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+
+ return true;
+ }
+
+ case CONTEXT_MENU_INTENT: {
+ Intent intent = item.getIntent();
+ startActivity(intent);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java
new file mode 100644
index 000000000..c60db33dd
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/activity/TaskEditActivity.java
@@ -0,0 +1,1378 @@
+/*
+ * ASTRID: Android's Simple Task Recording Dashboard
+ *
+ * Copyright (c) 2009 Tim Su
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.todoroo.astrid.activity;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.app.TabActivity;
+import android.app.TimePickerDialog;
+import android.app.DatePickerDialog.OnDateSetListener;
+import android.app.TimePickerDialog.OnTimeSetListener;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TabHost;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+import android.widget.ToggleButton;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import com.flurry.android.FlurryAgent;
+import com.timsu.astrid.R;
+import com.timsu.astrid.data.enums.RepeatInterval;
+import com.timsu.astrid.data.task.TaskModelForEdit;
+import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
+import com.timsu.astrid.utilities.AstridUtilities;
+import com.timsu.astrid.widget.NumberPicker;
+import com.timsu.astrid.widget.NumberPickerDialog;
+import com.timsu.astrid.widget.TimeDurationControlSet;
+import com.timsu.astrid.widget.NumberPickerDialog.OnNumberPickedListener;
+import com.timsu.astrid.widget.TimeDurationControlSet.TimeDurationType;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.data.Property.IntegerProperty;
+import com.todoroo.andlib.data.Property.StringProperty;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.dao.Database;
+import com.todoroo.astrid.model.Metadata;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.StartupService;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.tags.TagService;
+import com.todoroo.astrid.tags.TagService.Tag;
+import com.todoroo.astrid.utility.Constants;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * This activity is responsible for creating new tasks and editing existing
+ * ones. It saves a task when it is paused (screen rotated, back button
+ * pressed) as long as the task has a title.
+ *
+ * @author timsu
+ *
+ */
+public final class TaskEditActivity extends TabActivity {
+
+ // --- bundle tokens
+
+ /**
+ * Task ID
+ */
+ public static final String ID_TOKEN = "i"; //$NON-NLS-1$
+
+ // --- request codes
+
+ @SuppressWarnings("unused")
+ private static final int REQUEST_CODE_OPERATION = 0;
+
+ // --- menu codes
+
+ private static final int MENU_SAVE_ID = Menu.FIRST;
+ private static final int MENU_DISCARD_ID = Menu.FIRST + 1;
+ private static final int MENU_DELETE_ID = Menu.FIRST + 2;
+
+ // --- result codes
+
+ public static final int RESULT_CODE_SAVED = RESULT_FIRST_USER;
+ public static final int RESULT_CODE_DISCARDED = RESULT_FIRST_USER + 1;
+ public static final int RESULT_CODE_DELETED = RESULT_FIRST_USER + 2;
+
+ // --- other constants
+
+ /** Number of tags a task can have */
+ private static final int MAX_TAGS = 5;
+
+ // --- services
+
+ @Autowired
+ private ExceptionService exceptionService;
+
+ @Autowired
+ private Database database;
+
+ @Autowired
+ private TaskService taskService;
+
+ @Autowired
+ private DateUtilities dateUtilities;
+
+ // --- UI components
+
+ private EditText title;
+
+ private final ArrayList controls = new ArrayList();
+
+ // --- other instance variables
+
+ /** task model */
+ private Task model = null;
+
+ /** whether task should be saved when this activity exits */
+ private boolean shouldSaveState = true;
+
+ /* ======================================================================
+ * ======================================================= initialization
+ * ====================================================================== */
+
+ public TaskEditActivity() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ new StartupService().onStartupApplication(this);
+
+ setUpUIComponents();
+
+ // disable keyboard until user requests it
+ AstridUtilities.suppressVirtualKeyboard(title);
+ }
+
+ /* ======================================================================
+ * ==================================================== UI initialization
+ * ====================================================================== */
+
+ /** Initialize UI components */
+ private void setUpUIComponents() {
+ Resources r = getResources();
+
+ // set up tab host
+ TabHost tabHost = getTabHost();
+ tabHost.setPadding(0, 4, 0, 0);
+ LayoutInflater.from(this).inflate(R.layout.task_edit_activity,
+ tabHost.getTabContentView(), true);
+ tabHost.addTab(tabHost.newTabSpec(r.getString(R.string.TEA_tab_basic)).
+ setIndicator(r.getString(R.string.TEA_tab_basic),
+ r.getDrawable(R.drawable.tea_tab_basic)).setContent(
+ R.id.tab_basic));
+ tabHost.addTab(tabHost.newTabSpec(r.getString(R.string.TEA_tab_extra)).
+ setIndicator(r.getString(R.string.TEA_tab_extra),
+ r.getDrawable(R.drawable.tea_tab_extra)).setContent(
+ R.id.tab_extra));
+ tabHost.addTab(tabHost.newTabSpec(r.getString(R.string.TEA_tab_addons)).
+ setIndicator(r.getString(R.string.TEA_tab_addons),
+ r.getDrawable(R.drawable.tea_tab_extensions)).setContent(
+ R.id.tab_addons));
+
+ // populate control set
+ title = (EditText) findViewById(R.id.title);
+
+ controls.add(new EditTextControlSet(Task.TITLE, R.id.title));
+ controls.add(new ImportanceControlSet(R.id.importance_container));
+ controls.add(new UrgencyControlSet(R.id.urgency));
+ controls.add(new HideUntilControlSet(R.id.hideUntil));
+ controls.add(new EditTextControlSet(Task.NOTES, R.id.notes));
+
+ controls.add( new ReminderControlSet(R.id.reminder_due,
+ R.id.reminder_overdue, R.id.reminder_alarm));
+ controls.add( new RandomReminderControlSet(R.id.reminder_random,
+ R.id.reminder_random_interval));
+ controls.add(new TagsControlSet(R.id.tags_container));
+ controls.add(new RepeatControlSet(R.id.repeat_value, R.id.repeat_interval));
+
+ controls.add(new CalendarControlSet(R.id.add_to_calendar, R.id.view_calendar_event));
+ controls.add(new TimeDurationTaskEditControlSet(Task.ESTIMATED_SECONDS,
+ R.id.estimatedDuration, 0, R.string.hour_minutes_dialog,
+ TimeDurationType.HOURS_MINUTES));
+ controls.add(new TimeDurationTaskEditControlSet(Task.ELAPSED_SECONDS, R.id.elapsedDuration,
+ 0, R.string.hour_minutes_dialog,
+ TimeDurationType.HOURS_MINUTES));
+
+ // read data
+ populateFields();
+
+ // set up listeners
+ setUpListeners();
+ }
+
+ /**
+ * @return true if task is newly created
+ */
+ private boolean isNewTask() {
+ return model.getValue(Task.TITLE).length() == 0;
+ }
+
+ /** Set up button listeners */
+ private void setUpListeners() {
+ final View.OnClickListener mSaveListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ saveButtonClick();
+ }
+ };
+ final View.OnClickListener mDiscardListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ discardButtonClick();
+ }
+ };
+ final View.OnClickListener mDeleteListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ deleteButtonClick();
+ }
+ };
+
+ // set up save, cancel, and delete buttons
+ Button saveButtonGeneral = (Button) findViewById(R.id.save_basic);
+ saveButtonGeneral.setOnClickListener(mSaveListener);
+ Button saveButtonDates = (Button) findViewById(R.id.save_extra);
+ saveButtonDates.setOnClickListener(mSaveListener);
+ Button saveButtonNotify = (Button) findViewById(R.id.save_addons);
+ saveButtonNotify.setOnClickListener(mSaveListener);
+
+ Button discardButtonGeneral = (Button) findViewById(R.id.discard_basic);
+ discardButtonGeneral.setOnClickListener(mDiscardListener);
+ Button discardButtonDates = (Button) findViewById(R.id.discard_extra);
+ discardButtonDates.setOnClickListener(mDiscardListener);
+ Button discardButtonNotify = (Button) findViewById(R.id.discard_addons);
+ discardButtonNotify.setOnClickListener(mDiscardListener);
+
+ Button deleteButtonGeneral = (Button) findViewById(R.id.delete_basic);
+ Button deleteButtonDates = (Button) findViewById(R.id.delete_extra);
+ Button deleteButtonNotify = (Button) findViewById(R.id.delete_addons);
+ if(isNewTask()) {
+ deleteButtonGeneral.setVisibility(View.GONE);
+ deleteButtonDates.setVisibility(View.GONE);
+ deleteButtonNotify.setVisibility(View.GONE);
+ } else {
+ deleteButtonGeneral.setOnClickListener(mDeleteListener);
+ deleteButtonDates.setOnClickListener(mDeleteListener);
+ deleteButtonNotify.setOnClickListener(mDeleteListener);
+ }
+ }
+
+ /* ======================================================================
+ * =============================================== model reading / saving
+ * ====================================================================== */
+
+ /**
+ * Loads action item from the given intent
+ * @param intent
+ */
+ @SuppressWarnings("nls")
+ protected void loadItem(Intent intent) {
+ long idParam = intent.getLongExtra(ID_TOKEN, -1L);
+ if(idParam == -1) {
+ exceptionService.reportError("task-edit-no-token", null);
+ finish();
+ return;
+ }
+
+ database.openForReading();
+ if(idParam == Task.NO_ID) {
+ model = new Task();
+ taskService.save(model, false);
+ } else {
+ model = taskService.fetchById(idParam, Task.PROPERTIES);
+ }
+
+ if(model.getValue(Task.TITLE).length() == 0)
+ FlurryAgent.onEvent("create-task");
+ FlurryAgent.onEvent("edit-task");
+
+ if(model == null) {
+ exceptionService.reportError("task-edit-no-task",
+ new NullPointerException("model"));
+ finish();
+ return;
+ }
+ }
+
+ /** Populate UI component values from the model */
+ private void populateFields() {
+ Resources r = getResources();
+ loadItem(getIntent());
+
+ if(isNewTask())
+ setTitle(R.string.TEA_view_titleNew);
+ else
+ setTitle(r.getString(R.string.TEA_view_title, model.getValue(Task.TITLE)));
+
+ for(TaskEditControlSet controlSet : controls)
+ controlSet.readFromModel();
+ }
+
+ /** Save task model from values in UI components */
+ private void save() {
+ for(TaskEditControlSet controlSet : controls)
+ controlSet.writeToModel();
+
+ taskService.save(model, false);
+ showSaveToast();
+ }
+
+ /* ======================================================================
+ * ======================================================= event handlers
+ * ====================================================================== */
+
+ protected void saveButtonClick() {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ /**
+ * Displays a Toast reporting that the selected task has been saved and, if
+ * it has a due date, that is due in 'x' amount of time, to 1 time-unit of
+ * precision
+ */
+ private void showSaveToast() {
+ // if we have no title, don't show a message
+ if(isNewTask())
+ return;
+
+ int stringResource;
+
+ long due = model.getValue(Task.DUE_DATE);
+ if (due != 0) {
+ long dueFromNow = due - System.currentTimeMillis();
+
+ if (dueFromNow < 0) {
+ stringResource = R.string.TEA_onTaskSave_overdue;
+ } else {
+ stringResource = R.string.TEA_onTaskSave_due;
+ }
+ String formattedDate = dateUtilities.getDurationString(dueFromNow, 2);
+ Toast.makeText(this,
+ getResources().getString(stringResource, formattedDate),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, R.string.TEA_onTaskSave_notDue,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ protected void discardButtonClick() {
+ shouldSaveState = false;
+ showCancelToast();
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ /**
+ * Show toast for task edit canceling
+ */
+ private void showCancelToast() {
+ Toast.makeText(this, R.string.TEA_onTaskCancel,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ protected void deleteButtonClick() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.DLG_confirm_title)
+ .setMessage(R.string.DLG_delete_this_task_question)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ taskService.delete(model);
+ shouldSaveState = false;
+ showDeleteToast();
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * Show toast for task edit deleting
+ */
+ private void showDeleteToast() {
+ Toast.makeText(this, R.string.TEA_onTaskDelete,
+ Toast.LENGTH_SHORT).show();
+ }
+
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case MENU_SAVE_ID:
+ saveButtonClick();
+ return true;
+ case MENU_DISCARD_ID:
+ discardButtonClick();
+ return true;
+ case MENU_DELETE_ID:
+ deleteButtonClick();
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuItem item;
+
+ item = menu.add(Menu.NONE, MENU_SAVE_ID, 0, R.string.TEA_save_label);
+ item.setIcon(android.R.drawable.ic_menu_save);
+ item.setAlphabeticShortcut('s');
+
+ item = menu.add(Menu.NONE, MENU_DISCARD_ID, 0, R.string.TEA_cancel_label);
+ item.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+ item.setAlphabeticShortcut('c');
+
+ item = menu.add(Menu.NONE, MENU_DELETE_ID, 0, R.string.TEA_delete_label);
+ item.setIcon(android.R.drawable.ic_menu_delete);
+ item.setAlphabeticShortcut('d');
+
+ return true;
+ }
+
+ @Override
+ protected void onPause() {
+ if(shouldSaveState)
+ save();
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ FlurryAgent.onEndSession(this);
+
+ // don't save if user accidentally created a new task
+ if(title.getText().length() == 0) {
+ if(model.isSaved())
+ taskService.delete(model);
+ showCancelToast();
+ return;
+ }
+ }
+
+ /* ======================================================================
+ * ========================================== UI component helper classes
+ * ====================================================================== */
+
+ // --- interface
+
+ /**
+ * Interface for working with controls that alter task data
+ */
+ public interface TaskEditControlSet {
+ /**
+ * Read data from model to update the control set
+ */
+ public void readFromModel();
+
+ /**
+ * Write data from control set to model
+ */
+ public void writeToModel();
+ }
+
+ // --- EditTextControlSet
+
+ /**
+ * Control set for mapping a Property to an EditText
+ * @author Tim Su
+ *
+ */
+ public class EditTextControlSet implements TaskEditControlSet {
+ private final EditText editText;
+ private final StringProperty property;
+
+ public EditTextControlSet(StringProperty property, int editText) {
+ this.property = property;
+ this.editText = (EditText)findViewById(editText);
+ }
+
+ @Override
+ public void readFromModel() {
+ editText.setText(model.getValue(property));
+ }
+
+ @Override
+ public void writeToModel() {
+ model.setValue(property, editText.getText().toString());
+ }
+ }
+
+ // --- TimeDurationTaskEditControlSet
+
+ /**
+ * Control set for mapping a Property to a TimeDurationControlSet
+ * @author Tim Su
+ *
+ */
+ public class TimeDurationTaskEditControlSet implements TaskEditControlSet {
+ private final TimeDurationControlSet controlSet;
+ private final IntegerProperty property;
+
+ public TimeDurationTaskEditControlSet(IntegerProperty property, int timeButtonId,
+ int prefixResource, int titleResource, TimeDurationType type) {
+ this.property = property;
+ this.controlSet = new TimeDurationControlSet(TaskEditActivity.this,
+ timeButtonId, prefixResource, titleResource, type);
+ }
+
+ @Override
+ public void readFromModel() {
+ controlSet.setTimeDuration(model.getValue(property));
+ }
+
+ @Override
+ public void writeToModel() {
+ model.setValue(property, controlSet.getTimeDurationInSeconds());
+ }
+ }
+
+ // --- ImportanceControlSet
+
+ /**
+ * Control Set for setting task importance
+ *
+ * @author Tim Su
+ *
+ */
+ public class ImportanceControlSet implements TaskEditControlSet {
+ private final List buttons = new LinkedList();
+ private final int[] colors = Task.getImportanceColors(getResources());
+
+ public ImportanceControlSet(int containerId) {
+ LinearLayout layout = (LinearLayout)findViewById(containerId);
+
+ for(int i = Task.IMPORTANCE_MOST; i <= Task.IMPORTANCE_LEAST; i++) {
+ final ToggleButton button = new ToggleButton(TaskEditActivity.this);
+ button.setLayoutParams(new LinearLayout.LayoutParams(
+ LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1));
+
+ StringBuilder label = new StringBuilder();
+ for(int j = Task.IMPORTANCE_LEAST; j >= 0; j--)
+ label.append('!');
+
+
+ button.setTextColor(colors[i]);
+ button.setTextOff(label);
+ button.setTextOn(label);
+
+ button.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ setImportance((Integer)button.getTag());
+ }
+ });
+ button.setTag(i);
+
+ buttons.add(button);
+ layout.addView(button);
+ }
+ }
+
+ public void setImportance(Integer i) {
+ for(CompoundButton b : buttons) {
+ if(b.getTag() == i) {
+ b.setTextSize(24);
+ b.setChecked(true);
+ } else {
+ b.setTextSize(16);
+ b.setChecked(false);
+ }
+ }
+ }
+
+ public int getImportance() {
+ for(CompoundButton b : buttons)
+ if(b.isChecked())
+ return (Integer) b.getTag();
+ return Task.getStaticDefaultValues().getAsInteger(Task.IMPORTANCE.name);
+ }
+
+ @Override
+ public void readFromModel() {
+ setImportance(model.getValue(Task.IMPORTANCE));
+ }
+
+ @Override
+ public void writeToModel() {
+ model.setValue(Task.IMPORTANCE, getImportance());
+ }
+ }
+
+ // --- UrgencyControlSet
+
+ private class UrgencyControlSet implements TaskEditControlSet,
+ OnItemSelectedListener, OnTimeSetListener, OnDateSetListener {
+
+ private static final int SPECIFIC_DATE = -1;
+
+ private final Spinner urgency;
+ private ArrayAdapter urgencyAdapter;
+
+ /**
+ * Container class for urgencies
+ *
+ * @author Tim Su
+ *
+ */
+ private class UrgencyValue {
+ public String label;
+ public long dueDate;
+ public boolean hasDueTime;
+
+ public UrgencyValue(String label, long dueDate, boolean hasDueTime) {
+ this.label = label;
+ this.dueDate = dueDate;
+ this.hasDueTime = hasDueTime;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+ }
+
+ public UrgencyControlSet(int urgency) {
+ this.urgency = (Spinner)findViewById(urgency);
+ this.urgency.setOnItemSelectedListener(this);
+ }
+
+ private UrgencyValue[] createUrgencyList(long specificDate, boolean hasDueTime) {
+ // set up base urgencies
+ String[] labels = getResources().getStringArray(R.array.TEA_urgency);
+ UrgencyValue[] urgencyValues = new UrgencyValue[labels.length];
+ urgencyValues[0] = new UrgencyValue(labels[0], 0, false);
+ urgencyValues[1] = new UrgencyValue(labels[1], DateUtilities.now(), false);
+ urgencyValues[2] = new UrgencyValue(labels[2],
+ DateUtilities.now() + DateUtilities.ONE_DAY, false);
+ String dayAfterTomorrow = new SimpleDateFormat("EEEE").format( //$NON-NLS-1$
+ new Date(DateUtilities.now() + 2 * DateUtilities.ONE_DAY));
+ urgencyValues[3] = new UrgencyValue(dayAfterTomorrow,
+ DateUtilities.now() + 2 * DateUtilities.ONE_DAY, false);
+ urgencyValues[4] = new UrgencyValue(labels[4],
+ DateUtilities.now() + DateUtilities.ONE_WEEK, false);
+ Date nextMonth = new Date();
+ nextMonth.setMonth(nextMonth.getMonth() + 1);
+ urgencyValues[5] = new UrgencyValue(labels[5],
+ nextMonth.getTime(), false);
+
+ urgencyValues[6] = new UrgencyValue(labels[6], SPECIFIC_DATE, false);
+ urgencyValues[7] = new UrgencyValue(labels[7], SPECIFIC_DATE, true);
+
+ if(specificDate > 0) {
+ UrgencyValue[] updated = new UrgencyValue[labels.length + 1];
+ for(int i = 0; i < labels.length; i++)
+ updated[i+1] = urgencyValues[i];
+ SimpleDateFormat format;
+ if(hasDueTime)
+ format = DateUtilities.getDateWithTimeFormat(TaskEditActivity.this);
+ else
+ format = DateUtilities.getDateFormat(TaskEditActivity.this);
+ updated[0] = new UrgencyValue(format.format(new Date(specificDate)),
+ specificDate, hasDueTime);
+ urgencyValues = updated;
+ }
+
+ return urgencyValues;
+ }
+
+ // --- listening for events
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ // if specific date or date & time selected, show dialog
+ // ... at conclusion of dialog, update our list
+ UrgencyValue item = urgencyAdapter.getItem(position);
+ if(item.dueDate == SPECIFIC_DATE) {
+ hasTime = item.hasDueTime;
+ intermediateDate = new Date();
+ intermediateDate.setSeconds(0);
+ DatePickerDialog datePicker = new DatePickerDialog(TaskEditActivity.this,
+ this, 1900 + intermediateDate.getYear(), intermediateDate.getMonth(), intermediateDate.getDate());
+ datePicker.show();
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // ignore
+ }
+
+ Date intermediateDate;
+ boolean hasTime;
+
+ public void onDateSet(DatePicker view, int year, int month, int monthDay) {
+ intermediateDate.setYear(year - 1900);
+ intermediateDate.setMonth(month);
+ intermediateDate.setDate(monthDay);
+ intermediateDate.setMinutes(0);
+
+ if(!hasTime) {
+ customDateFinished();
+ return;
+ }
+
+ new TimePickerDialog(TaskEditActivity.this, this,
+ intermediateDate.getHours(), intermediateDate.getMinutes(),
+ DateUtilities.is24HourFormat(TaskEditActivity.this)).show();
+ }
+
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ intermediateDate.setHours(hourOfDay);
+ intermediateDate.setMinutes(minute);
+ customDateFinished();
+ }
+
+ private void customDateFinished() {
+ UrgencyValue[] urgencyList = createUrgencyList(intermediateDate.getTime(),
+ hasTime);
+ urgencyAdapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item,
+ urgencyList);
+ urgencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ this.urgency.setAdapter(urgencyAdapter);
+ urgency.setSelection(0);
+ }
+
+ // --- setting up values
+
+ @Override
+ public void readFromModel() {
+ long dueDate = model.getValue(Task.DUE_DATE);
+ UrgencyValue[] urgencyList = createUrgencyList(dueDate, model.hasDueTime());
+
+ urgencyAdapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item,
+ urgencyList);
+ urgencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ this.urgency.setAdapter(urgencyAdapter);
+
+ if(isNewTask()) {
+ urgency.setSelection(Preferences.getIntegerFromString(R.string.p_default_urgency_key));
+ } else {
+ urgency.setSelection(0);
+ }
+ }
+
+ @Override
+ public void writeToModel() {
+ UrgencyValue item = urgencyAdapter.getItem(urgency.getSelectedItemPosition());
+ model.setDueDateAndTime(new Date(item.dueDate), item.hasDueTime);
+ }
+ }
+
+ /**
+ * Control set for specifying when a task should be hidden
+ *
+ * @author Tim Su
+ *
+ */
+ private class HideUntilControlSet implements TaskEditControlSet,
+ OnItemSelectedListener, OnDateSetListener {
+
+ private static final int NONE = 0;
+ private static final int ON_DUE_DATE = 1;
+ private static final int DUE_DATE_LESS_ONE = 2;
+ private static final int DUE_DATE_LESS_SEVEN = 3;
+ private static final int SPECIFIC_DATE = -1;
+ private final Spinner hideUntil;
+
+ public HideUntilControlSet(int hideUntil) {
+ this.hideUntil = (Spinner) findViewById(hideUntil);
+ this.hideUntil.setOnItemSelectedListener(this);
+ }
+
+ private ArrayAdapter adapter;
+
+ /**
+ * Container class for urgencies
+ *
+ * @author Tim Su
+ *
+ */
+ private class HideUntilValue {
+ public String label;
+ public long date;
+
+ public HideUntilValue(String label, long date) {
+ this.label = label;
+ this.date = date;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+ }
+
+ private HideUntilValue[] createHideUntilList(long specificDate) {
+ // set up base values
+ String[] labels = getResources().getStringArray(R.array.TEA_hideUntil);
+ HideUntilValue[] values = new HideUntilValue[labels.length];
+ values[0] = new HideUntilValue(labels[0], NONE);
+ values[1] = new HideUntilValue(labels[1], ON_DUE_DATE);
+ values[2] = new HideUntilValue(labels[2], DUE_DATE_LESS_ONE);
+ values[3] = new HideUntilValue(labels[3], DUE_DATE_LESS_SEVEN);
+ values[4] = new HideUntilValue(labels[4], SPECIFIC_DATE);
+
+ if(specificDate > DUE_DATE_LESS_SEVEN) {
+ HideUntilValue[] updated = new HideUntilValue[labels.length + 1];
+ for(int i = 0; i < labels.length; i++)
+ updated[i+1] = values[i];
+ SimpleDateFormat format = DateUtilities.getDateFormat(TaskEditActivity.this);
+ updated[0] = new HideUntilValue(format.format(new Date(specificDate)),
+ specificDate);
+ values = updated;
+ }
+
+ return values;
+ }
+
+ // --- listening for events
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ // if specific date selected, show dialog
+ // ... at conclusion of dialog, update our list
+ HideUntilValue item = adapter.getItem(position);
+ if(item.date == SPECIFIC_DATE) {
+ intermediateDate = new Date();
+ intermediateDate.setSeconds(0);
+ DatePickerDialog datePicker = new DatePickerDialog(TaskEditActivity.this,
+ this, 1900 + intermediateDate.getYear(), intermediateDate.getMonth(), intermediateDate.getDate());
+ datePicker.show();
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // ignore
+ }
+
+ Date intermediateDate;
+
+ public void onDateSet(DatePicker view, int year, int month, int monthDay) {
+ intermediateDate.setYear(year - 1900);
+ intermediateDate.setMonth(month);
+ intermediateDate.setDate(monthDay);
+ intermediateDate.setHours(0);
+ intermediateDate.setMinutes(0);
+ customDateFinished();
+ }
+
+ private void customDateFinished() {
+ HideUntilValue[] list = createHideUntilList(intermediateDate.getTime());
+ adapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item,
+ list);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ hideUntil.setAdapter(adapter);
+ hideUntil.setSelection(0);
+ }
+
+ // --- setting up values
+
+ @Override
+ public void readFromModel() {
+ long date = model.getValue(Task.HIDE_UNTIL);
+ long dueDate = model.getValue(Task.DUE_DATE);
+
+ if(date == dueDate)
+ date = ON_DUE_DATE;
+ else if(date + DateUtilities.ONE_DAY == dueDate)
+ date = DUE_DATE_LESS_ONE;
+ else if(date + DateUtilities.ONE_WEEK == dueDate)
+ date = DUE_DATE_LESS_SEVEN;
+
+ HideUntilValue[] list = createHideUntilList(date);
+ adapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item, list);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ hideUntil.setAdapter(adapter);
+
+ if(isNewTask()) {
+ hideUntil.setSelection(Preferences.getIntegerFromString(R.string.p_default_hideUntil_key));
+ } else if(date <= DUE_DATE_LESS_SEVEN){
+ hideUntil.setSelection((int)date);
+ } else {
+ hideUntil.setSelection(0);
+ }
+ }
+
+ @Override
+ public void writeToModel() {
+ HideUntilValue item = adapter.getItem(hideUntil.getSelectedItemPosition());
+ long dueDate = model.getValue(Task.DUE_DATE);
+ if(item.date == NONE)
+ model.setValue(Task.HIDE_UNTIL, 0L);
+ if(item.date == ON_DUE_DATE)
+ model.setValue(Task.HIDE_UNTIL, dueDate);
+ else if(item.date == DUE_DATE_LESS_ONE)
+ model.setValue(Task.HIDE_UNTIL, dueDate - DateUtilities.ONE_DAY);
+ else if(item.date == DUE_DATE_LESS_SEVEN)
+ model.setValue(Task.HIDE_UNTIL, dueDate - DateUtilities.ONE_WEEK);
+ else
+ model.setValue(Task.HIDE_UNTIL, item.date);
+ }
+
+ }
+
+ /**
+ * Control set dealing with reminder settings
+ *
+ * @author Tim Su
+ *
+ */
+ public class ReminderControlSet implements TaskEditControlSet {
+ private final CheckBox during, after;
+ private final Spinner mode;
+
+ public ReminderControlSet(int duringId, int afterId, int modeId) {
+ during = (CheckBox)findViewById(duringId);
+ after = (CheckBox)findViewById(afterId);
+ mode = (Spinner)findViewById(modeId);
+
+ String[] list = new String[] {
+ getString(R.string.TEA_reminder_alarm_off),
+ getString(R.string.TEA_reminder_alarm_on),
+ };
+ ArrayAdapter adapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item, list);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mode.setAdapter(adapter);
+ }
+
+ public void setValue(int flags) {
+ during.setChecked((flags & Task.NOTIFY_AT_DEADLINE) > 0);
+ after.setChecked((flags &
+ Task.NOTIFY_AFTER_DEADLINE) > 0);
+ mode.setSelection((flags &
+ TaskModelForEdit.NOTIFY_NONSTOP) > 0 ? 1 : 0);
+ }
+
+ public int getValue() {
+ int value = 0;
+ if(during.isChecked())
+ value |= Task.NOTIFY_AT_DEADLINE;
+ if(after.isChecked())
+ value |= Task.NOTIFY_AFTER_DEADLINE;
+ if(mode.getSelectedItemPosition() == 1)
+ value |= Task.NOTIFY_NONSTOP;
+ return value;
+ }
+
+ @Override
+ public void readFromModel() {
+ setValue(model.getValue(Task.REMINDER_FLAGS));
+ }
+
+ @Override
+ public void writeToModel() {
+ model.setValue(Task.REMINDER_FLAGS, getValue());
+ }
+ }
+
+ /**
+ * Control set dealing with random reminder settings
+ *
+ * @author Tim Su
+ *
+ */
+ public class RandomReminderControlSet implements TaskEditControlSet {
+ private final CheckBox settingCheckbox;
+ private final Spinner periodSpinner;
+
+ private final int[] hours;
+
+ public RandomReminderControlSet(int settingCheckboxId, int periodButtonId) {
+ settingCheckbox = (CheckBox)findViewById(settingCheckboxId);
+ periodSpinner = (Spinner)findViewById(periodButtonId);
+ periodSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView> arg0, View arg1,
+ int arg2, long arg3) {
+ settingCheckbox.setChecked(true);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ // ignore
+ }
+
+ });
+
+ // create adapter
+ ArrayAdapter adapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item,
+ getResources().getStringArray(R.array.TEA_reminder_random));
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ periodSpinner.setAdapter(adapter);
+
+ // create hour array
+ String[] hourStrings = getResources().getStringArray(R.array.TEA_reminder_random_hours);
+ hours = new int[hourStrings.length];
+ for(int i = 0; i < hours.length; i++)
+ hours[i] = Integer.parseInt(hourStrings[i]);
+ }
+
+ @Override
+ public void readFromModel() {
+ long time;
+ if(isNewTask()) {
+ time = Preferences.getIntegerFromString(R.string.p_rmd_default_random_hours) *
+ DateUtilities.ONE_HOUR;
+ } else {
+ time = model.getValue(Task.REMINDER_PERIOD);
+ }
+
+ settingCheckbox.setChecked(time > 0);
+ if(time == 0) {
+ time = 7*24;
+ }
+
+ int i;
+ for(i = 0; i < hours.length - 1; i++)
+ if(hours[i] * DateUtilities.ONE_HOUR >= time)
+ break;
+ periodSpinner.setSelection(i);
+ }
+
+ @Override
+ public void writeToModel() {
+ if(settingCheckbox.isChecked()) {
+ int hourValue = hours[periodSpinner.getSelectedItemPosition()];
+ model.setValue(Task.REMINDER_PERIOD, hourValue * DateUtilities.ONE_HOUR);
+ } else
+ model.setValue(Task.REMINDER_PERIOD, 0L);
+ }
+ }
+
+ /**
+ * Control set to manage adding and removing tags
+ *
+ * @author Tim Su
+ *
+ */
+ private class TagsControlSet implements TaskEditControlSet {
+
+ private final TagService tagService = new TagService();
+ private final Tag[] allTags;
+ private final LinearLayout tagsContainer;
+
+ public TagsControlSet(int tagsContainer) {
+ allTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE);
+ this.tagsContainer = (LinearLayout) findViewById(tagsContainer);
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public void readFromModel() {
+ // tags (only configure if not already set)
+ if(tagsContainer.getChildCount() == 0) {
+ TodorooCursor cursor = tagService.getTags(model.getId());
+ try {
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext())
+ addTag(cursor.get(Metadata.VALUE));
+ } finally {
+ cursor.close();
+ }
+ addTag("");
+ }
+ }
+
+ @Override
+ public void writeToModel() {
+ ArrayList tags = new ArrayList();
+
+ for(int i = 0; i < tagsContainer.getChildCount(); i++) {
+ TextView tagName = (TextView)tagsContainer.getChildAt(i).findViewById(R.id.text1);
+ if(tagName.getText().length() == 0)
+ continue;
+ tags.add(tagName.getText().toString());
+ }
+
+ tagService.synchronizeTags(model.getId(), tags);
+ }
+
+ /** Adds a tag to the tag field */
+ boolean addTag(String tagName) {
+ if (tagsContainer.getChildCount() >= MAX_TAGS) {
+ return false;
+ }
+
+ LayoutInflater inflater = getLayoutInflater();
+ final View tagItem = inflater.inflate(R.layout.tag_edit_row, null);
+ tagsContainer.addView(tagItem);
+
+ AutoCompleteTextView textView = (AutoCompleteTextView)tagItem.
+ findViewById(R.id.text1);
+ textView.setText(tagName);
+ ArrayAdapter tagsAdapter =
+ new ArrayAdapter(TaskEditActivity.this,
+ android.R.layout.simple_dropdown_item_1line, allTags);
+ textView.setAdapter(tagsAdapter);
+ textView.addTextChangedListener(new TextWatcher() {
+ @SuppressWarnings("nls")
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ if(start == 0 && tagsContainer.getChildAt(
+ tagsContainer.getChildCount()-1) == tagItem) {
+ addTag("");
+ }
+ }
+
+ public void afterTextChanged(Editable s) {
+ //
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ //
+ }
+ });
+
+ ImageButton reminderRemoveButton;
+ reminderRemoveButton = (ImageButton)tagItem.findViewById(R.id.button1);
+ reminderRemoveButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ tagsContainer.removeView(tagItem);
+ }
+ });
+
+ return true;
+ }
+ }
+
+ public class RepeatControlSet implements TaskEditControlSet {
+
+ private final Button repeatValue;
+ private final Spinner repeatInterval;
+
+ public RepeatControlSet(int repeatValue, int repeatInterval) {
+ this.repeatValue = (Button) findViewById(repeatValue);
+ this.repeatInterval = (Spinner) findViewById(repeatInterval);
+ ArrayAdapter repeatAdapter = new ArrayAdapter(
+ TaskEditActivity.this, android.R.layout.simple_spinner_item,
+ RepeatInterval.getLabels(getResources()));
+ repeatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ this.repeatInterval.setAdapter(repeatAdapter);
+
+ this.repeatValue.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ repeatValueClick();
+ }
+ });
+ }
+
+ /** Set up the repeat value button */
+ void setRepeatValue(int value) {
+ if(value == 0)
+ repeatValue.setText(R.string.repeat_value_unset);
+ else
+ repeatValue.setText(Integer.toString(value));
+ repeatValue.setTag(value);
+ }
+
+ private RepeatInfo getRepeatValue() {
+ if(repeatValue.getTag() == null || repeatValue.getTag().equals(0))
+ return null;
+ return new RepeatInfo(RepeatInterval.values()
+ [repeatInterval.getSelectedItemPosition()],
+ (Integer)repeatValue.getTag());
+ }
+
+ protected void repeatValueClick() {
+ final int tagValue = (Integer)repeatValue.getTag();
+
+ final Runnable openDialogRunnable = new Runnable() {
+ public void run() {
+ int dialogValue = tagValue;
+ if(dialogValue == 0)
+ dialogValue = 1;
+
+ new NumberPickerDialog(TaskEditActivity.this, new OnNumberPickedListener() {
+ public void onNumberPicked(NumberPicker view, int number) {
+ setRepeatValue(number);
+ }
+ }, getResources().getString(R.string.repeat_picker_title),
+ dialogValue, 1, 0, 31).show();
+ }
+ };
+
+ openDialogRunnable.run();
+ }
+
+
+ @Override
+ public void readFromModel() {
+ // repeats
+ RepeatInfo repeatInfo = RepeatInfo.fromSingleField(model.getValue(Task.REPEAT));
+ if(repeatInfo != null) {
+ setRepeatValue(repeatInfo.getValue());
+ repeatInterval.setSelection(repeatInfo.getInterval().ordinal());
+ } else
+ setRepeatValue(0);
+ }
+
+
+ @Override
+ public void writeToModel() {
+ RepeatInfo repeatInfo = getRepeatValue();
+ model.setValue(Task.REPEAT, RepeatInfo.toSingleField(repeatInfo));
+ }
+ }
+
+ /**
+ * Calendar Control Set
+ * @author Tim Su
+ *
+ */
+ private class CalendarControlSet implements TaskEditControlSet {
+
+ /** If task has no estimated time, how early to set a task in calendar */
+ private static final int DEFAULT_CAL_TIME = 3600;
+
+ private final CheckBox addToCalendar;
+ private final Button viewCalendarEvent;
+
+ public CalendarControlSet(int addToCalendar,
+ int viewCalendarEvent) {
+ this.addToCalendar = (CheckBox) findViewById(addToCalendar);
+ this.viewCalendarEvent = (Button) findViewById(viewCalendarEvent);
+ }
+
+ /** Take the values from the model and set the calendar start and end times
+ * based on these. Sets keys 'dtstart' and 'dtend'.
+ *
+ * @param preferred preferred due date or null
+ * @param definite definite due date or null
+ * @param estimatedSeconds estimated duration or null
+ * @param values
+ */
+ @SuppressWarnings("nls")
+ public void createCalendarStartEndTimes(Date preferred, Date definite,
+ Integer estimatedSeconds, ContentValues values) {
+ FlurryAgent.onEvent("create-calendar-event");
+
+ Long deadlineDate = null;
+ if (preferred != null && preferred.after(new Date()))
+ deadlineDate = preferred.getTime();
+ else if (definite != null)
+ deadlineDate = definite.getTime();
+ else
+ deadlineDate = System.currentTimeMillis() + 24*3600*1000L;
+
+ int estimatedTime = DEFAULT_CAL_TIME;
+ if(estimatedSeconds != null && estimatedSeconds > 0) {
+ estimatedTime = estimatedSeconds;
+ }
+ values.put("dtstart", deadlineDate - estimatedTime * 1000L);
+ values.put("dtend", deadlineDate);
+ }
+
+ protected void onPause() {
+ // create calendar event
+ /*if(addToCalendar.isChecked() && model.getCalendarUri() == null) {
+
+ Uri uri = Uri.parse("content://calendar/events");
+ ContentResolver cr = getContentResolver();
+
+ ContentValues values = new ContentValues();
+ values.put("title", title.getText().toString());
+ values.put("calendar_id", Preferences.getDefaultCalendarIDSafe(this));
+ values.put("description", notes.getText().toString());
+ values.put("hasAlarm", 0);
+ values.put("transparency", 0);
+ values.put("visibility", 0);
+
+ createCalendarStartEndTimes(model.getPreferredDueDate(),
+ model.getDefiniteDueDate(), model.getEstimatedSeconds(),
+ values);
+
+ Uri result = null;
+ try{
+ result = cr.insert(uri, values);
+ model.setCalendarUri(result.toString());
+ } catch (IllegalArgumentException e) {
+ Log.e("astrid", "Error creating calendar event!", e);
+ }
+ } */
+
+ // save save save
+
+ /* if(addToCalendar.isChecked() && model.getCalendarUri() != null) {
+ Uri result = Uri.parse(model.getCalendarUri());
+ Intent intent = new Intent(Intent.ACTION_EDIT, result);
+
+ ContentValues values = new ContentValues();
+ createCalendarStartEndTimes(model.getPreferredDueDate(),
+ model.getDefiniteDueDate(), model.getEstimatedSeconds(),
+ values);
+
+ intent.putExtra("beginTime", values.getAsLong("dtstart"));
+ intent.putExtra("endTime", values.getAsLong("dtend"));
+
+ startActivity(intent);
+ } */
+
+ }
+
+ @Override
+ public void readFromModel() {
+ }
+
+ @Override
+ public void writeToModel() {
+ }
+ }
+
+}
diff --git a/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
new file mode 100644
index 000000000..3f1898a9d
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/activity/TaskListActivity.java
@@ -0,0 +1,577 @@
+package com.todoroo.astrid.activity;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
+
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
+import android.widget.AbsListView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import com.flurry.android.FlurryAgent;
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.sql.QueryTemplate;
+import com.todoroo.andlib.utility.AndroidUtilities;
+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.dao.Database;
+import com.todoroo.astrid.filters.CoreFilterExposer;
+import com.todoroo.astrid.model.Metadata;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.reminders.Notifications;
+import com.todoroo.astrid.reminders.ReminderService;
+import com.todoroo.astrid.reminders.ReminderService.AlarmScheduler;
+import com.todoroo.astrid.service.MetadataService;
+import com.todoroo.astrid.service.StartupService;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.utility.Constants;
+
+/**
+ * Primary activity for the Bente application. Shows a list of upcoming
+ * tasks and a user's coaches.
+ *
+ * @author Tim Su
+ *
+ */
+public class TaskListActivity extends ListActivity implements OnScrollListener {
+
+ // --- activities
+
+ static final int ACTIVITY_EDIT_TASK = 0;
+ static final int ACTIVITY_SETTINGS = 1;
+ static final int ACTIVITY_PLUGINS = 2;
+
+ // --- menu codes
+
+ private static final int MENU_ADDONS_ID = Menu.FIRST + 1;
+ private static final int MENU_SETTINGS_ID = Menu.FIRST + 2;
+ private static final int MENU_HELP_ID = Menu.FIRST + 3;
+ private static final int MENU_ADDON_INTENT_ID = Menu.FIRST + 4;
+
+ private static final int CONTEXT_MENU_EDIT_TASK_ID = Menu.FIRST + 5;
+ private static final int CONTEXT_MENU_DELETE_TASK_ID = Menu.FIRST + 6;
+ private static final int CONTEXT_MENU_ADDON_INTENT_ID = Menu.FIRST + 7;
+
+ /** menu code indicating the end of the context menu */
+ private static final int CONTEXT_MENU_DEBUG = Menu.FIRST + 8;
+
+ // --- constants
+
+ /** token for passing a {@link Filter} object through extras */
+ public static final String TOKEN_FILTER = "filter"; //$NON-NLS-1$
+
+ /** token for passing a {@link Filter}'s title through extras */
+ public static final String TOKEN_FILTER_TITLE = "title"; //$NON-NLS-1$
+
+ /** token for passing a {@link Filter}'s sql through extras */
+ public static final String TOKEN_FILTER_SQL = "sql"; //$NON-NLS-1$
+
+ /** token for passing a {@link Filter}'s values for new tasks through extras */
+ public static final String TOKEN_FILTER_VALUES = "values"; //$NON-NLS-1$
+
+ // --- instance variables
+
+ @Autowired
+ protected ExceptionService exceptionService;
+
+ @Autowired
+ protected TaskService taskService;
+
+ @Autowired
+ protected MetadataService metadataService;
+
+ @Autowired
+ protected DialogUtilities dialogUtilities;
+
+ @Autowired
+ protected Database database;
+
+ protected TaskAdapter taskAdapter = null;
+ protected DetailReceiver detailReceiver = new DetailReceiver();
+
+ Filter filter;
+
+ /* ======================================================================
+ * ======================================================= initialization
+ * ====================================================================== */
+
+ public TaskListActivity() {
+ DependencyInjectionService.getInstance().inject(this);
+ }
+
+ /** Called when loading up the activity */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ super.onCreate(savedInstanceState);
+
+ new StartupService().onStartupApplication(this);
+ setContentView(R.layout.task_list_activity);
+
+ Bundle extras = getIntent().getExtras();
+ if(extras != null && extras.containsKey(TOKEN_FILTER)) {
+ // probably launched from filter list activity
+ filter = extras.getParcelable(TOKEN_FILTER);
+ } else if(extras != null && extras.containsKey(TOKEN_FILTER_SQL)) {
+ // launched from desktop shortcut, must create a fake filter
+ String title = extras.getString(TOKEN_FILTER_TITLE);
+ String sql = extras.getString(TOKEN_FILTER_SQL);
+ ContentValues values = extras.getParcelable(TOKEN_FILTER_VALUES);
+ filter = new Filter("", "", title, new QueryTemplate(), values); //$NON-NLS-1$ //$NON-NLS-2$
+ filter.sqlQuery = sql;
+ } else {
+ filter = CoreFilterExposer.buildInboxFilter(getResources());
+ }
+
+ if(database == null)
+ return;
+ if(Constants.DEBUG)
+ filter.title = "[D] " + filter.title; //$NON-NLS-1$
+
+ database.openForWriting();
+ setUpUiComponents();
+ setUpTaskList();
+
+ // cache some stuff
+ new Thread(new Runnable() {
+ public void run() {
+ loadContextMenuIntents();
+ }
+ });
+ }
+
+ /**
+ * Create options menu (displayed when user presses menu key)
+ *
+ * @return true if menu should be displayed
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if(menu.size() > 0)
+ return true;
+
+ MenuItem item;
+
+ item = menu.add(Menu.NONE, MENU_ADDONS_ID, Menu.NONE,
+ R.string.TLA_menu_addons);
+ item.setIcon(android.R.drawable.ic_menu_set_as);
+
+ item = menu.add(Menu.NONE, MENU_SETTINGS_ID, Menu.NONE,
+ R.string.TLA_menu_settings);
+ item.setIcon(android.R.drawable.ic_menu_preferences);
+
+ item = menu.add(Menu.NONE, MENU_HELP_ID, Menu.NONE,
+ R.string.TLA_menu_help);
+ item.setIcon(android.R.drawable.ic_menu_help);
+
+ // ask about plug-ins
+ Intent queryIntent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
+ PackageManager pm = getPackageManager();
+ List resolveInfoList = pm.queryIntentActivities(queryIntent, 0);
+ int length = resolveInfoList.size();
+ for(int i = 0; i < length; i++) {
+ ResolveInfo resolveInfo = resolveInfoList.get(i);
+ item = menu.add(Menu.NONE, MENU_ADDON_INTENT_ID, Menu.NONE,
+ resolveInfo.loadLabel(pm));
+ item.setIcon(resolveInfo.loadIcon(pm));
+ Intent intent = new Intent(AstridApiConstants.ACTION_TASK_LIST_MENU);
+ intent.setClassName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ item.setIntent(intent);
+ }
+
+ return true;
+ }
+
+ private void setUpUiComponents() {
+ ((ImageView)findViewById(R.id.back)).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(TaskListActivity.this,
+ FilterListActivity.class);
+ startActivity(intent);
+ finish();
+ }
+ });
+
+ ((TextView)findViewById(R.id.listLabel)).setText(filter.title);
+
+ ((ImageButton)findViewById(R.id.quickAddButton)).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ TextView quickAdd = (TextView)findViewById(R.id.quickAddText);
+ if(quickAdd.getText().length() > 0) {
+ quickAddTask(quickAdd.getText().toString());
+ loadTaskListContent(true);
+ }
+ }
+ });
+
+ ((ImageButton)findViewById(R.id.extendedAddButton)).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ TextView quickAdd = (TextView)findViewById(R.id.quickAddText);
+ Task task = quickAddTask(quickAdd.getText().toString());
+ Intent intent = new Intent(TaskListActivity.this, TaskEditActivity.class);
+ intent.putExtra(TaskEditActivity.ID_TOKEN, task.getId());
+ startActivityForResult(intent, ACTIVITY_EDIT_TASK);
+ }
+ });
+ }
+
+ /**
+ * Quick-add a new task
+ * @param title
+ * @return
+ */
+ @SuppressWarnings("nls")
+ protected Task quickAddTask(String title) {
+ try {
+ Task task = new Task();
+ task.setValue(Task.TITLE, title);
+ ContentValues forMetadata = null;
+ if(filter.valuesForNewTasks != null && filter.valuesForNewTasks.size() > 0) {
+ ContentValues forTask = new ContentValues();
+ forMetadata = new ContentValues();
+ for(Entry item : filter.valuesForNewTasks.valueSet()) {
+ if(item.getKey().startsWith(Task.TABLE.name))
+ AndroidUtilities.putInto(forTask, item.getKey(), item.getValue());
+ else
+ AndroidUtilities.putInto(forMetadata, item.getKey(), item.getValue());
+ }
+ task.mergeWith(forTask);
+ }
+ taskService.save(task, false);
+ if(forMetadata != null && forMetadata.size() > 0) {
+ Metadata metadata = new Metadata();
+ for(Entry item : forMetadata.valueSet()) {
+ metadata.setValue(Metadata.TASK, task.getId());
+ metadata.setValue(Metadata.KEY, item.getKey());
+ metadata.setValue(Metadata.VALUE, item.getValue().toString());
+ metadataService.save(metadata);
+ metadata.clear();
+ }
+ }
+
+ TextView quickAdd = (TextView)findViewById(R.id.quickAddText);
+ quickAdd.setText(""); //$NON-NLS-1$
+ return task;
+ } catch (Exception e) {
+ exceptionService.displayAndReportError(this, "quick-add-task", e);
+ return new Task();
+ }
+ }
+
+ /* ======================================================================
+ * ============================================================ lifecycle
+ * ====================================================================== */
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ FlurryAgent.onStartSession(this, Constants.FLURRY_KEY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ FlurryAgent.onEndSession(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(detailReceiver,
+ new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(detailReceiver);
+ }
+
+ /**
+ * Receiver which receives intents to add items to the filter list
+ *
+ * @author Tim Su
+ *
+ */
+ protected class DetailReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ Bundle extras = intent.getExtras();
+ long taskId = extras.getLong(AstridApiConstants.EXTRAS_TASK_ID);
+ Parcelable[] details = extras.getParcelableArray(AstridApiConstants.EXTRAS_ITEMS);
+ for(Parcelable detail : details)
+ taskAdapter.addDetails(getListView(), taskId, (TaskDetail)detail);
+ } catch (Exception e) {
+ exceptionService.reportError("receive-detail-" + //$NON-NLS-1$
+ intent.getStringExtra(AstridApiConstants.EXTRAS_PLUGIN), e);
+ }
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ taskService.cleanup();
+ if(requestCode == ACTIVITY_EDIT_TASK && resultCode != TaskEditActivity.RESULT_CODE_DISCARDED)
+ loadTaskListContent(true);
+ else if(requestCode == ACTIVITY_SETTINGS)
+ loadTaskListContent(true);
+ }
+
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ // do nothing
+ }
+
+ /**
+ * Detect when user is flinging the task, disable task adapter loading
+ * when this occurs to save resources and time.
+ */
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ switch (scrollState) {
+ case OnScrollListener.SCROLL_STATE_IDLE:
+ if(taskAdapter.isFling)
+ taskAdapter.notifyDataSetChanged();
+ taskAdapter.isFling = false;
+ break;
+ case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
+ if(taskAdapter.isFling)
+ taskAdapter.notifyDataSetChanged();
+ taskAdapter.isFling = false;
+ break;
+ case OnScrollListener.SCROLL_STATE_FLING:
+ taskAdapter.isFling = true;
+ break;
+ }
+ }
+
+ /* ======================================================================
+ * =================================================== managing list view
+ * ====================================================================== */
+
+ /**
+ * Load or re-load action items and update views
+ * @param requery
+ */
+ public void loadTaskListContent(boolean requery) {
+ int oldListItemSelected = getListView().getSelectedItemPosition();
+ Cursor taskCursor = taskAdapter.getCursor();
+
+ if(requery) {
+ taskCursor.requery();
+ taskAdapter.notifyDataSetChanged();
+ }
+ startManagingCursor(taskCursor);
+
+ if(oldListItemSelected != ListView.INVALID_POSITION &&
+ oldListItemSelected < taskCursor.getCount())
+ getListView().setSelection(oldListItemSelected);
+ }
+
+ /** Fill in the Action Item List with current items */
+ protected void setUpTaskList() {
+ // perform query
+ TodorooCursor currentCursor = taskService.fetchFiltered(
+ TaskAdapter.PROPERTIES, filter);
+ startManagingCursor(currentCursor);
+
+ // set up list adapters
+ taskAdapter = new TaskAdapter(this, R.layout.task_adapter_row,
+ currentCursor, false, null);
+ setListAdapter(taskAdapter);
+ getListView().setOnScrollListener(this);
+ registerForContextMenu(getListView());
+
+ loadTaskListContent(false);
+ }
+
+ /* ======================================================================
+ * ============================================================== actions
+ * ====================================================================== */
+
+ protected Pair[] contextMenuItemCache = null;
+
+ protected void loadContextMenuIntents() {
+ Intent queryIntent = new Intent(AstridApiConstants.ACTION_TASK_CONTEXT_MENU);
+ PackageManager pm = getPackageManager();
+ List resolveInfoList = pm.queryIntentActivities(queryIntent, 0);
+ int length = resolveInfoList.size();
+ contextMenuItemCache = new Pair[length];
+ for(int i = 0; i < length; i++) {
+ ResolveInfo resolveInfo = resolveInfoList.get(i);
+ Intent intent = new Intent(AstridApiConstants.ACTION_TASK_CONTEXT_MENU);
+ intent.setClassName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ CharSequence title = resolveInfo.loadLabel(pm);
+ contextMenuItemCache[i] = Pair.create(title, intent);
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ AdapterContextMenuInfo adapterInfo = (AdapterContextMenuInfo)menuInfo;
+ Task task = ((ViewHolder)adapterInfo.targetView.getTag()).task;
+ int id = (int)task.getId();
+ menu.setHeaderTitle(task.getValue(Task.TITLE));
+
+ menu.add(id, CONTEXT_MENU_EDIT_TASK_ID, Menu.NONE,
+ R.string.TAd_contextEditTask);
+
+ menu.add(id, CONTEXT_MENU_DELETE_TASK_ID, Menu.NONE,
+ R.string.TAd_contextDeleteTask);
+
+ if(Constants.DEBUG) {
+ menu.add("--- debug ---"); //$NON-NLS-1$
+ menu.add(id, CONTEXT_MENU_DEBUG, Menu.NONE,
+ "when alarm?"); //$NON-NLS-1$
+ menu.add(id, CONTEXT_MENU_DEBUG + 1, Menu.NONE,
+ "make notification"); //$NON-NLS-1$
+ }
+
+ if(contextMenuItemCache == null)
+ return;
+
+ // ask about plug-ins
+ long taskId = task.getId();
+ for(int i = 0; i < contextMenuItemCache.length; i++) {
+ Intent intent = contextMenuItemCache[i].getRight();
+ MenuItem item = menu.add(id, CONTEXT_MENU_ADDON_INTENT_ID, Menu.NONE,
+ contextMenuItemCache[i].getLeft());
+ intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, taskId);
+ item.setIntent(intent);
+ }
+ }
+
+ /** Show a dialog box and delete the task specified */
+ private void deleteTask(final Task task) {
+ new AlertDialog.Builder(this).setTitle(R.string.DLG_confirm_title)
+ .setMessage(R.string.DLG_delete_this_task_question).setIcon(
+ android.R.drawable.ic_dialog_alert).setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ taskService.delete(task);
+ loadTaskListContent(true);
+ }
+ }).setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, final MenuItem item) {
+ Intent intent;
+ long itemId;
+
+ // handle my own menus
+ switch (item.getItemId()) {
+ case MENU_ADDONS_ID:
+ dialogUtilities.okDialog(
+ this,
+ "if this were real life, I would display your " + //$NON-NLS-1$
+ "add-ons so you could enable/disable/rearrange them.", //$NON-NLS-1$
+ null);
+ return true;
+ case MENU_SETTINGS_ID:
+ intent = new Intent(this, EditPreferences.class);
+ startActivityForResult(intent, ACTIVITY_SETTINGS);
+ return true;
+ case MENU_HELP_ID:
+ // TODO
+ return true;
+
+
+ // context menu items
+ case CONTEXT_MENU_ADDON_INTENT_ID: {
+ intent = item.getIntent();
+ AndroidUtilities.startExternalIntent(this, intent);
+ return true;
+ }
+
+ case CONTEXT_MENU_EDIT_TASK_ID: {
+ itemId = item.getGroupId();
+ intent = new Intent(TaskListActivity.this, TaskEditActivity.class);
+ intent.putExtra(TaskEditActivity.ID_TOKEN, itemId);
+ startActivityForResult(intent, ACTIVITY_EDIT_TASK);
+ return true;
+ }
+
+ case CONTEXT_MENU_DELETE_TASK_ID: {
+ itemId = item.getGroupId();
+ Task task = new Task();
+ task.setId(itemId);
+ deleteTask(task);
+ return true;
+ }
+
+ // --- debug
+
+ case CONTEXT_MENU_DEBUG: {
+ itemId = item.getGroupId();
+ Task task = new Task();
+ task.setId(itemId);
+ final ReminderService reminderService = new ReminderService();
+ reminderService.setScheduler(new AlarmScheduler() {
+ @Override
+ public void createAlarm(Task theTask, long time, int type) {
+ Toast.makeText(TaskListActivity.this, "Scheduled Alarm: " + //$NON-NLS-1$
+ new Date(time), Toast.LENGTH_LONG).show();
+ reminderService.setScheduler(null);
+ }
+ });
+ reminderService.scheduleAlarm(task);
+ if(reminderService.getScheduler() != null)
+ Toast.makeText(this, "No alarms", Toast.LENGTH_LONG).show(); //$NON-NLS-1$
+ return true;
+ }
+
+ case CONTEXT_MENU_DEBUG + 1: {
+ itemId = item.getGroupId();
+ new Notifications().showNotification(itemId, 0, "test reminder"); //$NON-NLS-1$
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java b/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java
new file mode 100644
index 000000000..c0a4f3e69
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java
@@ -0,0 +1,203 @@
+/**
+ * See the file "LICENSE" for the full license governing this code.
+ */
+package com.todoroo.astrid.adapter;
+
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ImageView.ScaleType;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterCategory;
+import com.todoroo.astrid.api.FilterListHeader;
+import com.todoroo.astrid.api.FilterListItem;
+
+public class FilterAdapter extends BaseExpandableListAdapter {
+
+ private final ArrayList items;
+ protected final Activity activity;
+
+ public FilterAdapter(Activity activity) {
+ super();
+ this.activity = activity;
+ this.items = new ArrayList();
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public void add(FilterListItem item) {
+ items.add(item);
+ }
+
+ /* ======================================================================
+ * ========================================================== child nodes
+ * ====================================================================== */
+
+ public Object getChild(int groupPosition, int childPosition) {
+ FilterListItem item = items.get(groupPosition);
+ if(!(item instanceof FilterCategory))
+ return null;
+
+ return ((FilterCategory)item).children[childPosition];
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ FilterListItem item = items.get(groupPosition);
+ if(!(item instanceof FilterCategory))
+ return 0;
+ return ((FilterCategory)item).children.length;
+ }
+
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ FilterListItem item = (FilterListItem)getChild(groupPosition, childPosition);
+ View textView = getStandardView((Filter)item, true);
+ return textView;
+ }
+
+ /* ======================================================================
+ * ========================================================= parent nodes
+ * ====================================================================== */
+
+ public Object getGroup(int groupPosition) {
+ return items.get(groupPosition);
+ }
+
+ public int getGroupCount() {
+ return items.size();
+ }
+
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ View view = getView((FilterListItem)getGroup(groupPosition), false, isExpanded);
+ return view;
+ }
+
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ /* ======================================================================
+ * ================================================================ views
+ * ====================================================================== */
+
+ public View getView(FilterListItem item, boolean isChild, boolean isExpanded) {
+ if(item instanceof FilterListHeader)
+ return getHeaderView((FilterListHeader)item, isChild);
+ else if(item instanceof FilterCategory)
+ return getCategoryView((FilterCategory)item, isExpanded);
+ else
+ return getStandardView(item, isChild);
+ }
+
+ public View getCategoryView(FilterCategory filter, boolean isExpanded) {
+ AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, 64);
+
+ FrameLayout layout = new FrameLayout(activity);
+ layout.setLayoutParams(lp);
+
+ ImageView image = new ImageView(activity);
+ if(isExpanded)
+ image.setImageResource(R.drawable.expander_ic_maximized);
+ else
+ image.setImageResource(R.drawable.expander_ic_minimized);
+ FrameLayout.LayoutParams llp = new FrameLayout.LayoutParams(
+ 32, 32);
+ llp.gravity = Gravity.CENTER_VERTICAL;
+ image.setLayoutParams(llp);
+ image.setScaleType(ScaleType.FIT_CENTER);
+ layout.addView(image);
+
+ TextView textView = new TextView(activity);
+ textView.setGravity(Gravity.CENTER_VERTICAL);
+ textView.setText(filter.listingTitle);
+ textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Category);
+
+ View view = augmentView(textView, filter);
+ view.setPadding(60, 2, 0, 2);
+ view.setLayoutParams(llp);
+
+ layout.addView(view);
+
+ return layout;
+ }
+
+ /**
+ * Decorate textview and add an image if the filter requests it
+ * @param textView
+ * @param filter
+ * @return final view ready to be added
+ */
+ private View augmentView(TextView textView, FilterListItem filter) {
+ if(filter.listingIcon != null) {
+ LinearLayout layout = new LinearLayout(activity);
+ layout.setGravity(textView.getGravity());
+ layout.setOrientation(LinearLayout.HORIZONTAL);
+
+ ImageView icon = new ImageView(activity);
+ icon.setImageBitmap(filter.listingIcon);
+ icon.setPadding(0, 0, 15, 0);
+ layout.addView(icon);
+ layout.addView(textView);
+ return layout;
+ }
+
+ return textView;
+ }
+
+ public View getStandardView(FilterListItem filter, boolean isChild) {
+ AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, 64);
+
+ TextView textView = new TextView(activity);
+ textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ textView.setText(filter.listingTitle);
+ textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Filter);
+
+ View view = augmentView(textView, filter);
+ view.setBackgroundDrawable(null);
+ view.setLayoutParams(lp);
+ view.setPadding(isChild ? 50 : 10, 0, 0, 0);
+
+ return view;
+ }
+
+ public View getHeaderView(FilterListHeader header, boolean isChild) {
+ AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, 40);
+
+ TextView textView = new TextView(activity);
+ textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ textView.setTextAppearance(activity, R.style.TextAppearance_FLA_Header);
+ textView.setText(header.listingTitle);
+
+ View view = augmentView(textView, header);
+ view.setBackgroundResource(R.drawable.edit_titlebar);
+ view.setLayoutParams(lp);
+ view.setPadding(isChild ? 50 : 10, 0, 0, 0);
+
+ return view;
+ }
+}
diff --git a/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
new file mode 100644
index 000000000..861c5d202
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/adapter/TaskAdapter.java
@@ -0,0 +1,429 @@
+package com.todoroo.astrid.adapter;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Paint;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+import android.view.View.OnKeyListener;
+import android.widget.CheckBox;
+import android.widget.CursorAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.Property;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.TaskDetail;
+import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * Adapter for displaying a user's tasks as a list
+ *
+ * @author Tim Su
+ *
+ */
+public class TaskAdapter extends CursorAdapter {
+
+ public interface OnCompletedTaskListener {
+ public void onCompletedTask(Task item, boolean newState);
+ }
+
+ // --- other constants
+
+ /** Properties that need to be read from the action item */
+ public static final Property>[] PROPERTIES = new Property>[] {
+ Task.ID,
+ Task.TITLE,
+ Task.IMPORTANCE,
+ Task.DUE_DATE,
+ Task.COMPLETION_DATE,
+ Task.HIDE_UNTIL,
+ Task.DELETION_DATE,
+ };
+
+ private static int[] IMPORTANCE_COLORS = null;
+
+ // --- instance variables
+
+ @Autowired
+ ExceptionService exceptionService;
+
+ @Autowired
+ TaskService taskService;
+
+ @Autowired
+ DialogUtilities dialogUtilities;
+
+ @Autowired
+ Boolean debug;
+
+ protected final Activity activity;
+ protected final HashMap completedItems;
+ protected final HashMap> detailCache;
+ public boolean isFling = false;
+ private final int resource;
+ private final LayoutInflater inflater;
+ protected OnCompletedTaskListener onCompletedTaskListener = null;
+ private final int fontSize;
+
+ /**
+ * Constructor
+ *
+ * @param activity
+ * @param resource
+ * layout resource to inflate
+ * @param c
+ * database cursor
+ * @param autoRequery
+ * whether cursor is automatically re-queried on changes
+ * @param onCompletedTaskListener
+ * task listener. can be null
+ */
+ public TaskAdapter(Activity activity, int resource,
+ TodorooCursor c, boolean autoRequery,
+ OnCompletedTaskListener onCompletedTaskListener) {
+ super(activity, c, autoRequery);
+ DependencyInjectionService.getInstance().inject(this);
+
+ inflater = (LayoutInflater) activity.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ this.resource = resource;
+ this.activity = activity;
+ this.onCompletedTaskListener = onCompletedTaskListener;
+
+ completedItems = new HashMap();
+ detailCache = new HashMap>();
+ fontSize = Preferences.getIntegerFromString(R.string.p_fontSize);
+
+ IMPORTANCE_COLORS = Task.getImportanceColors(activity.getResources());
+ }
+
+ /* ======================================================================
+ * =========================================================== view setup
+ * ====================================================================== */
+
+ /** Creates a new view for use in the list view */
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View view = inflater.inflate(resource, parent, false);
+
+ // create view holder
+ ViewHolder viewHolder = new ViewHolder();
+ viewHolder.task = new Task();
+ 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 = (LinearLayout)view.findViewById(R.id.details);
+ viewHolder.importance = (View)view.findViewById(R.id.importance);
+ view.setTag(viewHolder);
+
+ // add UI component listeners
+ addListeners(view);
+
+ // populate view content
+ bindView(view, context, cursor);
+
+ return view;
+ }
+
+ /** Populates a view with content */
+ @Override
+ public void bindView(View view, Context context, Cursor c) {
+ TodorooCursor cursor = (TodorooCursor)c;
+ Task actionItem = ((ViewHolder)view.getTag()).task;
+ actionItem.readFromCursor(cursor);
+
+ setFieldContentsAndVisibility(view, actionItem);
+ setTaskAppearance(view, actionItem.isCompleted());
+ }
+
+ /** Helper method to set the visibility based on if there's stuff inside */
+ private static void setVisibility(TextView v) {
+ if(v.getText().length() > 0)
+ v.setVisibility(View.VISIBLE);
+ else
+ v.setVisibility(View.GONE);
+ }
+
+ /**
+ * View Holder saves a lot of findViewById lookups.
+ *
+ * @author Tim Su
+ *
+ */
+ public static class ViewHolder {
+ public Task task;
+ public TextView nameView;
+ public CheckBox completeBox;
+ public TextView dueDate;
+ public LinearLayout details;
+ public View importance;
+ public TextView loadingDetails;
+ }
+
+ /** Helper method to set the contents and visibility of each field */
+ private void setFieldContentsAndVisibility(View view, Task task) {
+ Resources r = activity.getResources();
+ ViewHolder viewHolder = (ViewHolder)view.getTag();
+
+ // name
+ final TextView nameView = viewHolder.nameView; {
+ String nameValue = task.getValue(Task.TITLE);
+ long hiddenUntil = task.getValue(Task.HIDE_UNTIL);
+ if(task.getValue(Task.DELETION_DATE) > 0)
+ nameValue = r.getString(R.string.TAd_deletedFormat, nameValue);
+ if(hiddenUntil > DateUtilities.now())
+ nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
+ nameView.setText(nameValue);
+ }
+
+ // complete box
+ final CheckBox completeBox = viewHolder.completeBox; {
+ // show item as completed if it was recently checked
+ if(completedItems.containsKey(task.getId()))
+ task.setValue(Task.COMPLETION_DATE, DateUtilities.now());
+ completeBox.setChecked(task.isCompleted());
+ completeBox.setVisibility(View.VISIBLE);
+ }
+
+ // due date / completion date
+ final TextView dueDateView = viewHolder.dueDate; {
+ if(!task.isCompleted() && task.hasDueDate()) {
+ long dueDate = task.getValue(Task.DUE_DATE);
+ long secondsLeft = dueDate - DateUtilities.now();
+ if(secondsLeft > 0) {
+ dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate);
+ } else {
+ dueDateView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDueDate_Overdue);
+ }
+
+ String dateValue;
+ Date dueDateAsDate = DateUtilities.unixtimeToDate(dueDate);
+ if (task.hasDueTime()) {
+ dateValue = DateUtilities.getDateWithTimeFormat(activity).format(dueDateAsDate);
+ } else {
+ dateValue = DateUtilities.getDateFormat(activity).format(dueDateAsDate);
+ }
+ dueDateView.setText(dateValue);
+ setVisibility(dueDateView);
+ } else {
+ dueDateView.setVisibility(View.GONE);
+ }
+ }
+
+ // other information - send out a request for it (only if not fling)
+ final LinearLayout detailsView = viewHolder.details;
+ if(!isFling) {
+ detailsView.removeViews(2, detailsView.getChildCount() - 2);
+ if(detailCache.containsKey(task.getId())) {
+ ArrayList details = detailCache.get(task.getId());
+ int length = details.size();
+ for(int i = 0; i < length; i++)
+ detailsView.addView(detailToView(details.get(i)));
+ } else {
+ detailCache.put(task.getId(), new ArrayList());
+ Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_DETAILS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
+ activity.sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+
+ // add loading message
+ if(viewHolder.loadingDetails == null) {
+ viewHolder.loadingDetails = new TextView(activity);
+ viewHolder.loadingDetails.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails);
+ viewHolder.loadingDetails.setText(R.string.TAd_loading);
+ detailsView.addView(viewHolder.loadingDetails);
+ }
+ }
+ }
+
+ // importance bar - must be set at end when view height is determined
+ final View importanceView = viewHolder.importance; {
+ int value = task.getValue(Task.IMPORTANCE);
+ importanceView.setBackgroundColor(IMPORTANCE_COLORS[value]);
+ importanceView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Respond to a request to add details for a task
+ *
+ * @param taskId
+ */
+ public synchronized void addDetails(ListView list, long taskId, TaskDetail detail) {
+ if(detail == null)
+ return;
+
+ ArrayList details = detailCache.get(taskId);
+ 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;
+
+ viewHolder.details.addView(detailToView(detail));
+ viewHolder.details.setVisibility(View.VISIBLE);
+
+ if(viewHolder.loadingDetails != null) {
+ viewHolder.details.removeView(viewHolder.loadingDetails);
+ viewHolder.loadingDetails = null;
+ }
+
+ break;
+ }
+ }
+
+ /**
+ * Create a new view for the given detail
+ *
+ * @param detail
+ */
+ private View detailToView(TaskDetail detail) {
+ TextView textView = new TextView(activity);
+ textView.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemDetails);
+ textView.setText(detail.text);
+ if(detail.color != 0)
+ textView.setTextColor(detail.color);
+ return textView;
+ }
+
+ private final View.OnClickListener completeBoxListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ View container = (View) v.getParent();
+ Task task = ((ViewHolder)container.getTag()).task;
+
+ completeTask(task, ((CheckBox)v).isChecked());
+ // set check box to actual action item state
+ setTaskAppearance(container, 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);
+
+ // key press listener
+ container.setOnKeyListener(new OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if(event.getAction() != KeyEvent.ACTION_UP)
+ return false;
+
+ // hot-key to set task priority - 1-4 or ALT + Q-R
+ if(event.getNumber() >= '1' && event.getNumber() <= '4') {
+ int importance = event.getNumber() - '1';
+ Task task = ((ViewHolder)container.getTag()).task;
+ task.setValue(Task.IMPORTANCE, importance);
+ taskService.save(task, false);
+ setFieldContentsAndVisibility(container, task);
+ }
+
+ return false;
+ }
+ });
+ }
+
+ class ContextMenuListener implements OnCreateContextMenuListener {
+
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ // this is all a big sham. it's actually handled in Task List Activity
+ }
+ }
+
+ /* ======================================================================
+ * ======================================================= event handlers
+ * ====================================================================== */
+
+ /**
+ * Call me when the parent presses trackpad
+ */
+ public void onTrackpadPressed(View container) {
+ if(container == null)
+ return;
+
+ final CheckBox completeBox = ((CheckBox)container.findViewById(R.id.completeBox));
+ completeBox.performClick();
+ }
+
+ /** Helper method to adjust a tasks' appearance if the task is completed or
+ * uncompleted.
+ *
+ * @param actionItem
+ * @param name
+ * @param progress
+ */
+ void setTaskAppearance(View container, boolean state) {
+ CheckBox completed = (CheckBox)container.findViewById(R.id.completeBox);
+ TextView name = (TextView)container.findViewById(R.id.title);
+
+ completed.setChecked(state);
+
+ if(state) {
+ name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle_Completed);
+ } else {
+ name.setPaintFlags(name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
+ name.setTextAppearance(activity, R.style.TextAppearance_TAd_ItemTitle);
+ }
+ name.setTextSize(fontSize);
+ }
+
+ /**
+ * This method is called when user completes a task via check box or other
+ * means
+ *
+ * @param container
+ * container for the action item
+ * @param newState
+ * state that this task should be set to
+ * @param completeBox
+ * the box that was clicked. can be null
+ */
+ protected void completeTask(final Task actionItem, final boolean newState) {
+ if(actionItem == null)
+ return;
+
+ if (newState != actionItem.isCompleted()) {
+ completedItems.put(actionItem.getId(), newState);
+ taskService.setComplete(actionItem, newState);
+
+ if(onCompletedTaskListener != null)
+ onCompletedTaskListener.onCompletedTask(actionItem, newState);
+ }
+ }
+
+}
diff --git a/astrid/src/com/todoroo/astrid/api/SearchFilter.java b/astrid/src/com/todoroo/astrid/api/SearchFilter.java
new file mode 100644
index 000000000..9a1958ceb
--- /dev/null
+++ b/astrid/src/com/todoroo/astrid/api/SearchFilter.java
@@ -0,0 +1,83 @@
+package com.todoroo.astrid.api;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Special filter that triggers the search functionality when accessed.
+ *
+ * @author Tim Su
+ *
+ */
+public class SearchFilter extends FilterListItem {
+
+ /**
+ * Plug-in Identifier
+ */
+ public final String plugin;
+
+ /**
+ * Constructor for creating a new SearchFilter
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
+ * @param listingTitle
+ * Title of this item as displayed on the lists page, e.g. Inbox
+ */
+ public SearchFilter(String plugin, String listingTitle) {
+ this.plugin = plugin;
+ this.listingTitle = listingTitle;
+ }
+
+ /**
+ * Constructor for creating a new SearchFilter
+ *
+ * @param plugin
+ * {@link Plugin} identifier that encompasses object
+ */
+ protected SearchFilter(String plugin) {
+ this.plugin = plugin;
+ }
+
+ // --- parcelable
+
+ /**
+ * {@inheritDoc}
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(plugin);
+ super.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Parcelable creator
+ */
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+
+ /**
+ * {@inheritDoc}
+ */
+ public SearchFilter createFromParcel(Parcel source) {
+ SearchFilter item = new SearchFilter(source.readString());
+ item.readFromParcel(source);
+ return item;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public SearchFilter[] newArray(int size) {
+ return new SearchFilter[size];
+ }
+
+ };
+
+}
diff --git a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
index d685d2233..339af654c 100644
--- a/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/MetadataDao.java
@@ -10,11 +10,11 @@ import android.database.Cursor;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
-import com.todoroo.andlib.data.sql.Criterion;
-import com.todoroo.andlib.data.sql.Join;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Join;
+import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
diff --git a/astrid/src/com/todoroo/astrid/dao/TaskDao.java b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
index 527c5dd19..7bad5a16e 100644
--- a/astrid/src/com/todoroo/astrid/dao/TaskDao.java
+++ b/astrid/src/com/todoroo/astrid/dao/TaskDao.java
@@ -11,14 +11,15 @@ import android.content.Intent;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
-import com.todoroo.andlib.data.sql.Criterion;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.model.Task;
+import com.todoroo.astrid.reminders.ReminderService;
/**
* Data Access layer for {@link Task}-related operations.
@@ -34,6 +35,8 @@ public class TaskDao extends GenericDao {
@Autowired
Database database;
+ ReminderService reminderService;
+
public TaskDao() {
super(Task.class);
DependencyInjectionService.getInstance().inject(this);
@@ -47,43 +50,48 @@ public class TaskDao extends GenericDao {
*/
public static class TaskCriteria {
- /** Returns tasks by id */
+ /** @returns tasks by id */
public static Criterion byId(long id) {
return Task.ID.eq(id);
}
- /** Return tasks that have not yet been completed */
+ /** @return tasks that were deleted */
+ public static Criterion isDeleted() {
+ return Task.DELETION_DATE.neq(0);
+ }
+
+ /** @return tasks that have not yet been completed or deleted */
public static Criterion isActive() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),
Task.DELETION_DATE.eq(0));
}
- /** Return tasks that are not hidden at given unixtime */
- public static Criterion isVisible(int time) {
- return Task.HIDDEN_UNTIL.lt(time);
+ /** @return tasks that are not hidden at given unixtime */
+ public static Criterion isVisible(long time) {
+ return Task.HIDE_UNTIL.lt(time);
}
- /** Returns tasks that have a due date */
+ /** @return tasks that have a due date */
public static Criterion hasDeadlines() {
return Task.DUE_DATE.neq(0);
}
- /** Returns tasks that are due before a certain unixtime */
- public static Criterion dueBefore(int time) {
+ /** @return tasks that are due before a certain unixtime */
+ public static Criterion dueBefore(long time) {
return Criterion.and(Task.DUE_DATE.gt(0), Task.DUE_DATE.lt(time));
}
- /** Returns tasks that are due after a certain unixtime */
- public static Criterion dueAfter(int time) {
+ /** @return tasks that are due after a certain unixtime */
+ public static Criterion dueAfter(long time) {
return Task.DUE_DATE.gt(time);
}
- /** Returns tasks completed before a given unixtime */
- public static Criterion completedBefore(int time) {
+ /** @return tasks completed before a given unixtime */
+ public static Criterion completedBefore(long time) {
return Criterion.and(Task.COMPLETION_DATE.gt(0), Task.COMPLETION_DATE.lt(time));
}
- /** Returns tasks that have a blank or null title */
+ /** @return tasks that have a blank or null title */
@SuppressWarnings("nls")
public static Criterion hasNoTitle() {
return Criterion.or(Task.TITLE.isNull(), Task.TITLE.eq(""));
@@ -127,31 +135,34 @@ public class TaskDao extends GenericDao {
boolean saveSuccessful;
if (task.getId() == Task.NO_ID) {
- saveSuccessful = createItem(task);
+ saveSuccessful = createNew(task);
} else {
ContentValues values = task.getSetValues();
if(values.size() == 0)
return true;
beforeSave(task, values, duringSync);
- saveSuccessful = saveItem(task);
+ saveSuccessful = saveExisting(task);
afterSave(task, values, duringSync);
}
+ if(saveSuccessful)
+ task.markSaved();
+
return saveSuccessful;
}
@Override
- public boolean createItem(AbstractModel item) {
+ public boolean createNew(AbstractModel item) {
if(!item.containsValue(Task.CREATION_DATE))
item.setValue(Task.CREATION_DATE, DateUtilities.now());
item.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
- return super.createItem(item);
+ return super.createNew(item);
}
@Override
- public boolean saveItem(AbstractModel item) {
+ public boolean saveExisting(AbstractModel item) {
item.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
- return super.saveItem(item);
+ return super.saveExisting(item);
}
/**
@@ -184,18 +195,94 @@ public class TaskDao extends GenericDao {
* @param duringSync whether this save occurs as part of a sync
*/
private void afterSave(Task task, ContentValues values, boolean duringSync) {
+ if(values.containsKey(Task.COMPLETION_DATE.name) && task.isCompleted())
+ afterComplete(task, values, duringSync);
+ else {
+ if(reminderService == null)
+ reminderService = new ReminderService();
+ reminderService.scheduleAlarm(task);
+ }
+
if(duringSync)
return;
- // if task was completed, fire task completed notification
- if(values.containsKey(Task.COMPLETION_DATE.name) &&
- values.getAsInteger(Task.COMPLETION_DATE.name) > 0 && !duringSync) {
+ // due date was updated, update calendar event
+ /*if((values.containsKey(AbstractTaskModel.DEFINITE_DUE_DATE) ||
+ values.containsKey(AbstractTaskModel.PREFERRED_DUE_DATE)) &&
+ !values.containsKey(AbstractTaskModel.CALENDAR_URI)) {
+ try {
+ Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(),
+ new String[] { AbstractTaskModel.CALENDAR_URI });
+ cursor.moveToFirst();
+ String uriAsString = cursor.getString(0);
+ cursor.close();
+ if(uriAsString != null && uriAsString.length() > 0) {
+ ContentResolver cr = context.getContentResolver();
+ Uri uri = Uri.parse(uriAsString);
+
+ Integer estimated = null;
+ if(values.containsKey(AbstractTaskModel.ESTIMATED_SECONDS))
+ estimated = values.getAsInteger(AbstractTaskModel.ESTIMATED_SECONDS);
+ else { // read from event
+ Cursor event = cr.query(uri, new String[] {"dtstart", "dtend"},
+ null, null, null);
+ event.moveToFirst();
+ estimated = (event.getInt(1) - event.getInt(0))/1000;
+ }
+
+ // create new start and end date for this event
+ ContentValues newValues = new ContentValues();
+ TaskEditActivity.createCalendarStartEndTimes(task.getPreferredDueDate(),
+ task.getDefiniteDueDate(), estimated, newValues); TODO
+ cr.update(uri, newValues, null, null);
+ }
+ } catch (Exception e) {
+ // ignore calendar event - event could be deleted or whatever
+ Log.e("astrid", "Error moving calendar event", e);
+ }
+ }*/
+ }
+ /**
+ * Called after the task was just completed
+ *
+ * @param task
+ * @param values
+ * @param duringSync
+ */
+ private void afterComplete(Task task, ContentValues values, boolean duringSync) {
+ /*Cursor cursor = fetchTaskCursor(task.getTaskIdentifier(),
+ TaskModelForHandlers.FIELD_LIST);
+ TaskModelForHandlers model = new TaskModelForHandlers(cursor, values);
+
+ // handle repeat
+ RepeatInfo repeatInfo = model.getRepeat();
+ if(repeatInfo != null) {
+ model.repeatTaskBy(context, this, repeatInfo);
+ database.update(tasksTable, values, KEY_ROWID + "=" +
+ task.getTaskIdentifier().getId(), null);
+ }
+
+ // handle sync-on-complete
+ if((model.getFlags() & TaskModelForHandlers.FLAG_SYNC_ON_COMPLETE) > 0 &&
+ !duringSync) {
+ Synchronizer synchronizer = new Synchronizer(model.getTaskIdentifier());
+ synchronizer.synchronize(context, new SynchronizerListener() {
+ public void onSynchronizerFinished(int numServicesSynced) {
+ TaskListSubActivity.shouldRefreshTaskList = true;
+ }
+ });
+ }
+
+ cursor.close();
+ cleanupTask(task.getTaskIdentifier(), repeatInfo != null);*/
+
+ // send broadcast
+ if(!duringSync) {
Context context = ContextManager.getContext();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED);
broadcastIntent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId());
context.sendOrderedBroadcast(broadcastIntent, null);
-
}
}
diff --git a/astrid/src/com/todoroo/astrid/model/Task.java b/astrid/src/com/todoroo/astrid/model/Task.java
index 61419a230..e5ac9ff11 100644
--- a/astrid/src/com/todoroo/astrid/model/Task.java
+++ b/astrid/src/com/todoroo/astrid/model/Task.java
@@ -6,9 +6,12 @@
package com.todoroo.astrid.model;
+import java.util.Date;
+
import android.content.ContentValues;
+import android.content.res.Resources;
-import com.timsu.astrid.data.enums.RepeatInterval;
+import com.timsu.astrid.R;
import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
@@ -18,7 +21,7 @@ import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.utility.DateUtilities;
-import com.todoroo.astrid.api.AstridContentProvider.AstridTask;
+import com.todoroo.astrid.utility.Preferences;
/**
* Data Model which represents a task users need to accomplish.
@@ -27,7 +30,7 @@ import com.todoroo.astrid.api.AstridContentProvider.AstridTask;
*
*/
@SuppressWarnings("nls")
-public class Task extends AbstractModel {
+public final class Task extends AbstractModel {
// --- table
@@ -37,43 +40,39 @@ public class Task extends AbstractModel {
/** ID */
public static final LongProperty ID = new LongProperty(
- TABLE, AstridTask.ID);
+ TABLE, ID_PROPERTY_NAME);
/** Name of Task */
public static final StringProperty TITLE = new StringProperty(
- TABLE, AstridTask.TITLE);
-
- /** Urgency of Task (see urgency flags) */
- public static final IntegerProperty URGENCY = new IntegerProperty(
- TABLE, AstridTask.URGENCY);
+ TABLE, "title");
/** Importance of Task (see importance flags) */
public static final IntegerProperty IMPORTANCE = new IntegerProperty(
- TABLE, AstridTask.IMPORTANCE);
+ TABLE, "importance");
/** Unixtime Task is due, 0 if not set */
- public static final IntegerProperty DUE_DATE = new IntegerProperty(
- TABLE, AstridTask.DUE_DATE);
+ public static final LongProperty DUE_DATE = new LongProperty(
+ TABLE, "dueDate");
- /** Unixtime Task should be hidden until */
- public static final IntegerProperty HIDDEN_UNTIL = new IntegerProperty(
- TABLE, AstridTask.HIDDEN_UNTIL);
+ /** Unixtime Task should be hidden until, 0 if not set */
+ public static final LongProperty HIDE_UNTIL = new LongProperty(
+ TABLE, "hideUntil");
/** Unixtime Task was created */
- public static final IntegerProperty CREATION_DATE = new IntegerProperty(
- TABLE, AstridTask.CREATION_DATE);
+ public static final LongProperty CREATION_DATE = new LongProperty(
+ TABLE, "created");
/** Unixtime Task was last touched */
- public static final IntegerProperty MODIFICATION_DATE = new IntegerProperty(
- TABLE, AstridTask.MODIFICATION_DATE);
+ public static final LongProperty MODIFICATION_DATE = new LongProperty(
+ TABLE, "modified");
/** Unixtime Task was completed. 0 means active */
- public static final IntegerProperty COMPLETION_DATE = new IntegerProperty(
- TABLE, AstridTask.COMPLETION_DATE);
+ public static final LongProperty COMPLETION_DATE = new LongProperty(
+ TABLE, "completed");
- /** Unixtime Task was deleted. 0 means active */
- public static final IntegerProperty DELETION_DATE = new IntegerProperty(
- TABLE, AstridTask.DELETION_DATE);
+ /** Unixtime Task was deleted. 0 means not deleted */
+ public static final LongProperty DELETION_DATE = new LongProperty(
+ TABLE, "deleted");
// --- for migration purposes from astrid 2 (eventually we will want to
// move these into the metadata table and treat them as plug-ins
@@ -90,19 +89,19 @@ public class Task extends AbstractModel {
public static final IntegerProperty TIMER_START = new IntegerProperty(
TABLE, "timerStart");
- public static final IntegerProperty PREFERRED_DUE_DATE = new IntegerProperty(
- TABLE, "preferredDueDate");
-
public static final IntegerProperty POSTPONE_COUNT = new IntegerProperty(
TABLE, "postponeCount");
- public static final IntegerProperty NOTIFICATIONS = new IntegerProperty(
- TABLE, "notifications");
-
- public static final IntegerProperty NOTIFICATION_FLAGS = new IntegerProperty(
+ /** Flags for when to send reminders */
+ public static final IntegerProperty REMINDER_FLAGS = new IntegerProperty(
TABLE, "notificationFlags");
- public static final IntegerProperty LAST_NOTIFIED = new IntegerProperty(
+ /** Reminder period, in milliseconds. 0 means disabled */
+ public static final LongProperty REMINDER_PERIOD = new LongProperty(
+ TABLE, "notifications");
+
+ /** Unixtime the last reminder was triggered */
+ public static final LongProperty REMINDER_LAST = new LongProperty(
TABLE, "lastNotified");
public static final IntegerProperty REPEAT = new IntegerProperty(
@@ -117,24 +116,40 @@ public class Task extends AbstractModel {
/** List of all properties for this model */
public static final Property>[] PROPERTIES = generateProperties(Task.class);
- // --- urgency flags
+ // --- flags
+
+ // --- notification flags
+
+ /** whether to send a reminder at deadline */
+ public static final int NOTIFY_AT_DEADLINE = 1 << 1;
- public static final int URGENCY_NONE = AstridTask.URGENCY_NONE;
- public static final int URGENCY_TODAY = AstridTask.URGENCY_TODAY;
- public static final int URGENCY_THIS_WEEK = AstridTask.URGENCY_THIS_WEEK;
- public static final int URGENCY_THIS_MONTH = AstridTask.URGENCY_THIS_MONTH;
- public static final int URGENCY_WITHIN_THREE_MONTHS = AstridTask.URGENCY_WITHIN_THREE_MONTHS;
- public static final int URGENCY_WITHIN_SIX_MONTHS = AstridTask.URGENCY_WITHIN_SIX_MONTHS;
- public static final int URGENCY_WITHIN_A_YEAR = AstridTask.URGENCY_WITHIN_A_YEAR;
- public static final int URGENCY_SPECIFIC_DAY = AstridTask.URGENCY_SPECIFIC_DAY;
- public static final int URGENCY_SPECIFIC_DAY_TIME = AstridTask.URGENCY_SPECIFIC_DAY_TIME;
+ /** whether to send reminders while task is overdue */
+ public static final int NOTIFY_AFTER_DEADLINE = 1 << 2;
- // --- importance flags
+ /** reminder mode non-stop */
+ public static final int NOTIFY_NONSTOP = 1 << 3;
- public static final int IMPORTANCE_DO_OR_DIE = AstridTask.IMPORTANCE_DO_OR_DIE;
- public static final int IMPORTANCE_MUST_DO = AstridTask.IMPORTANCE_MUST_DO;
- public static final int IMPORTANCE_SHOULD_DO = AstridTask.IMPORTANCE_SHOULD_DO;
- public static final int IMPORTANCE_NONE = AstridTask.IMPORTANCE_NONE;
+ // --- importance settings
+
+ public static final int IMPORTANCE_DO_OR_DIE = 0;
+ public static final int IMPORTANCE_MUST_DO = 1;
+ public static final int IMPORTANCE_SHOULD_DO = 2;
+ public static final int IMPORTANCE_NONE = 3;
+
+ /**
+ * @return colors that correspond to importance values
+ */
+ public static int[] getImportanceColors(Resources r) {
+ return new int[] {
+ r.getColor(R.color.importance_1),
+ r.getColor(R.color.importance_2),
+ r.getColor(R.color.importance_3),
+ r.getColor(R.color.importance_4),
+ };
+ }
+
+ public static final int IMPORTANCE_MOST = IMPORTANCE_DO_OR_DIE;
+ public static final int IMPORTANCE_LEAST = IMPORTANCE_NONE;
// --- defaults
@@ -144,11 +159,20 @@ public class Task extends AbstractModel {
static {
defaultValues.put(TITLE.name, "");
defaultValues.put(DUE_DATE.name, 0);
- defaultValues.put(HIDDEN_UNTIL.name, 0);
+ defaultValues.put(HIDE_UNTIL.name, 0);
defaultValues.put(COMPLETION_DATE.name, 0);
defaultValues.put(DELETION_DATE.name, 0);
- defaultValues.put(URGENCY.name, URGENCY_NONE);
defaultValues.put(IMPORTANCE.name, IMPORTANCE_NONE);
+
+ defaultValues.put(CALENDAR_URI.name, "");
+ defaultValues.put(REPEAT.name, 0);
+ defaultValues.put(REMINDER_PERIOD.name, 0);
+ defaultValues.put(REMINDER_FLAGS.name, 0);
+ defaultValues.put(ESTIMATED_SECONDS.name, 0);
+ defaultValues.put(ELAPSED_SECONDS.name, 0);
+ defaultValues.put(POSTPONE_COUNT.name, 0);
+ defaultValues.put(NOTES.name, "");
+ defaultValues.put(TIMER_START.name, 0);
}
private static boolean defaultValuesLoaded = false;
@@ -161,10 +185,8 @@ public class Task extends AbstractModel {
* Call to load task default values from preferences.
*/
public static void refreshDefaultValues() {
- /*defaultValues.put(URGENCY.name,
- Preferences.getIntegerFromString(R.string.EPr_default_urgency_key));
defaultValues.put(IMPORTANCE.name,
- Preferences.getIntegerFromString(R.string.EPr_default_importance_key));*/
+ Preferences.getIntegerFromString(R.string.p_default_importance_key));
defaultValuesLoaded = true;
}
@@ -225,7 +247,7 @@ public class Task extends AbstractModel {
/** Checks whether task is hidden. Requires HIDDEN_UNTIL */
public boolean isHidden() {
- return getValue(HIDDEN_UNTIL) > DateUtilities.now();
+ return getValue(HIDE_UNTIL) > DateUtilities.now();
}
/** Checks whether task is done. Requires DUE_DATE */
@@ -233,23 +255,60 @@ public class Task extends AbstractModel {
return getValue(DUE_DATE) > 0;
}
- // --- data access methods for migration properties
+ /**
+ * @return true if hours, minutes, and seconds indicate end of day
+ */
+ private static boolean isEndOfDay(Date date) {
+ int hours = date.getHours();
+ int minutes = date.getMinutes();
+ int seconds = date.getSeconds();
+ return hours == 23 && minutes == 59 && seconds == 59;
+ }
+
+ /**
+ * Sets due date for this task. If this due date has no time associated,
+ * we move it to the last millisecond of the day.
+ *
+ * @param date
+ * @param hasDueTime
+ */
+ public void setDueDateAndTime(Date dueDate, boolean hasDueTime) {
+ if(dueDate == null || dueDate.getTime() == 0) {
+ setValue(Task.DUE_DATE, 0L);
+ return;
+ }
- /** Number of bits to shift repeat value by */
- public static final int REPEAT_VALUE_OFFSET = 3;
+ if(!hasDueTime) {
+ dueDate.setHours(23);
+ dueDate.setMinutes(59);
+ dueDate.setSeconds(59);
+ } else if(isEndOfDay(dueDate)) {
+ dueDate.setSeconds(58);
+ }
+ setValue(Task.DUE_DATE, dueDate.getTime());
+ }
/**
- * @return RepeatInfo corresponding to
+ * Checks whether this due date has a due time or only a date
*/
- public RepeatInfo getRepeatInfo() {
- int repeat = getValue(REPEAT);
- if(repeat == 0)
- return null;
- int value = repeat >> REPEAT_VALUE_OFFSET;
- RepeatInterval interval = RepeatInterval.values()
- [repeat - (value << REPEAT_VALUE_OFFSET)];
+ public boolean hasDueTime() {
+ return !isEndOfDay(new Date(getValue(DUE_DATE)));
+ }
- return new RepeatInfo(interval, value);
+ /**
+ * Returns the set state of the given flag on the given property
+ * @param property
+ * @param flag
+ * @return
+ */
+ public boolean getFlag(IntegerProperty property, int flag) {
+ return (getValue(property) & flag) > 0;
}
-}
\ No newline at end of file
+ /**
+ * @return repeat data structure. Requires REPEAT
+ */
+ public RepeatInfo getRepeatInfo() {
+ return RepeatInfo.fromSingleField(getValue(Task.REPEAT));
+ }
+}
diff --git a/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java b/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java
index 53ea765b2..8b667cf2f 100644
--- a/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java
+++ b/astrid/src/com/todoroo/astrid/service/Astrid2To3UpgradeHelper.java
@@ -1,18 +1,25 @@
package com.todoroo.astrid.service;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
+import android.app.Activity;
+import android.app.ProgressDialog;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;
+import com.timsu.astrid.R;
import com.timsu.astrid.data.AbstractController;
import com.timsu.astrid.data.alerts.Alert;
import com.timsu.astrid.data.task.AbstractTaskModel;
+import com.timsu.astrid.utilities.TasksXmlExporter;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.GenericDao;
import com.todoroo.andlib.data.Property;
@@ -20,14 +27,17 @@ import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.alarms.Alarm;
-import com.todoroo.astrid.alarms.AlarmsDatabase;
+import com.todoroo.astrid.alarms.AlarmDatabase;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.tags.TagService;
+import com.todoroo.astrid.utility.Preferences;
public class Astrid2To3UpgradeHelper {
@@ -55,6 +65,9 @@ public class Astrid2To3UpgradeHelper {
@Autowired
private String alertsTable;
+ @Autowired
+ private DialogUtilities dialogUtilities;
+
// --- implementation
public Astrid2To3UpgradeHelper() {
@@ -90,6 +103,20 @@ public class Astrid2To3UpgradeHelper {
public void upgrade2To3() {
Context context = ContextManager.getContext();
+ // pop up a progress dialog
+ ProgressDialog dialog = null;
+ if(context instanceof Activity)
+ dialog = dialogUtilities.progressDialog(context, context.getString(R.string.DLG_wait));
+
+ // initiate a backup
+ try {
+ TasksXmlExporter exporter = new TasksXmlExporter(true);
+ exporter.setContext(ContextManager.getContext());
+ exporter.exportTasks(TasksXmlExporter.getExportDirectory());
+ } catch (Exception e) {
+ // unable to create a backup before upgrading :(
+ }
+
database.openForWriting();
// --- upgrade tasks table
@@ -104,12 +131,11 @@ public class Astrid2To3UpgradeHelper {
propertyMap.put(AbstractTaskModel.ELAPSED_SECONDS, Task.ELAPSED_SECONDS);
propertyMap.put(AbstractTaskModel.TIMER_START, Task.TIMER_START);
propertyMap.put(AbstractTaskModel.DEFINITE_DUE_DATE, Task.DUE_DATE);
- propertyMap.put(AbstractTaskModel.PREFERRED_DUE_DATE, Task.PREFERRED_DUE_DATE);
- propertyMap.put(AbstractTaskModel.HIDDEN_UNTIL, Task.HIDDEN_UNTIL);
+ propertyMap.put(AbstractTaskModel.HIDDEN_UNTIL, Task.HIDE_UNTIL);
propertyMap.put(AbstractTaskModel.POSTPONE_COUNT, Task.POSTPONE_COUNT);
- propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.NOTIFICATIONS);
- propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.NOTIFICATION_FLAGS);
- propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.LAST_NOTIFIED);
+ propertyMap.put(AbstractTaskModel.NOTIFICATIONS, Task.REMINDER_PERIOD);
+ propertyMap.put(AbstractTaskModel.NOTIFICATION_FLAGS, Task.REMINDER_FLAGS);
+ propertyMap.put(AbstractTaskModel.LAST_NOTIFIED, Task.REMINDER_LAST);
propertyMap.put(AbstractTaskModel.REPEAT, Task.REPEAT);
propertyMap.put(AbstractTaskModel.CREATION_DATE, Task.CREATION_DATE);
propertyMap.put(AbstractTaskModel.COMPLETION_DATE, Task.COMPLETION_DATE);
@@ -122,7 +148,7 @@ public class Astrid2To3UpgradeHelper {
migrateTagsToMetadata();
// --- upgrade alerts
- AlarmsDatabase alarmsDatabase = new AlarmsDatabase();
+ AlarmDatabase alarmsDatabase = new AlarmDatabase();
alarmsDatabase.openForWriting();
propertyMap.clear();
propertyMap.put(AbstractController.KEY_ROWID, Alarm.ID);
@@ -137,7 +163,20 @@ public class Astrid2To3UpgradeHelper {
// --- clean up database
metadataService.cleanup();
+ // --- upgrade properties
+ SharedPreferences prefs = Preferences.getPrefs(context);
+ Editor editor = prefs.edit();
+ if(prefs.contains(context.getString(R.string.p_rmd_default_random_hours))) {
+ // convert days => hours
+ editor.putString(context.getString(R.string.p_rmd_default_random_hours),
+ Integer.toString(Preferences.getIntegerFromString(R.string.p_rmd_default_random_hours) * 24));
+ }
+
+
database.close();
+
+ if(dialog != null)
+ dialog.dismiss();
}
// --- database upgrade helpers
@@ -146,6 +185,7 @@ public class Astrid2To3UpgradeHelper {
public int columnIndex;
public Cursor cursor;
public AbstractModel model;
+ public StringBuilder upgradeNotes;
}
/**
@@ -165,22 +205,7 @@ public class Astrid2To3UpgradeHelper {
@Override
public Void visitInteger(Property property, UpgradeVisitorContainer data) {
- int value;
-
- // convert long date -> integer
- if(property == Task.COMPLETION_DATE ||
- property == Task.CREATION_DATE ||
- property == Task.DELETION_DATE ||
- property == Task.DUE_DATE ||
- property == Task.HIDDEN_UNTIL ||
- property == Task.LAST_NOTIFIED ||
- property == Task.MODIFICATION_DATE ||
- property == Task.PREFERRED_DUE_DATE ||
- property == Alarm.TIME)
- value = (int) (data.cursor.getLong(data.columnIndex) / 1000L);
- else
- value = data.cursor.getInt(data.columnIndex);
-
+ int value = data.cursor.getInt(data.columnIndex);
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
@@ -189,6 +214,26 @@ public class Astrid2To3UpgradeHelper {
@Override
public Void visitLong(Property property, UpgradeVisitorContainer data) {
long value = data.cursor.getLong(data.columnIndex);
+
+ // special handling for due date
+ if(property == Task.DUE_DATE) {
+ long preferredDueDate = data.cursor.getLong(data.cursor.getColumnIndex(AbstractTaskModel.PREFERRED_DUE_DATE));
+ if(value == 0)
+ value = preferredDueDate;
+ else if(preferredDueDate != 0) {
+ // had both absolute and preferred due dates. write
+ // preferred due date into notes field
+ if(data.upgradeNotes == null)
+ data.upgradeNotes = new StringBuilder();
+ data.upgradeNotes.append("Goal Deadline: " +
+ DateUtilities.getFormattedDate(ContextManager.getContext(),
+ new Date(preferredDueDate)));
+ }
+ } else if(property == Task.REMINDER_PERIOD) {
+ // old period was stored in seconds
+ value *= 1000L;
+ }
+
data.model.setValue(property, value);
Log.d("upgrade", "wrote " + value + " to -> " + property + " of model id " + data.cursor.getLong(1));
return null;
@@ -235,7 +280,18 @@ public class Astrid2To3UpgradeHelper {
container.columnIndex = cursor.getColumnIndex(entry.getKey());
entry.getValue().accept(visitor, container);
}
- dao.createItem(container.model);
+
+ // special tweak for adding upgrade notes to tasks
+ if(container.upgradeNotes != null) {
+ if(container.model.getValue(Task.NOTES).length() == 0)
+ container.model.setValue(Task.NOTES, container.upgradeNotes.toString());
+ else {
+ container.model.setValue(Task.NOTES,
+ container.model.getValue(Task.NOTES) + "\n\n" +
+ container.upgradeNotes);
+ }
+ }
+ dao.createNew(container.model);
}
upgradeDb.close();
@@ -292,7 +348,7 @@ public class Astrid2To3UpgradeHelper {
metadata.setValue(Metadata.TASK, task);
metadata.setValue(Metadata.KEY, TagService.KEY);
metadata.setValue(Metadata.VALUE, tag);
- metadataDao.createItem(metadata);
+ metadataDao.createNew(metadata);
metadata.clearValue(Metadata.ID);
}
}
diff --git a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
index bc1694729..34c310d84 100644
--- a/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
+++ b/astrid/src/com/todoroo/astrid/service/AstridDependencyInjector.java
@@ -62,12 +62,19 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
injectables.put("exceptionService", ExceptionService.class);
injectables.put("errorDialogTitleResource", R.string.DLG_error);
+ // TODO
+ injectables.put("errorDialogBodyGeneric", R.string.DLG_error);
+ injectables.put("errorDialogBodyNullError", R.string.DLG_error);
+ injectables.put("errorDialogBodySocketTimeout", R.string.DLG_error);
+
// com.todoroo.android.utility
injectables.put("dialogUtilities", DialogUtilities.class);
injectables.put("informationDialogTitleResource", R.string.DLG_information_title);
+ injectables.put("confirmDialogTitleResource", R.string.DLG_confirm_title);
injectables.put("dateUtilities", DateUtilities.class);
injectables.put("yearsResource", R.plurals.DUt_years);
injectables.put("monthsResource", R.plurals.DUt_months);
+ injectables.put("weeksResource", R.plurals.DUt_weeks);
injectables.put("daysResource", R.plurals.DUt_days);
injectables.put("hoursResource", R.plurals.DUt_hours);
injectables.put("minutesResource", R.plurals.DUt_minutes);
@@ -85,6 +92,7 @@ public class AstridDependencyInjector implements AbstractDependencyInjector {
// com.todoroo.astrid.service
injectables.put("taskService", TaskService.class);
injectables.put("metadataService", MetadataService.class);
+ injectables.put("upgradeService", UpgradeService.class);
// com.timsu.astrid.data
injectables.put("tasksTable", "tasks");
diff --git a/astrid/src/com/todoroo/astrid/service/FlurryReporter.java b/astrid/src/com/todoroo/astrid/service/FlurryReporter.java
index ace1af9e8..570f099d9 100644
--- a/astrid/src/com/todoroo/astrid/service/FlurryReporter.java
+++ b/astrid/src/com/todoroo/astrid/service/FlurryReporter.java
@@ -16,6 +16,9 @@ public class FlurryReporter implements ErrorReporter {
@SuppressWarnings("nls")
public void handleError(String name, Throwable error) {
+ if(error == null)
+ return;
+
String message = error.toString();
StringWriter writer = new StringWriter();
diff --git a/astrid/src/com/todoroo/astrid/service/MetadataService.java b/astrid/src/com/todoroo/astrid/service/MetadataService.java
index 3e1d6373f..1e71fb419 100644
--- a/astrid/src/com/todoroo/astrid/service/MetadataService.java
+++ b/astrid/src/com/todoroo/astrid/service/MetadataService.java
@@ -3,11 +3,11 @@ package com.todoroo.astrid.service;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.data.Property.CountProperty;
-import com.todoroo.andlib.data.sql.Criterion;
-import com.todoroo.andlib.data.sql.Order;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Order;
+import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.model.Metadata;
@@ -82,4 +82,12 @@ public class MetadataService {
public void deleteWhere(Criterion where) {
metadataDao.deleteWhere(where);
}
+
+ /**
+ * Save a single piece of metadata
+ * @param metadata
+ */
+ public void save(Metadata metadata) {
+ metadataDao.persist(metadata);
+ }
}
diff --git a/astrid/src-legacy/com/timsu/astrid/utilities/StartupReceiver.java b/astrid/src/com/todoroo/astrid/service/StartupService.java
similarity index 63%
rename from astrid/src-legacy/com/timsu/astrid/utilities/StartupReceiver.java
rename to astrid/src/com/todoroo/astrid/service/StartupService.java
index a49ad7510..018930484 100644
--- a/astrid/src-legacy/com/timsu/astrid/utilities/StartupReceiver.java
+++ b/astrid/src/com/todoroo/astrid/service/StartupService.java
@@ -1,4 +1,4 @@
-package com.timsu.astrid.utilities;
+package com.todoroo.astrid.service;
import java.util.List;
@@ -6,85 +6,96 @@ import android.Manifest;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.util.Log;
import com.timsu.astrid.R;
-import com.timsu.astrid.activities.SyncPreferences;
import com.timsu.astrid.appwidget.AstridAppWidgetProvider.UpdateService;
import com.timsu.astrid.sync.SynchronizationService;
+import com.timsu.astrid.utilities.BackupService;
+import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
-import com.todoroo.astrid.service.AstridDependencyInjector;
-import com.todoroo.astrid.service.UpgradeService;
-
-public class StartupReceiver extends BroadcastReceiver {
-
- private static boolean hasStartedUp = false;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.service.ExceptionService;
+import com.todoroo.andlib.service.ExceptionService.TodorooUncaughtExceptionHandler;
+import com.todoroo.astrid.dao.Database;
+import com.todoroo.astrid.reminders.ReminderService;
+import com.todoroo.astrid.utility.Constants;
+import com.todoroo.astrid.utility.Preferences;
+
+/**
+ * Service which handles jobs that need to be run when Astrid starts up.
+ *
+ * @author Tim Su
+ *
+ */
+public class StartupService {
static {
AstridDependencyInjector.initialize();
}
- @Override
- /** Called when the system is started up */
- public void onReceive(Context context, Intent intent) {
- ContextManager.setContext(context);
- Notifications.scheduleAllAlarms(context);
+ public StartupService() {
+ DependencyInjectionService.getInstance().inject(this);
}
+ // --- application startup
+
+ @Autowired
+ ExceptionService exceptionService;
+
+ @Autowired
+ UpgradeService upgradeService;
+
+ @Autowired
+ TaskService taskService;
+
+ @Autowired
+ Database database;
+
+ /**
+ * bit to prevent multiple initializations
+ */
+ private static boolean hasStartedUp = false;
+
/** Called when this application is started up */
- public static void onStartupApplication(final Context context) {
+ public synchronized void onStartupApplication(final Context context) {
if(hasStartedUp)
return;
+ // set uncaught exception handler
+ Thread.setDefaultUncaughtExceptionHandler(new TodorooUncaughtExceptionHandler());
+
+ // sets up context manager
ContextManager.setContext(context);
- int latestSetVersion = Preferences.getCurrentVersion(context);
+ // read current version
+ int latestSetVersion = Preferences.getCurrentVersion();
int version = 0;
try {
PackageManager pm = context.getPackageManager();
- PackageInfo pi = pm.getPackageInfo("com.timsu.astrid", 0);
+ PackageInfo pi = pm.getPackageInfo(Constants.PACKAGE, 0);
version = pi.versionCode;
} catch (Exception e) {
- Log.e("StartupAstrid", "Error getting version!", e);
+ exceptionService.reportError("astrid-startup-package-read", e); //$NON-NLS-1$
}
- // if we just got upgraded, set the alarms
+ // invoke upgrade service
boolean justUpgraded = latestSetVersion != version;
- final int finalVersion = version;
if(justUpgraded) {
- // perform version-specific processing
- if(latestSetVersion <= 99) {
- if(Preferences.getSyncOldAutoSyncFrequency(context) != null) {
- float value = Preferences.getSyncOldAutoSyncFrequency(context);
- Preferences.setSyncAutoSyncFrequency(context,
- Math.round(value * 3600));
- DialogUtilities.okDialog(context, context.getResources().getString(
- R.string.sync_upgrade_v99), new OnClickListener() {
- public void onClick(DialogInterface dialog,
- int which) {
- context.startActivity(new Intent(context, SyncPreferences.class));
- }
- });
- }
- }
-
- Preferences.setCurrentVersion(context, finalVersion);
- new UpgradeService().performUpgrade(latestSetVersion);
+ upgradeService.performUpgrade(latestSetVersion);
+ Preferences.setCurrentVersion(version);
}
-
// perform startup activities in a background thread
new Thread(new Runnable() {
public void run() {
// schedule alarms
- Notifications.scheduleAllAlarms(context);
+ new ReminderService().scheduleAllAlarms();
// start widget updating alarm
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
@@ -100,10 +111,13 @@ public class StartupReceiver extends BroadcastReceiver {
// start backup service
BackupService.scheduleService(context);
+
+ database.openForWriting();
+ taskService.cleanup();
}
}).start();
- Preferences.setPreferenceDefaults(context);
+ Preferences.setPreferenceDefaults();
// check for task killers
if(!Constants.OEM)
@@ -112,8 +126,10 @@ public class StartupReceiver extends BroadcastReceiver {
hasStartedUp = true;
}
+ private static final String P_TASK_KILLER_HELP = "taskkiller"; //$NON-NLS-1$
+
private static void showTaskKillerHelp(final Context context) {
- if(!Preferences.shouldShowTaskKillerHelp(context))
+ if(!Preferences.getBoolean(P_TASK_KILLER_HELP, false))
return;
// search for task killers. if they exist, show the help!
@@ -123,18 +139,16 @@ public class StartupReceiver extends BroadcastReceiver {
outer: for (PackageInfo app : apps) {
if(app == null || app.requestedPermissions == null)
continue;
- if(app.packageName.startsWith("com.android"))
+ if(app.packageName.startsWith("com.android")) //$NON-NLS-1$
continue;
for (String permission : app.requestedPermissions) {
if (Manifest.permission.RESTART_PACKAGES.equals(permission)) {
CharSequence appName = app.applicationInfo.loadLabel(pm);
- Log.e("astrid", "found task killer: " + app.packageName);
-
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(DialogInterface arg0,
int arg1) {
- Preferences.disableTaskKillerHelp(context);
+ Preferences.setBoolean(P_TASK_KILLER_HELP, true);
}
};
diff --git a/astrid/src/com/todoroo/astrid/service/TaskService.java b/astrid/src/com/todoroo/astrid/service/TaskService.java
index b245280e4..f78e1d5e0 100644
--- a/astrid/src/com/todoroo/astrid/service/TaskService.java
+++ b/astrid/src/com/todoroo/astrid/service/TaskService.java
@@ -2,10 +2,11 @@ package com.todoroo.astrid.service;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Task;
@@ -38,7 +39,7 @@ public class TaskService {
}
/**
- * Mark the given action item as completed and save it.
+ * Mark the given task as completed and save it.
*
* @param item
*/
@@ -46,7 +47,7 @@ public class TaskService {
if(completed)
item.setValue(Task.COMPLETION_DATE, DateUtilities.now());
else
- item.setValue(Task.COMPLETION_DATE, 0);
+ item.setValue(Task.COMPLETION_DATE, 0L);
taskDao.save(item, false);
}
@@ -64,12 +65,23 @@ public class TaskService {
}
/**
- * Delete the given action item
+ * Delete the given task. Instead of deleting from the database, we set
+ * the deleted flag.
*
* @param model
*/
- public void delete(long itemId) {
- taskDao.delete(itemId);
+ public void delete(Task item) {
+ if(!item.isSaved())
+ return;
+ else if(item.containsValue(Task.TITLE) && item.getValue(Task.TITLE).length() == 0) {
+ taskDao.delete(item.getId());
+ } else {
+ long id = item.getId();
+ item.clear();
+ item.setId(id);
+ item.setValue(Task.DELETION_DATE, DateUtilities.now());
+ taskDao.save(item, false);
+ }
}
/**
@@ -91,4 +103,12 @@ public class TaskService {
}
}
+ public TodorooCursor fetchFiltered(Property>[] properties,
+ Filter filter) {
+ if(filter == null || filter.sqlQuery == null)
+ return taskDao.query(Query.select(properties));
+ else
+ return taskDao.query(Query.select(properties).withQueryTemplate(filter.sqlQuery));
+ }
+
}
diff --git a/astrid/src/com/todoroo/astrid/service/UpgradeService.java b/astrid/src/com/todoroo/astrid/service/UpgradeService.java
index 4d6b42939..28ebaf433 100644
--- a/astrid/src/com/todoroo/astrid/service/UpgradeService.java
+++ b/astrid/src/com/todoroo/astrid/service/UpgradeService.java
@@ -22,6 +22,9 @@ public final class UpgradeService {
if(from < 1)
return;
+ if(from < 135)
+ new Astrid2To3UpgradeHelper().upgrade2To3();
+
// display changelog
showChangeLog(from);
}
@@ -60,6 +63,12 @@ public final class UpgradeService {
"Fixed crashes occuring with certain languages (Swedish, Turkish)",
"Fixed other crashes that users have reported",
});
+ if(from <= 134)
+ newVersionString(changeLog, "3.0.0 (?/??/10)", new String[] {
+ "Astrid is brand new under the hood! You won't see many " +
+ "changes yet but Astrid received a much-needed makeover " +
+ "that allows it to do a lot of new tricks. Stay tuned!",
+ });
if(changeLog.length() == 0)
return;
diff --git a/astrid/src/com/todoroo/astrid/utility/Constants.java b/astrid/src/com/todoroo/astrid/utility/Constants.java
index f0d6ee054..76dfdd434 100644
--- a/astrid/src/com/todoroo/astrid/utility/Constants.java
+++ b/astrid/src/com/todoroo/astrid/utility/Constants.java
@@ -7,4 +7,29 @@ public final class Constants {
*/
public static final String FLURRY_KEY = "T3JAY9TV2JFMJR4YTG16"; //$NON-NLS-1$
+ /**
+ * Application Package
+ */
+ public static final String PACKAGE = "com.timsu.astrid"; //$NON-NLS-1$
+
+ /**
+ * Whether this is an OEM installation
+ */
+ public static final boolean OEM = false;
+
+ /**
+ * Whether to display synchronization options
+ */
+ public static final boolean SYNCHRONIZE = !OEM;
+
+ /**
+ * Interval to update the widget (in order to detect hidden tasks
+ * becoming visible)
+ */
+ public static final long WIDGET_UPDATE_INTERVAL = 30 * 60 * 1000L;
+
+ /**
+ * Whether to turn on debugging logging and UI
+ */
+ public static final boolean DEBUG = true;
}
diff --git a/astrid/src/com/todoroo/astrid/utility/Preferences.java b/astrid/src/com/todoroo/astrid/utility/Preferences.java
index a6045ea22..1e5a4b936 100644
--- a/astrid/src/com/todoroo/astrid/utility/Preferences.java
+++ b/astrid/src/com/todoroo/astrid/utility/Preferences.java
@@ -8,12 +8,10 @@ import android.preference.PreferenceManager;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
-import com.todoroo.astrid.model.Task;
public class Preferences {
private static final String P_CURRENT_VERSION = "cv"; //$NON-NLS-1$
- private static final String P_READ_INTRODUCTION = "ri"; //$NON-NLS-1$
/** Set preference defaults, if unset. called at startup */
public static void setPreferenceDefaults() {
@@ -22,18 +20,42 @@ public class Preferences {
Editor editor = prefs.edit();
Resources r = context.getResources();
- if(getIntegerFromString(R.string.EPr_default_urgency_key) == null) {
- editor.putString(r.getString(R.string.EPr_default_urgency_key),
- Integer.toString(Task.URGENCY_THIS_WEEK));
- }
- if(getIntegerFromString(R.string.EPr_default_importance_key) == null) {
- editor.putString(r.getString(R.string.EPr_default_importance_key),
- Integer.toString(Task.IMPORTANCE_SHOULD_DO));
- }
+ setIfUnset(prefs, editor, r, R.string.p_default_urgency_key, 4);
+ setIfUnset(prefs, editor, r, R.string.p_default_importance_key, 2);
+ setIfUnset(prefs, editor, r, R.string.p_default_hideUntil_key, 0);
+ setIfUnset(prefs, editor, r, R.string.p_fontSize, 22);
editor.commit();
}
+ /**
+ * Helper to write to editor if key specified is null
+ * @param prefs
+ * @param editor
+ * @param r
+ * @param keyResource
+ * @param value
+ */
+ public static void setIfUnset(SharedPreferences prefs, Editor editor, Resources r, int keyResource, int value) {
+ String key = r.getString(keyResource);
+ if(!prefs.contains(key))
+ editor.putString(key, Integer.toString(value));
+ }
+
+ /**
+ * Helper to write to editor if key specified is null
+ * @param prefs
+ * @param editor
+ * @param r
+ * @param keyResource
+ * @param value
+ */
+ public static void setIfUnset(SharedPreferences prefs, Editor editor, Resources r, int keyResource, boolean value) {
+ String key = r.getString(keyResource);
+ if(!prefs.contains(key))
+ editor.putBoolean(key, value);
+ }
+
/* ======================================================================
* ========================================================= system prefs
* ====================================================================== */
@@ -52,20 +74,6 @@ public class Preferences {
editor.commit();
}
- /** ReadIntroduction: whether the user has read the introductory notes */
- public static boolean hasReadIntroduction() {
- Context context = ContextManager.getContext();
- return getPrefs(context).getBoolean(P_READ_INTRODUCTION, false);
- }
-
- /** ReadIntroduction: whether the user has read the introductory notes */
- public static void setReadIntroduction(boolean value) {
- Context context = ContextManager.getContext();
- Editor editor = getPrefs(context).edit();
- editor.putBoolean(P_READ_INTRODUCTION, value);
- editor.commit();
- }
-
/* ======================================================================
* ======================================================= helper methods
* ====================================================================== */
@@ -124,7 +132,6 @@ public class Preferences {
/** Gets an integer value from a string preference. Returns null
* if the value is not set or not an integer.
*
- * @param context
* @param keyResource resource from string.xml
* @return integer value, or null on error
*/
@@ -145,7 +152,6 @@ public class Preferences {
/** Gets an float value from a string preference. Returns null
* if the value is not set or not an flat.
*
- * @param context
* @param keyResource resource from string.xml
* @return
*/
@@ -163,7 +169,6 @@ public class Preferences {
/** Gets a boolean preference (e.g. a CheckBoxPreference setting)
*
- * @param context
* @param key
* @param defValue
* @return default if value is unset otherwise the value
@@ -172,4 +177,36 @@ public class Preferences {
Context context = ContextManager.getContext();
return getPrefs(context).getBoolean(key, defValue);
}
+
+ /** Gets a boolean preference (e.g. a CheckBoxPreference setting)
+ *
+ * @param keyResource
+ * @param defValue
+ * @return default if value is unset otherwise the value
+ */
+ public static boolean getBoolean(int keyResources, boolean defValue) {
+ return getBoolean(ContextManager.getString(keyResources), defValue);
+ }
+
+ /**
+ * Sets boolean preference
+ * @param key
+ * @param value
+ */
+ public static void setBoolean(String key, boolean value) {
+ Context context = ContextManager.getContext();
+ Editor editor = getPrefs(context).edit();
+ editor.putBoolean(key, value);
+ editor.commit();
+ }
+
+ /**
+ * Sets string preference from integer value
+ */
+ public static void setStringFromInteger(int keyResource, int newValue) {
+ Context context = ContextManager.getContext();
+ Editor editor = getPrefs(context).edit();
+ editor.putString(context.getString(keyResource), Integer.toString(newValue));
+ editor.commit();
+ }
}
diff --git a/astrid/translation-notes b/astrid/translation-notes
new file mode 100644
index 000000000..4b81f330d
--- /dev/null
+++ b/astrid/translation-notes
@@ -0,0 +1,76 @@
+reminders
+
+ Absolute Deadline!
+ Goal Deadline!
+ Working on:
+
+ You have $NUM tagged $TAG!
+
+task edit
+
+
+ Astrid: Editing Task
+ Astrid: Editing
+ Astrid: New Task
+
+
+ Basic
+ Dates
+ Alerts
+
+
+ Summary
+ Task Name
+ How Important is it?
+ Tags:
+ Tag Name
+
+ How Long Will it Take?
+ Time Already Spent on Task
+ Absolute Deadline
+ Goal Deadline
+ Add Task To Calendar
+ Open Calendar Event
+ Hide Until This Date
+ Repeat Every
+ No Repeat Set
+ Hide Until This Task is Done
+ Notes
+ Enter Task Notes
+
+ Periodic Reminders
+ Every
+ Notify me...
+ As Deadlines Approach
+ At Deadlines
+ After Absolute Deadline Passes
+ Alarm Clock Mode
+ Fixed Reminders
+ Add New Reminder
+
+
+ Time (hours : minutes)
+ Remind Me Every
+ Repeat Every (0 to disable)
+ Help: Astrid Repeats
+
+To use repeats, set at least one of the deadlines above. When you complete this task, the deadline will be automatically advanced.
+\n\n
+If you don\'t want to see the new task right after you complete the old one, you should use the \"Hide Until\" field, which will also be advanced automatically.
+\n
+
+ Don\'t Show Help Anymore
+
+
+ Save
+ Discard
+ Edit
+ Delete
+ Click to Set
+ Start Timer
+ Stop Timer
+
+ Save
+ Task Saved: due in %s
+ Task Saved: due %s ago
+ Task Saved
\ No newline at end of file
diff --git a/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java b/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java
index e1edf70ec..498aa30d0 100644
--- a/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java
+++ b/tests/src/com/todoroo/andlib/service/TestDependencyInjector.java
@@ -12,9 +12,9 @@ public class TestDependencyInjector implements AbstractDependencyInjector {
* Dependencies this class knows how to handle
*/
private final HashMap injectables = new HashMap();
-
+
private String name;
-
+
public TestDependencyInjector(String name) {
this.name = name;
}
@@ -22,26 +22,43 @@ public class TestDependencyInjector implements AbstractDependencyInjector {
public void addInjectable(String field, Object injection) {
injectables.put(field, injection);
}
-
+
public Object getInjection(Object object, Field field) {
if(injectables.containsKey(field.getName())) {
return injectables.get(field.getName());
}
-
+
return null;
}
-
+
// --- static stuff
-
+
/**
* Install TestDependencyInjector above other injectors
*/
public synchronized static TestDependencyInjector initialize(String name) {
- ArrayList list =
+ deinitialize(name);
+
+ ArrayList list =
new ArrayList(Arrays.asList(DependencyInjectionService.getInstance().getInjectors()));
+
+ TestDependencyInjector instance = new TestDependencyInjector(name);
+ list.add(0, instance);
+ DependencyInjectionService.getInstance().setInjectors(list.toArray(new AbstractDependencyInjector[list.size()]));
+ return instance;
+ }
+
+ /**
+ * Remove an installed TestDependencyInjector
+ * @param string
+ */
+ public static void deinitialize(String name) {
+ ArrayList list =
+ new ArrayList(Arrays.asList(DependencyInjectionService.getInstance().getInjectors()));
+
for(Iterator i = list.iterator(); i.hasNext(); ) {
AbstractDependencyInjector injector = i.next();
-
+
// if another one of these injectors already exists in the
// stack, remove it
if(injector instanceof TestDependencyInjector) {
@@ -49,11 +66,6 @@ public class TestDependencyInjector implements AbstractDependencyInjector {
i.remove();
}
}
-
- TestDependencyInjector instance = new TestDependencyInjector(name);
- list.add(0, instance);
- DependencyInjectionService.getInstance().setInjectors(list.toArray(new AbstractDependencyInjector[list.size()]));
- return instance;
}
}
diff --git a/tests/src/com/todoroo/andlib/test/TranslationTests.java b/tests/src/com/todoroo/andlib/test/TranslationTests.java
index 24b4f8893..2d44f9ec5 100644
--- a/tests/src/com/todoroo/andlib/test/TranslationTests.java
+++ b/tests/src/com/todoroo/andlib/test/TranslationTests.java
@@ -203,14 +203,24 @@ abstract public class TranslationTests extends TodorooTestCase {
final Resources r = getContext().getResources();
final int[] arrays = getResourceIds(getArrayResources());
final int[] sizes = new int[arrays.length];
+ final StringBuilder failures = new StringBuilder();
+
for(int i = 0; i < arrays.length; i++) {
- sizes[i] = r.getStringArray(arrays[i]).length;
+ try {
+ sizes[i] = r.getStringArray(arrays[i]).length;
+ } catch (Resources.NotFoundException e) {
+ String name = r.getResourceName(arrays[i]);
+ failures.append(String.format("error opening %s: %s\n",
+ name, e.getMessage()));
+ sizes[i] = -1;
+ }
}
- final StringBuilder failures = new StringBuilder();
forEachLocale(new Runnable() {
public void run() {
for(int i = 0; i < arrays.length; i++) {
+ if(sizes[i] == -1)
+ continue;
int size = r.getStringArray(arrays[i]).length;
if(size != sizes[i]) {
String name = r.getResourceName(arrays[i]);
diff --git a/tests/src/com/todoroo/astrid/dao/MetadataDaoTests.java b/tests/src/com/todoroo/astrid/dao/MetadataDaoTests.java
index 44c35fb69..00a9c37a1 100644
--- a/tests/src/com/todoroo/astrid/dao/MetadataDaoTests.java
+++ b/tests/src/com/todoroo/astrid/dao/MetadataDaoTests.java
@@ -2,8 +2,8 @@ package com.todoroo.astrid.dao;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
diff --git a/tests/src/com/todoroo/astrid/dao/TaskDaoTests.java b/tests/src/com/todoroo/astrid/dao/TaskDaoTests.java
index 4ed60d1f0..5cedb25b7 100644
--- a/tests/src/com/todoroo/astrid/dao/TaskDaoTests.java
+++ b/tests/src/com/todoroo/astrid/dao/TaskDaoTests.java
@@ -2,9 +2,9 @@ package com.todoroo.astrid.dao;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.TodorooCursor;
-import com.todoroo.andlib.data.sql.Order;
-import com.todoroo.andlib.data.sql.Query;
import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.sql.Order;
+import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Task;
@@ -85,7 +85,7 @@ public class TaskDaoTests extends DatabaseTestCase {
// create hidden task
task = new Task();
task.setValue(Task.TITLE, "hidden");
- task.setValue(Task.HIDDEN_UNTIL, DateUtilities.now() + 10000);
+ task.setValue(Task.HIDE_UNTIL, DateUtilities.now() + 10000);
assertTrue(taskDao.save(task, false));
// create task with deadlines
diff --git a/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java b/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java
index 290a00f55..f2fb6035e 100644
--- a/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java
+++ b/tests/src/com/todoroo/astrid/legacy/data/task/AbstractTaskModel.java
@@ -28,6 +28,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
+import com.timsu.astrid.data.task.AbstractTaskModel.RepeatInfo;
import com.todoroo.astrid.legacy.data.AbstractController;
import com.todoroo.astrid.legacy.data.AbstractModel;
import com.todoroo.astrid.legacy.data.enums.Importance;
@@ -354,6 +355,26 @@ public abstract class AbstractTaskModel extends AbstractModel {
public int getValue() {
return value;
}
+
+ public static int toSingleField(RepeatInfo repeatInfo) {
+ int repeat;
+ if(repeatInfo == null)
+ repeat = 0;
+ else
+ repeat = (repeatInfo.value << REPEAT_VALUE_OFFSET) +
+ repeatInfo.interval.ordinal();
+ return repeat;
+ }
+
+ public static RepeatInfo fromSingleField(int repeat) {
+ if(repeat == 0)
+ return null;
+ int value = repeat >> REPEAT_VALUE_OFFSET;
+ RepeatInterval interval = RepeatInterval.values()
+ [repeat - (value << REPEAT_VALUE_OFFSET)];
+
+ return new RepeatInfo(interval, value);
+ }
}
diff --git a/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java b/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java
index 6ad360800..8105e58bc 100644
--- a/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java
+++ b/tests/src/com/todoroo/astrid/legacy/data/task/TaskController.java
@@ -530,7 +530,6 @@ public class TaskController extends AbstractController {
*/
public TaskController(Context activity) {
super(activity);
- Log.e("HEY", "task table is " + tasksTable);
}
/**
@@ -547,7 +546,6 @@ public class TaskController extends AbstractController {
SQLiteOpenHelper databaseHelper = new TaskModelDatabaseHelper(
context, tasksTable, tasksTable);
database = databaseHelper.getWritableDatabase();
- Log.e("HEY", "task table is " + tasksTable);
}
/** Closes database resource */
diff --git a/tests/src/com/todoroo/astrid/model/TaskTests.java b/tests/src/com/todoroo/astrid/model/TaskTests.java
index 0686ee4ad..1b99ebcd2 100644
--- a/tests/src/com/todoroo/astrid/model/TaskTests.java
+++ b/tests/src/com/todoroo/astrid/model/TaskTests.java
@@ -22,20 +22,14 @@ public class TaskTests extends DatabaseTestCase {
assertTrue(Task.IMPORTANCE_MUST_DO < Task.IMPORTANCE_SHOULD_DO);
assertTrue(Task.IMPORTANCE_SHOULD_DO < Task.IMPORTANCE_NONE);
- ArrayList