Wrote code for custom filter. A bunch of code. Time to see if it works

pull/14/head
Tim Su 14 years ago
parent b50e22996c
commit 0f77edb13e

@ -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 <tim@todoroo.com>
*
*/
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.
* <p>
* 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
* <p>
* Examples:
* <ul>
* <li><code>SELECT _id FROM tasks WHERE dueDate <= %s</code>
* <li><code>SELECT task FROM metadata WHERE value = '%s'</code>
* </ul>
*/
@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<CustomFilterCriterion> CREATOR = new Parcelable.Creator<CustomFilterCriterion>() {
/**
* {@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];
}
};
}

@ -37,6 +37,10 @@ public class Field extends DBObject<Field> {
return UnaryCriterion.lt(this, value);
}
public Criterion lte(final Object value) {
return UnaryCriterion.lte(this, value);
}
public Criterion isNull() {
return UnaryCriterion.isNull(this);
}

@ -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

@ -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 <tim@todoroo.com>
*
*/
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<CustomFilterCriterion> criteria =
new ArrayList<CustomFilterCriterion>();
// --- 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<CriterionInstance> startingCriteria = new ArrayList<CriterionInstance>();
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);
}
}

@ -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 <tim@todoroo.com>
*
*/
public class CustomFilterAdapter extends ArrayAdapter<CriterionInstance> {
private final Activity activity;
private final LayoutInflater inflater;
public CustomFilterAdapter(Activity activity, List<CriterionInstance> 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);
}
}

@ -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 <tim@todoroo.com>
*
*/
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);
}
}

@ -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 <tim@todoroo.com>
*
*/
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<IntentFilter> CREATOR = new Parcelable.Creator<IntentFilter>() {
/**
* {@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];
}
};
}

@ -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 <tim@todoroo.com>
*
*/
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<SearchFilter> CREATOR = new Parcelable.Creator<SearchFilter>() {
/**
* {@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];
}
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/background_gradient"
android:orientation="vertical">
<!-- Filter Name -->
<EditText android:id="@+id/filterName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/CFA_filterName_hint"
android:text="@string/CFA_filterName_new"/>
<!-- List -->
<ListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="100"
android:scrollbars="vertical"
android:cacheColorHint="#00000000"/>
<!-- buttons -->
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:padding="5dip"
android:background="@drawable/edit_header"
android:baselineAligned="false">
<Button android:id="@+id/add"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="@drawable/tango_add"
android:text="@string/CFA_button_add" />
<Button
android:id="@+id/discard_basic"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/CFA_button_save"
android:drawableRight="@drawable/tango_save" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See the file "LICENSE" for the full license governing this code. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:background="@android:drawable/list_selector_background"
android:paddingLeft="4dip"
android:paddingRight="6dip"
android:orientation="horizontal">
<!-- filter intersection type icon -->
<ImageView android:id="@+id/type"
android:layout_width="32dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:scaleType="center"/>
<!-- filter icon -->
<ImageView android:id="@+id/icon"
android:layout_width="32dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:visibility="gone"/>
<!-- filter text -->
<TextView android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="100"
android:paddingLeft="5dip"
android:textSize="22sp"
android:gravity="center_vertical"/>
<!-- filter graphic -->
<com.todoroo.astrid.core.FilterView android:id="@+id/filter"
android:layout_width="64dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:paddingLeft="5dip"
android:paddingRight="5dip"/>
</LinearLayout>

@ -30,29 +30,49 @@
<string name="CFA_filterName_copy">Copy of %s</string>
<!-- Filter Starting Universe: all tasks -->
<string name="CFA_universe_all">All Tasks</string>
<!-- =================================== old stuff, TODO translation === -->
<!-- sort recent modification filter -->
<string name="BFE_Recent">Recently Modified</string>
<!-- Completed Filter -->
<string name="BFE_Completed">Completed Tasks</string>
<!-- hidden tasks filter -->
<string name="BFE_Hidden">Hidden Tasks</string>
<!-- sort Alphabetical filter -->
<string name="BFE_Alphabetical">By Title</string>
<!-- sort Due Date filter -->
<string name="BFE_DueDate">By Due Date</string>
<!-- sort Importance filter -->
<string name="BFE_Importance">By Importance</string>
<!-- deleted tasks filter -->
<string name="BFE_Deleted">Deleted Tasks</string>
<string name="CFA_universe_all">Active Tasks</string>
<!-- Filter Criteria Type: add (at the begging of title of the criteria) -->
<string name="CFA_type_add">or</string>
<!-- Filter Criteria Type: subtract (at the begging of title of the criteria) -->
<string name="CFA_type_subtract">not</string>
<!-- Filter Criteria Type: intersect (at the begging of title of the criteria) -->
<string name="CFA_type_intersect">also</string>
<!-- Filter Criteria Context Menu: delete -->
<string name="CFA_context_delete">Delete</string>
<!-- Filter Button: add new -->
<string name="CFA_button_add">Add Criteria</string>
<!-- Filter Button: save & go -->
<string name="CFA_button_save">Save &amp; View</string>
<!-- =========================================== CustomFilterCriteria == -->
<!-- Criteria: due by X - display text -->
<string name="CFC_dueBefore_text">Due By: %s</string>
<!-- Criteria: due by X - name of criteria -->
<string name="CFC_dueBefore_name">Due By...</string>
<string-array name="CFC_dueBefore_entries">
<!-- Criteria: due by X - options -->
<item>Yesterday</item>
<item>Today</item>
<item>Tomorrow</item>
<item>Day After Tomorrow</item>
<item>Next Week</item>
</string-array>
<!-- Criteria: importance - display text -->
<string name="CFC_importance_text">Importance >= %s</string>
<!-- Criteria: importance - name of criteria -->
<string name="CFC_importance_name">Importance...</string>
<!-- Criteria: tag - display text -->
<string name="CFC_tag_text">Tagged: %s</string>
<!-- Criteria: tag - name of criteria -->
<string name="CFC_tag_name">Tagged...</string>
</resources>

@ -50,7 +50,7 @@
</style>
<style name="TextAppearance.TAd_ItemDetails">
<item name="android:textSize">10sp</item>
<item name="android:textSize">11sp</item>
<item name="android:textColor">#ff777777</item>
</style>

@ -68,6 +68,13 @@ public class TaskDao extends GenericDao<Task> {
return Task.DELETION_DATE.eq(0);
}
/** @return tasks that have not yet been completed or deleted */
public static Criterion activeAndVisible() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),
Task.DELETION_DATE.eq(0),
Task.HIDE_UNTIL.lt(Functions.now()));
}
/** @return tasks that have not yet been completed or deleted */
public static Criterion isActive() {
return Criterion.and(Task.COMPLETION_DATE.eq(0),

Loading…
Cancel
Save