Moved a bunch of API files into the API project. Yay for code reuse.

Please import the github project todoroo/astridApi. :)
pull/14/head
Tim Su 16 years ago
parent be125eca89
commit c6da0907ff

@ -2,10 +2,10 @@
<classpath> <classpath>
<classpathentry excluding="javax/xml/validation/|javax/xml/transform/dom/|javax/xml/parsers/|org/jaxp/transform/dom/|org/jaxp/transform/sax/|org/jaxp/transform/stax/|org/jaxp/transform/stream/|org/jaxp/stream/events/|org/jaxp/stream/util/|org/jaxp/parsers/|org/jaxp/stream/|org/jaxp/validation/" kind="src" path="src"/> <classpathentry excluding="javax/xml/validation/|javax/xml/transform/dom/|javax/xml/parsers/|org/jaxp/transform/dom/|org/jaxp/transform/sax/|org/jaxp/transform/stax/|org/jaxp/transform/stream/|org/jaxp/stream/events/|org/jaxp/stream/util/|org/jaxp/parsers/|org/jaxp/stream/|org/jaxp/validation/" kind="src" path="src"/>
<classpathentry kind="src" path="src-legacy"/> <classpathentry kind="src" path="src-legacy"/>
<classpathentry kind="src" path="api-src"/>
<classpathentry kind="src" path="common-src"/> <classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java" kind="src" path="plugin-src"/> <classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/> <classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="astridApi"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="lib" path="libs/annotations.jar"/> <classpathentry exported="true" kind="lib" path="libs/annotations.jar"/>
<classpathentry exported="true" kind="lib" path="libs/commons-codec-1.3.jar"/> <classpathentry exported="true" kind="lib" path="libs/commons-codec-1.3.jar"/>

@ -36,4 +36,11 @@
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature> <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
</natures> </natures>
<linkedResources>
<link>
<name>astridApi</name>
<type>2</type>
<locationURI>_android_astridApi/src</locationURI>
</link>
</linkedResources>
</projectDescription> </projectDescription>

@ -1,93 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an add-onn for Astrid. Users can enable or disable add-ons,
* which affect all other extension points that share the same identifier.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class Addon implements Parcelable {
/**
* Add-on Identifier
*/
public String addon = 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 addon
* @param title
* @param author
* @param description
*/
public Addon(String addon, String title, String author, String description) {
this.addon = addon;
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(addon);
dest.writeString(title);
dest.writeString(author);
dest.writeString(description);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<Addon> CREATOR = new Parcelable.Creator<Addon>() {
/**
* {@inheritDoc}
*/
public Addon createFromParcel(Parcel source) {
return new Addon(source.readString(), source.readString(),
source.readString(), source.readString());
}
/**
* {@inheritDoc}
*/
public Addon[] newArray(int size) {
return new Addon[size];
};
};
}

@ -1,242 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.widget.RemoteViews;
/**
* Constants for interfacing with Astrid.
*
* @author Tim Su <tim@todoroo.com>
*/
@SuppressWarnings("nls")
public class AstridApiConstants {
// --- General Constants
/**
* Astrid broadcast base package name
*/
public static final String PACKAGE = "com.todoroo.astrid";
/**
* Permission for reading tasks and receiving to GET_FILTERS intent
*/
public static final String PERMISSION_READ = PACKAGE + ".READ";
/**
* Permission for writing and creating tasks
*/
public static final String PERMISSION_WRITE = PACKAGE + ".WRITE";
// --- Content Provider
/**
* URI to append to base content URI for making group-by queries
*/
public static final String GROUP_BY_URI = "/groupby/";
// --- Broadcast Extras
/**
* Extras name for task id
*/
public static final String EXTRAS_TASK_ID = "task";
/**
* Extras name for a response item broadcast to astrid
*/
public static final String EXTRAS_RESPONSE = "response";
/**
* Extras name for plug-in identifier
*/
public static final String EXTRAS_ADDON = "addon";
/**
* Extras name for whether task detail request is extended
*/
public static final String EXTRAS_EXTENDED = "extended";
/**
* Extras name for old task due date
*/
public static final String EXTRAS_OLD_DUE_DATE= "oldDueDate";
/**
* Extras name for new task due date
*/
public static final String EXTRAS_NEW_DUE_DATE = "newDueDate";
/**
* Extras name for sync provider name
*/
public static final String EXTRAS_NAME = "name";
// --- Add-ons API
/**
* Action name for broadcast intent requesting add-ons
*/
public static final String BROADCAST_REQUEST_ADDONS = PACKAGE + ".REQUEST_ADDONS";
/**
* Action name for broadcast intent sending add-ons back to Astrid
* @extra EXTRAS_RESPONSE an {@link Addon} object
*/
public static final String BROADCAST_SEND_ADDONS = PACKAGE + ".SEND_ADDONS";
// --- Filters API
/**
* Action name for broadcast intent requesting filters
*/
public static final String BROADCAST_REQUEST_FILTERS = PACKAGE + ".REQUEST_FILTERS";
/**
* Action name for broadcast intent sending filters back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link FilterListItem}s
*/
public static final String BROADCAST_SEND_FILTERS = PACKAGE + ".SEND_FILTERS";
// --- Edit Controls API
/**
* Action name for broadcast intent requesting task edit controls
* @extra EXTRAS_TASK_ID id of the task user is editing
*/
public static final String BROADCAST_REQUEST_EDIT_CONTROLS = PACKAGE + ".REQUEST_EDIT_CONTROLS";
/**
* Action name for broadcast intent sending task edit controls back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE a {@link RemoteViews} with your edit controls
*/
public static final String BROADCAST_SEND_EDIT_CONTROLS = PACKAGE + ".SEND_EDIT_CONTROLS";
// --- Task Details API
/**
* Action name for broadcast intent requesting details for a task.
* Extended details are displayed when a user presses on a task.
*
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_EXTENDED whether request is for standard or extended details
*/
public static final String BROADCAST_REQUEST_DETAILS = PACKAGE + ".REQUEST_DETAILS";
/**
* Action name for broadcast intent sending details back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_EXTENDED whether request is for standard or extended details
* @extra EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_DETAILS = PACKAGE + ".SEND_DETAILS";
// --- Sync Action API
/**
* Action name for broadcast intent requesting a listing of active
* sync actions users can activate from the menu
*/
public static final String BROADCAST_REQUEST_SYNC_ACTIONS = PACKAGE + ".REQUEST_SYNC_ACTIONS";
/**
* Action name for broadcast intent sending sync provider information back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE a {@link SyncAction} to invoke synchronization
*/
public static final String BROADCAST_SEND_SYNC_ACTIONS = PACKAGE + ".SEND_SYNC_ACTIONS";
// --- Task Actions API
/**
* Action name for broadcast intent requesting actions for a task
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_ACTIONS = PACKAGE + ".REQUEST_ACTIONS";
/**
* Action name for broadcast intent sending actions back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_RESPONSE a String
*/
public static final String BROADCAST_SEND_ACTIONS = PACKAGE + ".SEND_ACTIONS";
// --- Task Decorations API
/**
* Action name for broadcast intent requesting task list decorations for a task
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_REQUEST_DECORATIONS = PACKAGE + ".REQUEST_DECORATIONS";
/**
* Action name for broadcast intent sending decorations back to Astrid
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_RESPONSE a {@link TaskDecoration}
*/
public static final String BROADCAST_SEND_DECORATIONS = PACKAGE + ".SEND_DECORATIONS";
// --- Actions API
/**
* Action name for intents to be displayed on task context menu
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String ACTION_TASK_CONTEXT_MENU = PACKAGE + ".CONTEXT_MENU";
/**
* Action name for intents to be displayed on Astrid's task list menu
* @extra EXTRAS_ADDON your add-on identifier
* @extra EXTRAS_RESPONSE an array of {@link Intent}s
*/
public static final String ACTION_TASK_LIST_MENU = PACKAGE + ".TASK_LIST_MENU";
/**
* Action name for intents to be displayed in Astrid's settings. By default,
* your application will be put into the category named by your application,
* but you can add a string meta-data with name "category" to override this.
*/
public static final String ACTION_SETTINGS = PACKAGE + ".SETTINGS";
// --- Events API
/**
* Action name for broadcast intent notifying add-ons that Astrid started up
*/
public static final String BROADCAST_EVENT_STARTUP = PACKAGE + ".STARTUP";
/**
* Action name for broadcast intent notifying Astrid task list to refresh
*/
public static final String BROADCAST_EVENT_REFRESH = PACKAGE + ".REFRESH";
/**
* Action name for broadcast intent notifying Astrid to clear detail cache
* because an event occurred that potentially affects all tasks (e.g.
* logging out of a sync provider). Use this call carefully, as loading
* details can degrade the performance of Astrid.
*/
public static final String BROADCAST_EVENT_FLUSH_DETAILS = PACKAGE + ".FLUSH_DETAILS";
/**
* Action name for broadcast intent notifying that task was completed
* @extra EXTRAS_TASK_ID id of the task
*/
public static final String BROADCAST_EVENT_TASK_COMPLETED = PACKAGE + ".TASK_COMPLETED";
/**
* Action name for broadcast intent notifying that task was created from repeating template
* @extra EXTRAS_TASK_ID id of the task
* @extra EXTRAS_OLD_DUE_DATE task old due date (could be 0)
* @extra EXTRAS_NEW_DUE_DATE task new due date (will not be 0)
*/
public static final String BROADCAST_EVENT_TASK_REPEATED = PACKAGE + ".TASK_REPEATED";
}

@ -1,159 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/**
* CustomFilterCriteria allow users to build a custom filter by chaining
* together criteria
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class CustomFilterCriterion implements Parcelable {
/**
* Criteria Identifier. This identifier allows saved filters to be reloaded.
* <p>
* e.g "duedate"
*/
@CheckForNull
public String identifier;
/**
* Criteria Title. If the title contains ?, this is replaced by the entry
* label string selected.
* <p>
* e.g "Due: ?"
*/
@CheckForNull
public String text;
/**
* Criterion SQL. This query should return task id's. If this contains
* ?, it will be replaced by the entry value
* <p>
* Examples:
* <ul>
* <li><code>SELECT _id FROM tasks WHERE dueDate <= ?</code>
* <li><code>SELECT task FROM metadata WHERE value = '?'</code>
* </ul>
*/
@CheckForNull
public String sql;
/**
* Values to apply to a task when quick-adding a task from a filter
* created from this criterion. ? will be replaced with the entry value.
* For example, when a user views tasks tagged 'ABC', the
* tasks they create should also be tagged 'ABC'. If set to null, no
* additional values will be stored for a task.
*/
@CheckForNull
public ContentValues valuesForNewTasks = null;
/**
* Array of entries for user to select from
*/
@CheckForNull
public String[] entryTitles;
/**
* Array of entry values corresponding to entries
*/
@CheckForNull
public String[] entryValues;
/**
* Icon for this criteria. Can be null for no bitmap
*/
@CheckForNull
public Bitmap icon;
/**
* Criteria name. This is displayed when users are selecting a criteria
*/
@CheckForNull
public String name;
/**
* Create a new CustomFilterCriteria object
*
* @param title
* @param sql
* @param valuesForNewTasks
* @param entryTitles
* @param entryValues
* @param icon
* @param name
*/
public CustomFilterCriterion(String identifier, String title, String sql,
ContentValues valuesForNewTasks, String[] entryTitles,
String[] entryValues, Bitmap icon, String name) {
this.identifier = identifier;
this.text = title;
this.sql = sql;
this.valuesForNewTasks = valuesForNewTasks;
this.entryTitles = entryTitles;
this.entryValues = entryValues;
this.icon = icon;
this.name = name;
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(identifier);
dest.writeString(text);
dest.writeString(sql);
dest.writeParcelable(valuesForNewTasks, 0);
dest.writeStringArray(entryTitles);
dest.writeStringArray(entryValues);
dest.writeParcelable(icon, 0);
dest.writeString(name);
}
/**
* Parcelable Creator Object
*/
public static final Parcelable.Creator<CustomFilterCriterion> CREATOR = new Parcelable.Creator<CustomFilterCriterion>() {
/**
* {@inheritDoc}
*/
public CustomFilterCriterion createFromParcel(Parcel source) {
CustomFilterCriterion item = new CustomFilterCriterion(
source.readString(), source.readString(), source.readString(),
(ContentValues)source.readParcelable(ContentValues.class.getClassLoader()),
source.createStringArray(), source.createStringArray(),
(Bitmap)source.readParcelable(Bitmap.class.getClassLoader()),
source.readString());
return item;
}
/**
* {@inheritDoc}
*/
public CustomFilterCriterion[] newArray(int size) {
return new CustomFilterCriterion[size];
}
};
}

@ -1,164 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.ContentValues;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.andlib.sql.QueryTemplate;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/**
* A <code>FilterListFilter</code> allows users to display tasks that have
* something in common.
* <p>
* A plug-in can expose new <code>FilterListFilter</code>s to the system by
* responding to the <code>com.todoroo.astrid.GET_FILTERS</code> broadcast
* intent.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class Filter extends FilterListItem {
// --- constants
/** Constant for valuesForNewTasks to indicate the value should be replaced
* with the current time as long */
public static final long VALUE_NOW = Long.MIN_VALUE + 1;
// --- instance variables
/**
* Expanded title of this filter. This is displayed at the top
* of the screen when user is viewing this filter.
* <p>
* e.g "Tasks With Notes"
*/
@CheckForNull
public String title;
/**
* {@link PermaSql} query for this filter. The query will be appended to the select
* statement after "<code>SELECT fields FROM table %s</code>". It is
* recommended that you use a {@link QueryTemplate} to construct your
* query.
* <p>
* Examples:
* <ul>
* <li><code>"WHERE completionDate = 0"</code>
* <li><code>"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"</code>
* </ul>
*/
@CheckForNull
public String sqlQuery;
/**
* 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
* additional values will be stored for a task. Can use {@link PermaSql}
*/
@CheckForNull
public ContentValues valuesForNewTasks = null;
/**
* Utility constructor for creating a Filter object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param title
* Expanded title of this filter when user is viewing this
* filter, e.g. Inbox (20 tasks)
* @param sqlQuery
* SQL query for this list (see {@link sqlQuery} for examples).
* @param valuesForNewTasks
* see {@link sqlForNewTasks}
*/
public Filter(String listingTitle, String title,
QueryTemplate sqlQuery, ContentValues valuesForNewTasks) {
this(listingTitle, title, sqlQuery == null ? null : sqlQuery.toString(),
valuesForNewTasks);
}
/**
* Utility constructor for creating a Filter object
* @param listingTitle
* Title of this item as displayed on the lists page, e.g. Inbox
* @param title
* Expanded title of this filter when user is viewing this
* filter, e.g. Inbox (20 tasks)
* @param sqlQuery
* SQL query for this list (see {@link sqlQuery} for examples).
* @param valuesForNewTasks
* see {@link sqlForNewTasks}
*/
public Filter(String listingTitle, String title,
String sqlQuery, ContentValues valuesForNewTasks) {
this.listingTitle = listingTitle;
this.title = title;
this.sqlQuery = sqlQuery;
this.valuesForNewTasks = valuesForNewTasks;
}
/**
* Utility constructor
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected Filter() {
// do nothing
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(title);
dest.writeString(sqlQuery);
dest.writeParcelable(valuesForNewTasks, 0);
}
/**
* Parcelable Creator Object
*/
public static final Parcelable.Creator<Filter> CREATOR = new Parcelable.Creator<Filter>() {
/**
* {@inheritDoc}
*/
public Filter createFromParcel(Parcel source) {
Filter item = new Filter();
item.readFromParcel(source);
item.title = source.readString();
item.sqlQuery = source.readString();
item.valuesForNewTasks = source.readParcelable(ContentValues.class.getClassLoader());
return item;
}
/**
* {@inheritDoc}
*/
public Filter[] newArray(int size) {
return new Filter[size];
}
};
}

@ -1,98 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/**
* A <code>FilterCategory</code> groups common {@link Filter}s and allows
* a user to show/hide all of its children.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterCategory extends FilterListItem {
/**
* {@link Filter}s contained by this category
*/
@CheckForNull
public Filter[] children;
/**
* Constructor for creating a new FilterCategory
* @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, Filter[] children) {
this.listingTitle = listingTitle;
this.children = children;
}
/**
* Constructor for creating a new FilterCategory
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected FilterCategory() {
//
}
// --- parcelable
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeParcelableArray(children, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<FilterCategory> CREATOR = new Parcelable.Creator<FilterCategory>() {
/**
* {@inheritDoc}
*/
public FilterCategory createFromParcel(Parcel source) {
FilterCategory item = new FilterCategory();
item.readFromParcel(source);
Parcelable[] parcelableChildren = source.readParcelableArray(
FilterCategory.class.getClassLoader());
item.children = new Filter[parcelableChildren.length];
for(int i = 0; i < item.children.length; i++) {
if(parcelableChildren[i] instanceof FilterListItem)
item.children[i] = (Filter) parcelableChildren[i];
else
item.children[i] = null;
}
return item;
}
/**
* {@inheritDoc}
*/
public FilterCategory[] newArray(int size) {
return new FilterCategory[size];
}
};
}

@ -1,61 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Section Header for Filter List
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class FilterListHeader extends FilterListItem {
/**
* Constructor for creating a new FilterListHeader
* @param listingTitle
* @param listingIconResource
* @param priority
*/
public FilterListHeader(String listingTitle) {
this.listingTitle = listingTitle;
}
/**
* Constructor for creating a new FilterListHeader
*
* @param plugin
* {@link Addon} identifier that encompasses object
*/
protected FilterListHeader() {
//
}
// --- parcelable
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
public static final Parcelable.Creator<FilterListHeader> CREATOR = new Parcelable.Creator<FilterListHeader>() {
public FilterListHeader createFromParcel(Parcel source) {
FilterListHeader item = new FilterListHeader();
item.readFromParcel(source);
return item;
}
public FilterListHeader[] newArray(int size) {
return new FilterListHeader[size];
}
};
}

