diff --git a/astrid/api-src/com/todoroo/astrid/api/CustomFilterCriterion.java b/astrid/api-src/com/todoroo/astrid/api/CustomFilterCriterion.java new file mode 100644 index 000000000..63a274ccd --- /dev/null +++ b/astrid/api-src/com/todoroo/astrid/api/CustomFilterCriterion.java @@ -0,0 +1,172 @@ +/** + * 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 + * + */ +public final class CustomFilterCriterion extends FilterListItem { + + // --- constants + + /** 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$ + + // --- instance variables + + /** + * Criteria Title. If the title contains %s, this is replaced by the entry + * label string selected. + *

+ * e.g "Due: %s" + */ + @CheckForNull + public String text; + + /** + * Criterion SQL. This query should return task id's. If this contains + * %s, it will be replaced by the entry value + *

+ * Examples: + *

+ */ + @CheckForNull + public String sql; + + /** + * Values to apply to a task when quick-adding a task from a filter + * created from this criterion. %s 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 title, String sql, + ContentValues valuesForNewTasks, String[] entryTitles, + String[] entryValues, Bitmap icon, String name) { + 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} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + 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 CREATOR = new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + public CustomFilterCriterion createFromParcel(Parcel source) { + CustomFilterCriterion item = new CustomFilterCriterion( + 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]; + } + + }; +} diff --git a/astrid/common-src/com/todoroo/andlib/sql/Field.java b/astrid/common-src/com/todoroo/andlib/sql/Field.java index e967eb664..47d44bcb5 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/Field.java +++ b/astrid/common-src/com/todoroo/andlib/sql/Field.java @@ -37,6 +37,10 @@ public class Field extends DBObject { return UnaryCriterion.lt(this, value); } + public Criterion lte(final Object value) { + return UnaryCriterion.lte(this, value); + } + public Criterion isNull() { return UnaryCriterion.isNull(this); } diff --git a/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java index 98467b9a0..0dcff2bd5 100644 --- a/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java +++ b/astrid/common-src/com/todoroo/andlib/sql/UnaryCriterion.java @@ -63,6 +63,10 @@ public class UnaryCriterion extends Criterion { 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 diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java new file mode 100644 index 000000000..9ecc313a1 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java @@ -0,0 +1,251 @@ +package com.todoroo.astrid.core; + +import java.util.ArrayList; +import java.util.List; + +import android.app.ListActivity; +import android.content.ContentValues; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.MenuItem; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.Button; + +import com.timsu.astrid.R; +import com.todoroo.andlib.data.Property.CountProperty; +import com.todoroo.andlib.service.Autowired; +import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.api.CustomFilterCriterion; +import com.todoroo.astrid.dao.Database; +import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; +import com.todoroo.astrid.model.Metadata; +import com.todoroo.astrid.model.Task; +import com.todoroo.astrid.tags.TagService; +import com.todoroo.astrid.tags.TagService.Tag; + +/** + * Activity that allows users to build custom filters + * + * @author Tim Su + * + */ +public class CustomFilterActivity extends ListActivity { + + static final int MENU_GROUP_FILTER = 0; + static final int MENU_GROUP_FILTER_OPTION = 1; + + // --- hierarchy of filter classes + + public static class CriterionInstance { + public static final int TYPE_ADD = 0; + public static final int TYPE_SUBTRACT = 1; + public static final int TYPE_INTERSECT = 2; + public static final int TYPE_UNIVERSE = 3; + + /** criteria for this instance */ + public CustomFilterCriterion criterion; + + /** which of the entries is selected */ + public int selectedIndex; + + /** type of join */ + public int type; + + /** statistics for {@link FilterView} */ + public int start, end, max; + } + + private CustomFilterAdapter adapter; + private final ArrayList criteria = + new ArrayList(); + + // --- activity + + @Autowired + Database database; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.custom_filter_activity); + setTitle(R.string.CFA_title); + + DependencyInjectionService.getInstance().inject(this); + populateCriteria(); + + List startingCriteria = new ArrayList(); + startingCriteria.add(getStartingUniverse()); + adapter = new CustomFilterAdapter(this, startingCriteria); + setListAdapter(adapter); + updateList(); + + setUpListeners(); + } + + /** + * Populate criteria list with built in and plugin criteria + */ + @SuppressWarnings("nls") + private void populateCriteria() { + Resources r = getResources(); + + // built in criteria: due date + String[] entryValues = new String[] { + CustomFilterCriterion.VALUE_EOD_YESTERDAY, + CustomFilterCriterion.VALUE_EOD, + CustomFilterCriterion.VALUE_EOD_TOMORROW, + CustomFilterCriterion.VALUE_EOD_DAY_AFTER, + CustomFilterCriterion.VALUE_EOD_NEXT_WEEK, + }; + ContentValues values = new ContentValues(); + values.put(Task.DUE_DATE.name, "%s"); + CustomFilterCriterion criterion = new CustomFilterCriterion( + getString(R.string.CFC_dueBefore_text), + Query.select(Task.ID).from(Task.TABLE).where(Task.DUE_DATE.lte("%s")).toString(), + values, r.getStringArray(R.array.CFC_dueBefore_entries), + entryValues, ((BitmapDrawable)r.getDrawable(R.drawable.tango_calendar)).getBitmap(), + getString(R.string.CFC_dueBefore_name)); + criteria.add(criterion); + + // built in criteria: importance + entryValues = new String[] { + Integer.toString(Task.IMPORTANCE_DO_OR_DIE), + Integer.toString(Task.IMPORTANCE_MUST_DO), + Integer.toString(Task.IMPORTANCE_SHOULD_DO), + Integer.toString(Task.IMPORTANCE_NONE), + }; + values = new ContentValues(); + values.put(Task.IMPORTANCE.name, "%s"); + criterion = new CustomFilterCriterion( + getString(R.string.CFC_importance_text), + Query.select(Task.ID).from(Task.TABLE).where(Task.IMPORTANCE.lte("%s")).toString(), + values, r.getStringArray(R.array.EPr_default_importance), + entryValues, ((BitmapDrawable)r.getDrawable(R.drawable.tango_warning)).getBitmap(), + getString(R.string.CFC_importance_name)); + criteria.add(criterion); + + // built in criteria: tags + Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, Criterion.all); + String[] tagNames = new String[tags.length]; + for(int i = 0; i < tags.length; i++) + tagNames[i] = tags[i].tag; + values = new ContentValues(); + values.put(Metadata.KEY.name, TagService.KEY); + values.put(TagService.TAG.name, "%s"); + criterion = new CustomFilterCriterion( + getString(R.string.CFC_tag_text), + Query.select(Metadata.TASK).from(Metadata.TABLE).where(Criterion.and( + MetadataCriteria.withKey(TagService.KEY), + TagService.TAG.eq("%s"))).toString(), + values, tagNames, tagNames, + ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), + getString(R.string.CFC_tag_name)); + criteria.add(criterion); + } + + private CriterionInstance getStartingUniverse() { + CriterionInstance instance = new CriterionInstance(); + instance.criterion = new CustomFilterCriterion(getString(R.string.CFA_universe_all), + null, null, null, null, null, null); + instance.type = CriterionInstance.TYPE_UNIVERSE; + return instance; + } + + private void setUpListeners() { + ((Button)findViewById(R.id.add)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + getListView().showContextMenu(); + } + }); + + getListView().setOnCreateContextMenuListener(this); + } + + // --- listeners and action events + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + for(int i = 0; i < criteria.size(); i++) { + CustomFilterCriterion item = criteria.get(i); + MenuItem menuItem = menu.add(MENU_GROUP_FILTER, i, i, item.name); + if(item.icon != null) + menuItem.setIcon(new BitmapDrawable(item.icon)); + } + } + + /** + * Recalculate all sizes + */ + @SuppressWarnings("nls") + void updateList() { + int max = 0, last = -1; + StringBuilder sql = new StringBuilder(Query.select(new CountProperty()).from(Task.TABLE).toString()). + append(" WHERE "); + for(int i = 0; i < adapter.getCount(); i++) { + CriterionInstance instance = adapter.getItem(i); + + switch(instance.type) { + case CriterionInstance.TYPE_ADD: + sql.append("OR "); + break; + case CriterionInstance.TYPE_SUBTRACT: + sql.append("AND NOT "); + break; + case CriterionInstance.TYPE_INTERSECT: + sql.append("AND "); + break; + case CriterionInstance.TYPE_UNIVERSE: + } + + // special code for all tasks universe + if(instance.criterion.sql == null) + sql.append(TaskCriteria.activeAndVisible()).append(' '); + else + sql.append(Task.ID).append(" IN (").append(instance.criterion.sql).append(") "); + + Cursor cursor = database.getDatabase().rawQuery(sql.toString(), null); + try { + cursor.moveToNext(); + max = Math.max(max, cursor.getCount()); + instance.start = last == -1 ? cursor.getInt(0) : last; + instance.end = cursor.getInt(0); + } finally { + cursor.close(); + } + } + + for(int i = 0; i < adapter.getCount(); i++) { + CriterionInstance instance = adapter.getItem(i); + instance.max = max; + } + + adapter.notifyDataSetInvalidated(); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + if(item.getGroupId() == MENU_GROUP_FILTER) { + CustomFilterCriterion criterion = criteria.get(item.getItemId()); + CriterionInstance instance = new CriterionInstance(); + instance.criterion = criterion; + adapter.add(instance); + return true; + } + + else if(item.getGroupId() == MENU_GROUP_FILTER_OPTION) + return adapter.onMenuItemSelected(item); + + + return super.onMenuItemSelected(featureId, item); + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterAdapter.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterAdapter.java new file mode 100644 index 000000000..01fdfa529 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterAdapter.java @@ -0,0 +1,165 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.core; + +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View.OnCreateContextMenuListener; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.timsu.astrid.R; +import com.todoroo.astrid.api.CustomFilterCriterion; +import com.todoroo.astrid.core.CustomFilterActivity.CriterionInstance; +import com.todoroo.astrid.model.AddOn; + +/** + * Adapter for {@link AddOn}s + * + * @author Tim Su + * + */ +public class CustomFilterAdapter extends ArrayAdapter { + + private final Activity activity; + private final LayoutInflater inflater; + + public CustomFilterAdapter(Activity activity, List objects) { + super(activity, R.id.name, objects); + this.activity = activity; + inflater = (LayoutInflater) activity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + } + + // --- view event handling + + View.OnClickListener filterClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + ViewHolder viewHolder = (ViewHolder) v.getTag(); + if(viewHolder == null) + return; + if(viewHolder.item.type == CriterionInstance.TYPE_UNIVERSE) + return; + + // keep the filter options in the name context menu + viewHolder.name.showContextMenu(); + } + }; + + OnCreateContextMenuListener createContextMenuListener = new OnCreateContextMenuListener() { + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { + ViewHolder viewHolder = (ViewHolder) ((View)view.getParent()).getTag(); + CustomFilterCriterion criteria = viewHolder.item.criterion; + if(criteria.entryTitles == null || + criteria.entryTitles.length == 0) + return; + + menu.setHeaderTitle(criteria.name); + menu.setGroupCheckable(CustomFilterActivity.MENU_GROUP_FILTER_OPTION, true, true); + + for(int i = 0; i < criteria.entryTitles.length; i++) { + menu.add(CustomFilterActivity.MENU_GROUP_FILTER_OPTION, + getPosition(viewHolder.item), + i, criteria.entryTitles[i]); + } + } + }; + + + public boolean onMenuItemSelected(MenuItem item) { + CriterionInstance instance = getItem(item.getItemId()); + instance.selectedIndex = item.getOrder(); + ((CustomFilterActivity)activity).updateList(); + return true; + } + + // --- view construction + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if(convertView == null) { + convertView = inflater.inflate(R.layout.custom_filter_row, parent, false); + ViewHolder viewHolder = new ViewHolder(); + viewHolder.type = (ImageView) convertView.findViewById(R.id.type); + viewHolder.icon = (ImageView) convertView.findViewById(R.id.icon); + viewHolder.name= (TextView) convertView.findViewById(R.id.name); + viewHolder.filterView = (FilterView) convertView.findViewById(R.id.filter); + convertView.setTag(viewHolder); + } + + ViewHolder viewHolder = (ViewHolder)convertView.getTag(); + viewHolder.item = getItem(position); + initializeView(convertView); + + // listeners + convertView.setOnClickListener(filterClickListener); + viewHolder.name.setOnCreateContextMenuListener(createContextMenuListener); + + return convertView; + } + + private class ViewHolder { + public CriterionInstance item; + public ImageView type; + public ImageView icon; + public TextView name; + public FilterView filterView; + } + + @SuppressWarnings("nls") + private void initializeView(View convertView) { + ViewHolder viewHolder = (ViewHolder) convertView.getTag(); + CriterionInstance item = viewHolder.item; + + String entryTitle = ""; + if(item.selectedIndex >= 0 && item.selectedIndex < item.criterion.entryTitles.length) { + entryTitle = item.criterion.entryTitles[item.selectedIndex]; + } + String title = item.criterion.text.replace("%s", entryTitle); + boolean notFirst = getPosition(item) > 1; + + viewHolder.type.setVisibility(item.type == CriterionInstance.TYPE_UNIVERSE ? + View.GONE : View.VISIBLE); + switch(item.type) { + case CriterionInstance.TYPE_ADD: + viewHolder.type.setImageResource(R.drawable.arrow_join); + if(notFirst) + title = activity.getString(R.string.CFA_type_add) + " " + title; + break; + case CriterionInstance.TYPE_SUBTRACT: + viewHolder.type.setImageResource(R.drawable.arrow_branch); + if(notFirst) + title = activity.getString(R.string.CFA_type_subtract) + " " + title; + break; + case CriterionInstance.TYPE_INTERSECT: + viewHolder.type.setImageResource(R.drawable.arrow_down); + if(notFirst) + title = activity.getString(R.string.CFA_type_intersect) + " " + title; + break; + } + + viewHolder.icon.setVisibility(item.criterion.icon == null ? View.GONE : + View.VISIBLE); + if(item.criterion.icon != null) + viewHolder.icon.setImageBitmap(item.criterion.icon); + + viewHolder.name.setText(title); + + viewHolder.filterView.setMax(item.max); + viewHolder.filterView.setStart(item.start); + viewHolder.filterView.setEnd(item.end); + } + + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/core/FilterView.java b/astrid/plugin-src/com/todoroo/astrid/core/FilterView.java new file mode 100644 index 000000000..520f08620 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/FilterView.java @@ -0,0 +1,75 @@ +package com.todoroo.astrid.core; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.util.AttributeSet; +import android.view.View; + +/** + * Draws filters + * + * @author Tim Su + * + */ +public class FilterView extends View { + + private int start = 0, end = 0, max = 1; + + private static final int FILTER_COLOR = Color.rgb(85, 155, 255); + private static final int BG_COLOR = Color.WHITE; + private static final int TEXT_COLOR = Color.BLACK; + + // --- boilerplate + + public void setStart(int start) { + this.start = start; + } + + public void setEnd(int end) { + this.end = end; + } + + public void setMax(int max) { + this.max = max; + } + + public FilterView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public FilterView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FilterView(Context context) { + super(context); + } + + // --- painting code + + @Override + protected void onDraw(Canvas canvas) { + Paint paint = new Paint(); + paint.setColor(BG_COLOR); + paint.setStyle(Paint.Style.FILL); + canvas.drawRect(0, 0, getWidth(), getHeight(), paint); + + paint.setColor(FILTER_COLOR); + Path path = new Path(); + path.moveTo(getWidth() * (0.5f - 0.5f * start / max), 0); + path.lineTo(getWidth() * (0.5f + 0.5f * start / max), 0); + path.lineTo(getWidth() * (0.5f + 0.5f * end / max), getHeight()); + path.lineTo(getWidth() * (0.5f - 0.5f * end / max), getHeight()); + path.close(); + canvas.drawPath(path, paint); + + paint.setColor(TEXT_COLOR); + paint.setTextAlign(Paint.Align.CENTER); + paint.setTextSize(16); + canvas.drawText(Integer.toString(end), getWidth() / 2, getHeight() / 2, paint); + } + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/core/IntentFilter.java b/astrid/plugin-src/com/todoroo/astrid/core/IntentFilter.java new file mode 100644 index 000000000..8e3619205 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/IntentFilter.java @@ -0,0 +1,81 @@ +package com.todoroo.astrid.core; + +import android.app.PendingIntent; +import android.os.Parcel; +import android.os.Parcelable; + +import com.todoroo.astrid.api.FilterListItem; + +/** + * Special filter that triggers the search functionality when accessed. + * + * @author Tim Su + * + */ +public class IntentFilter extends FilterListItem { + + public PendingIntent intent; + + /** + * Constructor for creating a new IntentFilter + * + * @param listingTitle + * Title of this item as displayed on the lists page, e.g. Inbox + * @param intent + * intent to load + */ + public IntentFilter(String listingTitle, PendingIntent intent) { + this.listingTitle = listingTitle; + this.intent = intent; + } + + /** + * Constructor for creating a new IntentFilter used internally + */ + protected IntentFilter(PendingIntent intent) { + this.intent = intent; + } + + // --- parcelable + + /** + * {@inheritDoc} + */ + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(intent, 0); + super.writeToParcel(dest, flags); + } + + /** + * Parcelable creator + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + public IntentFilter createFromParcel(Parcel source) { + IntentFilter item = new IntentFilter((PendingIntent) source.readParcelable( + PendingIntent.class.getClassLoader())); + item.readFromParcel(source); + return item; + } + + /** + * {@inheritDoc} + */ + public IntentFilter[] newArray(int size) { + return new IntentFilter[size]; + } + + }; + +} diff --git a/astrid/plugin-src/com/todoroo/astrid/core/SearchFilter.java b/astrid/plugin-src/com/todoroo/astrid/core/SearchFilter.java new file mode 100644 index 000000000..9b3440edb --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/core/SearchFilter.java @@ -0,0 +1,73 @@ +package com.todoroo.astrid.core; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.todoroo.astrid.api.FilterListItem; + +/** + * Special filter that triggers the search functionality when accessed. + * + * @author Tim Su + * + */ +public class SearchFilter extends FilterListItem { + + /** + * Constructor for creating a new SearchFilter + * + * @param listingTitle + * Title of this item as displayed on the lists page, e.g. Inbox + */ + public SearchFilter(String listingTitle) { + this.listingTitle = listingTitle; + } + + /** + * Constructor for creating a new SearchFilter + */ + protected SearchFilter() { + // + } + + // --- parcelable + + /** + * {@inheritDoc} + */ + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + /** + * Parcelable creator + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + public SearchFilter createFromParcel(Parcel source) { + SearchFilter item = new SearchFilter(); + item.readFromParcel(source); + return item; + } + + /** + * {@inheritDoc} + */ + public SearchFilter[] newArray(int size) { + return new SearchFilter[size]; + } + + }; + +} diff --git a/astrid/res/drawable/arrow_branch.png b/astrid/res/drawable/arrow_branch.png new file mode 100644 index 000000000..68cee8c73 Binary files /dev/null and b/astrid/res/drawable/arrow_branch.png differ diff --git a/astrid/res/drawable/arrow_down.png b/astrid/res/drawable/arrow_down.png new file mode 100644 index 000000000..e9e134311 Binary files /dev/null and b/astrid/res/drawable/arrow_down.png differ diff --git a/astrid/res/drawable/arrow_join.png b/astrid/res/drawable/arrow_join.png new file mode 100644 index 000000000..171d453fb Binary files /dev/null and b/astrid/res/drawable/arrow_join.png differ diff --git a/astrid/res/drawable/gnome_filter.png b/astrid/res/drawable/gnome_filter.png new file mode 100644 index 000000000..5e6328928 Binary files /dev/null and b/astrid/res/drawable/gnome_filter.png differ diff --git a/astrid/res/layout/custom_filter_activity.xml b/astrid/res/layout/custom_filter_activity.xml new file mode 100644 index 000000000..331111af6 --- /dev/null +++ b/astrid/res/layout/custom_filter_activity.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + +