diff --git a/api/src/com/todoroo/astrid/data/TagData.java b/api/src/com/todoroo/astrid/data/TagData.java
index a32017f0e..bdd8bab79 100644
--- a/api/src/com/todoroo/astrid/data/TagData.java
+++ b/api/src/com/todoroo/astrid/data/TagData.java
@@ -128,6 +128,9 @@ public final class TagData extends RemoteModel {
/** whether tag is emergent */
public static final int FLAG_EMERGENT = 1 << 2;
+ /** whether tag represents a featured list */
+ public static final int FLAG_FEATURED = 1 << 3;
+
// --- defaults
/** Default values container */
diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index 449664d82..1d1769d3d 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -341,7 +341,16 @@
-
+
+
+
+
+
+
+
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java
index bd00b20b5..f8d4d6376 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/TagViewFragment.java
@@ -103,8 +103,6 @@ public class TagViewFragment extends TaskListFragment {
private boolean justDeleted = false;
- //private ImageAdapter galleryAdapter;
-
// --- UI initialization
@Override
@@ -149,7 +147,7 @@ public class TagViewFragment extends TaskListFragment {
*/
@Override
protected View getListBody(ViewGroup root) {
- ViewGroup parent = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.task_list_body_tag, root, false);
+ ViewGroup parent = (ViewGroup) getActivity().getLayoutInflater().inflate(getTaskListBodyLayout(), root, false);
taskListView = super.getListBody(parent);
parent.addView(taskListView);
@@ -157,6 +155,10 @@ public class TagViewFragment extends TaskListFragment {
return parent;
}
+ protected int getTaskListBodyLayout() {
+ return R.layout.task_list_body_tag;
+ }
+
private void showListSettingsPopover() {
if (!AstridPreferences.canShowPopover())
return;
@@ -302,7 +304,7 @@ public class TagViewFragment extends TaskListFragment {
Preferences.setLong(LAST_FETCH_KEY + tagData.getId(), DateUtilities.now());
}
- private void setUpMembersGallery() {
+ protected void setUpMembersGallery() {
LinearLayout membersView = (LinearLayout)getView().findViewById(R.id.shared_with);
membersView.setOnClickListener(settingsListener);
try {
@@ -537,7 +539,7 @@ public class TagViewFragment extends TaskListFragment {
@Override
protected boolean hasDraggableOption() {
- return true;
+ return tagData != null && !tagData.getFlag(TagData.FLAGS, TagData.FLAG_FEATURED);
}
@Override
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmDataService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmDataService.java
index c70a562b3..c57b88aea 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmDataService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmDataService.java
@@ -19,6 +19,7 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
@@ -204,6 +205,27 @@ public final class ActFmDataService {
}
}
+ @SuppressWarnings("nls")
+ public void saveFeaturedList(JSONObject featObject) throws JSONException {
+ TodorooCursor cursor = tagDataService.query(Query.select(TagData.PROPERTIES).where(
+ Criterion.and(Functions.bitwiseAnd(TagData.FLAGS, TagData.FLAG_FEATURED).gt(0), TagData.REMOTE_ID.eq(featObject.get("id")))));
+ try {
+ cursor.moveToNext();
+ TagData tagData = new TagData();
+ if (!cursor.isAfterLast()) {
+ tagData.readFromCursor(cursor);
+ if(!tagData.getValue(TagData.NAME).equals(featObject.getString("name")))
+ TagService.getInstance().rename(tagData.getValue(TagData.NAME), featObject.getString("name"));
+ cursor.moveToNext();
+ }
+ ActFmSyncService.JsonHelper.featuredListFromJson(featObject, tagData);
+ tagDataService.save(tagData);
+
+ } finally {
+ cursor.close();
+ }
+ }
+
/**
* Save / Merge JSON user
* @param userObject
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java
index 18a12c9fc..ea1a7b186 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncService.java
@@ -42,6 +42,7 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query;
@@ -836,7 +837,26 @@ public final class ActFmSyncService {
if(serverTime == 0) {
Long[] remoteIdArray = remoteIds.toArray(new Long[remoteIds.size()]);
- tagDataService.deleteWhere(Criterion.not(TagData.REMOTE_ID.in(remoteIdArray)));
+ tagDataService.deleteWhere(Criterion.and(
+ Criterion.not(Functions.bitwiseAnd(TagData.FLAGS, TagData.FLAG_FEATURED).gt(0)),
+ Criterion.not(TagData.REMOTE_ID.in(remoteIdArray))));
+ }
+
+ return result.optInt("time", 0);
+ }
+
+ public int fetchFeaturedLists(int serverTime) throws JSONException, IOException {
+ if (!checkForToken())
+ return 0;
+ JSONObject result = actFmInvoker.invoke("featured_lists",
+ "token", token, "modified_after", serverTime);
+ JSONArray featuredLists = result.getJSONArray("list");
+ if (featuredLists.length() > 0)
+ Preferences.setBoolean(R.string.p_show_featured_lists, true);
+
+ for (int i = 0; i < featuredLists.length(); i++) {
+ JSONObject featObject = featuredLists.getJSONObject(i);
+ actFmDataService.saveFeaturedList(featObject);
}
return result.optInt("time", 0);
@@ -1196,6 +1216,7 @@ public final class ActFmSyncService {
} catch (SQLiteConstraintException e) {
taskDao.handleSQLiteConstraintException(remote);
}
+
ids.add(remote.getId());
metadataService.synchronizeMetadata(remote.getId(), metadata, MetadataCriteria.withKey(TagService.KEY));
synchronizeAttachments(item, remote);
@@ -1428,10 +1449,23 @@ public final class ActFmSyncService {
* @throws JSONException
*/
public static void tagFromJson(JSONObject json, TagData model) throws JSONException {
+ parseTagDataFromJson(json, model, false);
+ }
+
+ public static void featuredListFromJson(JSONObject json, TagData model) throws JSONException {
+ parseTagDataFromJson(json, model, true);
+ }
+
+ private static void parseTagDataFromJson(JSONObject json, TagData model, boolean featuredList) throws JSONException {
model.clearValue(TagData.REMOTE_ID);
model.setValue(TagData.REMOTE_ID, json.getLong("id"));
model.setValue(TagData.NAME, json.getString("name"));
- readUser(json.getJSONObject("user"), model, TagData.USER_ID, TagData.USER);
+
+ if (!featuredList)
+ readUser(json.getJSONObject("user"), model, TagData.USER_ID, TagData.USER);
+
+ if (featuredList)
+ model.setFlag(TagData.FLAGS, TagData.FLAG_FEATURED, true);
if(json.has("picture"))
model.setValue(TagData.PICTURE, json.optString("picture", ""));
diff --git a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java
index 80a432253..12b0f5da1 100644
--- a/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java
+++ b/astrid/plugin-src/com/todoroo/astrid/actfm/sync/ActFmSyncV2Provider.java
@@ -21,6 +21,7 @@ import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Join;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.Preferences;
@@ -134,6 +135,8 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
private static final String LAST_TAG_FETCH_TIME = "actfm_lastTag"; //$NON-NLS-1$
+ private static final String LAST_FEATURED_TAG_FETCH_TIME = "actfm_last_featuredTag"; //$NON-NLS-1$
+
private static final String LAST_USERS_FETCH_TIME = "actfm_lastUsers"; //$NON-NLS-1$
// --- synchronize active tasks
@@ -145,9 +148,9 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
new Thread(new Runnable() {
public void run() {
callback.started();
- callback.incrementMax(140);
+ callback.incrementMax(160);
- final AtomicInteger finisher = new AtomicInteger(4);
+ final AtomicInteger finisher = new AtomicInteger(5);
actFmPreferenceService.recordSyncStart();
updateUserStatus();
@@ -158,6 +161,8 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
startUpdatesFetcher(manual, callback, finisher);
+ startFeaturedListFetcher(callback, finisher);
+
actFmSyncService.waitUntilEmpty();
startTaskFetcher(manual, callback, finisher);
@@ -254,7 +259,30 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
}
}
});
+ }
+ /** fetch changes to tags */
+ private void startFeaturedListFetcher(final SyncResultCallback callback,
+ final AtomicInteger finisher) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ int time = Preferences.getInt(LAST_FEATURED_TAG_FETCH_TIME, 0);
+ try {
+ time = actFmSyncService.fetchFeaturedLists(time);
+ Preferences.setInt(LAST_FEATURED_TAG_FETCH_TIME, time);
+ } catch (JSONException e) {
+ handler.handleException("actfm-sync", e, e.toString()); //$NON-NLS-1$
+ } catch (IOException e) {
+ handler.handleException("actfm-sync", e, e.toString()); //$NON-NLS-1$
+ } finally {
+ callback.incrementProgress(20);
+ if(finisher.decrementAndGet() == 0) {
+ finishSync(callback);
+ }
+ }
+ }
+ }).start();
}
/** @return runnable to fetch changes to tags */
@@ -341,10 +369,12 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
private void pushQueuedTags(final SyncResultCallback callback,
final AtomicInteger finisher, int lastTagSyncTime) {
TodorooCursor tagDataCursor = tagDataService.query(Query.select(TagData.PROPERTIES)
- .where(Criterion.or(
+ .where(Criterion.and(
+ Functions.bitwiseAnd(TagData.FLAGS, TagData.FLAG_FEATURED).eq(0),
+ Criterion.or(
TagData.REMOTE_ID.eq(0),
Criterion.and(TagData.REMOTE_ID.gt(0),
- TagData.MODIFICATION_DATE.gt(lastTagSyncTime)))));
+ TagData.MODIFICATION_DATE.gt(lastTagSyncTime))))));
try {
pushQueued(callback, finisher, tagDataCursor, true, tagPusher);
} finally {
diff --git a/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java b/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java
new file mode 100644
index 000000000..8828eacfa
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/people/PeopleFilterMode.java
@@ -0,0 +1,59 @@
+package com.todoroo.astrid.people;
+
+import android.content.Context;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.activity.FilterListFragment;
+import com.todoroo.astrid.activity.FilterModeSpec;
+import com.todoroo.astrid.activity.TaskListFragment;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.api.FilterWithUpdate;
+import com.todoroo.astrid.helper.AsyncImageView;
+import com.todoroo.astrid.ui.MainMenuPopover;
+
+public class PeopleFilterMode implements FilterModeSpec {
+
+ private AsyncImageView imageView;
+
+ @Override
+ public Filter getDefaultFilter(Context context) {
+ Filter defaultFilter = PeopleFilterExposer.mySharedTasks(context);
+ return defaultFilter;
+ }
+
+ @Override
+ public Class extends FilterListFragment> getFilterListClass() {
+ return PeopleListFragment.class;
+ }
+
+ @Override
+ public void onFilterItemClickedCallback(FilterListItem item) {
+ if (imageView == null)
+ return;
+ if (item instanceof FilterWithUpdate)
+ imageView.setUrl(((FilterWithUpdate) item).imageUrl);
+ else
+ imageView.setUrl(null);
+ }
+
+ public void setImageView(AsyncImageView imageView) {
+ this.imageView = imageView;
+ }
+
+ @Override
+ public int[] getForbiddenMenuItems() {
+ return FORBIDDEN_MENU_ITEMS;
+ }
+
+ @Override
+ public int getMainMenuIconAttr() {
+ return R.attr.asPeopleMenu;
+ }
+
+ private static final int[] FORBIDDEN_MENU_ITEMS = {
+ TaskListFragment.MENU_NEW_FILTER_ID,
+ TaskListFragment.MENU_ADDONS_ID,
+ MainMenuPopover.MAIN_MENU_ITEM_FRIENDS
+ };
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/people/PeopleListFragment.java b/astrid/plugin-src/com/todoroo/astrid/people/PeopleListFragment.java
index a73069b4c..25ad25207 100644
--- a/astrid/plugin-src/com/todoroo/astrid/people/PeopleListFragment.java
+++ b/astrid/plugin-src/com/todoroo/astrid/people/PeopleListFragment.java
@@ -22,8 +22,8 @@ public class PeopleListFragment extends FilterListFragment {
@Override
protected int getLayout(Activity activity) {
if (AstridPreferences.useTabletLayout(activity))
- return R.layout.people_list_fragment_3pane;
+ return R.layout.filter_list_fragment_alternative_3pane;
else
- return R.layout.people_list_fragment;
+ return R.layout.filter_list_fragment_alternative;
}
}
diff --git a/astrid/plugin-src/com/todoroo/astrid/people/PeopleViewActivity.java b/astrid/plugin-src/com/todoroo/astrid/people/PeopleViewActivity.java
deleted file mode 100644
index 52c96ef42..000000000
--- a/astrid/plugin-src/com/todoroo/astrid/people/PeopleViewActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright (c) 2012 Todoroo Inc
- *
- * See the file "LICENSE" for the full license governing this code.
- */
-package com.todoroo.astrid.people;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.timsu.astrid.R;
-import com.todoroo.andlib.utility.AndroidUtilities;
-import com.todoroo.astrid.activity.FilterListFragment;
-import com.todoroo.astrid.activity.TaskListActivity;
-import com.todoroo.astrid.activity.TaskListFragment;
-import com.todoroo.astrid.api.Filter;
-import com.todoroo.astrid.api.FilterListItem;
-import com.todoroo.astrid.api.FilterWithUpdate;
-import com.todoroo.astrid.helper.AsyncImageView;
-import com.todoroo.astrid.ui.MainMenuPopover;
-
-public class PeopleViewActivity extends TaskListActivity {
-
- private AsyncImageView imageView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- imageView = (AsyncImageView) findViewById(R.id.person_image);
- imageView.setDefaultImageResource(R.drawable.icn_default_person_image);
- }
-
- @Override
- protected int getContentView() {
- return R.layout.people_view_wrapper_activity;
- }
-
- @Override
- protected Filter getDefaultFilter() {
- Filter defaultFilter = PeopleFilterExposer.mySharedTasks(this);
- return defaultFilter;
- }
-
- @Override
- protected Class extends FilterListFragment> getFilterListClass() {
- return PeopleListFragment.class;
- }
-
- @Override
- protected int getHeaderView() {
- return R.layout.header_people_view;
- }
-
- @Override
- public boolean onFilterItemClicked(FilterListItem item) {
- boolean result = super.onFilterItemClicked(item);
- if (result && item instanceof FilterWithUpdate)
- imageView.setUrl(((FilterWithUpdate) item).imageUrl);
- else
- imageView.setUrl(null);
- return result;
- }
-
- private static final int[] FORBIDDEN_MENU_ITEMS = {
- TaskListFragment.MENU_NEW_FILTER_ID,
- TaskListFragment.MENU_ADDONS_ID,
- MainMenuPopover.MAIN_MENU_ITEM_FRIENDS
- };
-
- @Override
- public boolean shouldAddMenuItem(int itemId) {
- return AndroidUtilities.indexOf(FORBIDDEN_MENU_ITEMS, itemId) < 0;
- }
-
- @Override
- public void mainMenuItemSelected(int item, Intent customIntent) {
- if (item == MainMenuPopover.MAIN_MENU_ITEM_LISTS) {
- finish();
- return;
- }
-
- super.mainMenuItemSelected(item, customIntent);
- }
-}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java
index 0d695b170..54113cb64 100644
--- a/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagFilterExposer.java
@@ -6,6 +6,7 @@
package com.todoroo.astrid.tags;
import java.util.ArrayList;
+import java.util.List;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -60,9 +61,11 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
private static final String TAG = "tag"; //$NON-NLS-1$
- @Autowired TagDataService tagDataService;
+ @Autowired protected TagDataService tagDataService;
@Autowired GtasksPreferenceService gtasksPreferenceService;
+ protected boolean addUntaggedFilter = true;
+
/** Create filter from new tag object */
@SuppressWarnings("nls")
public static FilterWithCustomIntent filterFromTag(Context context, Tag tag, Criterion criterion) {
@@ -141,7 +144,7 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
}
- private FilterListItem[] prepareFilters(Context context) {
+ protected FilterListItem[] prepareFilters(Context context) {
DependencyInjectionService.getInstance().inject(this);
ContextManager.setContext(context);
@@ -155,13 +158,18 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
}
private void addTags(ArrayList list) {
- ArrayList tagList = TagService.getInstance().getTagList();
+ List tagList = getTagList();
list.add(filterFromTags(tagList.toArray(new Tag[tagList.size()]),
R.string.tag_FEx_header));
}
+ protected List getTagList() {
+ return TagService.getInstance().getTagList();
+ }
+
private FilterCategory filterFromTags(Tag[] tags, int name) {
- Filter[] filters = new Filter[tags.length + 1];
+ int length = addUntaggedFilter ? tags.length + 1 : tags.length;
+ Filter[] filters = new Filter[length];
Context context = ContextManager.getContext();
Resources r = context.getResources();
@@ -170,22 +178,30 @@ public class TagFilterExposer extends BroadcastReceiver implements AstridFilterE
int themeFlags = isTablet ? ThemeService.FLAG_FORCE_LIGHT : 0;
// --- untagged
- int untaggedLabel = gtasksPreferenceService.isLoggedIn() ?
- R.string.tag_FEx_untagged_w_astrid : R.string.tag_FEx_untagged;
- Filter untagged = new Filter(r.getString(untaggedLabel),
- r.getString(R.string.tag_FEx_untagged),
- TagService.untaggedTemplate(),
- null);
- untagged.listingIcon = ((BitmapDrawable)r.getDrawable(
- ThemeService.getDrawable(R.drawable.gl_lists, themeFlags))).getBitmap();
- filters[0] = untagged;
-
- for(int i = 0; i < tags.length; i++)
- filters[i+1] = filterFromTag(context, tags[i], TaskCriteria.activeAndVisible());
+ if (addUntaggedFilter) {
+ int untaggedLabel = gtasksPreferenceService.isLoggedIn() ?
+ R.string.tag_FEx_untagged_w_astrid : R.string.tag_FEx_untagged;
+ Filter untagged = new Filter(r.getString(untaggedLabel),
+ r.getString(R.string.tag_FEx_untagged),
+ TagService.untaggedTemplate(),
+ null);
+ untagged.listingIcon = ((BitmapDrawable)r.getDrawable(
+ ThemeService.getDrawable(R.drawable.gl_lists, themeFlags))).getBitmap();
+ filters[0] = untagged;
+ }
+
+ for(int i = 0; i < tags.length; i++) {
+ int index = addUntaggedFilter ? i + 1 : i;
+ filters[index] = constructFilter(context, tags[i]);
+ }
FilterCategory filter = new FilterCategory(context.getString(name), filters);
return filter;
}
+ protected Filter constructFilter(Context context, Tag tag) {
+ return filterFromTag(context, tag, TaskCriteria.activeAndVisible());
+ }
+
// --- tag manipulation activities
public abstract static class TagActivity extends Activity {
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
index 7802e4e58..78290471e 100644
--- a/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/TagService.java
@@ -330,7 +330,7 @@ public final class TagService {
tagData.readFromCursor(cursor);
String tagName = tagData.getValue(TagData.NAME).trim();
Tag tag = new Tag(tagData);
- if(tagData.getValue(TagData.DELETION_DATE) > 0 || tagData.getFlag(TagData.FLAGS, TagData.FLAG_EMERGENT)) {
+ if(tagData.getValue(TagData.DELETION_DATE) > 0 || tagData.getFlag(TagData.FLAGS, TagData.FLAG_EMERGENT) || tagData.getFlag(TagData.FLAGS, TagData.FLAG_FEATURED)) {
tags.remove(tagName);
continue;
}
@@ -356,6 +356,37 @@ public final class TagService {
return tagList;
}
+ public ArrayList getFeaturedLists() {
+ HashMap tags = new HashMap();
+
+ TodorooCursor cursor = tagDataService.query(Query.select(TagData.PROPERTIES)
+ .where(Functions.bitwiseAnd(TagData.FLAGS, TagData.FLAG_FEATURED).gt(0)));
+ try {
+ TagData tagData = new TagData();
+ for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ tagData.readFromCursor(cursor);
+ if (tagData.getValue(TagData.DELETION_DATE) > 0)
+ continue;
+ String tagName = tagData.getValue(TagData.NAME).trim();
+ Tag tag = new Tag(tagData);
+ if(TextUtils.isEmpty(tag.tag))
+ continue;
+ tags.put(tagName, tag);
+ }
+ } finally {
+ cursor.close();
+ }
+ ArrayList tagList = new ArrayList(tags.values());
+ Collections.sort(tagList,
+ new Comparator() {
+ @Override
+ public int compare(Tag object1, Tag object2) {
+ return object1.tag.compareToIgnoreCase(object2.tag);
+ }
+ });
+ return tagList;
+ }
+
/**
* Save the given array of tags into the database
* @param taskId
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterAdapter.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterAdapter.java
new file mode 100644
index 000000000..9b45cc937
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterAdapter.java
@@ -0,0 +1,37 @@
+package com.todoroo.astrid.tags.reusable;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.widget.ListView;
+
+import com.todoroo.astrid.adapter.FilterAdapter;
+import com.todoroo.astrid.utility.Constants;
+
+public class FeaturedListFilterAdapter extends FilterAdapter {
+ public static final String BROADCAST_REQUEST_FEATURED_LISTS = Constants.PACKAGE + ".REQUEST_FEATURED_LISTS"; //$NON-NLS-1$
+ public static final String BROADCAST_SEND_FEATURED_LISTS = Constants.PACKAGE + ".SEND_FEATURED_LISTS"; //$NON-NLS-1$
+
+ public FeaturedListFilterAdapter(Activity activity, ListView listView,
+ int rowLayout, boolean skipIntentFilters) {
+ super(activity, listView, rowLayout, skipIntentFilters);
+ }
+
+ @Override
+ public void getLists() {
+ Intent broadcastIntent = new Intent(BROADCAST_REQUEST_FEATURED_LISTS);
+ activity.sendBroadcast(broadcastIntent);
+ }
+
+ @Override
+ public void registerRecevier() {
+ IntentFilter peopleFilter = new IntentFilter(BROADCAST_SEND_FEATURED_LISTS);
+ activity.registerReceiver(filterReceiver, peopleFilter);
+ getLists();
+ }
+
+ @Override
+ public void unregisterRecevier() {
+ activity.unregisterReceiver(filterReceiver);
+ }
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterExposer.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterExposer.java
new file mode 100644
index 000000000..bedd759bc
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterExposer.java
@@ -0,0 +1,102 @@
+package com.todoroo.astrid.tags.reusable;
+
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.ContextManager;
+import com.todoroo.andlib.sql.Criterion;
+import com.todoroo.andlib.sql.Functions;
+import com.todoroo.andlib.sql.Order;
+import com.todoroo.andlib.sql.Query;
+import com.todoroo.andlib.sql.QueryTemplate;
+import com.todoroo.astrid.actfm.TagViewFragment;
+import com.todoroo.astrid.api.AstridApiConstants;
+import com.todoroo.astrid.api.Filter;
+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.data.Metadata;
+import com.todoroo.astrid.data.TagData;
+import com.todoroo.astrid.data.TaskApiDao.TaskCriteria;
+import com.todoroo.astrid.tags.TagFilterExposer;
+import com.todoroo.astrid.tags.TagService;
+import com.todoroo.astrid.tags.TagService.Tag;
+
+public class FeaturedListFilterExposer extends TagFilterExposer {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ addUntaggedFilter = false;
+ FilterListItem[] listAsArray = prepareFilters(context);
+
+ Intent broadcastIntent = new Intent(FeaturedListFilterAdapter.BROADCAST_SEND_FEATURED_LISTS);
+ broadcastIntent.putExtra(AstridApiConstants.EXTRAS_RESPONSE, listAsArray);
+ context.sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
+ }
+
+ private static FilterWithCustomIntent filterFromFeaturedList(Tag tag, Criterion criterion) {
+ String title = tag.tag;
+ QueryTemplate tagTemplate = tag.queryTemplate(criterion);
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Metadata.KEY.name, TagService.KEY);
+ contentValues.put(TagService.TAG.name, tag.tag);
+
+ FilterWithUpdate filter = new FilterWithUpdate(tag.tag,
+ title, tagTemplate,
+ contentValues);
+
+ Class> fragmentClass = FeaturedTaskListFragment.class;
+ filter.customTaskList = new ComponentName(ContextManager.getContext(), fragmentClass);
+ if(tag.image != null)
+ filter.imageUrl = tag.image;
+
+ Bundle extras = new Bundle();
+ extras.putString(TagViewFragment.EXTRA_TAG_NAME, tag.tag);
+ extras.putLong(TagViewFragment.EXTRA_TAG_REMOTE_ID, tag.remoteId);
+ filter.customExtras = extras;
+
+ return filter;
+ }
+
+ public static Filter getDefaultFilter() {
+ TodorooCursor firstFilter = PluginServices.getTagDataService()
+ .query(Query.select(TagData.PROPERTIES)
+ .where(Criterion.and(
+ Functions.bitwiseAnd(TagData.FLAGS, TagData.FLAG_FEATURED).gt(0),
+ TagData.DELETION_DATE.eq(0),
+ TagData.NAME.isNotNull(),
+ TagData.NAME.neq(""))) //$NON-NLS-1$
+ .orderBy(Order.asc(TagData.NAME))
+ .limit(1));
+ try {
+ if (firstFilter.getCount() > 0) {
+ firstFilter.moveToFirst();
+ TagData tagData = new TagData(firstFilter);
+ Tag tag = new Tag(tagData);
+ return filterFromFeaturedList(tag, TaskCriteria.activeAndVisible());
+ } else {
+ return null;
+ }
+ } finally {
+ firstFilter.close();
+ }
+ }
+
+ @Override
+ protected Filter constructFilter(Context context, Tag tag) {
+ return filterFromFeaturedList(tag, TaskCriteria.activeAndVisible());
+ }
+
+ @Override
+ protected List getTagList() {
+ return TagService.getInstance().getFeaturedLists();
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterMode.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterMode.java
new file mode 100644
index 000000000..17b88d631
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFilterMode.java
@@ -0,0 +1,53 @@
+package com.todoroo.astrid.tags.reusable;
+
+import android.content.Context;
+
+import com.timsu.astrid.R;
+import com.todoroo.astrid.activity.FilterListFragment;
+import com.todoroo.astrid.activity.FilterModeSpec;
+import com.todoroo.astrid.activity.TaskListFragment;
+import com.todoroo.astrid.api.Filter;
+import com.todoroo.astrid.api.FilterListItem;
+import com.todoroo.astrid.core.CoreFilterExposer;
+import com.todoroo.astrid.ui.MainMenuPopover;
+
+public class FeaturedListFilterMode implements FilterModeSpec {
+
+ @Override
+ public int[] getForbiddenMenuItems() {
+ return FORBIDDEN_MENU_ITEMS;
+ }
+
+ private static final int[] FORBIDDEN_MENU_ITEMS = {
+ TaskListFragment.MENU_NEW_FILTER_ID,
+ TaskListFragment.MENU_ADDONS_ID,
+ MainMenuPopover.MAIN_MENU_ITEM_FEATURED_LISTS
+ };
+
+ @Override
+ public Class extends FilterListFragment> getFilterListClass() {
+ return FeaturedListFragment.class;
+ }
+
+ @Override
+ public Filter getDefaultFilter(Context context) {
+ Filter defaultFilter = FeaturedListFilterExposer.getDefaultFilter();
+ if (defaultFilter == null)
+ return CoreFilterExposer.buildInboxFilter(context.getResources());
+ else
+ return defaultFilter;
+ }
+
+ @Override
+ public int getMainMenuIconAttr() {
+ return R.attr.asMainMenu;
+ }
+
+ @Override
+ public void onFilterItemClickedCallback(FilterListItem item) {
+ //
+ }
+
+
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFragment.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFragment.java
new file mode 100644
index 000000000..55fe921a3
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedListFragment.java
@@ -0,0 +1,25 @@
+package com.todoroo.astrid.tags.reusable;
+
+import android.app.Activity;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.astrid.activity.FilterListFragment;
+import com.todoroo.astrid.adapter.FilterAdapter;
+
+public class FeaturedListFragment extends FilterListFragment {
+
+ @Override
+ protected FilterAdapter instantiateAdapter() {
+ return new FeaturedListFilterAdapter(getActivity(), null, R.layout.filter_adapter_row, false);
+ }
+
+ @Override
+ protected int getLayout(Activity activity) {
+ if (AndroidUtilities.isTabletSized(activity))
+ return R.layout.filter_list_fragment_alternative_3pane;
+ else
+ return R.layout.filter_list_fragment_alternative;
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java
new file mode 100644
index 000000000..58968b738
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/FeaturedTaskListFragment.java
@@ -0,0 +1,113 @@
+package com.todoroo.astrid.tags.reusable;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.sql.Query;
+import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.astrid.actfm.TagViewFragment;
+import com.todoroo.astrid.adapter.TaskAdapter;
+import com.todoroo.astrid.data.TagData;
+import com.todoroo.astrid.data.Task;
+import com.todoroo.astrid.service.TagDataService;
+import com.todoroo.astrid.utility.Flags;
+
+public class FeaturedTaskListFragment extends TagViewFragment {
+
+ @Autowired private TagDataService tagDataService;
+
+ @Override
+ protected TaskAdapter createTaskAdapter(TodorooCursor cursor) {
+ return new ReusableTaskAdapter(this, R.layout.reusable_task_adapter_row,
+ cursor, sqlQueryTemplate, false, null);
+ }
+
+ @Override
+ protected void setupQuickAddBar() {
+ super.setupQuickAddBar();
+ quickAddBar.setVisibility(View.GONE);
+ ((TextView) getView().findViewById(android.R.id.empty)).setOnClickListener(null);
+ }
+
+ @Override
+ public void onTaskListItemClicked(long taskId) {
+ // Do nothing
+ }
+
+ @Override
+ protected int getTaskListBodyLayout() {
+ return R.layout.task_list_body_featured_list;
+ }
+
+ @Override
+ protected void setUpMembersGallery() {
+ // Repurposed this method to set up listener for clone list button
+ View clone = getView().findViewById(R.id.clone_list);
+ clone.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Clone list
+ if (taskAdapter == null || taskAdapter.getCount() == 0) {
+ Toast.makeText(getActivity(), R.string.actfm_feat_list_clone_empty, Toast.LENGTH_LONG).show();
+ return;
+ }
+ final String localName = tagData.getValue(TagData.NAME) + " " + getString(R.string.actfm_feat_list_suffix); //$NON-NLS-1$
+ long remoteId = 0;
+ TodorooCursor existing = tagDataService.query(Query.select(TagData.REMOTE_ID)
+ .where(TagData.NAME.eqCaseInsensitive(localName)));
+ try {
+ if (existing.getCount() > 0) {
+ existing.moveToFirst();
+ TagData match = new TagData(existing);
+ remoteId = match.getValue(TagData.REMOTE_ID);
+ }
+
+ } finally {
+ existing.close();
+ }
+
+ final ProgressDialog pd = DialogUtilities.progressDialog(getActivity(), getString(R.string.actfm_feat_list_cloning));
+
+ final long finalRemoteId = remoteId;
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ TodorooCursor tasks = taskService.fetchFiltered(taskAdapter.getQuery(), null, Task.PROPERTIES);
+ try {
+ Task t = new Task();
+ for (tasks.moveToFirst(); !tasks.isAfterLast(); tasks.moveToNext()) {
+ t.readFromCursor(tasks);
+ taskService.cloneReusableTask(t,
+ localName, finalRemoteId);
+ }
+ Activity activity = getActivity();
+ if (activity != null) {
+ DialogUtilities.dismissDialog(activity, pd);
+ DialogUtilities.okDialog(activity, getString(R.string.actfm_feat_list_clone_success), null);
+ }
+
+ Flags.set(Flags.REFRESH);
+ } finally {
+ tasks.close();
+ }
+ }
+ }).start();
+ }
+ });
+ }
+
+ @Override
+ protected void refresh() {
+ loadTaskListContent(true);
+ ((TextView)taskListView.findViewById(android.R.id.empty)).setText(R.string.TLA_no_items);
+ setUpMembersGallery();
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/tags/reusable/ReusableTaskAdapter.java b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/ReusableTaskAdapter.java
new file mode 100644
index 000000000..afce2760b
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/tags/reusable/ReusableTaskAdapter.java
@@ -0,0 +1,84 @@
+package com.todoroo.astrid.tags.reusable;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.data.TodorooCursor;
+import com.todoroo.andlib.utility.Preferences;
+import com.todoroo.astrid.activity.TaskListFragment;
+import com.todoroo.astrid.adapter.TaskAdapter;
+import com.todoroo.astrid.data.Task;
+import com.todoroo.astrid.utility.Flags;
+
+public class ReusableTaskAdapter extends TaskAdapter {
+
+ public ReusableTaskAdapter(TaskListFragment fragment, int resource,
+ Cursor c, AtomicReference query, boolean autoRequery,
+ OnCompletedTaskListener onCompletedTaskListener) {
+ super(fragment, resource, c, query, autoRequery, onCompletedTaskListener);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ ViewGroup view = (ViewGroup)inflater.inflate(resource, parent, false);
+
+ ReusableTaskViewHolder viewHolder = new ReusableTaskViewHolder();
+ viewHolder.task = new Task();
+ viewHolder.clone = (ImageView) view.findViewById(R.id.clone_task);
+ viewHolder.title = (TextView) view.findViewById(R.id.title);
+
+ boolean showFullTaskTitle = Preferences.getBoolean(R.string.p_fullTaskTitle, false);
+ if (showFullTaskTitle) {
+ viewHolder.title.setMaxLines(Integer.MAX_VALUE);
+ }
+
+ view.setTag(viewHolder);
+ for(int i = 0; i < view.getChildCount(); i++)
+ view.getChildAt(i).setTag(viewHolder);
+
+ viewHolder.clone.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ReusableTaskViewHolder holder = (ReusableTaskViewHolder) v.getTag();
+ taskService.cloneReusableTask(holder.task, null, 0);
+ Toast.makeText(fragment.getActivity(), R.string.actfm_feat_list_task_clone_success, Toast.LENGTH_LONG).show();
+ Flags.set(Flags.REFRESH);
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor c) {
+ TodorooCursor cursor = (TodorooCursor)c;
+ ReusableTaskViewHolder viewHolder = (ReusableTaskViewHolder) view.getTag();
+
+ Task task = viewHolder.task;
+ task.clear();
+ task.readFromCursor(cursor);
+
+ viewHolder.title.setText(task.getValue(Task.TITLE));
+ if (Preferences.getBoolean(R.string.p_allowCompressedTaskRows, false)) {
+ view.setMinimumHeight(0);
+ } else {
+ view.setMinimumHeight(minRowHeight);
+ }
+ }
+
+ public static class ReusableTaskViewHolder {
+ public Task task;
+ public ImageView clone;
+ public TextView title;
+ }
+
+}
diff --git a/astrid/res/drawable-hdpi/icn_clone_down.png b/astrid/res/drawable-hdpi/icn_clone_down.png
new file mode 100644
index 000000000..d99f8ad7f
Binary files /dev/null and b/astrid/res/drawable-hdpi/icn_clone_down.png differ
diff --git a/astrid/res/drawable-hdpi/icn_clone_up.png b/astrid/res/drawable-hdpi/icn_clone_up.png
new file mode 100644
index 000000000..1ece4dce0
Binary files /dev/null and b/astrid/res/drawable-hdpi/icn_clone_up.png differ
diff --git a/astrid/res/drawable-hdpi/icn_featured_lists.png b/astrid/res/drawable-hdpi/icn_featured_lists.png
new file mode 100644
index 000000000..e2bda464f
Binary files /dev/null and b/astrid/res/drawable-hdpi/icn_featured_lists.png differ
diff --git a/astrid/res/drawable-hdpi/icn_featured_lists_dark.png b/astrid/res/drawable-hdpi/icn_featured_lists_dark.png
new file mode 100644
index 000000000..a764b1bf6
Binary files /dev/null and b/astrid/res/drawable-hdpi/icn_featured_lists_dark.png differ
diff --git a/astrid/res/drawable/btn_clone.xml b/astrid/res/drawable/btn_clone.xml
new file mode 100644
index 000000000..ed1a20a15
--- /dev/null
+++ b/astrid/res/drawable/btn_clone.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/drawable/icn_clone_down.png b/astrid/res/drawable/icn_clone_down.png
new file mode 100644
index 000000000..c2c4bf5b1
Binary files /dev/null and b/astrid/res/drawable/icn_clone_down.png differ
diff --git a/astrid/res/drawable/icn_clone_up.png b/astrid/res/drawable/icn_clone_up.png
new file mode 100644
index 000000000..30cd930ab
Binary files /dev/null and b/astrid/res/drawable/icn_clone_up.png differ
diff --git a/astrid/res/drawable/icn_featured_lists.png b/astrid/res/drawable/icn_featured_lists.png
index e2e8f5916..9ffff8074 100644
Binary files a/astrid/res/drawable/icn_featured_lists.png and b/astrid/res/drawable/icn_featured_lists.png differ
diff --git a/astrid/res/drawable/icn_featured_lists_dark.png b/astrid/res/drawable/icn_featured_lists_dark.png
new file mode 100644
index 000000000..7b34e0deb
Binary files /dev/null and b/astrid/res/drawable/icn_featured_lists_dark.png differ
diff --git a/astrid/res/layout-land/people_list_fragment.xml b/astrid/res/layout-land/filter_list_fragment_alternative.xml
similarity index 89%
rename from astrid/res/layout-land/people_list_fragment.xml
rename to astrid/res/layout-land/filter_list_fragment_alternative.xml
index d7a64a442..8c7491d4d 100644
--- a/astrid/res/layout-land/people_list_fragment.xml
+++ b/astrid/res/layout-land/filter_list_fragment_alternative.xml
@@ -18,6 +18,8 @@
android:layout_alignParentTop="true"
android:scrollbars="vertical"
android:divider="@android:color/transparent"
+ android:dividerHeight="1px"
+ android:background="@android:color/transparent"
android:cacheColorHint="#00000000"/>
diff --git a/astrid/res/layout/featured_list_activity.xml b/astrid/res/layout/featured_list_activity.xml
new file mode 100644
index 000000000..99789bebb
--- /dev/null
+++ b/astrid/res/layout/featured_list_activity.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/astrid/res/layout/people_list_fragment.xml b/astrid/res/layout/filter_list_fragment_alternative.xml
similarity index 89%
rename from astrid/res/layout/people_list_fragment.xml
rename to astrid/res/layout/filter_list_fragment_alternative.xml
index e452d04f0..f3609e5a7 100644
--- a/astrid/res/layout/people_list_fragment.xml
+++ b/astrid/res/layout/filter_list_fragment_alternative.xml
@@ -18,6 +18,8 @@
android:layout_alignParentTop="true"
android:scrollbars="vertical"
android:divider="@android:color/transparent"
+ android:dividerHeight="1px"
+ android:background="@android:color/transparent"
android:cacheColorHint="#00000000"/>
diff --git a/astrid/res/layout/people_list_fragment_3pane.xml b/astrid/res/layout/filter_list_fragment_alternative_3pane.xml
similarity index 88%
rename from astrid/res/layout/people_list_fragment_3pane.xml
rename to astrid/res/layout/filter_list_fragment_alternative_3pane.xml
index d5144a3f3..db5720bc1 100644
--- a/astrid/res/layout/people_list_fragment_3pane.xml
+++ b/astrid/res/layout/filter_list_fragment_alternative_3pane.xml
@@ -18,6 +18,8 @@
android:layout_alignParentTop="true"
android:scrollbars="vertical"
android:divider="@android:color/transparent"
+ android:dividerHeight="1px"
+ android:background="@android:color/transparent"
android:cacheColorHint="#00000000"/>
diff --git a/astrid/res/layout/header_nav_views.xml b/astrid/res/layout/header_nav_views.xml
index 12a87b63a..8430fb867 100644
--- a/astrid/res/layout/header_nav_views.xml
+++ b/astrid/res/layout/header_nav_views.xml
@@ -54,6 +54,13 @@
+
+