@ -1,80 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/**
* Represents an item displayed by Astrid's FilterListActivity
*
* @author Tim Su <tim@todoroo.com>
*
*/
abstract public class FilterListItem implements Parcelable {
/**
* Title of this item displayed on the Filters page
*/
@CheckForNull
public String listingTitle = null;
/**
* Bitmap for icon used on listing page. <code>null</code> => no icon
*/
@CheckForNull
public Bitmap listingIcon = null;
/**
* Text Color. <code>0</code> => default color
*/
public int color = 0;
/**
* Context Menu labels. The context menu will be displayed when users
* long-press on this filter list item.
*/
@CheckForNull
public String contextMenuLabels[] = new String[0];
/**
* Context menu intents. This intent will be started when the corresponding
* content menu label is invoked. This array must be the same size as
* the contextMenuLabels array.
*/
@CheckForNull
public Intent contextMenuIntents[] = new Intent[0];
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(listingTitle);
dest.writeParcelable(listingIcon, 0);
dest.writeInt(color);
// write array lengths before arrays
dest.writeStringArray(contextMenuLabels);
dest.writeTypedArray(contextMenuIntents, 0);
}
/**
* Utility method to read FilterListItem properties from a parcel.
*
* @param source
*/
public void readFromParcel(Parcel source) {
listingTitle = source.readString();
listingIcon = source.readParcelable(Bitmap.class.getClassLoader());
color = source.readInt();
contextMenuLabels = source.createStringArray();
contextMenuIntents = source.createTypedArray(Intent.CREATOR);
}
}

