mirror of https://github.com/tasks/tasks
Wrote code for custom filter. A bunch of code. Time to see if it works
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];
|
||||
}
|
||||
|
||||
};
|
||||
}
|
@ -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>
|
Loading…
Reference in New Issue