diff --git a/api/src/com/todoroo/andlib/utility/AndroidUtilities.java b/api/src/com/todoroo/andlib/utility/AndroidUtilities.java index 838d32200..05737ce2a 100644 --- a/api/src/com/todoroo/andlib/utility/AndroidUtilities.java +++ b/api/src/com/todoroo/andlib/utility/AndroidUtilities.java @@ -583,4 +583,13 @@ public class AndroidUtilities { return dest; } + /** + * Capitalize the first character + * @param string + * @return + */ + public static String capitalize(String string) { + return string.substring(0, 1).toUpperCase() + string.substring(1); + } + } diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml index 76cfe8d26..7fd2967a0 100644 --- a/astrid/AndroidManifest.xml +++ b/astrid/AndroidManifest.xml @@ -226,6 +226,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -359,32 +385,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/astrid/astrid.launch b/astrid/astrid.launch index 7b969a0ea..3ffc7de86 100644 --- a/astrid/astrid.launch +++ b/astrid/astrid.launch @@ -5,9 +5,9 @@ - + - + diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java index 954d7e861..256d22561 100644 --- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java +++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmPreferenceService.java @@ -3,9 +3,12 @@ package com.todoroo.astrid.actfm.sync; import org.json.JSONException; import org.json.JSONObject; +import android.text.TextUtils; + import com.timsu.astrid.R; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.data.RemoteModel; +import com.todoroo.astrid.data.Update; import com.todoroo.astrid.sync.SyncProviderUtilities; /** @@ -78,6 +81,19 @@ public class ActFmPreferenceService extends SyncProviderUtilities { } } + @SuppressWarnings("nls") + public static String updateToString(Update update) { + JSONObject updateUser = ActFmPreferenceService.userFromModel(update); + String description = update.getValue(Update.ACTION); + String message = update.getValue(Update.MESSAGE); + if(update.getValue(Update.ACTION_CODE).equals("task_comment") || + update.getValue(Update.ACTION_CODE).equals("tag_comment")) + description = message; + else if(!TextUtils.isEmpty(message)) + description += " " + message; + return String.format("%s: %s", updateUser.optString("name"), description); + } + @SuppressWarnings("nls") private synchronized static JSONObject thisUser() { if(user == null) { diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java index 7947dd862..a13b6652f 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CoreFilterExposer.java @@ -12,7 +12,6 @@ import android.graphics.drawable.BitmapDrawable; import com.timsu.astrid.R; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.sql.Criterion; -import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.astrid.activity.FilterListActivity; @@ -41,22 +40,10 @@ public final class CoreFilterExposer extends BroadcastReceiver { // core filters Filter inbox = buildInboxFilter(r); - SearchFilter searchFilter = new SearchFilter(r.getString(R.string.BFE_Search)); - searchFilter.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_search)).getBitmap(); - - Filter recent = new Filter(r.getString(R.string.BFE_Recent), - r.getString(R.string.BFE_Recent), - new QueryTemplate().where( - TaskCriteria.ownedByMe()).orderBy( - Order.desc(Task.MODIFICATION_DATE)).limit(15), - null); - recent.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_new)).getBitmap(); // transmit filter list - FilterListItem[] list = new FilterListItem[3]; + FilterListItem[] list = new FilterListItem[1]; list[0] = inbox; - list[1] = recent; - list[2] = searchFilter; Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list); context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); @@ -74,7 +61,7 @@ public final class CoreFilterExposer extends BroadcastReceiver { Criterion.and(MetadataCriteria.withKey(TagService.KEY), TagService.TAG.like("x_%", "x"))))))), //$NON-NLS-1$ //$NON-NLS-2$ null); - inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.tango_home)).getBitmap(); + inbox.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_inbox)).getBitmap(); return inbox; } diff --git a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterExposer.java index d00c4f473..b0c95bf53 100644 --- a/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/core/CustomFilterExposer.java @@ -6,6 +6,7 @@ package com.todoroo.astrid.core; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -16,17 +17,22 @@ import android.os.Bundle; import com.timsu.astrid.R; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.DependencyInjectionService; +import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; +import com.todoroo.andlib.sql.QueryTemplate; +import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.astrid.activity.FilterListActivity; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; -import com.todoroo.astrid.api.FilterCategory; +import com.todoroo.astrid.api.FilterCategoryWithNewButton; import com.todoroo.astrid.api.FilterListItem; -import com.todoroo.astrid.api.IntentFilter; +import com.todoroo.astrid.api.PermaSql; import com.todoroo.astrid.dao.StoreObjectDao; +import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.StoreObject; +import com.todoroo.astrid.data.Task; /** * Exposes Astrid's built in filters to the {@link FilterListActivity} @@ -45,21 +51,16 @@ public final class CustomFilterExposer extends BroadcastReceiver { PendingIntent customFilterIntent = PendingIntent.getActivity(context, 0, new Intent(context, CustomFilterActivity.class), 0); - IntentFilter customFilter = new IntentFilter(r.getString(R.string.BFE_Custom), - customFilterIntent); - customFilter.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.gnome_filter)).getBitmap(); - - Filter[] savedFilters = buildSavedFilters(context); - - FilterListItem[] list; - if(savedFilters.length == 0) { - list = new FilterListItem[1]; - } else { - list = new FilterListItem[2]; - list[1] = new FilterCategory(r.getString(R.string.BFE_Saved), savedFilters); - } - list[0] = customFilter; + Filter[] savedFilters = buildSavedFilters(context, r); + + FilterCategoryWithNewButton heading = new FilterCategoryWithNewButton(r.getString(R.string.BFE_Saved), savedFilters); + heading.label = r.getString(R.string.tag_FEx_add_new); + heading.intent = customFilterIntent; + + + FilterListItem[] list = new FilterListItem[1]; + list[0] = heading; // transmit filter list Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); @@ -67,15 +68,36 @@ public final class CustomFilterExposer extends BroadcastReceiver { context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ); } - private Filter[] buildSavedFilters(Context context) { + private Filter[] buildSavedFilters(Context context, Resources r) { StoreObjectDao dao = PluginServices.getStoreObjectDao(); TodorooCursor cursor = dao.query(Query.select(StoreObject.PROPERTIES).where( StoreObject.TYPE.eq(SavedFilter.TYPE)).orderBy(Order.asc(SavedFilter.NAME))); try { - Filter[] list = new Filter[cursor.getCount()]; + Filter[] list = new Filter[cursor.getCount() + 2]; + + // stock filters + String todayTitle = AndroidUtilities.capitalize(r.getString(R.string.today)); + ContentValues todayValues = new ContentValues(); + todayValues.put(Task.DUE_DATE.name, PermaSql.VALUE_EOD); + list[0] = new Filter(todayTitle, + todayTitle, + new QueryTemplate().where( + Criterion.and(TaskCriteria.activeVisibleMine(), + Task.DUE_DATE.gt(0), + Task.DUE_DATE.lte(PermaSql.VALUE_EOD))), + todayValues); + list[0].listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_calendar)).getBitmap(); + + list[1] = new Filter(r.getString(R.string.BFE_Recent), + r.getString(R.string.BFE_Recent), + new QueryTemplate().where( + TaskCriteria.ownedByMe()).orderBy( + Order.desc(Task.MODIFICATION_DATE)).limit(15), + null); + list[1].listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_pencil)).getBitmap(); StoreObject savedFilter = new StoreObject(); - for(int i = 0; i < list.length; i++) { + for(int i = 2; i < list.length; i++) { cursor.moveToNext(); savedFilter.readFromCursor(cursor); list[i] = SavedFilter.load(savedFilter); @@ -85,6 +107,7 @@ public final class CustomFilterExposer extends BroadcastReceiver { deleteIntent.putExtra(TOKEN_FILTER_NAME, list[i].title); list[i].contextMenuLabels = new String[] { context.getString(R.string.BFE_Saved_delete) }; list[i].contextMenuIntents = new Intent[] { deleteIntent }; + list[i].listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_sliders)).getBitmap(); } return list; diff --git a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksFilterExposer.java index c87829fde..99927d4d6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/gtasks/GtasksFilterExposer.java @@ -3,6 +3,7 @@ */ package com.todoroo.astrid.gtasks; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; @@ -22,8 +23,7 @@ import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; -import com.todoroo.astrid.api.FilterCategory; -import com.todoroo.astrid.api.FilterListHeader; +import com.todoroo.astrid.api.FilterCategoryWithNewButton; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterWithCustomIntent; import com.todoroo.astrid.api.PermaSql; @@ -91,14 +91,14 @@ public class GtasksFilterExposer extends BroadcastReceiver { for(int i = 0; i < lists.length; i++) listFilters[i] = filterFromList(context, lists[i]); - FilterListHeader header = new FilterListHeader(context.getString(R.string.gtasks_FEx_header)); - FilterCategory listsCategory = new FilterCategory(context.getString(R.string.gtasks_FEx_list), + FilterCategoryWithNewButton listsCategory = new FilterCategoryWithNewButton(context.getString(R.string.gtasks_FEx_header), listFilters); + listsCategory.label = context.getString(R.string.tag_FEx_add_new); + listsCategory.intent = PendingIntent.getActivity(context, 0, new Intent(context, GtasksListAdder.class), 0); // transmit filter list - FilterListItem[] list = new FilterListItem[2]; - list[0] = header; - list[1] = listsCategory; + FilterListItem[] list = new FilterListItem[1]; + list[0] = listsCategory; Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_SEND_FILTERS); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_ADDON, GtasksPreferenceService.IDENTIFIER); broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, list); diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java index a6bff4654..0b72ff79c 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagCustomFilterCriteriaExposer.java @@ -51,7 +51,7 @@ public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { MetadataDao.MetadataCriteria.withKey(TagService.KEY), TagService.TAG.eq("?"))).toString(), values, tagNames, tagNames, - ((BitmapDrawable)r.getDrawable(R.drawable.filter_tags1)).getBitmap(), + ((BitmapDrawable)r.getDrawable(R.drawable.gl_list)).getBitmap(), context.getString(R.string.CFC_tag_name)); ret[j++] = criterion; } @@ -67,7 +67,7 @@ public class TagCustomFilterCriteriaExposer extends BroadcastReceiver { 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(), + ((BitmapDrawable)r.getDrawable(R.drawable.gl_list)).getBitmap(), context.getString(R.string.CFC_tag_contains_name)); ret[j++] = criterion; } diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java index 4951581c9..bf9983fd6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java @@ -4,9 +4,14 @@ package com.todoroo.astrid.tags; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; import android.app.Activity; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; @@ -32,16 +37,19 @@ import com.todoroo.andlib.sql.QueryTemplate; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.astrid.actfm.TagViewActivity; +import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterCategory; -import com.todoroo.astrid.api.FilterListHeader; +import com.todoroo.astrid.api.FilterCategoryWithNewButton; import com.todoroo.astrid.api.FilterListItem; import com.todoroo.astrid.api.FilterWithCustomIntent; +import com.todoroo.astrid.api.FilterWithUpdate; import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.TagData; +import com.todoroo.astrid.data.Update; import com.todoroo.astrid.service.AstridDependencyInjector; import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.tags.TagService.Tag; @@ -70,7 +78,7 @@ public class TagFilterExposer extends BroadcastReceiver { contentValues.put(Metadata.KEY.name, TagService.KEY); contentValues.put(TagService.TAG.name, tag.tag); - FilterWithCustomIntent filter = new FilterWithCustomIntent(listTitle, + FilterWithUpdate filter = new FilterWithUpdate(listTitle, title, tagTemplate, contentValues); if(tag.count == 0) @@ -85,6 +93,10 @@ public class TagFilterExposer extends BroadcastReceiver { newTagIntent(context, DeleteTagActivity.class, tag) }; filter.customTaskList = new ComponentName(ContextManager.getContext(), TagViewActivity.class); + if(tag.image != null) + filter.imageUrl = tag.image; + if(tag.updateText != null) + filter.updateText = tag.updateText; Bundle extras = new Bundle(); extras.putString(TagViewActivity.EXTRA_TAG_NAME, tag.tag); extras.putLong(TagViewActivity.EXTRA_TAG_REMOTE_ID, tag.remoteId); @@ -113,21 +125,9 @@ public class TagFilterExposer extends BroadcastReceiver { ContextManager.setContext(context); tagService = TagService.getInstance(); - Resources r = context.getResources(); ArrayList list = new ArrayList(); - // --- header - FilterListHeader tagsHeader = new FilterListHeader(context.getString(R.string.tag_FEx_header)); - list.add(tagsHeader); - // --- untagged - Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged), - r.getString(R.string.tag_FEx_untagged), - tagService.untaggedTemplate(), - null); - untagged.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.filter_untagged)).getBitmap(); - list.add(untagged); - addTags(list); // transmit filter list @@ -139,60 +139,71 @@ public class TagFilterExposer extends BroadcastReceiver { } private void addTags(ArrayList list) { - HashSet tagNames = new HashSet(); - - // active tags - Tag[] myTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_SIZE, - Criterion.and(TaskCriteria.ownedByMe(), TaskCriteria.activeAndVisible())); - for(Tag tag : myTags) - tagNames.add(tag.tag); - if(myTags.length > 0) - list.add(filterFromTags(myTags, R.string.tag_FEx_category_mine)); - - // find all tag data not in active tag list - TodorooCursor cursor = tagDataService.query(Query.select( - TagData.NAME, TagData.TASK_COUNT, TagData.REMOTE_ID).where(TagData.DELETION_DATE.eq(0))); - ArrayList notListed = new ArrayList(); + HashMap tags = new HashMap(); + + Tag[] tagsByAlpha = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA, + TaskCriteria.activeAndVisible()); + for(Tag tag : tagsByAlpha) + if(!TextUtils.isEmpty(tag.tag)) + tags.put(tag.tag, tag); + + TodorooCursor cursor = tagDataService.query(Query.select(TagData.PROPERTIES)); try { - ArrayList sharedTags = new ArrayList(); + TagData tagData = new TagData(); for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { - String tagName = cursor.get(TagData.NAME); - if(tagNames.contains(tagName)) + tagData.readFromCursor(cursor); + String tagName = tagData.getValue(TagData.NAME).trim(); + Tag tag = new Tag(tagData); + if(tagData.getValue(TagData.DELETION_DATE) > 0 && !tags.containsKey(tagName)) continue; + if(TextUtils.isEmpty(tag.tag)) continue; - Tag tag = new Tag(tagName, cursor.get(TagData.TASK_COUNT), - cursor.get(TagData.REMOTE_ID)); - if(tag.count > 0) - sharedTags.add(tag); - else - notListed.add(tag); - tagNames.add(tagName); + tags.put(tagName, tag); + + Update update = tagDataService.getLatestUpdate(tagData); + if(update != null) + tag.updateText = ActFmPreferenceService.updateToString(update); } - if(sharedTags.size() > 0) - list.add(filterFromTags(sharedTags.toArray(new Tag[sharedTags.size()]), R.string.tag_FEx_category_shared)); } finally { cursor.close(); } - // find inactive tags, intersect tag list - Tag[] inactiveTags = tagService.getGroupedTags(TagService.GROUPED_TAGS_BY_ALPHA, - Criterion.and(TaskCriteria.notDeleted(), Criterion.not(TaskCriteria.activeAndVisible()))); - for(Tag tag : inactiveTags) { - if(!tagNames.contains(tag.tag) && !TextUtils.isEmpty(tag.tag)) { - notListed.add(tag); - tag.count = 0; + ArrayList tagList = new ArrayList(tags.values()); + Collections.sort(tagList, + new Comparator() { + @Override + public int compare(Tag object1, Tag object2) { + return object1.tag.compareTo(object2.tag); } + }); + for(Iterator> i = tags.entrySet().iterator(); i.hasNext(); ) { + Entry entry = i.next(); + if(TextUtils.isEmpty(entry.getValue().tag)) + i.remove(); } - if(notListed.size() > 0) - list.add(filterFromTags(notListed.toArray(new Tag[notListed.size()]), - R.string.tag_FEx_category_inactive)); + + list.add(filterFromTags(tagList.toArray(new Tag[tagList.size()]), + R.string.tag_FEx_header)); } private FilterCategory filterFromTags(Tag[] tags, int name) { - Filter[] filters = new Filter[tags.length]; + Filter[] filters = new Filter[tags.length + 1]; + Resources r = ContextManager.getContext().getResources(); + + Filter untagged = new Filter(r.getString(R.string.tag_FEx_untagged), + r.getString(R.string.tag_FEx_untagged), + tagService.untaggedTemplate(), + null); + untagged.listingIcon = ((BitmapDrawable)r.getDrawable(R.drawable.gl_lists)).getBitmap(); + filters[0] = untagged; + Context context = ContextManager.getContext(); for(int i = 0; i < tags.length; i++) - filters[i] = filterFromTag(context, tags[i], TaskCriteria.activeAndVisible()); - return new FilterCategory(context.getString(name), filters); + filters[i + 1] = filterFromTag(context, tags[i], TaskCriteria.activeAndVisible()); + FilterCategoryWithNewButton filter = new FilterCategoryWithNewButton(context.getString(name), filters); + filter.label = r.getString(R.string.tag_FEx_add_new); + filter.intent = PendingIntent.getActivity(context, 0, + TagsPlugin.newTagDialog(context), 0); + return filter; } // --- tag manipulation activities diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java index ba7937d27..2144c49b6 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java @@ -11,6 +11,7 @@ import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Field; +import com.todoroo.andlib.sql.Functions; import com.todoroo.andlib.sql.Join; import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Query; @@ -73,7 +74,7 @@ public final class TagService { * Property for retrieving count of aggregated rows */ private static final CountProperty COUNT = new CountProperty(); - public static final Order GROUPED_TAGS_BY_ALPHA = Order.asc(TAG); + public static final Order GROUPED_TAGS_BY_ALPHA = Order.asc(Functions.upper(TAG)); public static final Order GROUPED_TAGS_BY_SIZE = Order.desc(COUNT); /** @@ -84,8 +85,10 @@ public final class TagService { */ public static final class Tag { public String tag; - int count; - long remoteId; + public int count; + public long remoteId; + public String image; + public String updateText; public Tag(String tag, int count, long remoteId) { this.tag = tag; @@ -93,6 +96,13 @@ public final class TagService { this.remoteId = remoteId; } + public Tag(TagData tagData) { + tag = tagData.getValue(TagData.NAME); + count = tagData.getValue(TagData.TASK_COUNT); + remoteId = tagData.getValue(TagData.REMOTE_ID); + image = tagData.getValue(TagData.PICTURE); + } + @Override public String toString() { return tag; diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java index 704d71091..8e61f8999 100644 --- a/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java +++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagsPlugin.java @@ -1,6 +1,5 @@ package com.todoroo.astrid.tags; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -29,11 +28,11 @@ public class TagsPlugin extends BroadcastReceiver { * Create new tag data * @param activity */ - public void showNewTagDialog(final Activity activity) { - Intent intent = new Intent(activity, TagViewActivity.class); + public static Intent newTagDialog(Context context) { + Intent intent = new Intent(context, TagViewActivity.class); intent.putExtra(TagViewActivity.EXTRA_NEW_TAG, true); intent.putExtra(TagViewActivity.EXTRA_START_TAB, "settings"); //$NON-NLS-1$ - activity.startActivity(intent); + return intent; } } diff --git a/astrid/res/drawable-hdpi/header_tags_normal.png b/astrid/res/drawable-hdpi/header_tags_normal.png index 8e358507e..671189f80 100644 Binary files a/astrid/res/drawable-hdpi/header_tags_normal.png and b/astrid/res/drawable-hdpi/header_tags_normal.png differ diff --git a/astrid/res/drawable-hdpi/header_tags_pressed.png b/astrid/res/drawable-hdpi/header_tags_pressed.png index 6039ca41f..81b85888a 100644 Binary files a/astrid/res/drawable-hdpi/header_tags_pressed.png and b/astrid/res/drawable-hdpi/header_tags_pressed.png differ diff --git a/astrid/res/drawable/edit_titlebar.9.png b/astrid/res/drawable/edit_titlebar.9.png new file mode 100644 index 000000000..5363c7f19 Binary files /dev/null and b/astrid/res/drawable/edit_titlebar.9.png differ diff --git a/astrid/res/drawable/edit_titlebar.png b/astrid/res/drawable/edit_titlebar.png deleted file mode 100644 index 170cc7887..000000000 Binary files a/astrid/res/drawable/edit_titlebar.png and /dev/null differ diff --git a/astrid/res/drawable/filter_btn_background.9.png b/astrid/res/drawable/filter_btn_background.9.png new file mode 100644 index 000000000..c95ed57d5 Binary files /dev/null and b/astrid/res/drawable/filter_btn_background.9.png differ diff --git a/astrid/res/drawable/filter_calendar.png b/astrid/res/drawable/filter_calendar.png new file mode 100644 index 000000000..c99e0919b Binary files /dev/null and b/astrid/res/drawable/filter_calendar.png differ diff --git a/astrid/res/drawable/filter_count.xml b/astrid/res/drawable/filter_count.xml new file mode 100644 index 000000000..9801e9848 --- /dev/null +++ b/astrid/res/drawable/filter_count.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/astrid/res/drawable/filter_inbox.png b/astrid/res/drawable/filter_inbox.png new file mode 100644 index 000000000..12d49902d Binary files /dev/null and b/astrid/res/drawable/filter_inbox.png differ diff --git a/astrid/res/drawable/filter_new.png b/astrid/res/drawable/filter_new.png new file mode 100644 index 000000000..85e1aaca6 Binary files /dev/null and b/astrid/res/drawable/filter_new.png differ diff --git a/astrid/res/drawable/filter_pencil.png b/astrid/res/drawable/filter_pencil.png new file mode 100644 index 000000000..8b9f792bf Binary files /dev/null and b/astrid/res/drawable/filter_pencil.png differ diff --git a/astrid/res/drawable/filter_sliders.png b/astrid/res/drawable/filter_sliders.png new file mode 100644 index 000000000..efd407614 Binary files /dev/null and b/astrid/res/drawable/filter_sliders.png differ diff --git a/astrid/res/drawable/filter_tags1.png b/astrid/res/drawable/filter_tags1.png deleted file mode 100644 index 82f360e07..000000000 Binary files a/astrid/res/drawable/filter_tags1.png and /dev/null differ diff --git a/astrid/res/drawable/filter_tags2.png b/astrid/res/drawable/filter_tags2.png deleted file mode 100644 index 5059e6665..000000000 Binary files a/astrid/res/drawable/filter_tags2.png and /dev/null differ diff --git a/astrid/res/drawable/filter_untagged.png b/astrid/res/drawable/filter_untagged.png deleted file mode 100644 index e729647dd..000000000 Binary files a/astrid/res/drawable/filter_untagged.png and /dev/null differ diff --git a/astrid/res/drawable/gl_list.png b/astrid/res/drawable/gl_list.png new file mode 100644 index 000000000..8bcb630f5 Binary files /dev/null and b/astrid/res/drawable/gl_list.png differ diff --git a/astrid/res/drawable/gl_lists.png b/astrid/res/drawable/gl_lists.png new file mode 100644 index 000000000..b2dfdc3e6 Binary files /dev/null and b/astrid/res/drawable/gl_lists.png differ diff --git a/astrid/res/layout/filter_adapter_row.xml b/astrid/res/layout/filter_adapter_row.xml index 2278f9c8f..5f044b4c2 100644 --- a/astrid/res/layout/filter_adapter_row.xml +++ b/astrid/res/layout/filter_adapter_row.xml @@ -1,51 +1,84 @@ - + + + + android:layout_marginLeft="55dp" + android:layout_marginTop="2dp" + android:paddingRight="30dp" + android:gravity="center_vertical" /> + + + + + + - + diff --git a/astrid/res/values/strings-filters.xml b/astrid/res/values/strings-filters.xml index 962d65201..2a44fcf65 100644 --- a/astrid/res/values/strings-filters.xml +++ b/astrid/res/values/strings-filters.xml @@ -19,7 +19,7 @@ Custom Filter... - Saved Filters + Filters Delete Filter diff --git a/astrid/res/values/styles.xml b/astrid/res/values/styles.xml index 25417c2ce..8c0e4cfb6 100644 --- a/astrid/res/values/styles.xml +++ b/astrid/res/values/styles.xml @@ -94,18 +94,18 @@ diff --git a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java index 2b2469224..406b94fae 100644 --- a/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java +++ b/astrid/src/com/todoroo/astrid/activity/FilterListActivity.java @@ -329,7 +329,7 @@ public class FilterListActivity extends ExpandableListActivity { Bitmap emblem = filter.listingIcon; if(emblem == null) emblem = ((BitmapDrawable) getResources().getDrawable( - R.drawable.filter_tags1)).getBitmap(); + R.drawable.gl_list)).getBitmap(); // create icon by superimposing astrid w/ icon DisplayMetrics metrics = new DisplayMetrics(); diff --git a/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java b/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java index f2694a69f..7a2c8e1a7 100644 --- a/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java +++ b/astrid/src/com/todoroo/astrid/adapter/FilterAdapter.java @@ -3,12 +3,15 @@ */ package com.todoroo.astrid.adapter; +import greendroid.widget.AsyncImageView; + import java.util.ArrayList; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import android.app.Activity; +import android.app.PendingIntent.CanceledException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -16,15 +19,18 @@ import android.content.IntentFilter; import android.graphics.Color; import android.os.Bundle; import android.os.Parcelable; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.widget.BaseExpandableListAdapter; import android.widget.Button; import android.widget.ExpandableListView; import android.widget.ImageView; +import android.widget.RelativeLayout; import android.widget.TextView; import com.timsu.astrid.R; @@ -34,11 +40,11 @@ import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.api.FilterCategory; +import com.todoroo.astrid.api.FilterCategoryWithNewButton; import com.todoroo.astrid.api.FilterListHeader; import com.todoroo.astrid.api.FilterListItem; -import com.todoroo.astrid.gtasks.GtasksListAdder; +import com.todoroo.astrid.api.FilterWithUpdate; import com.todoroo.astrid.service.TaskService; -import com.todoroo.astrid.tags.TagsPlugin; public class FilterAdapter extends BaseExpandableListAdapter { @@ -162,8 +168,11 @@ public class FilterAdapter extends BaseExpandableListAdapter { viewHolder.view = convertView; viewHolder.expander = (ImageView)convertView.findViewById(R.id.expander); viewHolder.icon = (ImageView)convertView.findViewById(R.id.icon); + viewHolder.urlImage = (AsyncImageView)convertView.findViewById(R.id.url_image); viewHolder.name = (TextView)convertView.findViewById(R.id.name); + viewHolder.activity = (TextView)convertView.findViewById(R.id.activity); viewHolder.selected = (ImageView)convertView.findViewById(R.id.selected); + viewHolder.size = (TextView)convertView.findViewById(R.id.size); viewHolder.decoration = null; convertView.setTag(viewHolder); } @@ -174,7 +183,10 @@ public class FilterAdapter extends BaseExpandableListAdapter { public FilterListItem item; public ImageView expander; public ImageView icon; + public AsyncImageView urlImage; public TextView name; + public TextView activity; + public TextView size; public ImageView selected; public View view; public View decoration; @@ -209,7 +221,7 @@ public class FilterAdapter extends BaseExpandableListAdapter { convertView = newView(convertView, parent); ViewHolder viewHolder = (ViewHolder) convertView.getTag(); viewHolder.item = (FilterListItem) getChild(groupPosition, childPosition); - populateView(viewHolder, true, false); + populateView(viewHolder, false); return convertView; } @@ -237,7 +249,7 @@ public class FilterAdapter extends BaseExpandableListAdapter { convertView = newView(convertView, parent); ViewHolder viewHolder = (ViewHolder) convertView.getTag(); viewHolder.item = (FilterListItem) getGroup(groupPosition); - populateView(viewHolder, false, isExpanded); + populateView(viewHolder, isExpanded); return convertView; } @@ -390,40 +402,70 @@ public class FilterAdapter extends BaseExpandableListAdapter { * ================================================================ views * ====================================================================== */ - public void populateView(ViewHolder viewHolder, boolean isChild, boolean isExpanded) { + public void populateView(ViewHolder viewHolder, boolean isExpanded) { FilterListItem filter = viewHolder.item; if(filter == null) return; viewHolder.view.setBackgroundResource(0); - viewHolder.expander.setVisibility(View.GONE); if(viewHolder.decoration != null) { ((ViewGroup)viewHolder.view).removeView(viewHolder.decoration); viewHolder.decoration = null; } - if(viewHolder.item instanceof FilterListHeader) { + if(viewHolder.item instanceof FilterListHeader || viewHolder.item instanceof FilterCategory) { viewHolder.name.setTextAppearance(activity, headerStyle); viewHolder.view.setBackgroundResource(R.drawable.edit_titlebar); - viewHolder.view.setPadding((int) ((isChild ? 33 : 7) * metrics.density), 5, 0, 5); - } else if(viewHolder.item instanceof FilterCategory) { - viewHolder.expander.setVisibility(View.VISIBLE); - if(isExpanded) - viewHolder.expander.setImageResource(R.drawable.expander_ic_maximized); - else - viewHolder.expander.setImageResource(R.drawable.expander_ic_minimized); - viewHolder.name.setTextAppearance(activity, categoryStyle); - viewHolder.view.setPadding((int)(7 * metrics.density), 8, 0, 8); + viewHolder.view.setPadding((int) (7 * metrics.density), 5, 0, 5); + viewHolder.view.getLayoutParams().height = (int) (40 * metrics.density); } else { viewHolder.name.setTextAppearance(activity, filterStyle); - viewHolder.view.setPadding((int) ((isChild ? 27 : 7) * metrics.density), 8, 0, 8); + viewHolder.view.setPadding((int) (7 * metrics.density), 8, 0, 8); + viewHolder.view.getLayoutParams().height = (int) (58 * metrics.density); + } + + if(viewHolder.item instanceof FilterCategory) { + viewHolder.expander.setVisibility(View.VISIBLE); + viewHolder.expander.setImageResource(isExpanded ? + R.drawable.expander_ic_maximized : R.drawable.expander_ic_minimized); + } else + viewHolder.expander.setVisibility(View.GONE); + + // update with filter attributes (listing icon, url, update text, size) + + viewHolder.urlImage.setVisibility(View.GONE); + viewHolder.activity.setVisibility(View.GONE); + viewHolder.icon.setVisibility(View.GONE); + + if(filter.listingIcon != null) { + viewHolder.icon.setVisibility(View.VISIBLE); + viewHolder.icon.setImageBitmap(filter.listingIcon); + } + + // title / size + if(filter.listingTitle.matches(".* \\(\\d+\\)$")) { //$NON-NLS-1$ + viewHolder.size.setVisibility(View.VISIBLE); + viewHolder.size.setText(filter.listingTitle.substring(filter.listingTitle.lastIndexOf('(') + 1, + filter.listingTitle.length() - 1)); + viewHolder.name.setText(filter.listingTitle.substring(0, filter.listingTitle.lastIndexOf(' '))); + } else { + viewHolder.name.setText(filter.listingTitle); + viewHolder.size.setVisibility(View.GONE); } - viewHolder.icon.setVisibility(filter.listingIcon != null ? View.VISIBLE : View.GONE); - viewHolder.icon.setImageBitmap(filter.listingIcon); + viewHolder.name.getLayoutParams().height = (int) (58 * metrics.density); + if(filter instanceof FilterWithUpdate) { + viewHolder.urlImage.setVisibility(View.VISIBLE); + viewHolder.urlImage.setDefaultImageResource(R.drawable.gl_list); + viewHolder.urlImage.setUrl(((FilterWithUpdate)filter).imageUrl); + if(!TextUtils.isEmpty(((FilterWithUpdate)filter).updateText)) { + viewHolder.activity.setText(((FilterWithUpdate)filter).updateText); + viewHolder.name.getLayoutParams().height = (int) (25 * metrics.density); + viewHolder.activity.setVisibility(View.VISIBLE); + } + } - viewHolder.name.setText(filter.listingTitle); if(filter.color != 0) viewHolder.name.setTextColor(filter.color); @@ -434,43 +476,33 @@ public class FilterAdapter extends BaseExpandableListAdapter { } else viewHolder.selected.setVisibility(View.GONE); - updateForActFm(viewHolder); - updateForGtasks(viewHolder); - } - - private void setupCustomHeader(ViewHolder viewHolder, String forTitle, View.OnClickListener buttonListener) { - if(viewHolder.item instanceof FilterListHeader && - viewHolder.item.listingTitle.equals(forTitle)) { - Button add = new Button(activity); - add.setText(R.string.tag_FEx_add_new); - add.setBackgroundResource(android.R.drawable.btn_default_small); - add.setCompoundDrawablesWithIntrinsicBounds(R.drawable.tango_add,0,0,0); - viewHolder.decoration = add; - add.setHeight((int)(35 * metrics.density)); - ((ViewGroup)viewHolder.view).addView(add); - - add.setOnClickListener(buttonListener); - } - } - - private void updateForActFm(ViewHolder viewHolder) { - setupCustomHeader(viewHolder, - activity.getString(R.string.tag_FEx_header), - new View.OnClickListener() { - @Override - public void onClick(View v) { - new TagsPlugin().showNewTagDialog(activity); - } - }); + if(filter instanceof FilterCategoryWithNewButton) + setupCustomHeader(viewHolder, (FilterCategoryWithNewButton) filter); } - private void updateForGtasks(ViewHolder viewHolder) { - setupCustomHeader(viewHolder, - activity.getString(R.string.gtasks_FEx_header), - new View.OnClickListener() { + private void setupCustomHeader(ViewHolder viewHolder, final FilterCategoryWithNewButton filter) { + Button add = new Button(activity); + add.setBackgroundResource(R.drawable.filter_btn_background); + add.setCompoundDrawablesWithIntrinsicBounds(R.drawable.filter_new,0,0,0); + add.setTextColor(Color.WHITE); + add.setShadowLayer(1, 1, 1, Color.BLACK); + add.setText(filter.label); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + (int)(32 * metrics.density)); + lp.rightMargin = (int) (4 * metrics.density); + lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + add.setLayoutParams(lp); + ((ViewGroup)viewHolder.view).addView(add); + viewHolder.decoration = add; + + add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - new GtasksListAdder().showNewListDialog(activity); + try { + filter.intent.send(); + } catch (CanceledException e) { + // do nothing + } } }); } diff --git a/astrid/src/com/todoroo/astrid/api/FilterCategoryWithNewButton.java b/astrid/src/com/todoroo/astrid/api/FilterCategoryWithNewButton.java new file mode 100644 index 000000000..29d4611bb --- /dev/null +++ b/astrid/src/com/todoroo/astrid/api/FilterCategoryWithNewButton.java @@ -0,0 +1,107 @@ +/** + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.api; + +import android.app.PendingIntent; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A FilterCategoryWithNewButton has a button for new filter creation + * + * @author Tim Su + * + */ +public class FilterCategoryWithNewButton extends FilterCategory { + + /** + * Intent to launch + */ + public PendingIntent intent; + + /** + * Label for new button + */ + public String label; + + /** + * Constructor for creating a new FilterCategory + * @param listingTitle + * Title of this item as displayed on the lists page, e.g. Inbox + * @param children + * filters belonging to this category + */ + public FilterCategoryWithNewButton(String listingTitle, Filter[] children) { + this.listingTitle = listingTitle; + this.children = children; + } + + /** + * Constructor for creating a new FilterCategory + * + * @param plugin + * {@link Addon} identifier that encompasses object + */ + protected FilterCategoryWithNewButton() { + // + } + + // --- parcelable + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(intent, 0); + dest.writeString(label); + } + + /** + * Parcelable creator + */ + @SuppressWarnings("hiding") + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + public FilterCategoryWithNewButton createFromParcel(Parcel source) { + FilterCategoryWithNewButton item = new FilterCategoryWithNewButton(); + item.readFromParcel(source); + + Parcelable[] parcelableChildren = source.readParcelableArray( + FilterCategoryWithNewButton.class.getClassLoader()); + item.children = new Filter[parcelableChildren.length]; + for(int i = 0; i < item.children.length; i++) { + if(parcelableChildren[i] instanceof FilterListItem) + item.children[i] = (Filter) parcelableChildren[i]; + else + item.children[i] = null; + } + + item.intent = source.readParcelable(PendingIntent.class.getClassLoader()); + item.label = source.readString(); + + return item; + } + + /** + * {@inheritDoc} + */ + public FilterCategoryWithNewButton[] newArray(int size) { + return new FilterCategoryWithNewButton[size]; + } + + }; +} diff --git a/astrid/src/com/todoroo/astrid/api/FilterWithUpdate.java b/astrid/src/com/todoroo/astrid/api/FilterWithUpdate.java new file mode 100644 index 000000000..ad3400e4d --- /dev/null +++ b/astrid/src/com/todoroo/astrid/api/FilterWithUpdate.java @@ -0,0 +1,87 @@ +package com.todoroo.astrid.api; + + +import android.content.ContentValues; +import android.os.Parcel; +import android.os.Parcelable; + +import com.todoroo.andlib.sql.QueryTemplate; + +public class FilterWithUpdate extends FilterWithCustomIntent { + + /** + * Update image URL + */ + public String imageUrl = null; + + /** + * Update message text + */ + public String updateText = null; + + protected FilterWithUpdate() { + super(); + } + + public FilterWithUpdate(String listingTitle, String title, + QueryTemplate sqlQuery, ContentValues valuesForNewTasks) { + super(listingTitle, title, sqlQuery, valuesForNewTasks); + } + + public FilterWithUpdate(String listingTitle, String title, + String sqlQuery, ContentValues valuesForNewTasks) { + super(listingTitle, title, sqlQuery, valuesForNewTasks); + } + + // --- parcelable + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(imageUrl); + dest.writeString(updateText); + } + + @Override + public void readFromParcel(Parcel source) { + super.readFromParcel(source); + imageUrl = source.readString(); + updateText = source.readString(); + } + + /** + * Parcelable Creator Object + */ + @SuppressWarnings("hiding") + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + public FilterWithUpdate createFromParcel(Parcel source) { + FilterWithUpdate item = new FilterWithUpdate(); + item.readFromParcel(source); + return item; + } + + /** + * {@inheritDoc} + */ + public FilterWithUpdate[] newArray(int size) { + return new FilterWithUpdate[size]; + } + + }; + +} diff --git a/astrid/src/com/todoroo/astrid/service/TagDataService.java b/astrid/src/com/todoroo/astrid/service/TagDataService.java index e98a1c5d4..6aec55e98 100644 --- a/astrid/src/com/todoroo/astrid/service/TagDataService.java +++ b/astrid/src/com/todoroo/astrid/service/TagDataService.java @@ -140,4 +140,27 @@ public class TagDataService { orderBy(Order.desc(Update.CREATION_DATE))); } + /** + * Return update + * @param tagData + * @return + */ + public Update getLatestUpdate(TagData tagData) { + if(tagData.getValue(Task.REMOTE_ID) < 1) + return null; + + @SuppressWarnings("nls") + TodorooCursor updates = updateDao.query(Query.select(Update.PROPERTIES).where( + Update.TAGS.like("%," + tagData.getValue(Task.REMOTE_ID) + ",%")). + orderBy(Order.desc(Update.CREATION_DATE)).limit(1)); + try { + if(updates.getCount() == 0) + return null; + updates.moveToFirst(); + return new Update(updates); + } finally { + updates.close(); + } + } + }