@ -1,61 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import java.util.Date;
import com.todoroo.andlib.utility.DateUtilities;
/**
* PermaSql allows for creating SQL statements that can be saved and used
* later without dates getting stale. It also allows these values to be
* used in
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class PermaSql {
// --- placeholder strings
/** value to be replaced with the current time as long */
public static final String VALUE_NOW = "NOW()"; //$NON-NLS-1$
/** value to be replaced by end of day as long */
public static final String VALUE_EOD = "EOD()"; //$NON-NLS-1$
/** value to be replaced by end of day yesterday as long */
public static final String VALUE_EOD_YESTERDAY = "EODY()"; //$NON-NLS-1$
/** value to be replaced by end of day tomorrow as long */
public static final String VALUE_EOD_TOMORROW = "EODT()"; //$NON-NLS-1$
/** value to be replaced by end of day day after tomorrow as long */
public static final String VALUE_EOD_DAY_AFTER = "EODTT()"; //$NON-NLS-1$
/** value to be replaced by end of day next week as long */
public static final String VALUE_EOD_NEXT_WEEK = "EODW()"; //$NON-NLS-1$
/** Replace placeholder strings with actual */
public static String replacePlaceholders(String value) {
if(value.contains(VALUE_NOW))
value = value.replace(VALUE_NOW, Long.toString(DateUtilities.now()));
if(value.contains(VALUE_EOD) || value.contains(VALUE_EOD_DAY_AFTER) ||
value.contains(VALUE_EOD_NEXT_WEEK) || value.contains(VALUE_EOD_TOMORROW) ||
value.contains(VALUE_EOD_YESTERDAY)) {
Date date = new Date();
date.setHours(23);
date.setMinutes(59);
date.setSeconds(59);
long time = date.getTime();
value = value.replace(VALUE_EOD_YESTERDAY, Long.toString(time - DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD, Long.toString(time));
value = value.replace(VALUE_EOD_TOMORROW, Long.toString(time + DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD_DAY_AFTER, Long.toString(time + 2 * DateUtilities.ONE_DAY));
value = value.replace(VALUE_EOD_NEXT_WEEK, Long.toString(time + 7 * DateUtilities.ONE_DAY));
}
return value;
}
}

@ -1,87 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an intent that can be called to perform synchronization
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class SyncAction implements Parcelable {
/**
* Label
*/
public String label = null;
/**
* Intent to call when invoking this operation
*/
public PendingIntent intent = null;
/**
* Create an EditOperation object
*
* @param label
* label to display
* @param intent
* intent to invoke. {@link EXTRAS_TASK_ID} will be passed
*/
public SyncAction(String label, PendingIntent intent) {
super();
this.label = label;
this.intent = intent;
}
/**
* Returns the label of this action
*/
@Override
public String toString() {
return label;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(label);
dest.writeParcelable(intent, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<SyncAction> CREATOR = new Parcelable.Creator<SyncAction>() {
/**
* {@inheritDoc}
*/
public SyncAction createFromParcel(Parcel source) {
return new SyncAction(source.readString(), (PendingIntent)source.readParcelable(
PendingIntent.class.getClassLoader()));
}
/**
* {@inheritDoc}
*/
public SyncAction[] newArray(int size) {
return new SyncAction[size];
};
};
}

@ -1,79 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents an intent that can be called on a task
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskAction implements Parcelable {
/**
* Label
*/
public String text = null;
/**
* Intent to call when invoking this operation
*/
public PendingIntent intent = null;
/**
* Create an EditOperation object
*
* @param text
* label to display
* @param intent
* intent to invoke. {@link EXTRAS_TASK_ID} will be passed
*/
public TaskAction(String text, PendingIntent intent) {
super();
this.text = text;
this.intent = intent;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeParcelable(intent, 0);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<TaskAction> CREATOR = new Parcelable.Creator<TaskAction>() {
/**
* {@inheritDoc}
*/
public TaskAction createFromParcel(Parcel source) {
return new TaskAction(source.readString(), (PendingIntent)source.readParcelable(
PendingIntent.class.getClassLoader()));
}
/**
* {@inheritDoc}
*/
public TaskAction[] newArray(int size) {
return new TaskAction[size];
};
};
}

@ -1,98 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.api;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;
import android.widget.RemoteViews.RemoteView;
import edu.umd.cs.findbugs.annotations.CheckForNull;
/**
* Represents a line of text displayed in the Task List
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class TaskDecoration implements Parcelable {
/**
* Place decoration between completion box and task title
*/
public static final int POSITION_LEFT = 0;
/**
* Place decoration between task title and importance bar
*/
public static final int POSITION_RIGHT = 1;
/**
* {@link RemoteView} decoration
*/
@CheckForNull
public RemoteViews decoration = null;
/**
* Decoration position
*/
public int position = POSITION_LEFT;
/**
* Decorated task background color. 0 is default
*/
public int color = 0;
/**
* Creates a TaskDetail object
* @param text
* text to display
* @param color
* color to use for text. Use <code>0</code> for default color
*/
public TaskDecoration(RemoteViews decoration, int position, int color) {
this.decoration = decoration;
this.position = position;
this.color = color;
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(decoration, 0);
dest.writeInt(position);
dest.writeInt(color);
}
/**
* Parcelable creator
*/
public static final Parcelable.Creator<TaskDecoration> CREATOR = new Parcelable.Creator<TaskDecoration>() {
/**
* {@inheritDoc}
*/
public TaskDecoration createFromParcel(Parcel source) {
return new TaskDecoration((RemoteViews)source.readParcelable(
RemoteViews.class.getClassLoader()),
source.readInt(), source.readInt());
}
/**
* {@inheritDoc}
*/
public TaskDecoration[] newArray(int size) {
return new TaskDecoration[size];
};
};
}

@ -16,11 +16,9 @@
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/> <booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/astrid"/> <listEntry value="/astrid"/>
<listEntry value="/astrid/AndroidManifest.xml"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/> <listEntry value="4"/>
<listEntry value="1"/>
</listAttribute> </listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>

@ -1,296 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.data;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.AndroidUtilities;
/**
* AbstractDatabase is a database abstraction which wraps a SQLite database.
* <p>
* Users of this class are in charge of the database's lifecycle - ensuring that
* the database is open when needed and closed when usage is finished. Within an
* activity, this is typically accomplished through the onResume and onPause
* methods, though if the database is not needed for the activity's entire
* lifecycle, it can be closed earlier.
* <p>
* Direct querying is not recommended for type safety reasons. Instead, use one
* of the service classes to issue the request and return a {@link TodorooCursor}.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
abstract public class AbstractDatabase {
// --- abstract methods
/**
* @return database name
*/
protected abstract String getName();
/**
* @return all tables in this database
*/
protected abstract Table[] getTables();
/**
* @return database version
*/
protected abstract int getVersion();
/**
* Called after database and tables are created. Use this method to
* create indices and perform other database maintenance
*/
protected abstract void onCreateTables();
/**
* Upgrades an open database from one version to the next
* @param oldVersion
* @param newVersion
* @return true if upgrade was handled, false otherwise
*/
protected abstract boolean onUpgrade(int oldVersion, int newVersion);
// --- protected variables
/**
* SQLiteOpenHelper that takes care of database operations
*/
protected SQLiteOpenHelper helper = null;
/**
* Internal pointer to open database. Hides the fact that there is a
* database and a wrapper by making a single monolithic interface
*/
protected SQLiteDatabase database = null;
// --- internal implementation
/**
* Return the name of the table containing these models
* @param modelType
* @return
*/
public final Table getTable(Class<? extends AbstractModel> modelType) {
for(Table table : getTables()) {
if(table.modelClass.equals(modelType))
return table;
}
throw new UnsupportedOperationException("Unknown model class " + modelType); //$NON-NLS-1$
}
protected synchronized final void initializeHelper() {
if(helper == null) {
if(ContextManager.getContext() == null)
throw new NullPointerException("Null context creating database helper");
helper = new DatabaseHelper(ContextManager.getContext(),
getName(), null, getVersion());
}
}
/**
* Open the database for writing. Must be closed afterwards. If user is
* out of disk space, database may be opened for reading instead
*/
public synchronized final void openForWriting() {
initializeHelper();
if(database != null && !database.isReadOnly() && database.isOpen())
return;
try {
database = helper.getWritableDatabase();
} catch (NullPointerException e) {
// don't know why this happens
throw new IllegalStateException(e);
} catch (final RuntimeException original) {
Log.e("database-" + getName(), "Error opening db",
original);
try {
// provide read-only database
openForReading();
} catch (Exception readException) {
// throw original write exception
throw original;
}
}
}
/**
* Open the database for reading. Must be closed afterwards
*/
public synchronized final void openForReading() {
initializeHelper();
if(database != null && database.isOpen())
return;
database = helper.getReadableDatabase();
}
/**
* Close the database if it has been opened previously
*/
public synchronized final void close() {
if(database != null) {
database.close();
}
database = null;
}
/**
* Clear all data in database. Warning: this does what it says. Any open
* database resources will be abruptly closed.
*/
public synchronized final void clear() {
close();
ContextManager.getContext().deleteDatabase(getName());
}
/**
* @return sql database. opens database if not yet open
*/
public synchronized final SQLiteDatabase getDatabase() {
if(database == null) {
AndroidUtilities.sleepDeep(300L);
openForWriting();
}
return database;
}
/**
* @return human-readable database name for debugging
*/
@Override
public String toString() {
return "DB:" + getName();
}
// --- database wrapper
/*
* @see android.database.sqlite.SQLiteDatabase#rawQuery(String sql, String[] selectionArgs)
*/
public synchronized Cursor rawQuery(String sql, String[] selectionArgs) {
return getDatabase().rawQuery(sql, selectionArgs);
}
/*
* @see android.database.sqlite.SQLiteDatabase#insert(String table, String nullColumnHack, ContentValues values)
*/
public synchronized long insert(String table, String nullColumnHack, ContentValues values) {
return getDatabase().insert(table, nullColumnHack, values);
}
/*
* @see android.database.sqlite.SQLiteDatabase#delete(String table, String whereClause, String[] whereArgs)
*/
public synchronized int delete(String table, String whereClause, String[] whereArgs) {
return getDatabase().delete(table, whereClause, whereArgs);
}
/*
* @see android.database.sqlite.SQLiteDatabase#update(String table, ContentValues values, String whereClause, String[] whereArgs)
*/
public synchronized int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
return getDatabase().update(table, values, whereClause, whereArgs);
}
// --- helper classes
/**
* Default implementation of Astrid database helper
*/
private class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
/**
* Called to create the database tables
*/
@Override
public synchronized void onCreate(SQLiteDatabase db) {
StringBuilder sql = new StringBuilder();
SqlConstructorVisitor sqlVisitor = new SqlConstructorVisitor();
// create tables
for(Table table : getTables()) {
sql.append("CREATE TABLE IF NOT EXISTS ").append(table.name).append('(').
append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
for(Property<?> property : table.getProperties()) {
if(AbstractModel.ID_PROPERTY.name.equals(property.name))
continue;
sql.append(',').append(property.accept(sqlVisitor, null));
}
sql.append(')');
db.execSQL(sql.toString());
sql.setLength(0);
}
// post-table-creation
database = db;
onCreateTables();
}
/**
* Called to upgrade the database to a new version
*/
@Override
public synchronized void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("database-" + getName(), String.format("Upgrading database from version %d to %d.",
oldVersion, newVersion));
database = db;
if(!AbstractDatabase.this.onUpgrade(oldVersion, newVersion)) {
// We don't know how to handle this case because someone forgot to
// implement the upgrade. We can't drop tables, we can only
// throw a nasty exception at this time
throw new IllegalStateException("Missing database migration " +
"from " + oldVersion + " to " + newVersion);
}
}
}
/**
* Visitor that returns SQL constructor for this property
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class SqlConstructorVisitor implements PropertyVisitor<String, Void> {
public String visitDouble(Property<Double> property, Void data) {
return String.format("%s REAL", property.name);
}
public String visitInteger(Property<Integer> property, Void data) {
return String.format("%s INTEGER", property.name);
}
public String visitLong(Property<Long> property, Void data) {
return String.format("%s INTEGER", property.name);
}
public String visitString(Property<String> property, Void data) {
return String.format("%s TEXT", property.name);
}
}
}

@ -1,431 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.data;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import android.content.ContentValues;
import android.os.Parcel;
import android.os.Parcelable;
import com.todoroo.andlib.data.Property.DoubleProperty;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.PropertyVisitor;
/**
* <code>AbstractModel</code> represents a row in a database.
* <p>
* A single database can be represented by multiple <code>AbstractModel</code>s
* corresponding to different queries that return a different set of columns.
* Each model exposes a set of properties that it contains.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public abstract class AbstractModel implements Parcelable {
// --- static variables
private static final ContentValuesSavingVisitor saver = new ContentValuesSavingVisitor();
// --- constants
/** id property common to all models */
protected static final String ID_PROPERTY_NAME = "_id"; //$NON-NLS-1$
/** id field common to all models */
public static final IntegerProperty ID_PROPERTY = new IntegerProperty(null, ID_PROPERTY_NAME);
/** sentinel for objects without an id */
public static final long NO_ID = 0;
// --- abstract methods
/** Get the default values for this object */
abstract public ContentValues getDefaultValues();
// --- data store variables and management
/* Data Source Ordering:
*
* In order to return the best data, we want to check first what the user
* has explicitly set (setValues), then the values we have read out of
* the database (values), then defaults (getDefaultValues)
*/
/** User set values */
protected ContentValues setValues = null;
/** Values from database */
protected ContentValues values = null;
/** Get database-read values for this object */
public ContentValues getDatabaseValues() {
return values;
}
/** Get the user-set values for this object */
public ContentValues getSetValues() {
return setValues;
}
/** Get a list of all field/value pairs merged across data sources */
public ContentValues getMergedValues() {
ContentValues mergedValues = new ContentValues();
ContentValues defaultValues = getDefaultValues();
if(defaultValues != null)
mergedValues.putAll(defaultValues);
if(values != null)
mergedValues.putAll(values);
if(setValues != null)
mergedValues.putAll(setValues);
return mergedValues;
}
/**
* Clear all data on this model
*/
public void clear() {
values = null;
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.
*/
@Override
public boolean equals(Object other) {
if(other == null || other.getClass() != getClass())
return false;
return getMergedValues().equals(((AbstractModel)other).getMergedValues());
}
@Override
public int hashCode() {
return getMergedValues().hashCode() ^ getClass().hashCode();
}
// --- data retrieval
/**
* Reads all properties from the supplied cursor and store
*/
protected synchronized void readPropertiesFromCursor(TodorooCursor<? extends AbstractModel> cursor) {
if (values == null)
values = new ContentValues();
// clears user-set values
setValues = null;
for (Property<?> property : cursor.getProperties()) {
saver.save(property, values, cursor.get(property));
}
}
/**
* Reads the given property. Make sure this model has this property!
*/
public synchronized <TYPE> TYPE getValue(Property<TYPE> property) {
Object value;
if(setValues != null && setValues.containsKey(property.name))
value = setValues.get(property.name);
else if(values != null && values.containsKey(property.name))
value = values.get(property.name);
else if(getDefaultValues().containsKey(property.name))
value = getDefaultValues().get(property.name);
else
throw new UnsupportedOperationException(
"Model Error: Did not read property " + property.name); //$NON-NLS-1$
// resolve properties that were retrieved with a different type than accessed
if(value instanceof String && property instanceof LongProperty)
return (TYPE) Long.valueOf((String)value);
else if(value instanceof String && property instanceof IntegerProperty)
return (TYPE) Integer.valueOf((String)value);
else if(value instanceof String && property instanceof DoubleProperty)
return (TYPE) Double.valueOf((String)value);
else if(value instanceof Integer && property instanceof LongProperty)
return (TYPE) Long.valueOf(((Number)value).longValue());
return (TYPE) value;
}
/**
* Utility method to get the identifier of the model, if it exists.
*
* @return {@value #NO_ID} if this model was not added to the database
*/
abstract public long getId();
protected long getIdHelper(LongProperty id) {
if(setValues != null && setValues.containsKey(id.name))
return setValues.getAsLong(id.name);
else if(values != null && values.containsKey(id.name))
return values.getAsLong(id.name);
else
return NO_ID;
}
public void setId(long id) {
if (setValues == null)
setValues = new ContentValues();
if(id == NO_ID)
setValues.remove(ID_PROPERTY_NAME);
else
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
*/
public boolean containsValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
return true;
if(values != null && values.containsKey(property.name))
return true;
return false;
}
/**
* @param property
* @return true if setValues or values contains this property, and the value
* stored is not null
*/
public boolean containsNonNullValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
return setValues.get(property.name) != null;
if(values != null && values.containsKey(property.name))
return values.get(property.name) != null;
return false;
}
// --- data storage
/**
* Check whether the user has changed this property value and it should be
* stored for saving in the database
*/
protected synchronized <TYPE> boolean shouldSaveValue(
Property<TYPE> property, TYPE newValue) {
// we've already decided to save it, so overwrite old value
if (setValues.containsKey(property.name))
return true;
// values contains this key, we should check it out
if(values != null && values.containsKey(property.name)) {
TYPE value = getValue(property);
if (value == null) {
if (newValue == null)
return false;
} else if (value.equals(newValue))
return false;
}
// otherwise, good to save
return true;
}
/**
* Sets the given property. Make sure this model has this property!
*/
public synchronized <TYPE> void setValue(Property<TYPE> property,
TYPE value) {
if (setValues == null)
setValues = new ContentValues();
if (!shouldSaveValue(property, value))
return;
saver.save(property, setValues, value);
}
/**
* Merges content values with those coming from another source
*/
public synchronized <TYPE> void mergeWith(ContentValues other) {
if (setValues == null)
setValues = new ContentValues();
setValues.putAll(other);
}
/**
* Clear the key for the given property
* @param property
*/
public synchronized void clearValue(Property<?> property) {
if(setValues != null && setValues.containsKey(property.name))
setValues.remove(property.name);
else if(values != null && values.containsKey(property.name))
values.remove(property.name);
else if(getDefaultValues().containsKey(property.name))
throw new IllegalArgumentException("Property has a default value"); //$NON-NLS-1$
}
// --- property management
/**
* Looks inside the given class and finds all declared properties
*/
protected static Property<?>[] generateProperties(Class<? extends AbstractModel> cls) {
ArrayList<Property<?>> properties = new ArrayList<Property<?>>();
if(cls.getSuperclass() != AbstractModel.class)
properties.addAll(Arrays.asList(generateProperties(
(Class<? extends AbstractModel>) cls.getSuperclass())));
// a property is public, static & extends Property
for(Field field : cls.getFields()) {
if((field.getModifiers() & Modifier.STATIC) == 0)
continue;
if(!Property.class.isAssignableFrom(field.getType()))
continue;
try {
properties.add((Property<?>) field.get(null));
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return properties.toArray(new Property<?>[properties.size()]);
}
/**
* Visitor that saves a value into a content values store
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class ContentValuesSavingVisitor implements PropertyVisitor<Void, Object> {
private ContentValues store;
public synchronized void save(Property<?> property, ContentValues newStore, Object value) {
this.store = newStore;
// we don't allow null values, as they indicate unset properties
// when the database was written
if(value != null)
property.accept(this, value);
}
public Void visitDouble(Property<Double> property, Object value) {
store.put(property.name, (Double) value);
return null;
}
public Void visitInteger(Property<Integer> property, Object value) {
store.put(property.name, (Integer) value);
return null;
}
public Void visitLong(Property<Long> property, Object value) {
store.put(property.name, (Long) value);
return null;
}
public Void visitString(Property<String> property, Object value) {
store.put(property.name, (String) value);
return null;
}
}
// --- parcelable helpers
/**
* {@inheritDoc}
*/
public int describeContents() {
return 0;
}
/**
* {@inheritDoc}
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(setValues, 0);
dest.writeParcelable(values, 0);
}
/**
* In addition to overriding this class, model classes should create
* a static final variable named "CREATOR" in order to satisfy the
* requirements of the Parcelable interface.
*/
abstract protected Parcelable.Creator<? extends AbstractModel> getCreator();
/**
* Parcelable creator helper
*/
protected static final class ModelCreator<TYPE extends AbstractModel>
implements Parcelable.Creator<TYPE> {
private final Class<TYPE> cls;
public ModelCreator(Class<TYPE> cls) {
super();
this.cls = cls;
}
/**
* {@inheritDoc}
*/
public TYPE createFromParcel(Parcel source) {
TYPE model;
try {
model = cls.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
model.setValues = source.readParcelable(ContentValues.class.getClassLoader());
model.values = source.readParcelable(ContentValues.class.getClassLoader());
return model;
}
/**
* {@inheritDoc}
*/
public TYPE[] newArray(int size) {
return (TYPE[]) Array.newInstance(cls, size);
};
};
}

@ -1,250 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.data;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.utility.Constants;
/**
* Abstract data access object
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class GenericDao<TYPE extends AbstractModel> {
private final Class<TYPE> modelClass;
private Table table;
private AbstractDatabase database;
public GenericDao(Class<TYPE> modelClass) {
this.modelClass = modelClass;
}
public GenericDao(Class<TYPE> modelClass, AbstractDatabase database) {
this.modelClass = modelClass;
setDatabase(database);
}
/** Gets table associated with this DAO */
public Table getTable() {
return table;
}
/**
* Sets database accessed by this DAO. Used for dependency-injected
* initialization by child classes and unit tests
*
* @param database
*/
public void setDatabase(AbstractDatabase database) {
if(database == this.database)
return;
this.database = database;
table = database.getTable(modelClass);
}
// --- dao methods
/**
* Construct a query with SQL DSL objects
*
* @param query
* @return
*/
public TodorooCursor<TYPE> query(Query query) {
query.from(table);
if(Constants.DEBUG)
Log.i("SQL-" + modelClass.getSimpleName(), query.toString()); //$NON-NLS-1$
Cursor cursor = database.rawQuery(query.toString(), null);
return new TodorooCursor<TYPE>(cursor, query.getFields());
}
/**
* Construct a query with raw SQL
*
* @param properties
* @param selection
* @param selectionArgs
* @return
*/
public TodorooCursor<TYPE> rawQuery(String selection, String[] selectionArgs, Property<?>... properties) {
String[] fields = new String[properties.length];
for(int i = 0; i < properties.length; i++)
fields[i] = properties[i].name;
return new TodorooCursor<TYPE>(database.getDatabase().query(table.name,
fields, selection, selectionArgs, null, null, null),
properties);
}
/**
* Returns object corresponding to the given identifier
*
* @param database
* @param table
* name of table
* @param properties
* properties to read
* @param id
* id of item
* @return null if no item found
*/
public TYPE fetch(long id, Property<?>... properties) {
TodorooCursor<TYPE> cursor = fetchItem(id, properties);
try {
if (cursor.getCount() == 0)
return null;
Constructor<TYPE> constructor = modelClass.getConstructor(TodorooCursor.class);
return constructor.newInstance(cursor);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} finally {
cursor.close();
}
}
/**
* Delete the given id
*
* @param database
* @param id
* @return true if delete was successful
*/
public boolean delete(long id) {
return database.delete(table.name,
AbstractModel.ID_PROPERTY.eq(id).toString(), null) > 0;
}
/**
* Delete all matching a clause
* @param database
* @param where
* @return # of deleted items
*/
public int deleteWhere(Criterion where) {
return database.delete(table.name,
where.toString(), null);
}
/**
* Save the given object to the database. Creates a new object if
* model id property has not been set
*
* @return true on success.
*/
public boolean persist(TYPE item) {
if (item.getId() == AbstractModel.NO_ID) {
return createNew(item);
} else {
ContentValues values = item.getSetValues();
if (values.size() == 0) // nothing changed
return true;
return saveExisting(item);
}
}
/**
* Creates the given item.
*
* @param database
* @param table
* table name
* @param item
* item model
* @return returns true on success.
*/
public boolean createNew(TYPE item) {
long newRow = database.insert(table.name,
AbstractModel.ID_PROPERTY.name, item.getMergedValues());
boolean result = newRow >= 0;
if(result) {
item.markSaved();
item.setId(newRow);
}
return result;
}
/**
* Saves the given item. Will not create a new item!
*
* @param database
* @param table
* table name
* @param item
* item model
* @return returns true on success.
*/
public boolean saveExisting(TYPE item) {
ContentValues values = item.getSetValues();
if(values == null || values.size() == 0) // nothing changed
return true;
boolean result = database.update(table.name, values,
AbstractModel.ID_PROPERTY.eq(item.getId()).toString(), null) > 0;
if(result)
item.markSaved();
return result;
}
/**
* Updates multiple rows of the database based on model set values
*
* @param item
* item model
* @param criterion
* @return returns true on success.
*/
public int updateMultiple(ContentValues values, Criterion criterion) {
if(values.size() == 0) // nothing changed
return 0;
return database.update(table.name, values, criterion.toString(), null);
}
// --- helper methods
/**
* Returns cursor to object corresponding to the given identifier
*
* @param database
* @param table
* name of table
* @param properties
* properties to read
* @param id
* id of item
* @return
*/
protected TodorooCursor<TYPE> fetchItem(long id, Property<?>... properties) {
TodorooCursor<TYPE> cursor = query(
Query.select(properties).where(AbstractModel.ID_PROPERTY.eq(id)));
cursor.moveToFirst();
return new TodorooCursor<TYPE>(cursor, properties);
}
}

@ -1,207 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.data;
import com.todoroo.andlib.sql.Field;
/**
* Property represents a typed column in a database.
*
* Within a given database row, the parameter may not exist, in which case the
* value is null, it may be of an incorrect type, in which case an exception is
* thrown, or the correct type, in which case the value is returned.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE>
* a database supported type, such as String or Integer
*/
@SuppressWarnings("nls")
public abstract class Property<TYPE> extends Field implements Cloneable {
// --- implementation
/** The database table name this property */
public final Table table;
/** The database column name for this property */
public final String name;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
protected Property(Table table, String columnName) {
this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
*/
protected Property(Table table, String columnName, String expression) {
super(expression);
this.table = table;
this.name = columnName;
}
/**
* Accept a visitor
*/
abstract public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data);
/**
* Return a clone of this property
*/
@Override
public Property<TYPE> clone() {
try {
return (Property<TYPE>) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
// --- helper classes and interfaces
/**
* Visitor interface for property classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface PropertyVisitor<RETURN, PARAMETER> {
public RETURN visitInteger(Property<Integer> property, PARAMETER data);
public RETURN visitLong(Property<Long> property, PARAMETER data);
public RETURN visitDouble(Property<Double> property, PARAMETER data);
public RETURN visitString(Property<String> property, PARAMETER data);
}
// --- children
/**
* Integer property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class IntegerProperty extends Property<Integer> {
public IntegerProperty(Table table, String name) {
super(table, name);
}
protected IntegerProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitInteger(this, data);
}
}
/**
* String property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class StringProperty extends Property<String> {
public StringProperty(Table table, String name) {
super(table, name);
}
protected StringProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitString(this, data);
}
}
/**
* Double property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class DoubleProperty extends Property<Double> {
public DoubleProperty(Table table, String name) {
super(table, name);
}
protected DoubleProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitDouble(this, data);
}
}
/**
* Long property type. See {@link Property}
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class LongProperty extends Property<Long> {
public LongProperty(Table table, String name) {
super(table, name);
}
protected LongProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@Override
public <RETURN, PARAMETER> RETURN accept(
PropertyVisitor<RETURN, PARAMETER> visitor, PARAMETER data) {
return visitor.visitLong(this, data);
}
}
// --- pseudo-properties
/** Runs a SQL function and returns the result as a string */
public static class StringFunctionProperty extends StringProperty {
public StringFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Runs a SQL function and returns the result as a string */
public static class IntegerFunctionProperty extends IntegerProperty {
public IntegerFunctionProperty(String function, String columnName) {
super(null, columnName, function);
alias = columnName;
}
}
/** Counting in aggregated tables. Returns the result of COUNT(1) */
public static final class CountProperty extends IntegerFunctionProperty {
public CountProperty() {
super("COUNT(1)", "count");
}
}
}

@ -1,68 +0,0 @@
package com.todoroo.andlib.data;
import com.todoroo.andlib.sql.Field;
import com.todoroo.andlib.sql.SqlTable;
/**
* Table class. Most fields are final, so methods such as <code>as</code> will
* clone the table when it returns.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public final class Table extends SqlTable {
public final String name;
public final Class<? extends AbstractModel> modelClass;
public Table(String name, Class<? extends AbstractModel> modelClass) {
this(name, modelClass, null);
}
public Table(String name, Class<? extends AbstractModel> modelClass, String alias) {
super(name);
this.name = name;
this.alias = alias;
this.modelClass = modelClass;
}
/**
* Reads a list of properties from model class by reflection
* @return property array
*/
@SuppressWarnings("nls")
public Property<?>[] getProperties() {
try {
return (Property<?>[])modelClass.getField("PROPERTIES").get(null);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
// --- for sql-dsl
/**
* Create a new join table based on this table, but with an alias
*/
@Override
public Table as(String newAlias) {
return new Table(name, modelClass, newAlias);
}
/**
* Create a field object based on the given property
* @param property
* @return
*/
@SuppressWarnings("nls")
public Field field(Property<?> property) {
if(alias != null)
return Field.field(alias + "." + property.name);
return Field.field(name + "." + property.name);
}
}

@ -1,109 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.data;
import java.util.WeakHashMap;
import android.database.Cursor;
import android.database.CursorWrapper;
import com.todoroo.andlib.data.Property.PropertyVisitor;
/**
* AstridCursor wraps a cursor and allows users to query for individual
* {@link Property} types or read an entire {@link AbstractModel} from
* a database row.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE> a model type that is returned by this cursor
*/
public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Properties read by this cursor */
private final Property<?>[] properties;
/** Weakly cache field name to column id references for this cursor.
* Because it's a weak hash map, entire keys can be discarded by GC */
private final WeakHashMap<String, Integer> columnIndexCache;
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
*
* @param cursor
* @param properties properties read from this cursor
*/
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
/**
* Get the value for the given property on the underlying {@link Cursor}
*
* @param <PROPERTY_TYPE> type to return
* @param property to retrieve
* @return
*/
public <PROPERTY_TYPE> PROPERTY_TYPE get(Property<PROPERTY_TYPE> property) {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* Gets entire property list
* @return
*/
public Property<?>[] getProperties() {
return properties;
}
/**
* Use cache to get the column index for the given field name
*/
public synchronized int getColumnIndexFromCache(String field) {
Integer index = columnIndexCache.get(field);
if(index == null) {
index = getColumnIndexOrThrow(field);
columnIndexCache.put(field, index);
}
return index;
}
/**
* Visitor that reads the given property from a cursor
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class CursorReadingVisitor implements PropertyVisitor<Object, TodorooCursor<?>> {
public Object visitDouble(Property<Double> property,
TodorooCursor<?> cursor) {
return cursor.getDouble(cursor.getColumnIndexFromCache(property.name));
}
public Object visitInteger(Property<Integer> property,
TodorooCursor<?> cursor) {
return cursor.getInt(cursor.getColumnIndexFromCache(property.name));
}
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getLong(cursor.getColumnIndexFromCache(property.name));
}
public Object visitString(Property<String> property,
TodorooCursor<?> cursor) {
return cursor.getString(cursor.getColumnIndexFromCache(property.name));
}
}
}

@ -1,26 +0,0 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
/**
* A Dependency Injector knows how to inject certain dependencies based
* on the field that is passed in.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface AbstractDependencyInjector {
/**
* Gets the injected object for this field. If implementing class does not
* know how to handle this dependency, it should return null
*
* @param object
* object to perform dependency injection on
* @param field
* field tagged with {link Autowired} annotation
* @return object to assign to this field, or null
*/
abstract Object getInjection(Object object, Field field);
}

@ -1,19 +0,0 @@
package com.todoroo.andlib.service;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Autowired is an annotation that tells the dependency injector to
* set up the class as appropriate.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
//
}

@ -1,57 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import android.content.Context;
import android.content.res.Resources;
/**
* Singleton class to manage current application context
* b
* @author Tim Su <tim@todoroo.com>
*
*/
public final class ContextManager {
/**
* Global application context
*/
private static Context context = null;
/**
* Sets the global context
* @param context
*/
public static void setContext(Context context) {
ContextManager.context = context;
}
/**
* Gets the global context
*/
public static Context getContext() {
return context;
}
/**
* Convenience method to read a string from the resources
*
* @param resid
* @param parameters
* @return
*/
public static String getString(int resId, Object... formatArgs) {
return context.getString(resId, formatArgs);
}
/**
* Convenience method to read resources
*
* @return
*/
public static Resources getResources() {
return context.getResources();
}
}

@ -1,149 +0,0 @@
package com.todoroo.andlib.service;
import java.lang.reflect.Field;
import java.util.Arrays;
import android.util.Log;
import com.todoroo.astrid.utility.Constants;
/**
* Simple Dependency Injection Service for Android.
* <p>
* Add dependency injectors to the injector chain, then invoke this method
* against classes you wish to perform dependency injection for.
* <p>
* All errors encountered are handled as warnings, so if dependency injection
* seems to be failing, check the logs for more information.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class DependencyInjectionService {
private static final String QUALIFIED_PACKAGE = "com.t"; //$NON-NLS-1$
/**
* Dependency injectors. Use getters and setters to modify this list
*/
private AbstractDependencyInjector[] injectors = {};
/**
* Perform dependency injection in the caller object
*
* @param caller
* object to perform DI on
*/
@SuppressWarnings("nls")
public void inject(Object caller) {
// Traverse through class and all parent classes, looking for
// fields declared with the @Autowired annotation and using
// dependency injection to set them as appropriate
Class<?> cls = caller.getClass();
while(cls != null) {
String packageName = cls.getPackage().getName();
if(!packageName.startsWith(QUALIFIED_PACKAGE))
break;
for(Field field : cls.getDeclaredFields()) {
if(field.getAnnotation(Autowired.class) != null) {
field.setAccessible(true);
try {
handleField(caller, field);
} catch (IllegalStateException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
} catch (IllegalAccessException e) {
throw new RuntimeException(String.format("Unable to set field '%s' of type '%s'",
field.getName(), field.getType()), e);
}
}
}
cls = cls.getSuperclass();
}
}
/**
* This method returns the appropriate dependency object based on the type
* that this autowired field accepts
*
* @param caller
* calling object
* @param field
* field to inject
*/
@SuppressWarnings("nls")
private synchronized void handleField(Object caller, Field field)
throws IllegalStateException, IllegalArgumentException,
IllegalAccessException {
if(field.getType().isPrimitive())
throw new IllegalStateException(String.format(
"Tried to dependency-inject primative field '%s' of type '%s'",
field.getName(), field.getType()));
// field has already been processed, ignore
if (field.get(caller) != null) {
return;
}
for (AbstractDependencyInjector injector : injectors) {
Object injection = injector.getInjection(caller, field);
if (injection != null) {
if(Constants.DEBUG)
Log.d("INJECTOR", injector + ":" + caller + "." + field.getName() + " => " + injection);
field.set(caller, injection);
return;
}
}
throw new IllegalStateException(
String.format("No dependency injector found for autowired " +
"field '%s' in class '%s'. Injectors: %s",
field.getName(), caller.getClass().getName(),
Arrays.asList(getInjectors())));
}
// --- static methods
private static DependencyInjectionService instance = null;
DependencyInjectionService() {
// prevent instantiation
}
/**
* Gets the singleton instance of the dependency injection service.
* @return
*/
public synchronized static DependencyInjectionService getInstance() {
if(instance == null)
instance = new DependencyInjectionService();
return instance;
}
/**
* Gets the array of installed injectors
* @return
*/
public synchronized AbstractDependencyInjector[] getInjectors() {
return injectors;
}
/**
* Sets the array of installed injectors
* @param injectors
*/
public synchronized void setInjectors(AbstractDependencyInjector[] injectors) {
this.injectors = injectors;
}
}

@ -1,165 +0,0 @@
package com.todoroo.andlib.service;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.SocketTimeoutException;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
/**
* Exception handling utility class - reports and logs errors
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class ExceptionService {
@Autowired
public ErrorReporter[] errorReporters;
@Autowired
public Integer errorDialogTitleResource;
@Autowired
public Integer errorDialogBodyGeneric;
@Autowired
public Integer errorDialogBodyNullError;
@Autowired
public Integer errorDialogBodySocketTimeout;
public ExceptionService() {
DependencyInjectionService.getInstance().inject(this);
}
/**
* Report the error via registered error handlers
*
* @param name Internal error name. Not displayed to user
* @param error Exception encountered. Message will be displayed to user
*/
public void reportError(String name, Throwable error) {
if(errorReporters == null)
return;
for(ErrorReporter reporter : errorReporters)
reporter.handleError(name, error);
}
/**
* Display error dialog if context is activity and report error
*
* @param context Application Context
* @param name Internal error name. Not displayed to user
* @param error Exception encountered. Message will be displayed to user
*/
public void displayAndReportError(final Context context, String name, Throwable error) {
if(context instanceof Activity) {
final String messageToDisplay;
// pretty up the message when displaying to user
if(error == null)
messageToDisplay = context.getString(errorDialogBodyNullError);
else if(error instanceof SocketTimeoutException)
messageToDisplay = context.getString(errorDialogBodySocketTimeout);
else
messageToDisplay = context.getString(errorDialogBodyGeneric, error);
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
try {
new AlertDialog.Builder(context)
.setTitle(errorDialogTitleResource)
.setMessage(messageToDisplay)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, null)
.show();
} catch (Exception e) {
// suppress errors during dialog creation
}
}
});
}
reportError(name, error);
}
/**
* Error reporter interface
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface ErrorReporter {
public void handleError(String name, Throwable error);
}
/**
* AndroidLogReporter reports errors to LogCat
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class AndroidLogReporter implements ErrorReporter {
/**
* Report the error to the logs
*
* @param name
* @param error
*/
public void handleError(String name, Throwable error) {
String tag = null;
if(ContextManager.getContext() != null) {
PackageManager pm = ContextManager.getContext().getPackageManager();
try {
String appName = pm.getApplicationInfo(ContextManager.getContext().
getPackageName(), 0).name;
tag = appName + "-" + name; //$NON-NLS-1$
} catch (NameNotFoundException e) {
// give up
}
}
if(tag == null)
tag = "unknown-" + name; //$NON-NLS-1$
if(error == null)
Log.e(tag, "Exception: " + name); //$NON-NLS-1$
else
Log.e(tag, error.toString(), error);
}
}
/**
* Uncaught exception handler uses the exception utilities class to
* report errors
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class TodorooUncaughtExceptionHandler implements UncaughtExceptionHandler {
private final UncaughtExceptionHandler defaultUEH;
@Autowired
protected ExceptionService exceptionService;
public TodorooUncaughtExceptionHandler() {
defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
DependencyInjectionService.getInstance().inject(this);
}
public void uncaughtException(Thread thread, Throwable ex) {
if(exceptionService != null)
exceptionService.reportError("uncaught", ex); //$NON-NLS-1$
defaultUEH.uncaughtException(thread, ex);
}
}
}

@ -1,13 +0,0 @@
package com.todoroo.andlib.service;
import java.io.IOException;
public class HttpErrorException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpErrorException(int code, String message) {
super(String.format("%d %s", code, message)); //$NON-NLS-1$
}
}

@ -1,165 +0,0 @@
package com.todoroo.andlib.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import android.util.Log;
/**
* RestClient allows Android to consume web requests.
* <p>
* Portions by Praeda:
* http://senior.ceng.metu.edu.tr/2009/praeda/2009/01/11/a-simple
* -restful-client-at-android/
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpRestClient implements RestClient {
private static final int HTTP_UNAVAILABLE_END = 599;
private static final int HTTP_UNAVAILABLE_START = 500;
private static final int HTTP_OK = 200;
private static final int TIMEOUT_MILLIS = 30000;
private static WeakReference<HttpClient> httpClient = null;
@Autowired
private Boolean debug;
public HttpRestClient() {
DependencyInjectionService.getInstance().inject(this);
}
private static String convertStreamToString(InputStream is) {
/*
* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there's no more data to read. Each line will
* appended to a StringBuilder and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 16384);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n"); //$NON-NLS-1$
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
private synchronized static HttpClient getClient() {
if (httpClient == null || httpClient.get() == null) {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_MILLIS);
HttpConnectionParams.setSoTimeout(params, TIMEOUT_MILLIS);
HttpClient client = new DefaultHttpClient(params);
httpClient = new WeakReference<HttpClient>(client);
return client;
} else {
return httpClient.get();
}
}
private String processHttpResponse(HttpResponse response) throws IOException {
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode >= HTTP_UNAVAILABLE_START && statusCode <= HTTP_UNAVAILABLE_END) {
throw new HttpUnavailableException();
} else if(statusCode != HTTP_OK) {
throw new HttpErrorException(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase());
}
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream contentStream = entity.getContent();
try {
return convertStreamToString(contentStream);
} finally {
contentStream.close();
}
}
return null;
}
/**
* Issue an HTTP GET for the given URL, return the response
*
* @param url url with url-encoded params
* @return response, or null if there was no response
* @throws IOException
*/
public synchronized String get(String url) throws IOException {
if(debug)
Log.d("http-rest-client-get", url); //$NON-NLS-1$
try {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = getClient().execute(httpGet);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
/**
* Issue an HTTP POST for the given URL, return the response
*
* @param url
* @param data
* url-encoded data
* @throws IOException
*/
public synchronized String post(String url, String data) throws IOException {
if(debug)
Log.d("http-rest-client-post", url + " | " + data); //$NON-NLS-1$ //$NON-NLS-2$
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(data));
HttpResponse response = getClient().execute(httpPost);
return processHttpResponse(response);
} catch (IOException e) {
throw e;
} catch (Exception e) {
IOException ioException = new IOException(e.getMessage());
ioException.initCause(e);
throw ioException;
}
}
}

