From e1f47b734c0320979f81a907cdeea1376af239f8 Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Wed, 29 Sep 2010 13:42:50 -0700 Subject: [PATCH 1/7] Adding missing ADD_ONs. (But there are more.) --- astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java index a4168d86e..a6b7eb0fe 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java @@ -133,6 +133,7 @@ public class TagFilterExposer extends BroadcastReceiver { // transmit filter list Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TagsPlugin.IDENTIFIER); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } From 48802ec93d30a8af60a584f09f2c7109b7019c66 Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Tue, 28 Sep 2010 17:16:35 -0700 Subject: [PATCH 2/7] scoping --- .../astrid/core/CustomFilterActivity.java | 190 +++++++++--------- 1 file changed, 100 insertions(+), 90 deletions(-) diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java index 780546346..2a63b3606 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java @@ -157,104 +157,114 @@ public class CustomFilterActivity extends ListActivity { Resources r = getResources(); // built in criteria: due date - String[] entryValues = new String[] { - "0", - PermaSql.VALUE_EOD_YESTERDAY, - PermaSql.VALUE_EOD, - PermaSql.VALUE_EOD_TOMORROW, - PermaSql.VALUE_EOD_DAY_AFTER, - PermaSql.VALUE_EOD_NEXT_WEEK, - PermaSql.VALUE_EOD_NEXT_MONTH, - }; - ContentValues values = new ContentValues(); - values.put(Task.DUE_DATE.name, "?"); - CustomFilterCriterion criterion = new MultipleSelectCriterion( - IDENTIFIER_DUEDATE, - getString(R.string.CFC_dueBefore_text), - Query.select(Task.ID).from(Task.TABLE).where( - Criterion.and( - TaskCriteria.activeAndVisible(), - Criterion.or( - Field.field("?").eq(0), - Task.DUE_DATE.gt(0)), - Task.DUE_DATE.lte("?"))).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); + { + String[] entryValues = new String[] { + "0", + PermaSql.VALUE_EOD_YESTERDAY, + PermaSql.VALUE_EOD, + PermaSql.VALUE_EOD_TOMORROW, + PermaSql.VALUE_EOD_DAY_AFTER, + PermaSql.VALUE_EOD_NEXT_WEEK, + PermaSql.VALUE_EOD_NEXT_MONTH, + }; + ContentValues values = new ContentValues(); + values.put(Task.DUE_DATE.name, "?"); + CustomFilterCriterion criterion = new MultipleSelectCriterion( + IDENTIFIER_DUEDATE, + getString(R.string.CFC_dueBefore_text), + Query.select(Task.ID).from(Task.TABLE).where( + Criterion.and( + TaskCriteria.activeAndVisible(), + Criterion.or( + Field.field("?").eq(0), + Task.DUE_DATE.gt(0)), + Task.DUE_DATE.lte("?"))).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), - }; - String[] entries = new String[] { - "!!!!", "!!!", "!!", "!" - }; - values = new ContentValues(); - values.put(Task.IMPORTANCE.name, "?"); - criterion = new MultipleSelectCriterion( - IDENTIFIER_IMPORTANCE, - getString(R.string.CFC_importance_text), - Query.select(Task.ID).from(Task.TABLE).where( - Criterion.and(TaskCriteria.activeAndVisible(), - Task.IMPORTANCE.lte("?"))).toString(), - values, entries, - entryValues, ((BitmapDrawable)r.getDrawable(R.drawable.tango_warning)).getBitmap(), - getString(R.string.CFC_importance_name)); - criteria.add(criterion); + { + String[] 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), + }; + String[] entries = new String[] { + "!!!!", "!!!", "!!", "!" + }; + ContentValues values = new ContentValues(); + values.put(Task.IMPORTANCE.name, "?"); + CustomFilterCriterion criterion = new MultipleSelectCriterion( + IDENTIFIER_IMPORTANCE, + getString(R.string.CFC_importance_text), + Query.select(Task.ID).from(Task.TABLE).where( + Criterion.and(TaskCriteria.activeAndVisible(), + Task.IMPORTANCE.lte("?"))).toString(), + values, entries, + 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, - TaskCriteria.activeAndVisible()); - 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, "?"); - criterion = new MultipleSelectCriterion( - IDENTIFIER_TAG, - getString(R.string.CFC_tag_text), - Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( - Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( - TaskCriteria.activeAndVisible(), - MetadataCriteria.withKey(TagService.KEY), - TagService.TAG.eq("?"))).toString(), - values, tagNames, tagNames, - ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), - getString(R.string.CFC_tag_name)); - criteria.add(criterion); + { + Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, + TaskCriteria.activeAndVisible()); + String[] tagNames = new String[tags.length]; + for(int i = 0; i < tags.length; i++) + tagNames[i] = tags[i].tag; + ContentValues values = new ContentValues(); + values.put(Metadata.KEY.name, TagService.KEY); + values.put(TagService.TAG.name, "?"); + CustomFilterCriterion criterion = new MultipleSelectCriterion( + IDENTIFIER_TAG, + getString(R.string.CFC_tag_text), + Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( + Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( + TaskCriteria.activeAndVisible(), + MetadataCriteria.withKey(TagService.KEY), + TagService.TAG.eq("?"))).toString(), + values, tagNames, tagNames, + ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), + getString(R.string.CFC_tag_name)); + criteria.add(criterion); + } // built in criteria: tags containing X - criterion = new TextInputCriterion( - IDENTIFIER_TAG, - getString(R.string.CFC_tag_contains_text), - Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( - Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( - TaskCriteria.activeAndVisible(), - MetadataCriteria.withKey(TagService.KEY), - TagService.TAG.like("%?%"))).toString(), - null, getString(R.string.CFC_tag_contains_name), "", - ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap(), - getString(R.string.CFC_tag_contains_name)); - criteria.add(criterion); + { + CustomFilterCriterion criterion = new TextInputCriterion( + IDENTIFIER_TAG, + getString(R.string.CFC_tag_contains_text), + Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( + Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( + TaskCriteria.activeAndVisible(), + MetadataCriteria.withKey(TagService.KEY), + TagService.TAG.like("%?%"))).toString(), + null, getString(R.string.CFC_tag_contains_name), "", + ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap(), + getString(R.string.CFC_tag_contains_name)); + criteria.add(criterion); + } // built in criteria: title containing X - values = new ContentValues(); - values.put(Task.TITLE.name, "?"); - criterion = new TextInputCriterion( - IDENTIFIER_TAG, - getString(R.string.CFC_title_contains_text), - Query.select(Task.ID).from(Task.TABLE).where( - Criterion.and(TaskCriteria.activeAndVisible(), - Task.TITLE.like("%?%"))).toString(), - null, getString(R.string.CFC_title_contains_name), "", - ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(), - getString(R.string.CFC_title_contains_name)); - criteria.add(criterion); + { + ContentValues values = new ContentValues(); + values.put(Task.TITLE.name, "?"); + CustomFilterCriterion criterion = new TextInputCriterion( + IDENTIFIER_TAG, + getString(R.string.CFC_title_contains_text), + Query.select(Task.ID).from(Task.TABLE).where( + Criterion.and(TaskCriteria.activeAndVisible(), + Task.TITLE.like("%?%"))).toString(), + null, getString(R.string.CFC_title_contains_name), "", + ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(), + getString(R.string.CFC_title_contains_name)); + criteria.add(criterion); + } } From 11c30a522cb6305ab47cc8a4e99f75f222e68f22 Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Tue, 28 Sep 2010 17:47:12 -0700 Subject: [PATCH 3/7] API-ifying custom filter criteria. Moving tag custom filter criteria into their own exposer. --- astrid/AndroidManifest.xml | 6 + .../astrid/core/CustomFilterActivity.java | 112 +++++++++--------- .../tags/TagCustomFilterCriteriaExposer.java | 79 ++++++++++++ 3 files changed, 144 insertions(+), 53 deletions(-) create mode 100644 astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 498c34a9b..0e9bea3f4 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -283,6 +283,12 @@ + + + + + + diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java index 2a63b3606..2a44da053 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java @@ -5,14 +5,16 @@ import java.util.List; import java.util.Map.Entry; import android.app.ListActivity; -import android.content.ContentValues; -import android.content.Intent; +import android.content.*; +import android.content.IntentFilter; import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; +import android.os.Parcelable; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; @@ -28,21 +30,12 @@ import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Field; -import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Query; import com.todoroo.astrid.activity.TaskListActivity; -import com.todoroo.astrid.api.CustomFilterCriterion; -import com.todoroo.astrid.api.Filter; -import com.todoroo.astrid.api.MultipleSelectCriterion; -import com.todoroo.astrid.api.PermaSql; -import com.todoroo.astrid.api.TextInputCriterion; +import com.todoroo.astrid.api.*; import com.todoroo.astrid.dao.Database; -import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.tags.TagService; -import com.todoroo.astrid.tags.TagService.Tag; /** * Activity that allows users to build custom filters @@ -119,9 +112,12 @@ public class CustomFilterActivity extends ListActivity { private TextView filterName; private CustomFilterAdapter adapter; + private boolean criteriaRequested = false; private final ArrayList criteria = new ArrayList(); + private FilterCriteriaReceiver filterCriteriaReceiver = new FilterCriteriaReceiver(); + // --- activity @Autowired @@ -150,10 +146,15 @@ public class CustomFilterActivity extends ListActivity { } /** - * Populate criteria list with built in and plugin criteria + * Populate criteria list with built in and plugin criteria. The request is sent to every application + * registered to listen for this broadcast. Each plugin can then add criteria to this activity. */ @SuppressWarnings("nls") private void populateCriteria() { + if (criteriaRequested) return; + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_CUSTOM_FILTER_CRITERIA); + sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + Resources r = getResources(); // built in criteria: due date @@ -210,46 +211,6 @@ public class CustomFilterActivity extends ListActivity { criteria.add(criterion); } - // built in criteria: tags - { - Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, - TaskCriteria.activeAndVisible()); - String[] tagNames = new String[tags.length]; - for(int i = 0; i < tags.length; i++) - tagNames[i] = tags[i].tag; - ContentValues values = new ContentValues(); - values.put(Metadata.KEY.name, TagService.KEY); - values.put(TagService.TAG.name, "?"); - CustomFilterCriterion criterion = new MultipleSelectCriterion( - IDENTIFIER_TAG, - getString(R.string.CFC_tag_text), - Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( - Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( - TaskCriteria.activeAndVisible(), - MetadataCriteria.withKey(TagService.KEY), - TagService.TAG.eq("?"))).toString(), - values, tagNames, tagNames, - ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), - getString(R.string.CFC_tag_name)); - criteria.add(criterion); - } - - // built in criteria: tags containing X - { - CustomFilterCriterion criterion = new TextInputCriterion( - IDENTIFIER_TAG, - getString(R.string.CFC_tag_contains_text), - Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( - Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( - TaskCriteria.activeAndVisible(), - MetadataCriteria.withKey(TagService.KEY), - TagService.TAG.like("%?%"))).toString(), - null, getString(R.string.CFC_tag_contains_name), "", - ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap(), - getString(R.string.CFC_tag_contains_name)); - criteria.add(criterion); - } - // built in criteria: title containing X { ContentValues values = new ContentValues(); @@ -266,6 +227,20 @@ public class CustomFilterActivity extends ListActivity { criteria.add(criterion); } + criteriaRequested = true; + } + + @Override + protected void onResume() { + super.onResume(); + registerReceiver(filterCriteriaReceiver, new IntentFilter(AstridApiConstants.BROADCAST_SEND_CUSTOM_FILTER_CRITERIA)); + populateCriteria(); + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(filterCriteriaReceiver); } private CriterionInstance getStartingUniverse() { @@ -503,4 +478,35 @@ public class CustomFilterActivity extends ListActivity { return super.onMenuItemSelected(featureId, item); } + public class FilterCriteriaReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + try { + final Parcelable[] filters = intent.getExtras(). + getParcelableArray(AstridApiConstants.EXTRAS_RESPONSE); + final List addedCriterions = new ArrayList(filters.length); + for (Parcelable filter : filters) { + addedCriterions.add((CustomFilterCriterion)filter); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + criteria.addAll(addedCriterions); + } + }); + } catch (Exception e) { + String addon; + try { + addon = intent.getStringExtra(AstridApiConstants.EXTRAS_ADDON); + } catch (Exception e1) { + Log.e("receive-custom-filter-criteria-error-retrieving-addon", + e.toString(), e); + return; + } + Log.e("receive-custom-filter-criteria-" + //$NON-NLS-1$ + addon, + e.toString(), e); + } + } + } } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java new file mode 100644 index 000000000..1296f0f38 --- /dev/null +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java @@ -0,0 +1,79 @@ +package com.todoroo.astrid.tags; + +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; +import com.timsu.astrid.R; +import com.todoroo.andlib.sql.Criterion; +import com.todoroo.andlib.sql.Join; +import com.todoroo.andlib.sql.Query; +import com.todoroo.astrid.api.AstridApiConstants; +import com.todoroo.astrid.api.CustomFilterCriterion; +import com.todoroo.astrid.api.MultipleSelectCriterion; +import com.todoroo.astrid.api.TextInputCriterion; +import com.todoroo.astrid.dao.MetadataDao; +import com.todoroo.astrid.dao.TaskDao; +import com.todoroo.astrid.data.Metadata; +import com.todoroo.astrid.data.Task; + +public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { + private static final String IDENTIFIER_TAG = "tag"; //$NON-NLS-1$ + + @Override + public void onReceive(Context context, Intent intent) { + Resources r = context.getResources(); + + CustomFilterCriterion[] ret = new CustomFilterCriterion[2]; + int j = 0; + + // built in criteria: tags + { + TagService.Tag[] tags = TagService.getInstance().getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, + TaskDao.TaskCriteria.activeAndVisible()); + String[] tagNames = new String[tags.length]; + for(int i = 0; i < tags.length; i++) + tagNames[i] = tags[i].tag; + ContentValues values = new ContentValues(); + values.put(Metadata.KEY.name, TagService.KEY); + values.put(TagService.TAG.name, "?"); + CustomFilterCriterion criterion = new MultipleSelectCriterion( + IDENTIFIER_TAG, + context.getString(R.string.CFC_tag_text), + Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( + Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( + TaskDao.TaskCriteria.activeAndVisible(), + MetadataDao.MetadataCriteria.withKey(TagService.KEY), + TagService.TAG.eq("?"))).toString(), + values, tagNames, tagNames, + ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), + context.getString(R.string.CFC_tag_name)); + ret[j++] = criterion; + } + + // built in criteria: tags containing X + { + CustomFilterCriterion criterion = new TextInputCriterion( + IDENTIFIER_TAG, + context.getString(R.string.CFC_tag_contains_text), + Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( + Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( + TaskDao.TaskCriteria.activeAndVisible(), + MetadataDao.MetadataCriteria.withKey(TagService.KEY), + TagService.TAG.like("%?%"))).toString(), + null, context.getString(R.string.CFC_tag_contains_name), "", + ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags2)).getBitmap(), + context.getString(R.string.CFC_tag_contains_name)); + ret[j++] = criterion; + } + + // transmit filter list + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_CUSTOM_FILTER_CRITERIA); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, ret); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, TagsPlugin.IDENTIFIER); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + + } +} From 9bf244812cc74bbe4fa3fd3c71e650dd04d9f18b Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Wed, 29 Sep 2010 10:31:23 -0700 Subject: [PATCH 4/7] Notice about non-unique identifier. --- .../com/todoroo/astrid/core/CustomFilterActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java index 2a44da053..eed38ac5b 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java @@ -216,7 +216,7 @@ public class CustomFilterActivity extends ListActivity { ContentValues values = new ContentValues(); values.put(Task.TITLE.name, "?"); CustomFilterCriterion criterion = new TextInputCriterion( - IDENTIFIER_TAG, + IDENTIFIER_TAG, // really? This isn't IDENTIFIER_TITLE? getString(R.string.CFC_title_contains_text), Query.select(Task.ID).from(Task.TABLE).where( Criterion.and(TaskCriteria.activeAndVisible(), From cb407bbd53ac64f3913a784a9e9977dc874b77c4 Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Wed, 29 Sep 2010 17:22:32 -0700 Subject: [PATCH 5/7] Producteev custom filters. --- astrid/AndroidManifest.xml | 6 + .../astrid/core/CustomFilterActivity.java | 15 ++- ...ProducteevCustomFilterCriteriaExposer.java | 116 ++++++++++++++++++ .../sync/ProducteevDataService.java | 28 +++-- .../producteev/sync/ProducteevUser.java | 32 ++++- astrid/res/values/strings-producteev.xml | 10 +- 6 files changed, 191 insertions(+), 16 deletions(-) create mode 100644 astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevCustomFilterCriteriaExposer.java diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 0e9bea3f4..df81d6190 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -423,6 +423,12 @@ + + + + + + users = new TreeSet(); + for (ProducteevDashboard dashboard : dashboards) { + users.addAll(dashboard.getUsers()); + } + int numUsers = users.size(); + String[] userNames = new String[numUsers]; + String[] userIds = new String[numUsers]; + int i = 0; + for (ProducteevUser user : users) { + userNames[i] = user.toString(); + userIds[i] = String.valueOf(user.getId()); + i++; + } + ContentValues values = new ContentValues(2); + values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY); + values.put(ProducteevTask.RESPONSIBLE_ID.name, "?"); + CustomFilterCriterion criterion = new MultipleSelectCriterion( + IDENTIFIER_PRODUCTEEV, // still not really sure what the point of this identifier is + context.getString(R.string.CFC_producteev_assigned_to_text), + // Todo: abstract these metadata queries, and unify this code with the CustomFilterExposers. + Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( + Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( + TaskDao.TaskCriteria.activeAndVisible(), + MetadataDao.MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), + ProducteevTask.RESPONSIBLE_ID.eq("?"))).toString(), + values, // what is this? + userNames, + userIds, + ((BitmapDrawable)r.getDrawable(R.drawable.silk_user_gray)).getBitmap(), + context.getString(R.string.CFC_producteev_assigned_to_name)); + ret[j++] = criterion; + } + + // transmit filter list + Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_CUSTOM_FILTER_CRITERIA); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, ProducteevUtilities.IDENTIFIER); + broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, ret); + context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); + } +} diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java index 5cfccb6a5..02683d78d 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevDataService.java @@ -206,31 +206,37 @@ public final class ProducteevDataService { return cursor; } - // --- dashboard methods - - private StoreObject[] dashboards = null; + private void readDashboards() { + if (dashboards == null) { + dashboards = readStoreObjects(ProducteevDashboard.TYPE); + } + } /** - * Reads dashboards + * Reads store objects. */ - private void readDashboards() { - if(dashboards != null) - return; - + public StoreObject[] readStoreObjects(String type) { + StoreObject[] ret; TodorooCursor cursor = storeObjectDao.query(Query.select(StoreObject.PROPERTIES). where(StoreObjectCriteria.byType(ProducteevDashboard.TYPE))); try { - dashboards = new StoreObject[cursor.getCount()]; - for(int i = 0; i < dashboards.length; i++) { + ret = new StoreObject[cursor.getCount()]; + for(int i = 0; i < ret.length; i++) { cursor.moveToNext(); StoreObject dashboard = new StoreObject(cursor); - dashboards[i] = dashboard; + ret[i] = dashboard; } } finally { cursor.close(); } + + return ret; } + // --- dashboard methods + + private StoreObject[] dashboards = null; + /** * @return a list of dashboards */ diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevUser.java b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevUser.java index 7af862b69..1d31b050c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevUser.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/sync/ProducteevUser.java @@ -8,7 +8,7 @@ import org.json.JSONObject; * @author Arne Jans */ @SuppressWarnings("nls") -public class ProducteevUser { +public class ProducteevUser implements Comparable { private final long id; @@ -82,4 +82,34 @@ public class ProducteevUser { displayString += email; return displayString; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ProducteevUser that = (ProducteevUser) o; + + if (id != that.id) return false; + if (email != null ? !email.equals(that.email) : that.email != null) return false; + if (firstname != null ? !firstname.equals(that.firstname) : that.firstname != null) return false; + if (lastname != null ? !lastname.equals(that.lastname) : that.lastname != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (email != null ? email.hashCode() : 0); + result = 31 * result + (firstname != null ? firstname.hashCode() : 0); + result = 31 * result + (lastname != null ? lastname.hashCode() : 0); + return result; + } + + @Override + public int compareTo(ProducteevUser o) { + int ret = toString().compareTo(o.toString()); + return ret == 0 ? (new Long(id).compareTo(o.id)) : ret; + } } diff --git a/astrid/res/values/strings-producteev.xml b/astrid/res/values/strings-producteev.xml index e2004c13b..0991e023d 100644 --- a/astrid/res/values/strings-producteev.xml +++ b/astrid/res/values/strings-producteev.xml @@ -123,5 +123,13 @@ <Default> - + + In workspace: ? + + In workspace... + + Assigned to: ? + + Assigned to... + \ No newline at end of file From eabe880f522fa0fe75be7d42572591f8d67a504c Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Wed, 29 Sep 2010 16:40:26 -0700 Subject: [PATCH 6/7] Fixed bug in producteev filters. If multiple users had the same name, then only one of them would show up in the filters. Also, if a user existed in multiple workspaces, one of them would get picked randomly for the filter, and all tasks assigned to that user but on a workspace other than the randomly picked one would not show up. Now all tasks by that user appear, regardless of workspace. Also, it's 9 lines shorter. :) --- .../producteev/ProducteevFilterExposer.java | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java index 7d88030ad..8d3e75e39 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevFilterExposer.java @@ -3,8 +3,7 @@ */ package com.todoroo.astrid.producteev; -import java.util.TreeMap; -import java.util.Map.Entry; +import java.util.TreeSet; import android.content.BroadcastReceiver; import android.content.ContentValues; @@ -14,7 +13,6 @@ import android.content.Intent; import com.timsu.astrid.R; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.QueryTemplate; -import com.todoroo.andlib.utility.Pair; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterCategory; @@ -27,6 +25,7 @@ import com.todoroo.astrid.data.StoreObject; import com.todoroo.astrid.producteev.sync.ProducteevDashboard; import com.todoroo.astrid.producteev.sync.ProducteevDataService; import com.todoroo.astrid.producteev.sync.ProducteevTask; +import com.todoroo.astrid.producteev.sync.ProducteevUser; /** * Exposes filters based on Producteev Dashboards @@ -59,20 +58,19 @@ public class ProducteevFilterExposer extends BroadcastReceiver { return filter; } - private Filter filterFromUser(Context context, String user, Pair ids) { - String title = context.getString(R.string.producteev_FEx_responsible_title, user); + private Filter filterFromUser(Context context, ProducteevUser user) { + String title = context.getString(R.string.producteev_FEx_responsible_title, user.toString()); ContentValues values = new ContentValues(); values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY); - values.put(ProducteevTask.DASHBOARD_ID.name, ids.getLeft()); values.put(ProducteevTask.ID.name, 0); values.put(ProducteevTask.CREATOR_ID.name, 0); - values.put(ProducteevTask.RESPONSIBLE_ID.name, ids.getRight()); - Filter filter = new Filter(user, title, new QueryTemplate().join( + values.put(ProducteevTask.RESPONSIBLE_ID.name, user.getId()); + Filter filter = new Filter(user.toString(), title, new QueryTemplate().join( ProducteevDataService.METADATA_JOIN).where(Criterion.and( MetadataCriteria.withKey(ProducteevTask.METADATA_KEY), TaskCriteria.isActive(), TaskCriteria.isVisible(), - ProducteevTask.RESPONSIBLE_ID.eq(ids.getRight()))), + ProducteevTask.RESPONSIBLE_ID.eq(user.getId()))), values); return filter; @@ -100,11 +98,11 @@ public class ProducteevFilterExposer extends BroadcastReceiver { dashboardFilters); // load responsible people - TreeMap> people = loadResponsiblePeople(dashboards); + TreeSet people = loadResponsiblePeople(dashboards); Filter[] peopleFilters = new Filter[people.size()]; int index = 0; - for(Entry> person : people.entrySet()) - peopleFilters[index++] = filterFromUser(context, person.getKey(), person.getValue()); + for (ProducteevUser person : people) + peopleFilters[index++] = filterFromUser(context, person); FilterCategory producteevUsers = new FilterCategory(context.getString(R.string.producteev_FEx_responsible), peopleFilters); @@ -124,22 +122,14 @@ public class ProducteevFilterExposer extends BroadcastReceiver { * @return people in a map of name => pair(dashboard id, user id) */ @SuppressWarnings("nls") - private TreeMap> loadResponsiblePeople( - StoreObject[] dashboards) { - TreeMap> results = new TreeMap>(); + private TreeSet loadResponsiblePeople(StoreObject[] dashboards) { + TreeSet users = new TreeSet(); for(StoreObject dashboard : dashboards) { - String users = dashboard.getValue(ProducteevDashboard.USERS); - String[] entries = users.split(";"); - for(String entry : entries) { - String[] data = entry.split(","); - if(data.length != 2) - continue; - results.put(data[1], new Pair( - dashboard.getValue(ProducteevDashboard.REMOTE_ID), data[0])); // name, id - } + ProducteevDashboard elDashboard = new ProducteevDashboard(dashboard); + users.addAll(elDashboard.getUsers()); } - return results; + return users; } } From a9b4f968d7fcbddf6d5d731f9b82cd29de4430b2 Mon Sep 17 00:00:00 2001 From: Joshua Ball Date: Wed, 29 Sep 2010 17:33:12 -0700 Subject: [PATCH 7/7] Made criterion identifiers unique. --- .../astrid/core/CustomFilterActivity.java | 46 +++++++++---------- ...ProducteevCustomFilterCriteriaExposer.java | 7 +-- .../tags/TagCustomFilterCriteriaExposer.java | 7 +-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java index 5769de23c..bb1e5a824 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterActivity.java @@ -1,7 +1,6 @@ package com.todoroo.astrid.core; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.Map.Entry; import android.app.ListActivity; @@ -45,7 +44,7 @@ import com.todoroo.astrid.data.Task; */ public class CustomFilterActivity extends ListActivity { - private static final String IDENTIFIER_TAG = "tag"; //$NON-NLS-1$ + private static final String IDENTIFIER_TITLE = "title"; //$NON-NLS-1$ private static final String IDENTIFIER_IMPORTANCE = "importance"; //$NON-NLS-1$ private static final String IDENTIFIER_DUEDATE = "dueDate"; //$NON-NLS-1$ private static final String IDENTIFIER_UNIVERSE = "active"; //$NON-NLS-1$ @@ -112,9 +111,7 @@ public class CustomFilterActivity extends ListActivity { private TextView filterName; private CustomFilterAdapter adapter; - private boolean criteriaRequested = false; - private final ArrayList criteria = - new ArrayList(); + private final Map criteria = Collections.synchronizedMap(new LinkedHashMap()); private FilterCriteriaReceiver filterCriteriaReceiver = new FilterCriteriaReceiver(); @@ -151,7 +148,6 @@ public class CustomFilterActivity extends ListActivity { */ @SuppressWarnings("nls") private void populateCriteria() { - if (criteriaRequested) return; Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_REQUEST_CUSTOM_FILTER_CRITERIA); sendOrderedBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); @@ -183,7 +179,7 @@ public class CustomFilterActivity extends ListActivity { 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); + criteria.put(IDENTIFIER_DUEDATE, criterion); } // built in criteria: importance @@ -208,7 +204,7 @@ public class CustomFilterActivity extends ListActivity { values, entries, entryValues, ((BitmapDrawable)r.getDrawable(R.drawable.tango_warning)).getBitmap(), getString(R.string.CFC_importance_name)); - criteria.add(criterion); + criteria.put(IDENTIFIER_IMPORTANCE, criterion); } // built in criteria: title containing X @@ -216,7 +212,7 @@ public class CustomFilterActivity extends ListActivity { ContentValues values = new ContentValues(); values.put(Task.TITLE.name, "?"); CustomFilterCriterion criterion = new TextInputCriterion( - IDENTIFIER_TAG, // really? This isn't IDENTIFIER_TITLE? + IDENTIFIER_TITLE, getString(R.string.CFC_title_contains_text), Query.select(Task.ID).from(Task.TABLE).where( Criterion.and(TaskCriteria.activeAndVisible(), @@ -224,10 +220,8 @@ public class CustomFilterActivity extends ListActivity { null, getString(R.string.CFC_title_contains_name), "", ((BitmapDrawable)r.getDrawable(R.drawable.tango_alpha)).getBitmap(), getString(R.string.CFC_title_contains_name)); - criteria.add(criterion); + criteria.put(IDENTIFIER_TITLE, criterion); } - - criteriaRequested = true; } @Override @@ -303,14 +297,15 @@ public class CustomFilterActivity extends ListActivity { return; } - for(int i = 0; i < criteria.size(); i++) { - CustomFilterCriterion item = criteria.get(i); + int i = 0; + for (CustomFilterCriterion item : criteria.values()) { try { menu.add(CustomFilterActivity.MENU_GROUP_FILTER, i, 0, item.name); } catch (NullPointerException e) { throw new NullPointerException("One of the criteria is null. Criteria: " + criteria); } + i++; } } }); @@ -453,12 +448,21 @@ public class CustomFilterActivity extends ListActivity { adapter.notifyDataSetInvalidated(); } + private V getNth(int index, Map map) { + int i = 0; + for (V v : map.values()) { + if (i == index) return v; + i++; + } + throw new IllegalArgumentException("out of bounds"); + } + @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { // group filter option if(item.getGroupId() == MENU_GROUP_FILTER) { // give an initial value for the row before adding it - CustomFilterCriterion criterion = criteria.get(item.getItemId()); + CustomFilterCriterion criterion = getNth(item.getItemId(), criteria); final CriterionInstance instance = new CriterionInstance(); instance.criterion = criterion; adapter.showOptionsFor(instance, new Runnable() { @@ -493,16 +497,10 @@ public class CustomFilterActivity extends ListActivity { try { final Parcelable[] filters = intent.getExtras(). getParcelableArray(AstridApiConstants.EXTRAS_RESPONSE); - final List addedCriterions = new ArrayList(filters.length); for (Parcelable filter : filters) { - addedCriterions.add((CustomFilterCriterion)filter); + CustomFilterCriterion filterCriterion = (CustomFilterCriterion) filter; + criteria.put(filterCriterion.identifier, filterCriterion); } - runOnUiThread(new Runnable() { - @Override - public void run() { - criteria.addAll(addedCriterions); - } - }); } catch (Exception e) { String addon; try { diff --git a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevCustomFilterCriteriaExposer.java b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevCustomFilterCriteriaExposer.java index cbb33cf30..02f20c6cf 100644 --- a/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevCustomFilterCriteriaExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/producteev/ProducteevCustomFilterCriteriaExposer.java @@ -27,7 +27,8 @@ import java.util.Set; import java.util.TreeSet; public class ProducteevCustomFilterCriteriaExposer extends BroadcastReceiver { - private static final String IDENTIFIER_PRODUCTEEV = "producteev"; // still don't really know what this is + private static final String IDENTIFIER_PRODUCTEEV_WORKSPACE = "producteev_workspace"; //$NON-NLS-1$ + private static final String IDENTIFIER_PRODUCTEEV_ASSIGNEE = "producteev_assignee"; //$NON-NLS-1$ @Override public void onReceive(Context context, Intent intent) { @@ -57,7 +58,7 @@ public class ProducteevCustomFilterCriteriaExposer extends BroadcastReceiver { values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY); values.put(ProducteevTask.DASHBOARD_ID.name, "?"); CustomFilterCriterion criterion = new MultipleSelectCriterion( - IDENTIFIER_PRODUCTEEV, + IDENTIFIER_PRODUCTEEV_WORKSPACE, context.getString(R.string.CFC_producteev_in_workspace_text), // Todo: abstract these metadata queries Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( @@ -91,7 +92,7 @@ public class ProducteevCustomFilterCriteriaExposer extends BroadcastReceiver { values.put(Metadata.KEY.name, ProducteevTask.METADATA_KEY); values.put(ProducteevTask.RESPONSIBLE_ID.name, "?"); CustomFilterCriterion criterion = new MultipleSelectCriterion( - IDENTIFIER_PRODUCTEEV, // still not really sure what the point of this identifier is + IDENTIFIER_PRODUCTEEV_ASSIGNEE, context.getString(R.string.CFC_producteev_assigned_to_text), // Todo: abstract these metadata queries, and unify this code with the CustomFilterExposers. Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java index 1296f0f38..70985eec1 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java @@ -20,7 +20,8 @@ import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { - private static final String IDENTIFIER_TAG = "tag"; //$NON-NLS-1$ + private static final String IDENTIFIER_TAG_IS = "tag_is"; //$NON-NLS-1$ + private static final String IDENTIFIER_TAG_CONTAINS = "tag_contains"; //$NON-NLS-1$ @Override public void onReceive(Context context, Intent intent) { @@ -40,7 +41,7 @@ public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { values.put(Metadata.KEY.name, TagService.KEY); values.put(TagService.TAG.name, "?"); CustomFilterCriterion criterion = new MultipleSelectCriterion( - IDENTIFIER_TAG, + IDENTIFIER_TAG_IS, context.getString(R.string.CFC_tag_text), Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and( @@ -56,7 +57,7 @@ public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { // built in criteria: tags containing X { CustomFilterCriterion criterion = new TextInputCriterion( - IDENTIFIER_TAG, + IDENTIFIER_TAG_CONTAINS, context.getString(R.string.CFC_tag_contains_text), Query.select(Metadata.TASK).from(Metadata.TABLE).join(Join.inner( Task.TABLE, Metadata.TASK.eq(Task.ID))).where(Criterion.and(