@ -1,25 +0,0 @@
package com.todoroo.andlib.service;
import java.io.IOException;
/**
* Exception displayed when a 500 error is received on an HTTP invocation
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class HttpUnavailableException extends IOException {
private static final long serialVersionUID = 5373340422464657279L;
public HttpUnavailableException() {
super();
DependencyInjectionService.getInstance().inject(this);
}
@Override
public String getMessage() {
return "Sorry, our servers are experiencing some issues. Please try again later!"; //$NON-NLS-1$ // FIXME
}
}

@ -1,49 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.service;
import android.app.Notification;
import android.content.Context;
/**
* Notification Manager stub
*
* @author timsu
*
*/
public interface NotificationManager {
public void cancel(int id);
public void cancelAll();
public void notify(int id, Notification notification);
/**
* Instantiation of notification manager that passes through to
* Android's notification manager
*
* @author timsu
*
*/
public static class AndroidNotificationManager implements NotificationManager {
private final android.app.NotificationManager nm;
public AndroidNotificationManager(Context context) {
nm = (android.app.NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
}
public void cancel(int id) {
nm.cancel(id);
}
public void cancelAll() {
nm.cancelAll();
}
public void notify(int id, Notification notification) {
nm.notify(id, notification);
}
}
}

@ -1,14 +0,0 @@
package com.todoroo.andlib.service;
import java.io.IOException;
/**
* RestClient stub invokes the HTML requests as desired
*
* @author Tim Su <tim@todoroo.com>
*
*/
public interface RestClient {
public String get(String url) throws IOException;
public String post(String url, String data) throws IOException;
}

@ -1,89 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND;
import static com.todoroo.andlib.sql.SqlConstants.EXISTS;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.NOT;
import static com.todoroo.andlib.sql.SqlConstants.OR;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class Criterion {
protected final Operator operator;
Criterion(Operator operator) {
this.operator = operator;
}
public static Criterion all = new Criterion(Operator.exists) {
@Override
protected void populate(StringBuilder sb) {
sb.append(1);
}
};
public static Criterion none = new Criterion(Operator.exists) {
@Override
protected void populate(StringBuilder sb) {
sb.append(0);
}
};
public static Criterion and(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.and) {
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(AND).append(SPACE).append(c);
}
}
};
}
public static Criterion or(final Criterion criterion, final Criterion... criterions) {
return new Criterion(Operator.or) {
@Override
protected void populate(StringBuilder sb) {
sb.append(criterion);
for (Criterion c : criterions) {
sb.append(SPACE).append(OR).append(SPACE).append(c.toString());
}
}
};
}
public static Criterion exists(final Query query) {
return new Criterion(Operator.exists) {
@Override
protected void populate(StringBuilder sb) {
sb.append(EXISTS).append(SPACE).append(LEFT_PARENTHESIS).append(query).append(RIGHT_PARENTHESIS);
}
};
}
public static Criterion not(final Criterion criterion) {
return new Criterion(Operator.not) {
@Override
protected void populate(StringBuilder sb) {
sb.append(NOT).append(SPACE);
criterion.populate(sb);
}
};
}
protected abstract void populate(StringBuilder sb);
@Override
public String toString() {
StringBuilder builder = new StringBuilder(LEFT_PARENTHESIS);
populate(builder);
builder.append(RIGHT_PARENTHESIS);
return builder.toString();
}
}

@ -1,67 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public abstract class DBObject<T extends DBObject<?>> implements Cloneable {
protected String alias;
protected final String expression;
protected DBObject(String expression){
this.expression = expression;
}
public T as(String newAlias) {
try {
T clone = (T) clone();
clone.alias = newAlias;
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public boolean hasAlias() {
return alias != null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DBObject<?> dbObject = (DBObject<?>) o;
if (alias != null ? !alias.equals(dbObject.alias) : dbObject.alias != null) return false;
if (expression != null ? !expression.equals(dbObject.expression) : dbObject.expression != null) return false;
return true;
}
@Override
public int hashCode() {
int result = alias != null ? alias.hashCode() : 0;
result = 31 * result + (expression != null ? expression.hashCode() : 0);
return result;
}
@Override
public final String toString() {
if (hasAlias()) {
return alias;
}
return expression;
}
public final String toStringInSelect() {
StringBuilder sb = new StringBuilder(expression);
if (hasAlias()) {
sb.append(SPACE).append(AS).append(SPACE).append(alias);
} else {
int pos = expression.indexOf('.');
if(pos > 0)
sb.append(SPACE).append(AS).append(SPACE).append(expression.substring(pos + 1));
}
return sb.toString();
}
}

@ -1,7 +0,0 @@
package com.todoroo.andlib.sql;
public class EqCriterion extends UnaryCriterion {
EqCriterion(Field field, Object value) {
super(field, Operator.eq, value);
}
}

@ -1,99 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.AND;
import static com.todoroo.andlib.sql.SqlConstants.BETWEEN;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Field extends DBObject<Field> {
protected Field(String expression) {
super(expression);
}
public static Field field(String expression) {
return new Field(expression);
}
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);
}
public Criterion gt(Object value) {
return UnaryCriterion.gt(this, value);
}
public Criterion lt(final Object value) {
return UnaryCriterion.lt(this, value);
}
public Criterion lte(final Object value) {
return UnaryCriterion.lte(this, value);
}
public Criterion isNull() {
return UnaryCriterion.isNull(this);
}
public Criterion isNotNull() {
return UnaryCriterion.isNotNull(this);
}
public Criterion between(final Object lower, final Object upper) {
final Field field = this;
return new Criterion(null) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(BETWEEN).append(SPACE).append(lower).append(SPACE).append(AND)
.append(SPACE).append(upper);
}
};
}
public Criterion like(final String value) {
return UnaryCriterion.like(this, value);
}
public Criterion like(String value, String escape) {
return UnaryCriterion.like(this, value, escape);
}
public <T> Criterion in(final T... value) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(SPACE);
for (T t : value) {
sb.append(t.toString()).append(COMMA);
}
sb.deleteCharAt(sb.length() - 1).append(RIGHT_PARENTHESIS);
}
};
}
public Criterion in(final Query query) {
final Field field = this;
return new Criterion(Operator.in) {
@Override
protected void populate(StringBuilder sb) {
sb.append(field).append(SPACE).append(Operator.in).append(SPACE).append(LEFT_PARENTHESIS).append(query)
.append(RIGHT_PARENTHESIS);
}
};
}
}

@ -1,28 +0,0 @@
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();
}
public static Field upper(Field title) {
return new Field("UPPER(" + title.toString() + ")");
}
/**
* @return SQL now (in milliseconds)
*/
public static Field now() {
return new Field("(strftime('%s','now')*1000)");
}
}

@ -1,14 +0,0 @@
package com.todoroo.andlib.sql;
import java.util.ArrayList;
import java.util.List;
public class GroupBy {
private List<Field> fields = new ArrayList<Field>();
public static GroupBy groupBy(Field field) {
GroupBy groupBy = new GroupBy();
groupBy.fields.add(field);
return groupBy;
}
}

@ -1,43 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.JOIN;
import static com.todoroo.andlib.sql.SqlConstants.ON;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Join {
private final SqlTable joinTable;
private final JoinType joinType;
private final Criterion[] criterions;
private Join(SqlTable table, JoinType joinType, Criterion... criterions) {
joinTable = table;
this.joinType = joinType;
this.criterions = criterions;
}
public static Join inner(SqlTable expression, Criterion... criterions) {
return new Join(expression, JoinType.INNER, criterions);
}
public static Join left(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.LEFT, criterions);
}
public static Join right(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.RIGHT, criterions);
}
public static Join out(SqlTable table, Criterion... criterions) {
return new Join(table, JoinType.OUT, criterions);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(joinType).append(SPACE).append(JOIN).append(SPACE).append(joinTable).append(SPACE).append(ON);
for (Criterion criterion : criterions) {
sb.append(SPACE).append(criterion);
}
return sb.toString();
}
}

@ -1,5 +0,0 @@
package com.todoroo.andlib.sql;
public enum JoinType {
INNER, LEFT, RIGHT, OUT
}

@ -1,57 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("nls")
public final class Operator {
private final String operator;
public static final Operator eq = new Operator("=");
public static final Operator neq = new Operator("<>");
public static final Operator isNull = new Operator("IS NULL");
public static final Operator isNotNull = new Operator("IS NOT NULL");
public static final Operator gt = new Operator(">");
public static final Operator lt = new Operator("<");
public static final Operator gte = new Operator(">=");
public static final Operator lte = new Operator("<=");
public static final Operator and = new Operator("AND");
public static final Operator or = new Operator("OR");
public static final Operator not = new Operator("NOT");
public static final Operator exists = new Operator("EXISTS");
public static final Operator like = new Operator("LIKE");
public static final Operator in = new Operator("IN");
private static final Map<Operator, Operator> contraryRegistry = new HashMap<Operator, Operator>();
static {
contraryRegistry.put(eq, neq);
contraryRegistry.put(neq, eq);
contraryRegistry.put(isNull, isNotNull);
contraryRegistry.put(isNotNull, isNull);
contraryRegistry.put(gt, lte);
contraryRegistry.put(lte, gt);
contraryRegistry.put(lt, gte);
contraryRegistry.put(gte, lt);
}
private Operator(String operator) {
this.operator = operator;
}
public Operator getContrary() {
if(!contraryRegistry.containsKey(this)){
Operator opposite = new Operator(not.toString() + SPACE + this.toString());
contraryRegistry.put(this, opposite);
contraryRegistry.put(opposite, this);
}
return contraryRegistry.get(this);
}
@Override
public String toString() {
return this.operator.toString();
}
}

@ -1,37 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class Order {
private final Object expression;
private final OrderType orderType;
private Order(Object expression) {
this(expression, OrderType.ASC);
}
private Order(Object expression, OrderType orderType) {
this.expression = expression;
this.orderType = orderType;
}
public static Order asc(Object expression) {
return new Order(expression);
}
public static Order desc(Object expression) {
return new Order(expression, OrderType.DESC);
}
@Override
public String toString() {
return expression + SPACE + orderType;
}
public Order reverse() {
if(orderType == OrderType.ASC)
return new Order(expression, OrderType.DESC);
else
return new Order(expression, OrderType.ASC);
}
}

@ -1,5 +0,0 @@
package com.todoroo.andlib.sql;
public enum OrderType {
DESC, ASC
}

@ -1,205 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.ALL;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.FROM;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LEFT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.RIGHT_PARENTHESIS;
import static com.todoroo.andlib.sql.SqlConstants.SELECT;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.WHERE;
import static com.todoroo.andlib.sql.SqlTable.table;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import com.todoroo.andlib.data.Property;
public final class Query {
private SqlTable table;
private String queryTemplate = null;
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Field> fields = new ArrayList<Field>();
private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
private int limits = -1;
private Query(Field... fields) {
this.fields.addAll(asList(fields));
}
public static Query select(Field... fields) {
return new Query(fields);
}
public Query from(SqlTable fromTable) {
this.table = fromTable;
return this;
}
public Query join(Join... join) {
joins.addAll(asList(join));
return this;
}
public Query where(Criterion criterion) {
criterions.add(criterion);
return this;
}
public Query groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy));
return this;
}
public Query orderBy(Order... order) {
orders.addAll(asList(order));
return this;
}
public Query limit(int limit) {
limits = limit;
return this;
}
public Query appendSelectFields(Property<?>... selectFields) {
this.fields.addAll(asList(selectFields));
return this;
}
@Override
public boolean equals(Object o) {
return this == o || !(o == null || getClass() != o.getClass()) && this.toString().equals(o.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
StringBuilder sql = new StringBuilder();
visitSelectClause(sql);
visitFromClause(sql);
visitJoinClause(sql);
if(queryTemplate == null) {
visitWhereClause(sql);
visitGroupByClause(sql);
visitOrderByClause(sql);
visitLimitClause(sql);
} else {
if(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();
}
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);
}
}
private void visitFromClause(StringBuilder sql) {
if (table == null) {
return;
}
sql.append(FROM).append(SPACE).append(table).append(SPACE);
}
private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE);
if (fields.isEmpty()) {
sql.append(ALL).append(SPACE);
return;
}
for (Field field : fields) {
sql.append(field.toStringInSelect()).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitLimitClause(StringBuilder sql) {
if(limits > -1)
sql.append(LIMIT).append(SPACE).append(limits).append(SPACE);
}
public SqlTable as(String alias) {
return table(LEFT_PARENTHESIS + this.toString() + RIGHT_PARENTHESIS).as(alias);
}
public Query having(Criterion criterion) {
this.havings.add(criterion);
return this;
}
/**
* Gets a list of fields returned by this query
* @return
*/
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;
}
}

@ -1,117 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
import static com.todoroo.andlib.sql.SqlConstants.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 <tim@todoroo.com>
*
*/
public final class QueryTemplate {
private final ArrayList<Criterion> criterions = new ArrayList<Criterion>();
private final ArrayList<Join> joins = new ArrayList<Join>();
private final ArrayList<Field> groupBies = new ArrayList<Field>();
private final ArrayList<Order> orders = new ArrayList<Order>();
private final ArrayList<Criterion> havings = new ArrayList<Criterion>();
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;
}
}

@ -1,25 +0,0 @@
package com.todoroo.andlib.sql;
@SuppressWarnings("nls")
public final class SqlConstants {
static final String SELECT = "SELECT";
static final String SPACE = " ";
static final String AS = "AS";
static final String COMMA = ",";
static final String FROM = "FROM";
static final String ON = "ON";
static final String JOIN = "JOIN";
static final String ALL = "*";
static final String LEFT_PARENTHESIS = "(";
static final String RIGHT_PARENTHESIS = ")";
static final String AND = "AND";
static final String BETWEEN = "BETWEEN";
static final String LIKE = "LIKE";
static final String OR = "OR";
static final String ORDER_BY = "ORDER BY";
static final String GROUP_BY = "GROUP BY";
static final String WHERE = "WHERE";
public static final String EXISTS = "EXISTS";
public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT";
}

@ -1,20 +0,0 @@
package com.todoroo.andlib.sql;
public class SqlTable extends DBObject<SqlTable> {
protected SqlTable(String expression) {
super(expression);
}
public static SqlTable table(String table) {
return new SqlTable(table);
}
@SuppressWarnings("nls")
protected String fieldExpression(String fieldName) {
if (hasAlias()) {
return alias + "." + fieldName;
}
return expression+"."+fieldName;
}
}

@ -1,111 +0,0 @@
package com.todoroo.andlib.sql;
import static com.todoroo.andlib.sql.SqlConstants.SPACE;
public class UnaryCriterion extends Criterion {
protected final Field expression;
protected final Object value;
UnaryCriterion(Field expression, Operator operator, Object value) {
super(operator);
this.expression = expression;
this.value = value;
}
@Override
protected void populate(StringBuilder sb) {
beforePopulateOperator(sb);
populateOperator(sb);
afterPopulateOperator(sb);
}
public static Criterion eq(Field expression, Object value) {
return new UnaryCriterion(expression, Operator.eq, value);
}
protected void beforePopulateOperator(StringBuilder sb) {
sb.append(expression);
}
protected void populateOperator(StringBuilder sb) {
sb.append(operator);
}
@SuppressWarnings("nls")
protected void afterPopulateOperator(StringBuilder sb) {
if(value == null)
return;
else if(value instanceof String)
sb.append("'").append(sanitize((String) value)).append("'");
else
sb.append(value);
}
/**
* Sanitize the given input for SQL
* @param input
* @return
*/
@SuppressWarnings("nls")
public static String sanitize(String input) {
return input.replace("'", "''");
}
public static Criterion neq(Field field, Object value) {
return new UnaryCriterion(field, Operator.neq, value);
}
public static Criterion gt(Field field, Object value) {
return new UnaryCriterion(field, Operator.gt, value);
}
public static Criterion lt(Field field, Object value) {
return new UnaryCriterion(field, Operator.lt, value);
}
public static Criterion lte(Field field, Object value) {
return new UnaryCriterion(field, Operator.lte, value);
}
public static Criterion isNull(Field field) {
return new UnaryCriterion(field, Operator.isNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
}
public static Criterion isNotNull(Field field) {
return new UnaryCriterion(field, Operator.isNotNull, null) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator);
}
};
}
public static Criterion like(Field field, String value) {
return new UnaryCriterion(field, Operator.like, value) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE);
}
};
}
public static Criterion like(Field field, String value, final String escape) {
return new UnaryCriterion(field, Operator.like, value) {
@Override
protected void populateOperator(StringBuilder sb) {
sb.append(SPACE).append(operator).append(SPACE);
}
@SuppressWarnings("nls")
@Override
protected void afterPopulateOperator(StringBuilder sb) {
super.afterPopulateOperator(sb);
sb.append(SPACE).append("ESCAPE").append(" '").append(sanitize(escape)).append("'");
}
};
}
}

@ -1,385 +0,0 @@
package com.todoroo.andlib.utility;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map.Entry;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.text.InputType;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.TextView;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService;
/**
* Android Utility Classes
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class AndroidUtilities {
public static final String SEPARATOR_ESCAPE = "!PIPE!"; //$NON-NLS-1$
public static final String SERIALIZATION_SEPARATOR = "|"; //$NON-NLS-1$
// --- utility methods
private static class ExceptionHelper {
@Autowired
public ExceptionService exceptionService;
public ExceptionHelper() {
DependencyInjectionService.getInstance().inject(this);
}
}
/** Suppress virtual keyboard until user's first tap */
public static void suppressVirtualKeyboard(final TextView editor) {
final int inputType = editor.getInputType();
editor.setInputType(InputType.TYPE_NULL);
editor.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
editor.setInputType(inputType);
editor.setOnTouchListener(null);
return false;
}
});
}
/**
* @return true if we're connected to the internet
*/
public static boolean isConnected(Context context) {
ConnectivityManager manager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
if (info == null)
return false;
if (info.getState() != State.CONNECTED)
return false;
return true;
}
/** Fetch the image specified by the given url */
public static Bitmap fetchImage(URL url) throws IOException {
InputStream is = null;
try {
URLConnection conn = url.openConnection();
conn.connect();
is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 16384);
try {
Bitmap bitmap = BitmapFactory.decodeStream(bis);
return bitmap;
} finally {
bis.close();
}
} finally {
if(is != null)
is.close();
}
}
/**
* Start the given intent, handling security exceptions if they arise
*
* @param context
* @param intent
* @param request request code. if negative, no request.
*/
public static void startExternalIntent(Context context, Intent intent, int request) {
try {
if(request > -1 && context instanceof Activity)
((Activity)context).startActivityForResult(intent, request);
else
context.startActivity(intent);
} catch (Exception e) {
ExceptionHelper helper = new ExceptionHelper();
helper.exceptionService.displayAndReportError(context,
"start-external-intent-" + intent.toString(), //$NON-NLS-1$
e);
}
}
/**
* Start the given intent, handling security exceptions if they arise
*
* @param activity
* @param intent
* @param requestCode
*/
public static void startExternalIntentForResult(
Activity activity, Intent intent, int requestCode) {
try {
activity.startActivityForResult(intent, requestCode);
} catch (SecurityException e) {
ExceptionHelper helper = new ExceptionHelper();
helper.exceptionService.displayAndReportError(activity,
"start-external-intent-" + intent.toString(), //$NON-NLS-1$
e);
}
}
/**
* 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());
}
/**
* Rips apart a content value into two string arrays, keys and value
*/
public static String[][] contentValuesToStringArrays(ContentValues source) {
String[][] result = new String[2][source.size()];
int i = 0;
for(Entry<String, Object> entry : source.valueSet()) {
result[0][i] = entry.getKey();
result[1][i++] = entry.getValue().toString();
}
return result;
}
/**
* Return index of value in array
* @param array array to search
* @param value value to look for
* @return
*/
public static <TYPE> int indexOf(TYPE[] array, TYPE value) {
for(int i = 0; i < array.length; i++)
if(array[i].equals(value))
return i;
return -1;
}
/**
* Serializes a content value into a string
*/
public static String contentValuesToSerializedString(ContentValues source) {
StringBuilder result = new StringBuilder();
for(Entry<String, Object> entry : source.valueSet()) {
result.append(entry.getKey().replace(SERIALIZATION_SEPARATOR, SEPARATOR_ESCAPE)).append(
SERIALIZATION_SEPARATOR);
Object value = entry.getValue();
if(value instanceof Integer)
result.append('i').append(value);
else if(value instanceof Double)
result.append('d').append(value);
else if(value instanceof Long)
result.append('l').append(value);
else if(value instanceof String)
result.append('s').append(value.toString());
else
throw new UnsupportedOperationException(value.getClass().toString());
result.append(SERIALIZATION_SEPARATOR);
}
return result.toString();
}
/**
* Turn ContentValues into a string
* @param string
* @return
*/
public static ContentValues contentValuesFromSerializedString(String string) {
if(string == null)
return new ContentValues();
String[] pairs = string.split("\\" + SERIALIZATION_SEPARATOR); //$NON-NLS-1$
ContentValues result = new ContentValues();
for(int i = 0; i < pairs.length; i += 2) {
String key = pairs[i].replaceAll(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR);
String value = pairs[i+1].substring(1);
switch(pairs[i+1].charAt(0)) {
case 'i':
result.put(key, Integer.parseInt(value));
break;
case 'd':
result.put(key, Double.parseDouble(value));
break;
case 'l':
result.put(key, Long.parseLong(value));
break;
case 's':
result.put(key, value.replace(SEPARATOR_ESCAPE, SERIALIZATION_SEPARATOR));
break;
}
}
return result;
}
/**
* Turn ContentValues into a string
* @param string
* @return
*/
@SuppressWarnings("nls")
public static ContentValues contentValuesFromString(String string) {
if(string == null)
return null;
String[] pairs = string.split("=");
ContentValues result = new ContentValues();
String key = null;
for(int i = 0; i < pairs.length; i++) {
String newKey = null;
int lastSpace = pairs[i].lastIndexOf(' ');
if(lastSpace != -1) {
newKey = pairs[i].substring(lastSpace + 1);
pairs[i] = pairs[i].substring(0, lastSpace);
} else {
newKey = pairs[i];
}
if(key != null)
result.put(key.trim(), pairs[i].trim());
key = newKey;
}
return result;
}
/**
* Returns true if a and b or null or a.equals(b)
* @param a
* @param b
* @return
*/
public static boolean equals(Object a, Object b) {
if(a == null && b == null)
return true;
if(a == null)
return false;
return a.equals(b);
}
/**
* Copy a file from one place to another
*
* @param in
* @param out
* @throws Exception
*/
public static void copyFile(File in, File out) throws Exception {
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
try {
byte[] buf = new byte[1024];
int i = 0;
while ((i = fis.read(buf)) != -1) {
fos.write(buf, 0, i);
}
} catch (Exception e) {
throw e;
} finally {
fis.close();
fos.close();
}
}
/**
* Find a child view of a certain type
* @param view
* @param type
* @return first view (by DFS) if found, or null if none
*/
public static <TYPE> TYPE findViewByType(View view, Class<TYPE> type) {
if(view == null)
return null;
if(type.isInstance(view))
return (TYPE) view;
if(view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for(int i = 0; i < group.getChildCount(); i++) {
TYPE v = findViewByType(group.getChildAt(i), type);
if(v != null)
return v;
}
}
return null;
}
/**
* @return Android SDK version as an integer. Works on all versions
*/
public static int getSdkVersion() {
return Integer.parseInt(android.os.Build.VERSION.SDK);
}
/**
* Copy databases to a given folder. Useful for debugging
* @param folder
*/
public static void copyDatabases(Context context, String folder) {
File folderFile = new File(folder);
if(!folderFile.exists())
folderFile.mkdir();
for(String db : context.databaseList()) {
File dbFile = context.getDatabasePath(db);
try {
copyFile(dbFile, new File(folderFile.getAbsolutePath() +
File.separator + db));
} catch (Exception e) {
Log.e("ERROR", "ERROR COPYING DB " + db, e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
/**
* Sort files by date so the newest file is on top
* @param files
*/
public static void sortFilesByDateDesc(File[] files) {
Arrays.sort(files, new Comparator<File>() {
public int compare(File o1, File o2) {
return Long.valueOf(o2.lastModified()).compareTo(Long.valueOf(o1.lastModified()));
}
});
}
/**
* Sleep, ignoring interruption
* @param l
*/
public static void sleepDeep(long l) {
try {
Thread.sleep(l);
} catch (InterruptedException e) {
// ignore
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,335 +0,0 @@
/*
* Copyright (c) 2009, Todoroo Inc
* All Rights Reserved
* http://www.todoroo.com
*/
package com.todoroo.andlib.utility;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import android.content.Context;
import android.content.res.Resources;
import android.text.format.DateUtils;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
public class DateUtilities {
@Autowired
public Integer yearsResource;
@Autowired
public Integer monthsResource;
@Autowired
public Integer weeksResource;
@Autowired
public Integer daysResource;
@Autowired
public Integer hoursResource;
@Autowired
public Integer minutesResource;
@Autowired
public Integer secondsResource;
@Autowired
public Integer daysAbbrevResource;
@Autowired
public Integer hoursAbbrevResource;
@Autowired
public Integer minutesAbbrevResource;
@Autowired
public Integer secondsAbbrevResource;
public DateUtilities() {
DependencyInjectionService.getInstance().inject(this);
}
/* ======================================================================
* ============================================================ long time
* ====================================================================== */
/** Convert unixtime into date */
public static final Date unixtimeToDate(long millis) {
if(millis == 0)
return null;
return new Date(millis);
}
/** Convert date into unixtime */
public static final long dateToUnixtime(Date date) {
if(date == null)
return 0;
return date.getTime();
}
/** Returns unixtime for current time */
public static final long now() {
return System.currentTimeMillis();
}
/** Returns unixtime one month from now */
public static final long oneMonthFromNow() {
Date date = new Date();
date.setMonth(date.getMonth() + 1);
return date.getTime();
}
/** 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;
}
/**
* @param context android context
* @param date time to format
* @return time, with hours and minutes
*/
@SuppressWarnings("nls")
public static String getTimeString(Context context, Date date) {
String value;
if (is24HourFormat(context)) {
value = "H:mm";
} else {
value = "h:mm a";
}
return new SimpleDateFormat(value).format(date);
}
/**
* @param context android context
* @param date date to format
* @return date, with month, day, and year
*/
@SuppressWarnings("nls")
public static String getDateString(Context context, Date date) {
String month = "'" + DateUtils.getMonthString(date.getMonth() +
Calendar.JANUARY, DateUtils.LENGTH_MEDIUM) + "'";
String value;
// united states, you are special
if (Locale.US.equals(Locale.getDefault())
|| Locale.CANADA.equals(Locale.getDefault()))
value = month + " d yyyy";
else
value = "d " + month + " yyyy";
return new SimpleDateFormat(value).format(date);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static String getDateStringWithWeekday(Context context, Date date) {
String weekday = DateUtils.getDayOfWeekString(date.getDay() + Calendar.SUNDAY,
DateUtils.LENGTH_LONG);
return weekday + ", " + getDateString(context, date);
}
/**
* @return date format as getDateFormat with weekday
*/
@SuppressWarnings("nls")
public static String getDateStringWithTimeAndWeekday(Context context, Date date) {
return getDateStringWithWeekday(context, date) + " " + getTimeString(context, date);
}
/**
* @return date with time at the end
*/
@SuppressWarnings("nls")
public static String getDateStringWithTime(Context context, Date date) {
return getDateString(context, date) + " " + getTimeString(context, 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(long duration, int unitsToShow) {
return getDurationString(duration, unitsToShow, false);
}
/**
* Format a time into the format: 5 days, 3 hours, 2 minutes
*
* @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(long duration, int unitsToShow, boolean withPreposition) {
Resources r = ContextManager.getContext().getResources();
short unitsDisplayed = 0;
duration = Math.abs(duration);
if(duration == 0)
return r.getQuantityString(secondsResource, 0, 0);
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;
}
}
StringBuilder result = new StringBuilder();
unitsDisplayed = displayUnits(r, yearsResource, unitsToShow, values[0],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, monthsResource, unitsToShow, values[1],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, weeksResource, unitsToShow, values[2],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, daysResource, unitsToShow, values[3],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, hoursResource, unitsToShow, values[4],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, minutesResource, unitsToShow, values[5],
unitsDisplayed, result);
unitsDisplayed = displayUnits(r, secondsResource, unitsToShow, values[6],
unitsDisplayed, result);
return result.toString().trim();
}
/** Display units, rounding up if necessary. Returns units to show */
private short displayUnits(Resources r, int resource, int unitsToShow, int value,
short unitsDisplayed, StringBuilder result) {
if(unitsDisplayed < unitsToShow && value > 0) {
result.append(r.getQuantityString(resource, value, value)).
append(' ');
unitsDisplayed++;
}
return unitsDisplayed;
}
/**
* Format a time into the format: 5 days, 3 hrs, 2 min
*
* @param r Resources to get strings from
* @param timeInSeconds
* @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)
* @return
*/
public String getAbbreviatedDurationString(Resources r, int timeInSeconds,
int unitsToShow) {
short days, hours, minutes, seconds;
short unitsDisplayed = 0;
timeInSeconds = Math.abs(timeInSeconds);
if(timeInSeconds == 0)
return r.getQuantityString(secondsAbbrevResource, 0, 0);
days = (short)(timeInSeconds / 24 / 3600);
timeInSeconds -= days*24*3600;
hours = (short)(timeInSeconds / 3600);
timeInSeconds -= hours * 3600;
minutes = (short)(timeInSeconds / 60);
timeInSeconds -= minutes * 60;
seconds = (short)timeInSeconds;
StringBuilder result = new StringBuilder();
if(days > 0) {
// round up if needed
if(unitsDisplayed == unitsToShow && hours >= 12)
days++;
result.append(r.getQuantityString(daysAbbrevResource, days, days)).
append(' ');
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && hours > 0) {
// round up if needed
if(unitsDisplayed == unitsToShow && minutes >= 30)
days++;
result.append(r.getQuantityString(hoursAbbrevResource, hours,
hours)).
append(' ');
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && minutes > 0) {
// round up if needed
if(unitsDisplayed == unitsToShow && seconds >= 30)
days++;
result.append(r.getQuantityString(minutesAbbrevResource, minutes,
minutes)).append(' ');
unitsDisplayed++;
}
if(unitsDisplayed < unitsToShow && seconds > 0) {
result.append(r.getQuantityString(secondsAbbrevResource, seconds,
seconds)).append(' ');
}
return result.toString().trim();
}
}

@ -1,188 +0,0 @@
package com.todoroo.andlib.utility;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
public class DialogUtilities {
@Autowired
public Integer informationDialogTitleResource;
@Autowired
public Integer confirmDialogTitleResource;
public DialogUtilities() {
DependencyInjectionService.getInstance().inject(this);
}
/**
* Displays a dialog box with a EditText and an ok / cancel
*
* @param activity
* @param text
* @param okListener
*/
public void viewDialog(final Activity activity, final String text,
final View view, final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(confirmDialogTitleResource)
.setMessage(text)
.setView(view)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with an OK button
*
* @param activity
* @param text
* @param okListener
*/
public void okDialog(final Activity activity, final String text,
final DialogInterface.OnClickListener okListener) {
if(activity.isFinishing())
return;
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(informationDialogTitleResource)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with an OK button
*
* @param activity
* @param text
* @param okListener
*/
public void okDialog(final Activity activity, final int icon, final CharSequence text,
final DialogInterface.OnClickListener okListener) {
if(activity.isFinishing())
return;
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(informationDialogTitleResource)
.setMessage(text)
.setIcon(icon)
.setPositiveButton(android.R.string.ok, okListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with OK and Cancel buttons and custom title
*
* @param activity
* @param title
* @param text
* @param okListener
* @param cancelListener
*/
public void okCancelDialog(final Activity activity, final String title,
final String text, final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a dialog box with OK and Cancel buttons
*
* @param activity
* @param text
* @param okListener
* @param cancelListener
*/
public void okCancelDialog(final Activity activity, final String text,
final DialogInterface.OnClickListener okListener,
final DialogInterface.OnClickListener cancelListener) {
if(activity.isFinishing())
return;
activity.runOnUiThread(new Runnable() {
public void run() {
new AlertDialog.Builder(activity)
.setTitle(confirmDialogTitleResource)
.setMessage(text)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, okListener)
.setNegativeButton(android.R.string.cancel, cancelListener)
.show().setOwnerActivity(activity);
}
});
}
/**
* Displays a progress dialog. Must be run on the UI thread
* @param context
* @param text
* @return
*/
public ProgressDialog progressDialog(Context context, String text) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setIndeterminate(true);
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(text);
dialog.show();
dialog.setOwnerActivity((Activity)context);
return dialog;
}
/**
* Dismiss a dialog off the UI thread
*
* @param activity
* @param dialog
*/
public void dismissDialog(Activity activity, final ProgressDialog dialog) {
activity.runOnUiThread(new Runnable() {
public void run() {
try {
dialog.dismiss();
} catch (Exception e) {
// could have killed activity
}
}
});
}
}

@ -1,66 +0,0 @@
package com.todoroo.andlib.utility;
import java.util.regex.Pattern;
/**
* E-mail Validator Copyright 2008 Les Hazlewood
* Licensed under the Apache License, Version 2.0 (the "License");
*/
@SuppressWarnings("nls")
public final class EmailValidator {
// RFC 2822 2.2.2 Structured Header Field Bodies
private static final String wsp = "[ \\t]"; // space or tab
private static final String fwsp = wsp + "*";
// RFC 2822 3.2.1 Primitive tokens
private static final String dquote = "\\\"";
// ASCII Control characters excluding white space:
private static final String noWsCtl = "\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F";
// all ASCII characters except CR and LF:
private static final String asciiText = "[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F]";
// RFC 2822 3.2.2 Quoted characters:
// single backslash followed by a text char
private static final String quotedPair = "(\\\\" + asciiText + ")";
// RFC 2822 3.2.4 Atom:
private static final String atext = "[a-zA-Z0-9\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~]";
private static final String dotAtomText = atext + "+" + "(" + "\\." + atext
+ "+)*";
private static final String dotAtom = fwsp + "(" + dotAtomText + ")" + fwsp;
// RFC 2822 3.2.5 Quoted strings:
// noWsCtl and the rest of ASCII except the doublequote and backslash
// characters:
private static final String qtext = "[" + noWsCtl
+ "\\x21\\x23-\\x5B\\x5D-\\x7E]";
private static final String qcontent = "(" + qtext + "|" + quotedPair + ")";
private static final String quotedString = dquote + "(" + fwsp + qcontent
+ ")*" + fwsp + dquote;
// RFC 1035 tokens for domain names:
private static final String letter = "[a-zA-Z]";
private static final String letDig = "[a-zA-Z0-9]";
private static final String letDigHyp = "[a-zA-Z0-9-]";
private static final String rfcLabel = letDig + "(" + letDigHyp + "{0,61}"
+ letDig + ")?";
private static final String rfc1035DomainName = rfcLabel + "(\\."
+ rfcLabel + ")*\\." + letter + "{2,6}";
private static final String domain = rfc1035DomainName;
private static final String localPart = "((" + dotAtom + ")|("
+ quotedString + "))";
private static final String addrSpec = localPart + "@" + domain;
// now compile a pattern for efficient re-use:
// if we're allowing quoted identifiers or not:
private static final String patternString = addrSpec;
public static final Pattern VALID_PATTERN = Pattern.compile(patternString);
public static boolean validateEmail(String value) {
return VALID_PATTERN.matcher(value).matches();
}
}

@ -1,56 +0,0 @@
package com.todoroo.andlib.utility;
/**
* Pair utility class
*
* @author Tim Su <tim@todoroo.com>
*
* @param <L>
* @param <R>
*/
public class Pair<L, R> {
private final L left;
private final R right;
public R getRight() {
return right;
}
public L getLeft() {
return left;
}
public Pair(final L left, final R right) {
this.left = left;
this.right = right;
}
public static <A, B> Pair<A, B> create(A left, B right) {
return new Pair<A, B>(left, right);
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Pair<?, ?>))
return false;
final Pair<?, ?> other = (Pair<?, ?>) o;
return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
}
public static final boolean equal(Object o1, Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
}
@Override
public int hashCode() {
int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
int hRight = getRight() == null ? 0 : getRight().hashCode();
return hLeft + (57 * hRight);
}
}

@ -1,119 +0,0 @@
package com.todoroo.andlib.utility;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* SoftHashMap from javaspecialists.eu Issue 98
* <http://www.javaspecialists.eu/archive/Issue098.html>
*
* @param <K>
* @param <V>
*/
public class SoftHashMap<K, V> extends AbstractMap<K, V> {
private static final long serialVersionUID = -3796460667941300642L;
/** The internal HashMap that will hold the SoftReference. */
private final Map<K, SoftReference<V>> hash = new HashMap<K, SoftReference<V>>();
private final Map<SoftReference<V>, K> reverseLookup = new HashMap<SoftReference<V>, K>();
/** Reference queue for cleared SoftReference objects. */
protected final ReferenceQueue<V> queue = new ReferenceQueue<V>();
@Override
public V get(Object key) {
expungeStaleEntries();
V result = null;
// We get the SoftReference represented by that key
SoftReference<V> soft_ref = hash.get(key);
if (soft_ref != null) {
// From the SoftReference we get the value, which can be
// null if it has been garbage collected
result = soft_ref.get();
if (result == null) {
// If the value has been garbage collected, remove the
// entry from the HashMap.
hash.remove(key);
reverseLookup.remove(soft_ref);
}
}
return result;
}
private void expungeStaleEntries() {
Reference<? extends V> sv;
while ((sv = queue.poll()) != null) {
hash.remove(reverseLookup.remove(sv));
}
}
@Override
public V put(K key, V value) {
expungeStaleEntries();
SoftReference<V> soft_ref = new SoftReference<V>(value, queue);
reverseLookup.put(soft_ref, key);
SoftReference<V> result = hash.put(key, soft_ref);
if (result == null)
return null;
reverseLookup.remove(result);
return result.get();
}
@Override
public V remove(Object key) {
expungeStaleEntries();
SoftReference<V> result = hash.remove(key);
if (result == null)
return null;
return result.get();
}
@Override
public void clear() {
hash.clear();
reverseLookup.clear();
}
@Override
public int size() {
expungeStaleEntries();
return hash.size();
}
/**
* Returns a copy of the key/values in the map at the point of calling.
* However, setValue still sets the value in the actual SoftHashMap.
*/
@Override
public Set<Entry<K, V>> entrySet() {
expungeStaleEntries();
Set<Entry<K, V>> result = new LinkedHashSet<Entry<K, V>>();
for (final Entry<K, SoftReference<V>> entry : hash.entrySet()) {
final V value = entry.getValue().get();
if (value != null) {
result.add(new Entry<K, V>() {
public K getKey() {
return entry.getKey();
}
public V getValue() {
return value;
}
public V setValue(V v) {
entry.setValue(new SoftReference<V>(v, queue));
return value;
}
});
}
}
return result;
}
}

@ -1,450 +0,0 @@
package com.todoroo.andlib.utility;
/*
* Copyright (C) 2008 Google Inc.
*
* 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.
*/
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
/**
* <p>UserTask enables proper and easy use of the UI thread. This class allows to
* perform background operations and publish results on the UI thread without
* having to manipulate threads and/or handlers.</p>
*
* <p>A user task is defined by a computation that runs on a background thread and
* whose result is published on the UI thread. A user task is defined by 3 generic
* types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
* and 4 steps, called <code>begin</code>, <code>doInBackground</code>,
* <code>processProgress<code> and <code>end</code>.</p>
*
* <h2>Usage</h2>
* <p>UserTask must be subclassed to be used. The subclass will override at least
* one method ({@link #doInBackground(Object[])}), and most often will override a
* second one ({@link #end(Object)}.)</p>
*
* <p>Here is an example of subclassing:</p>
* <pre>
* private class DownloadFilesTask extends UserTask&lt;URL, Integer, Long&gt; {
* public File doInBackground(URL... urls) {
* int count = urls.length;
* long totalSize = 0;
* for (int i = 0; i < count; i++) {
* totalSize += Downloader.downloadFile(urls[i]);
* publishProgress((int) ((i / (float) count) * 100));
* }
* }
*
* public void processProgress(Integer... progress) {
* setProgressPercent(progress[0]);
* }
*
* public void end(Long result) {
* showDialog("Downloaded " + result + " bytes");
* }
* }
* </pre>
*
* <p>Once created, a task is executed very simply:</p>
* <pre>
* new DownloadFilesTask().execute(new URL[] { ... });
* </pre>
*
* <h2>User task's generic types</h2>
* <p>The three types used by a user task are the following:</p>
* <ol>
* <li><code>Params</code>, the type of the parameters sent to the task upon
* execution.</li>
* <li><code>Progress</code>, the type of the progress units published during
* the background computation.</li>
* <li><code>Result</code>, the type of the result of the background
* computation.</li>
* </ol>
* <p>Not all types are always used by a user task. To mark a type as unused,
* simply use the type {@link Void}:</p>
* <pre>
* private class MyTask extends UserTask<Void, Void, Void) { ... }
* </pre>
*
* <h2>The 4 steps</h2>
* <p>When a user task is executed, the task goes through 4 steps:</p>
* <ol>
* <li>{@link #begin()}, invoked on the UI thread immediately after the task
* is executed. This step is normally used to setup the task, for instance by
* showing a progress bar in the user interface.</li>
* <li>{@link #doInBackground(Object[])}, invoked on the background thread
* immediately after {@link #begin()} finishes executing. This step is used
* to perform background computation that can take a long time. The parameters
* of the user task are passed to this step. The result of the computation must
* be returned by this step and will be passed back to the last step. This step
* can also use {@link #publishProgress(Object[])} to publish one or more units
* of progress. These values are published on the UI thread, in the
* {@link #processProgress(Object[])} step.</li>
* <li>{@link #processProgress(Object[])}, invoked on the UI thread after a
* call to {@link #publishProgress(Object[])}. The timing of the execution is
* undefined. This method is used to display any form of progress in the user
* interface while the background computation is still executing. For instance,
* it can be used to animate a progress bar or show logs in a text field.</li>
* <li>{@link #end(Object)}, invoked on the UI thread after the background
* computation finishes. The result of the background computation is passed to
* this step as a parameter.</li>
* </ol>
*
* <h2>Threading rules</h2>
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
* <li>The task instance must be created on the UI thread.</li>
* <li>{@link #execute(Object[])} must be invoked on the UI thread.</li>
* <li>Do not call {@link #begin()}, {@link #end(Object)},
* {@link #doInBackground(Object[])}, {@link #processProgress(Object[])}
* manually.</li>
* <li>The task can be executed only once (an exception will be thrown if
* a second execution is attempted.)</li>
* </ul>
*/
@SuppressWarnings("nls")
public abstract class UserTask<Params, Progress, Result> {
private static final String LOG_TAG = "UserTask";
private static final int CORE_POOL_SIZE = 4;
private static final int MAXIMUM_POOL_SIZE = 10;
private static final int KEEP_ALIVE = 10;
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
final Thread thread = new Thread(r, "UserTask #" + mCount.getAndIncrement());
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return thread;
}
};
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
protected static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private volatile Status mStatus = Status.PENDING;
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link UserTask#end(Object)} has finished.
*/
FINISHED,
}
/**
* Creates a new user task. This constructor must be invoked on the UI thread.
*/
public UserTask() {
synchronized(UserTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
}
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Result result = null;
try {
result = mFuture.get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
final Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new UserTaskResult<Result>(UserTask.this, result));
message.sendToTarget();
}
};
}
/**
* Returns the current status of this task.
*
* @return The current status.
*/
public final Status getStatus() {
return mStatus;
}
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {@link #execute(Object[])}
* by the caller of this task.
*
* This method can call {@link #publishProgress(Object[])} to publish updates
* on the UI thread.
*
* @params params The parameters of the task.
*
* @return A result, defined by the subclass of this task.
*
* @see #begin()
* @see #end(Object)
* @see #publishProgress(Object[])
*/
public Result doInBackground(@SuppressWarnings("unused") Params... params) {
return null;
}
/**
* Runs on the UI thread before {@link #doInBackground(Object[])}.
*
* @see #end(Object)
* @see #doInBackground(Object[])
*/
public void begin() {
// ...
}
/**
* Runs on the UI thread after {@link #doInBackground(Object[])}. The
* specified result is the value returned by {@link #doInBackground(Object[])}
* or null if the task was cancelled or an exception occured.
*
* @see #begin()
* @see #doInBackground(Object[])
*/
public void end(@SuppressWarnings("unused") Result result) {
// ...
}
/**
* Runs on the UI thread after {@link #publishProgress(Object[])} is invoked.
* The specified values are the values passed to {@link #publishProgress(Object[])}.
*
* @see #publishProgress(Object[])
* @see #doInBackground(Object[])
*/
public void processProgress(@SuppressWarnings("unused") Progress... values) {
// ...
}
/**
* Returns <tt>true</tt> if this task was cancelled before it completed
* normally.
*
* @return <tt>true</tt> if task was cancelled before it completed
*
* @see #cancel(boolean)
*/
public final boolean isCancelled() {
return mFuture.isCancelled();
}
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when <tt>cancel</tt> is called,
* this task should never run. If the task has already started,
* then the <tt>mayInterruptIfRunning</tt> parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete.
*
* @return <tt>false</tt> if the task could not be cancelled,
* typically because it has already completed normally;
* <tt>true</tt> otherwise
*
* @see #isCancelled()
*/
public final boolean cancel(boolean mayInterruptIfRunning) {
return mFuture.cancel(mayInterruptIfRunning);
}
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return The computed result.
*
* @throws CancellationException If the computation was cancelled.
* @throws ExecutionException If the computation threw an exception.
* @throws InterruptedException If the current thread was interrupted
* while waiting.
*/
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result.
*
* @return The computed result.
*
* @throws CancellationException If the computation was cancelled.
* @throws ExecutionException If the computation threw an exception.
* @throws InterruptedException If the current thread was interrupted
* while waiting.
* @throws TimeoutException If the wait timed out.
*/
public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return mFuture.get(timeout, unit);
}
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* This method must be invoked on the UI thread.
*
* @params params The parameters of the task.
*
* @return This instance of UserTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link com.google.android.photostream.UserTask.Status#RUNNING} or
* {@link com.google.android.photostream.UserTask.Status#FINISHED}.
*/
public final UserTask<Params, Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
begin();
mWorker.mParams = params;
try {
sExecutor.execute(mFuture);
} catch (RejectedExecutionException e) {
// cannot schedule because of some other error. just die quietly
}
return this;
}
/**
* This method can be invoked from {@link #doInBackground(Object[])} to
* publish updates on the UI thread while the background computation is
* still running. Each call to this method will trigger the execution of
* {@link #processProgress(Object[])} on the UI thread.
*
* @params values The progress values to update the UI with.
*
* @see #processProgress(Object[])
* @see #doInBackground(Object[])
*/
protected final void publishProgress(Progress... values) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new UserTaskResult<Progress>(this, values)).sendToTarget();
}
protected void finish(Result result) {
end(result);
mStatus = Status.FINISHED;
}
protected static class InternalHandler extends Handler {
@SuppressWarnings("unchecked")
@Override
public void handleMessage(Message msg) {
@SuppressWarnings("rawtypes")
UserTaskResult result = (UserTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.processProgress(result.mData);
break;
}
}
}
protected static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
protected static class UserTaskResult<Data> {
final UserTask<?, ?, ?> mTask;
final Data[] mData;
UserTaskResult(UserTask<?, ?, ?> task, Data... data) {
mTask = task;
mData = data;
}
}
}

@ -9,6 +9,7 @@
# Indicates whether an apk should be generated for each density. # Indicates whether an apk should be generated for each density.
split.density=false split.density=false
android.library.reference.1=../../astridApi/
# Project target. # Project target.
target=android-8 target=android-8
apk-configurations= apk-configurations=

@ -2,7 +2,7 @@ package com.todoroo.astrid.alarms;
import com.todoroo.andlib.data.Property.IntegerProperty; import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
/** /**
* Metadata entry for a task alarm * Metadata entry for a task alarm

@ -15,8 +15,8 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.widget.DateControlSet; import com.todoroo.andlib.widget.DateControlSet;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
/** /**
* Control set to manage adding and removing tags * Control set to manage adding and removing tags

@ -12,7 +12,7 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
/** /**
* Exposes Task Detail for tags, i.e. "Tags: frogs, animals" * Exposes Task Detail for tags, i.e. "Tags: frogs, animals"

@ -19,8 +19,8 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.reminders.Notifications; import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.reminders.ReminderService; import com.todoroo.astrid.reminders.ReminderService;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;

@ -9,7 +9,7 @@ import android.content.Intent;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
public class AlarmTaskRepeatListener extends BroadcastReceiver { public class AlarmTaskRepeatListener extends BroadcastReceiver {

@ -11,7 +11,6 @@ import android.view.View;
import android.view.ViewGroup.OnHierarchyChangeListener; import android.view.ViewGroup.OnHierarchyChangeListener;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.widget.TodorooPreferences; import com.todoroo.andlib.widget.TodorooPreferences;
@ -32,9 +31,6 @@ public class BackupPreferences extends TodorooPreferences {
static final String PREF_BACKUP_LAST_ERROR = "backupError"; //$NON-NLS-1$ static final String PREF_BACKUP_LAST_ERROR = "backupError"; //$NON-NLS-1$
@Autowired
private DialogUtilities dialogUtilities;
private int statusColor = Color.BLACK; private int statusColor = Color.BLACK;
@Override @Override
@ -66,7 +62,7 @@ public class BackupPreferences extends TodorooPreferences {
restorePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { restorePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
dialogUtilities.okCancelDialog(BackupPreferences.this, DialogUtilities.okCancelDialog(BackupPreferences.this,
getString(R.string.DLG_information_title), getString(R.string.DLG_information_title),
getString(R.string.backup_BPr_how_to_restore_dialog), getString(R.string.backup_BPr_how_to_restore_dialog),
AddOnActivity.createAddOnClicker(BackupPreferences.this, false), null); AddOnActivity.createAddOnClicker(BackupPreferences.this, false), null);
@ -115,7 +111,7 @@ public class BackupPreferences extends TodorooPreferences {
statusColor = Color.rgb(100, 0, 0); statusColor = Color.rgb(100, 0, 0);
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() { preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) { public boolean onPreferenceClick(Preference p) {
dialogUtilities.okDialog(BackupPreferences.this, error, null); DialogUtilities.okDialog(BackupPreferences.this, error, null);
return true; return true;
} }
}); });

@ -25,8 +25,8 @@ import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;

@ -32,12 +32,12 @@ import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.legacy.LegacyImportance; import com.todoroo.astrid.legacy.LegacyImportance;
import com.todoroo.astrid.legacy.LegacyRepeatInfo; import com.todoroo.astrid.legacy.LegacyRepeatInfo;
import com.todoroo.astrid.legacy.LegacyRepeatInfo.LegacyRepeatInterval; import com.todoroo.astrid.legacy.LegacyRepeatInfo.LegacyRepeatInterval;
import com.todoroo.astrid.legacy.LegacyTaskModel; import com.todoroo.astrid.legacy.LegacyTaskModel;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;

@ -1,331 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.common;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import android.app.Activity;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.widget.Toast;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.NotificationManager;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.utility.Constants;
/**
* A helper class for writing synchronization services for Astrid. This class
* contains logic for merging incoming changes and writing outgoing changes.
* <p>
* Use {@link initiate} as the entry point for your synchronization service,
* which should handle authentication and then call {@link synchronizeTasks} to
* initiate synchronization.
*
* @author timsu
*
*/
public abstract class SyncProvider<TYPE extends TaskContainer> {
// --- abstract methods - your services should implement these
/**
* Perform log in (launching activity if necessary) and sync. This is
* invoked when users manually request synchronization
*
* @param activity
* context
*/
abstract protected void initiateManual(Activity activity);
/**
* Perform synchronize. Since this can be called from background services,
* you should not open up new activities. Instead, if the user is not signed
* in, your service should do nothing.
*
* @param service
* context
*/
abstract protected void initiateBackground(Service service);
/**
* Updates the text of a notification and the intent to open when tapped
* @param context
* @param notification
*/
abstract protected void updateNotification(Context context, Notification n);
/**
* Deal with an exception that occurs during synchronization
*
* @param tag
* short string description of where error occurred
* @param e
* exception
* @param displayError
* whether to display error to the user
*/
abstract protected void handleException(String tag, Exception e, boolean displayError);
/**
* Create a task on the remote server.
*
* @param task
* task to create
* @return task created on remote server
*/
abstract protected TYPE create(TYPE task) throws IOException;
/**
* Push variables from given task to the remote server.
*
* @param task
* task proxy to push
* @param remoteTask
* remote task that we merged with. may be null
*/
abstract protected void push(TYPE task, TYPE remote) throws IOException;
/**
* Fetch remote task. Used to re-read merged tasks
*
* @param task
* task with id's to re-read
* @return new Task
*/
abstract protected TYPE pull(TYPE task) throws IOException;
/**
* Reads a task container from a task in the database
*
* @param task
*/
abstract protected TYPE read(TodorooCursor<Task> task) throws IOException;
/**
* Save task. Used to save local tasks that have been updated and remote
* tasks that need to be created locally
*
* @param task
*/
abstract protected void write(TYPE task) throws IOException;
/**
* Finds a task in the list with the same remote identifier(s) as
* the task passed in
*
* @return task from list if matches, null otherwise
*/
abstract protected int matchTask(ArrayList<TYPE> tasks, TYPE target);
/**
* Transfer remote identifier(s) from one task to another
*/
abstract protected void transferIdentifiers(TYPE source,
TYPE destination);
// --- implementation
private final Notification notification;
public SyncProvider() {
// initialize notification
int icon = android.R.drawable.stat_notify_sync;
long when = System.currentTimeMillis();
notification = new Notification(icon, null, when);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
}
public void synchronize(final Context context) {
// display toast
if(context instanceof Activity) {
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, R.string.SyP_progress_toast,
Toast.LENGTH_LONG).show();
}
});
initiateManual((Activity)context);
} else if(context instanceof Service) {
// display notification
updateNotification(context, notification);
final NotificationManager nm = new NotificationManager.AndroidNotificationManager(context);
nm.notify(Constants.NOTIFICATION_SYNC, notification);
// start next step in background thread
new Thread(new Runnable() {
public void run() {
try {
initiateBackground((Service)context);
} finally {
nm.cancel(Constants.NOTIFICATION_SYNC);
}
}
}).start();
}
}
// --- synchronization logic
/**
* Helper to synchronize remote tasks with our local database.
*
* This initiates the following process: 1. local changes are read 2. remote
* changes are read 3. local tasks are merged with remote changes and pushed
* across 4. remote changes are then read in
*
* @param data synchronization data structure
*/
protected void synchronizeTasks(SyncData<TYPE> data) throws IOException {
int length;
// create internal data structures
HashMap<String, Integer> remoteNewTaskNameMap = new HashMap<String, Integer>();
length = data.remoteUpdated.size();
for(int i = 0; i < length; i++) {
TYPE remote = data.remoteUpdated.get(i);
if(remote.task.getId() != Task.NO_ID)
continue;
remoteNewTaskNameMap.put(remote.task.getValue(Task.TITLE), i);
}
// 1. CREATE: grab newly created tasks and create them remotely
length = data.localCreated.getCount();
for(int i = 0; i < length; i++) {
data.localCreated.moveToNext();
TYPE local = read(data.localCreated);
try {
String taskTitle = local.task.getValue(Task.TITLE);
/* If there exists an incoming remote task with the same name and no
* mapping, we don't want to create this on the remote server,
* because user could have synchronized like this before. Instead,
* we create a mapping and do an update.
*/
if (remoteNewTaskNameMap.containsKey(taskTitle)) {
int remoteIndex = remoteNewTaskNameMap.remove(taskTitle);
TYPE remote = data.remoteUpdated.get(remoteIndex);
transferIdentifiers(remote, local);
push(local, remote);
// re-read remote task after merge, update remote task list
remote = pull(remote);
remote.task.setId(local.task.getId());
data.remoteUpdated.set(remoteIndex, remote);
} else {
create(local);
}
} catch (Exception e) {
handleException("sync-local-created", e, false); //$NON-NLS-1$
}
write(local);
}
// 2. UPDATE: for each updated local task
length = data.localUpdated.getCount();
for(int i = 0; i < length; i++) {
data.localUpdated.moveToNext();
TYPE local = read(data.localUpdated);
try {
if(local.task == null)
continue;
// if there is a conflict, merge
int remoteIndex = matchTask((ArrayList<TYPE>)data.remoteUpdated, local);
if(remoteIndex != -1) {
TYPE remote = data.remoteUpdated.get(remoteIndex);
push(local, remote);
// re-read remote task after merge
remote = pull(remote);
remote.task.setId(local.task.getId());
data.remoteUpdated.set(remoteIndex, remote);
} else {
push(local, null);
}
} catch (Exception e) {
handleException("sync-local-updated", e, false); //$NON-NLS-1$
}
write(local);
}
// 3. REMOTE: load remote information
// Rearrange remoteTasks so completed tasks get synchronized first.
// This prevents bugs where a repeated task has two copies come down
// the wire, the new version and the completed old version. The new
// version would get merged, then completed, if done in the wrong order.
Collections.sort(data.remoteUpdated, new Comparator<TYPE>() {
private static final int SENTINEL = -2;
private final int check(TYPE o1, TYPE o2, LongProperty property) {
long o1Property = o1.task.getValue(property);
long o2Property = o2.task.getValue(property);
if(o1Property != 0 && o2Property != 0)
return 0;
else if(o1Property != 0)
return -1;
else if(o2Property != 0)
return 1;
return SENTINEL;
}
public int compare(TYPE o1, TYPE o2) {
int comparison = check(o1, o2, Task.DELETION_DATE);
if(comparison != SENTINEL)
return comparison;
comparison = check(o1, o2, Task.COMPLETION_DATE);
if(comparison != SENTINEL)
return comparison;
return 0;
}
});
length = data.remoteUpdated.size();
for(int i = 0; i < length; i++) {
TYPE remote = data.remoteUpdated.get(i);
// don't synchronize new & deleted / completed tasks
if(!remote.task.isSaved() && (remote.task.isDeleted() ||
remote.task.isCompleted()))
continue;
try {
write(remote);
} catch (Exception e) {
handleException("sync-remote-updated", e, false); //$NON-NLS-1$
}
}
}
// --- helper classes
/** data structure builder */
protected static class SyncData<TYPE extends TaskContainer> {
public final ArrayList<TYPE> remoteUpdated;
public final TodorooCursor<Task> localCreated;
public final TodorooCursor<Task> localUpdated;
public SyncData(ArrayList<TYPE> remoteUpdated,
TodorooCursor<Task> localCreated,
TodorooCursor<Task> localUpdated) {
super();
this.remoteUpdated = remoteUpdated;
this.localCreated = localCreated;
this.localUpdated = localUpdated;
}
}
}

@ -1,206 +0,0 @@
package com.todoroo.astrid.common;
import java.util.Date;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.view.View;
import android.view.ViewGroup.OnHierarchyChangeListener;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.widget.TodorooPreferences;
/**
* Utility class for common synchronization action: displaying synchronization
* preferences and an action panel so users can initiate actions from the menu.
*
* @author Tim Su <tim@todoroo.com
*
*/
abstract public class SyncProviderPreferences extends TodorooPreferences {
// --- interface
/**
* @return your preference resource
*/
@Override
abstract public int getPreferenceResource();
/**
* kick off synchronization
*/
abstract public void startSync();
/**
* log user out
*/
abstract public void logOut();
/**
* @return get preference utilities
*/
abstract public SyncProviderUtilities getUtilities();
// --- implementation
@Autowired
private DialogUtilities dialogUtilities;
private int statusColor = Color.BLACK;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getListView().setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewRemoved(View parent, View child) {
//
}
@Override
public void onChildViewAdded(View parent, View child) {
View view = findViewById(R.id.status);
if(view != null)
view.setBackgroundColor(statusColor);
}
});
}
/**
*
* @param resource
* if null, updates all resources
*/
@Override
public void updatePreferences(Preference preference, Object value) {
final Resources r = getResources();
// interval
if (r.getString(getUtilities().getSyncIntervalKey()).equals(
preference.getKey())) {
int index = AndroidUtilities.indexOf(
r.getStringArray(R.array.sync_SPr_interval_values),
(String) value);
if (index <= 0)
preference.setSummary(R.string.sync_SPr_interval_desc_disabled);
else
preference.setSummary(r.getString(
R.string.sync_SPr_interval_desc,
r.getStringArray(R.array.sync_SPr_interval_entries)[index]));
}
// status
else if (r.getString(R.string.sync_SPr_status_key).equals(preference.getKey())) {
boolean loggedIn = getUtilities().isLoggedIn();
String status;
String subtitle = ""; //$NON-NLS-1$
// ! logged in - display message, click -> sync
if(!loggedIn) {
status = r.getString(R.string.sync_status_loggedout);
statusColor = Color.RED;
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
startSync();
finish();
return true;
}
});
}
// sync is occurring
else if(getUtilities().isOngoing()) {
status = r.getString(R.string.sync_status_ongoing);
statusColor = Color.rgb(0, 0, 100);
}
// last sync was error
else if(getUtilities().getLastAttemptedSyncDate() != 0) {
status = r.getString(R.string.sync_status_failed,
DateUtilities.getDateStringWithTime(SyncProviderPreferences.this,
new Date(getUtilities().getLastAttemptedSyncDate())));
if(getUtilities().getLastSyncDate() > 0) {
subtitle = r.getString(R.string.sync_status_failed_subtitle,
DateUtilities.getDateStringWithTime(SyncProviderPreferences.this,
new Date(getUtilities().getLastSyncDate())));
}
statusColor = Color.rgb(100, 0, 0);
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
String error = getUtilities().getLastError();
if(error != null)
dialogUtilities.okDialog(SyncProviderPreferences.this, error, null);
return true;
}
});
} else if(getUtilities().getLastSyncDate() > 0) {
status = r.getString(R.string.sync_status_success,
DateUtilities.getDateStringWithTime(SyncProviderPreferences.this,
new Date(getUtilities().getLastSyncDate())));
statusColor = Color.rgb(0, 100, 0);
} else {
status = r.getString(R.string.sync_status_never);
statusColor = Color.rgb(0, 0, 100);
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
startSync();
finish();
return true;
}
});
}
preference.setTitle(status);
preference.setSummary(subtitle);
View view = findViewById(R.id.status);
if(view != null)
view.setBackgroundColor(statusColor);
}
// sync button
else if (r.getString(R.string.sync_SPr_sync_key).equals(preference.getKey())) {
boolean loggedIn = getUtilities().isLoggedIn();
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
startSync();
finish();
return true;
}
});
if(!loggedIn)
preference.setTitle(R.string.sync_SPr_sync_log_in);
}
// log out button
else if (r.getString(R.string.sync_SPr_forget_key).equals(preference.getKey())) {
boolean loggedIn = getUtilities().isLoggedIn();
preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference p) {
dialogUtilities.okCancelDialog(SyncProviderPreferences.this,
r.getString(R.string.sync_forget_confirm), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
logOut();
initializePreference(getPreferenceScreen());
}
}, null);
return true;
}
});
if(!loggedIn)
preference.setEnabled(false);
}
}
}

@ -1,137 +0,0 @@
package com.todoroo.astrid.common;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.utility.DateUtilities;
/**
* Sync Provider Utility class for accessing preferences
*/
abstract public class SyncProviderUtilities {
/**
* @return your plugin identifier
*/
abstract public String getIdentifier();
/**
* @return key for sync interval
*/
abstract public int getSyncIntervalKey();
// --- implementation
private static final String PREF_TOKEN = "_token"; //$NON-NLS-1$
private static final String PREF_LAST_SYNC = "_last_sync"; //$NON-NLS-1$
private static final String PREF_LAST_ATTEMPTED_SYNC = "_last_attempted"; //$NON-NLS-1$
private static final String PREF_LAST_ERROR = "_last_error"; //$NON-NLS-1$
private static final String PREF_ONGOING = "_ongoing"; //$NON-NLS-1$
/** Get preferences object from the context */
protected static SharedPreferences getPrefs() {
return PreferenceManager.getDefaultSharedPreferences(ContextManager.getContext());
}
/**
* @return true if we have a token for this user, false otherwise
*/
public boolean isLoggedIn() {
return getPrefs().getString(getIdentifier() + PREF_TOKEN, null) != null;
}
/** authentication token, or null if doesn't exist */
public String getToken() {
return getPrefs().getString(getIdentifier() + PREF_TOKEN, null);
}
/** Sets the authentication token. Set to null to clear. */
public void setToken(String setting) {
Editor editor = getPrefs().edit();
editor.putString(getIdentifier() + PREF_TOKEN, setting);
editor.commit();
}
/** @return Last Successful Sync Date, or 0 */
public long getLastSyncDate() {
return getPrefs().getLong(getIdentifier() + PREF_LAST_SYNC, 0);
}
/** @return Last Attempted Sync Date, or 0 if it was successful */
public long getLastAttemptedSyncDate() {
return getPrefs().getLong(getIdentifier() + PREF_LAST_ATTEMPTED_SYNC, 0);
}
/** @return Last Error, or null if no last error */
public String getLastError() {
return getPrefs().getString(PREF_LAST_ERROR, null);
}
/** @return Last Error, or null if no last error */
public boolean isOngoing() {
return getPrefs().getBoolean(getIdentifier() + PREF_ONGOING, false);
}
/** Deletes Last Successful Sync Date */
public void clearLastSyncDate() {
Editor editor = getPrefs().edit();
editor.remove(getIdentifier() + PREF_LAST_SYNC);
editor.commit();
}
/** Set Last Successful Sync Date */
public void setLastError(String error) {
Editor editor = getPrefs().edit();
editor.putString(getIdentifier() + PREF_LAST_ERROR, error);
editor.commit();
}
/** Set Ongoing */
public void stopOngoing() {
Editor editor = getPrefs().edit();
editor.putBoolean(getIdentifier() + PREF_ONGOING, false);
editor.commit();
}
/** Set Last Successful Sync Date */
public void recordSuccessfulSync() {
Editor editor = getPrefs().edit();
editor.putLong(getIdentifier() + PREF_LAST_SYNC, DateUtilities.now());
editor.putLong(getIdentifier() + PREF_LAST_ATTEMPTED_SYNC, 0);
editor.commit();
}
/** Set Last Attempted Sync Date */
public void recordSyncStart() {
Editor editor = getPrefs().edit();
editor.putLong(getIdentifier() + PREF_LAST_ATTEMPTED_SYNC,
DateUtilities.now());
editor.putString(getIdentifier() + PREF_LAST_ERROR, null);
editor.putBoolean(getIdentifier() + PREF_ONGOING, true);
editor.commit();
}
/**
* Reads the frequency, in seconds, auto-sync should occur.
*
* @return seconds duration, or 0 if not desired
*/
public int getSyncAutoSyncFrequency() {
String value = getPrefs().getString(
ContextManager.getContext().getString(
getSyncIntervalKey()), null);
if (value == null)
return 0;
try {
return Integer.parseInt(value);
} catch (Exception e) {
return 0;
}
}
}

@ -1,34 +0,0 @@
package com.todoroo.astrid.common;
import java.util.ArrayList;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.model.Metadata;
import com.todoroo.astrid.model.Task;
/**
* Container class for transmitting tasks and including local and remote
* metadata. Synchronization Providers can subclass this class if desired.
*
* @see {@link SyncProvider}
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskContainer {
public Task task;
public ArrayList<Metadata> metadata;
/**
* Check if the metadata contains anything with the given key
* @param key
* @return first match. or null
*/
public Metadata findMetadata(String key) {
for(Metadata item : metadata) {
if(AndroidUtilities.equals(key, item.getValue(Metadata.KEY)))
return item;
}
return null;
}
}

@ -20,8 +20,8 @@ import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
/** /**

@ -38,8 +38,8 @@ import com.todoroo.astrid.api.PermaSql;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.tags.TagService.Tag; import com.todoroo.astrid.tags.TagService.Tag;

@ -18,7 +18,7 @@ import android.widget.TextView;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance; import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance;
import com.todoroo.astrid.model.AddOn; import com.todoroo.astrid.data.AddOn;
/** /**
* Adapter for {@link AddOn}s * Adapter for {@link AddOn}s

@ -15,7 +15,6 @@ import android.os.Bundle;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
@ -26,7 +25,7 @@ import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.api.FilterCategory; import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.StoreObjectDao; import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
/** /**
* Exposes Astrid's built in filters to the {@link FilterListActivity} * Exposes Astrid's built in filters to the {@link FilterListActivity}
@ -101,9 +100,6 @@ public final class CustomFilterExposer extends BroadcastReceiver {
*/ */
public static class DeleteActivity extends Activity { public static class DeleteActivity extends Activity {
@Autowired
DialogUtilities dialogUtilities;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -117,7 +113,7 @@ public final class CustomFilterExposer extends BroadcastReceiver {
final String name = getIntent().getStringExtra(TOKEN_FILTER_NAME); final String name = getIntent().getStringExtra(TOKEN_FILTER_NAME);
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
dialogUtilities.okCancelDialog(this, DialogUtilities.okCancelDialog(this,
getString(R.string.DLG_delete_this_item_question, name), getString(R.string.DLG_delete_this_item_question, name),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {

@ -33,7 +33,7 @@ public final class PluginServices {
MetadataService metadataService; MetadataService metadataService;
@Autowired @Autowired
DialogUtilities dialogUtilities; DialogUtilities DialogUtilities;
@Autowired @Autowired
DateUtilities dateUtilities; DateUtilities dateUtilities;
@ -79,7 +79,7 @@ public final class PluginServices {
} }
public static DialogUtilities getDialogUtilities() { public static DialogUtilities getDialogUtilities() {
return getInstance().dialogUtilities; return getInstance().DialogUtilities;
} }
public static DateUtilities getDateUtilities() { public static DateUtilities getDateUtilities() {

@ -10,7 +10,7 @@ import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance; import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance;
import com.todoroo.astrid.dao.StoreObjectDao; import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
/** /**
* {@link StoreObject} entries for a saved custom filter * {@link StoreObject} entries for a saved custom filter

@ -27,8 +27,8 @@ import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.gcal.Calendars.CalendarResult; import com.todoroo.astrid.gcal.Calendars.CalendarResult;
import com.todoroo.astrid.model.Task;
/** /**
* Control Set for managing repeats * Control Set for managing repeats

@ -12,7 +12,7 @@ import android.util.Log;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
public class GCalTaskCompleteListener extends BroadcastReceiver { public class GCalTaskCompleteListener extends BroadcastReceiver {

@ -16,7 +16,7 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.ShortcutActivity; import com.todoroo.astrid.activity.ShortcutActivity;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.reminders.Notifications; import com.todoroo.astrid.reminders.Notifications;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;

@ -11,7 +11,7 @@ import android.content.Intent;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
/** /**

@ -25,9 +25,9 @@ import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard; import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider; import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
@ -46,7 +46,6 @@ public class ProducteevControlSet implements TaskEditControlSet {
// --- instance variables // --- instance variables
private final Activity activity; private final Activity activity;
private final DialogUtilities dialogUtilites;
private final View view; private final View view;
private Task myTask; private Task myTask;
@ -65,7 +64,6 @@ public class ProducteevControlSet implements TaskEditControlSet {
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
this.activity = activity; this.activity = activity;
this.dialogUtilites = new DialogUtilities();
view = LayoutInflater.from(activity).inflate(R.layout.producteev_control, parent, true); view = LayoutInflater.from(activity).inflate(R.layout.producteev_control, parent, true);
@ -94,7 +92,7 @@ public class ProducteevControlSet implements TaskEditControlSet {
dialog.cancel(); dialog.cancel();
} else { } else {
// create the real dashboard, select it in the spinner and refresh responsiblespinner // create the real dashboard, select it in the spinner and refresh responsiblespinner
ProgressDialog progressDialog = dialogUtilites.progressDialog(context, ProgressDialog progressDialog = com.todoroo.andlib.utility.DialogUtilities.progressDialog(context,
context.getString(R.string.DLG_wait)); context.getString(R.string.DLG_wait));
try { try {
progressDialog.show(); progressDialog.show();
@ -107,11 +105,11 @@ public class ProducteevControlSet implements TaskEditControlSet {
adapter.insert(newDashboard, adapter.getCount()-1); adapter.insert(newDashboard, adapter.getCount()-1);
dashSelector.setSelection(adapter.getCount()-2); dashSelector.setSelection(adapter.getCount()-2);
refreshResponsibleSpinner(newDashboard.getUsers()); refreshResponsibleSpinner(newDashboard.getUsers());
dialogUtilites.dismissDialog(context, progressDialog); DialogUtilities.dismissDialog(context, progressDialog);
} }
} catch (Exception e) { } catch (Exception e) {
dialogUtilites.dismissDialog(context, progressDialog); DialogUtilities.dismissDialog(context, progressDialog);
dialogUtilites.okDialog(context, DialogUtilities.okDialog(context,
context.getString(R.string.DLG_error, e.getMessage()), context.getString(R.string.DLG_error, e.getMessage()),
new OnClickListener() { new OnClickListener() {
@Override @Override
@ -133,7 +131,7 @@ public class ProducteevControlSet implements TaskEditControlSet {
dashboardSelector.setSelection(lastDashboardSelection); dashboardSelector.setSelection(lastDashboardSelection);
} }
}; };
dialogUtilites.viewDialog(ProducteevControlSet.this.activity, DialogUtilities.viewDialog(ProducteevControlSet.this.activity,
ProducteevControlSet.this.activity.getString(R.string.producteev_create_dashboard_name), ProducteevControlSet.this.activity.getString(R.string.producteev_create_dashboard_name),
editor, editor,
okListener, okListener,

@ -11,8 +11,8 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.astrid.adapter.TaskAdapter; import com.todoroo.astrid.adapter.TaskAdapter;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard; import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevNote; import com.todoroo.astrid.producteev.sync.ProducteevNote;

@ -22,8 +22,8 @@ import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard; import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevTask; import com.todoroo.astrid.producteev.sync.ProducteevTask;

@ -32,7 +32,6 @@ import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
@ -49,9 +48,6 @@ import com.todoroo.astrid.utility.Preferences;
*/ */
public class ProducteevLoginActivity extends Activity { public class ProducteevLoginActivity extends Activity {
@Autowired
DialogUtilities dialogUtilities;
// --- ui initialization // --- ui initialization
public ProducteevLoginActivity() { public ProducteevLoginActivity() {
@ -130,7 +126,7 @@ public class ProducteevLoginActivity extends Activity {
private void performLogin(final String email, final String password) { private void performLogin(final String email, final String password) {
final ProgressDialog dialog = dialogUtilities.progressDialog(this, final ProgressDialog dialog = DialogUtilities.progressDialog(this,
getString(R.string.DLG_wait)); getString(R.string.DLG_wait));
final TextView errors = (TextView) findViewById(R.id.error); final TextView errors = (TextView) findViewById(R.id.error);
dialog.show(); dialog.show();
@ -168,7 +164,7 @@ public class ProducteevLoginActivity extends Activity {
private void performSignup(final String email, final String password, private void performSignup(final String email, final String password,
final String firstName, final String lastName) { final String firstName, final String lastName) {
final ProgressDialog dialog = dialogUtilities.progressDialog(this, final ProgressDialog dialog = DialogUtilities.progressDialog(this,
getString(R.string.DLG_wait)); getString(R.string.DLG_wait));
final TextView errors = (TextView) findViewById(R.id.error); final TextView errors = (TextView) findViewById(R.id.error);
dialog.show(); dialog.show();

@ -7,12 +7,12 @@ import android.preference.Preference;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.common.SyncProviderPreferences; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.common.SyncProviderUtilities;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.producteev.sync.ProducteevDashboard; import com.todoroo.astrid.producteev.sync.ProducteevDashboard;
import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevDataService;
import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider; import com.todoroo.astrid.producteev.sync.ProducteevSyncProvider;
import com.todoroo.astrid.sync.SyncProviderPreferences;
import com.todoroo.astrid.sync.SyncProviderUtilities;
/** /**
* Displays synchronization preferences and an action panel so users can * Displays synchronization preferences and an action panel so users can

@ -1,7 +1,7 @@
package com.todoroo.astrid.producteev; package com.todoroo.astrid.producteev;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.common.SyncProviderUtilities; import com.todoroo.astrid.sync.SyncProviderUtilities;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
/** /**

@ -5,7 +5,7 @@ import java.util.StringTokenizer;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
/** /**
* {@link StoreObject} entries for a Producteev Dashboard * {@link StoreObject} entries for a Producteev Dashboard

@ -26,9 +26,9 @@ import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria; import com.todoroo.astrid.dao.StoreObjectDao.StoreObjectCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.StoreObject; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.producteev.ProducteevUtilities; import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.producteev.api.ApiUtilities; import com.todoroo.astrid.producteev.api.ApiUtilities;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;

@ -4,7 +4,7 @@ import org.json.JSONObject;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.producteev.api.ApiUtilities; import com.todoroo.astrid.producteev.api.ApiUtilities;
/** /**

@ -35,11 +35,9 @@ import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.activity.ShortcutActivity; import com.todoroo.astrid.activity.ShortcutActivity;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.Filter;
import com.todoroo.astrid.common.SyncProvider; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.common.TaskContainer; import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.model.StoreObject;
import com.todoroo.astrid.model.Task;
import com.todoroo.astrid.producteev.ProducteevBackgroundService; import com.todoroo.astrid.producteev.ProducteevBackgroundService;
import com.todoroo.astrid.producteev.ProducteevFilterExposer; import com.todoroo.astrid.producteev.ProducteevFilterExposer;
import com.todoroo.astrid.producteev.ProducteevLoginActivity; import com.todoroo.astrid.producteev.ProducteevLoginActivity;
@ -50,6 +48,8 @@ import com.todoroo.astrid.producteev.api.ApiServiceException;
import com.todoroo.astrid.producteev.api.ApiUtilities; import com.todoroo.astrid.producteev.api.ApiUtilities;
import com.todoroo.astrid.producteev.api.ProducteevInvoker; import com.todoroo.astrid.producteev.api.ProducteevInvoker;
import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.sync.SyncContainer;
import com.todoroo.astrid.sync.SyncProvider;
import com.todoroo.astrid.tags.TagService; import com.todoroo.astrid.tags.TagService;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;
@ -72,9 +72,6 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
@Autowired @Autowired
protected ExceptionService exceptionService; protected ExceptionService exceptionService;
@Autowired
protected DialogUtilities dialogUtilities;
public ProducteevSyncProvider() { public ProducteevSyncProvider() {
super(); super();
DependencyInjectionService.getInstance().inject(this); DependencyInjectionService.getInstance().inject(this);
@ -132,7 +129,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
} }
if(displayError && context instanceof Activity && message != null) { if(displayError && context instanceof Activity && message != null) {
dialogUtilities.okDialog((Activity)context, DialogUtilities.okDialog((Activity)context,
message, null); message, null);
} }
} }
@ -691,7 +688,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
* @param remoteTask remote task proxy * @param remoteTask remote task proxy
* @return * @return
*/ */
private boolean shouldTransmit(TaskContainer task, Property<?> property, TaskContainer remoteTask) { private boolean shouldTransmit(SyncContainer task, Property<?> property, SyncContainer remoteTask) {
if(!task.task.containsValue(property)) if(!task.task.containsValue(property))
return false; return false;
@ -711,7 +708,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
} }
@Override @Override
protected void updateNotification(Context context, Notification notification) { protected int updateNotification(Context context, Notification notification) {
String notificationTitle = context.getString(R.string.producteev_notification_title); String notificationTitle = context.getString(R.string.producteev_notification_title);
Intent intent = new Intent(context, ProducteevPreferences.class); Intent intent = new Intent(context, ProducteevPreferences.class);
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0, PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
@ -719,7 +716,7 @@ public class ProducteevSyncProvider extends SyncProvider<ProducteevTaskContainer
notification.setLatestEventInfo(context, notification.setLatestEventInfo(context,
notificationTitle, context.getString(R.string.SyP_progress), notificationTitle, context.getString(R.string.SyP_progress),
notificationIntent); notificationIntent);
return ; return Constants.NOTIFICATION_SYNC;
} }
@Override @Override

@ -1,7 +1,7 @@
package com.todoroo.astrid.producteev.sync; package com.todoroo.astrid.producteev.sync;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.producteev.ProducteevUtilities; import com.todoroo.astrid.producteev.ProducteevUtilities;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;

@ -5,9 +5,9 @@ import java.util.Iterator;
import org.json.JSONObject; import org.json.JSONObject;
import com.todoroo.astrid.common.TaskContainer; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.sync.SyncContainer;
/** /**
* RTM Task Container * RTM Task Container
@ -15,7 +15,7 @@ import com.todoroo.astrid.model.Task;
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
public class ProducteevTaskContainer extends TaskContainer { public class ProducteevTaskContainer extends SyncContainer {
public Metadata pdvTask; public Metadata pdvTask;

@ -23,7 +23,7 @@ import com.todoroo.andlib.service.NotificationManager;
import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager; import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;

@ -23,7 +23,7 @@ import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.utility.Preferences; import com.todoroo.astrid.utility.Preferences;

@ -29,7 +29,7 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.service.ExceptionService; import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.NumberPicker; import com.todoroo.astrid.ui.NumberPicker;
import com.todoroo.astrid.ui.NumberPickerDialog; import com.todoroo.astrid.ui.NumberPickerDialog;
import com.todoroo.astrid.ui.NumberPickerDialog.OnNumberPickedListener; import com.todoroo.astrid.ui.NumberPickerDialog.OnNumberPickedListener;

@ -18,7 +18,7 @@ import com.google.ical.values.WeekdayNum;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
/** /**
* Exposes Task Detail for repeats, i.e. "Repeats every 2 days" * Exposes Task Detail for repeats, i.e. "Repeats every 2 days"

@ -19,7 +19,7 @@ import com.google.ical.values.RRule;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
public class RepeatTaskCompleteListener extends BroadcastReceiver { public class RepeatTaskCompleteListener extends BroadcastReceiver {

@ -24,7 +24,7 @@ import com.todoroo.astrid.api.FilterCategory;
import com.todoroo.astrid.api.FilterListHeader; import com.todoroo.astrid.api.FilterListHeader;
import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterListItem;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.tags.TagService.Tag; import com.todoroo.astrid.tags.TagService.Tag;
/** /**

@ -15,8 +15,8 @@ import com.todoroo.andlib.sql.QueryTemplate;
import com.todoroo.astrid.dao.MetadataDao; import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
/** /**
* Provides operations for working with tags * Provides operations for working with tags

@ -27,8 +27,8 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.tags.TagService.Tag; import com.todoroo.astrid.tags.TagService.Tag;
/** /**

@ -13,7 +13,7 @@ import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.TaskAction; import com.todoroo.astrid.api.TaskAction;
import com.todoroo.astrid.api.TaskDecoration; import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
/** /**
* Exposes {@link TaskDecoration} for timers * Exposes {@link TaskDecoration} for timers

@ -8,7 +8,7 @@ import com.timsu.astrid.R;
import com.todoroo.andlib.data.Property.IntegerProperty; import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet; import com.todoroo.astrid.activity.TaskEditActivity.TaskEditControlSet;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.ui.TimeDurationControlSet; import com.todoroo.astrid.ui.TimeDurationControlSet;
/** /**

@ -19,7 +19,7 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.api.TaskDecoration; import com.todoroo.astrid.api.TaskDecoration;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.model.Task; import com.todoroo.astrid.data.Task;
/** /**
* Exposes {@link TaskDecoration} for timers * Exposes {@link TaskDecoration} for timers

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save