Correctly handle folder heirarchies. (INCOMPLETE)

This commit is contained in:
dosiecki 2015-03-29 16:54:22 -07:00
parent 2d6f59a228
commit bbd9385a3f
11 changed files with 188 additions and 201 deletions

View file

@ -54,7 +54,7 @@ public class FolderItemsList extends ItemsList implements MarkAllReadDialogListe
@Override
protected FeedSet createFeedSet() {
return FeedSet.folder(this.folderName, FeedUtils.dbHelper.getFeedsForFolder(folderName));
return FeedUtils.feedSetFromFolderName(this.folderName);
}
@Override

View file

@ -24,7 +24,6 @@ public class BlurDatabase extends SQLiteOpenHelper {
db.execSQL(DatabaseConstants.COMMENT_SQL);
db.execSQL(DatabaseConstants.REPLY_SQL);
db.execSQL(DatabaseConstants.CLASSIFIER_SQL);
db.execSQL(DatabaseConstants.FEED_FOLDER_SQL);
db.execSQL(DatabaseConstants.SOCIALFEED_STORIES_SQL);
db.execSQL(DatabaseConstants.STARRED_STORIES_COUNT_SQL);
db.execSQL(DatabaseConstants.ACTION_SQL);
@ -42,7 +41,6 @@ public class BlurDatabase extends SQLiteOpenHelper {
db.execSQL(drop + DatabaseConstants.COMMENT_TABLE);
db.execSQL(drop + DatabaseConstants.REPLY_TABLE);
db.execSQL(drop + DatabaseConstants.CLASSIFIER_TABLE);
db.execSQL(drop + DatabaseConstants.FEED_FOLDER_MAP_TABLE);
db.execSQL(drop + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
db.execSQL(drop + DatabaseConstants.STARRED_STORY_COUNT_TABLE);
db.execSQL(drop + DatabaseConstants.ACTION_TABLE);

View file

@ -15,7 +15,7 @@ import android.util.Log;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Comment;
import com.newsblur.domain.Feed;
import com.newsblur.domain.FeedResult;
import com.newsblur.domain.Folder;
import com.newsblur.domain.Reply;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.Story;
@ -136,7 +136,6 @@ public class BlurDatabaseHelper {
public void cleanupFeedsFolders() {
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_TABLE, null, null);}
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FOLDER_TABLE, null, null);}
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_FOLDER_MAP_TABLE, null, null);}
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_TABLE, null, null);}
}
@ -147,7 +146,6 @@ public class BlurDatabaseHelper {
public void deleteFeed(String feedId) {
String[] selArgs = new String[] {feedId};
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_TABLE, DatabaseConstants.FEED_ID + " = ?", selArgs);}
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_FOLDER_MAP_TABLE, DatabaseConstants.FEED_FOLDER_FEED_ID + " = ?", selArgs);}
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_FEED_ID + " = ?", selArgs);}
}
@ -166,13 +164,11 @@ public class BlurDatabaseHelper {
}
}
public void insertFeedsFolders(List<ContentValues> feedValues,
List<ContentValues> folderValues,
List<ContentValues> ffmValues,
public void insertFeedsFolders(List<ContentValues> folderValues,
List<ContentValues> feedValues,
List<ContentValues> socialFeedValues) {
bulkInsertValues(DatabaseConstants.FEED_TABLE, feedValues);
bulkInsertValues(DatabaseConstants.FOLDER_TABLE, folderValues);
bulkInsertValues(DatabaseConstants.FEED_FOLDER_MAP_TABLE, ffmValues);
bulkInsertValues(DatabaseConstants.FEED_TABLE, feedValues);
bulkInsertValues(DatabaseConstants.SOCIALFEED_TABLE, socialFeedValues);
}
@ -336,17 +332,13 @@ public class BlurDatabaseHelper {
bulkInsertValues(DatabaseConstants.REPLY_TABLE, replyValues);
}
public Set<String> getFeedsForFolder(String folderName) {
Set<String> feedIds = new HashSet<String>();
String q = "SELECT " + DatabaseConstants.FEED_FOLDER_FEED_ID +
" FROM " + DatabaseConstants.FEED_FOLDER_MAP_TABLE +
" WHERE " + DatabaseConstants.FEED_FOLDER_FOLDER_NAME + " = ?" ;
Cursor c = dbRO.rawQuery(q, new String[]{folderName});
while (c.moveToNext()) {
feedIds.add(c.getString(c.getColumnIndexOrThrow(DatabaseConstants.FEED_FOLDER_FEED_ID)));
}
c.close();
return feedIds;
public Folder getFolder(String folderName) {
String[] selArgs = new String[] {folderName};
String selection = DatabaseConstants.FOLDER_NAME + " = ?";
Cursor c = dbRO.query(DatabaseConstants.FOLDER_TABLE, null, selection, selArgs, null, null, null);
Folder folder = Folder.fromCursor(c);
closeQuietly(c);
return folder;
}
public void markStoryHashesRead(List<String> hashes) {
@ -677,14 +669,14 @@ public class BlurDatabaseHelper {
return query(false, DatabaseConstants.SOCIALFEED_TABLE, null, DatabaseConstants.getBlogSelectionFromState(stateFilter), null, null, null, "UPPER(" + DatabaseConstants.SOCIAL_FEED_TITLE + ") ASC", null, cancellationSignal);
}
public Loader<Cursor> getFolderFeedMapLoader() {
public Loader<Cursor> getFoldersLoader() {
return new QueryCursorLoader(context) {
protected Cursor createCursor() {return getFolderFeedMapCursor(cancellationSignal);}
protected Cursor createCursor() {return getFoldersCursor(cancellationSignal);}
};
}
public Cursor getFolderFeedMapCursor(CancellationSignal cancellationSignal) {
return query(false, DatabaseConstants.FEED_FOLDER_MAP_TABLE, null, null, null, null, null, null, null, cancellationSignal);
public Cursor getFoldersCursor(CancellationSignal cancellationSignal) {
return query(false, DatabaseConstants.FOLDER_TABLE, null, null, null, null, null, null, null, cancellationSignal);
}
public Loader<Cursor> getFeedsLoader(final StateFilter stateFilter) {

View file

@ -15,8 +15,10 @@ public class DatabaseConstants {
private static final String INTEGER = " INTEGER";
public static final String FOLDER_TABLE = "folders";
public static final String FOLDER_ID = BaseColumns._ID;
public static final String FOLDER_NAME = "folder_name";
public static final String FOLDER_PARENT_NAMES = "folder_parent_names";
public static final String FOLDER_CHILDREN_NAMES = "folder_children_names";
public static final String FOLDER_FEED_IDS = "folder_feedids";
public static final String FEED_TABLE = "feeds";
public static final String FEED_ID = BaseColumns._ID;
@ -45,10 +47,6 @@ public class DatabaseConstants {
public static final String SOCIAL_FEED_NEUTRAL_COUNT = "nt";
public static final String SOCIAL_FEED_NEGATIVE_COUNT = "ng";
public static final String FEED_FOLDER_MAP_TABLE = "feed_folder_map";
public static final String FEED_FOLDER_FEED_ID = "feed_feed_id";
public static final String FEED_FOLDER_FOLDER_NAME = "feed_folder_name";
public static final String SOCIALFEED_STORY_MAP_TABLE = "socialfeed_story_map";
public static final String SOCIALFEED_STORY_USER_ID = "socialfeed_story_user_id";
public static final String SOCIALFEED_STORY_STORYID = "socialfeed_story_storyid";
@ -139,8 +137,10 @@ public class DatabaseConstants {
public static final String ACTION_INCLUDE_NEWER = "include_newer";
static final String FOLDER_SQL = "CREATE TABLE " + FOLDER_TABLE + " (" +
FOLDER_ID + INTEGER + " PRIMARY KEY AUTOINCREMENT, " +
FOLDER_NAME + TEXT + " UNIQUE " +
FOLDER_NAME + TEXT + " PRIMARY KEY, " +
FOLDER_PARENT_NAMES + TEXT + ", " +
FOLDER_CHILDREN_NAMES + TEXT + ", " +
FOLDER_FEED_IDS + TEXT +
")";
static final String FEED_SQL = "CREATE TABLE " + FEED_TABLE + " (" +
@ -245,12 +245,6 @@ public class DatabaseConstants {
CLASSIFIER_VALUE + TEXT +
")";
static final String FEED_FOLDER_SQL = "CREATE TABLE " + FEED_FOLDER_MAP_TABLE + " (" +
FEED_FOLDER_FOLDER_NAME + TEXT + " NOT NULL, " +
FEED_FOLDER_FEED_ID + INTEGER + " NOT NULL, " +
"PRIMARY KEY (" + FEED_FOLDER_FOLDER_NAME + ", " + FEED_FOLDER_FEED_ID + ") " +
")";
static final String SOCIALFEED_STORIES_SQL = "CREATE TABLE " + SOCIALFEED_STORY_MAP_TABLE + " (" +
SOCIALFEED_STORY_STORYID + TEXT + " NOT NULL, " +
SOCIALFEED_STORY_USER_ID + INTEGER + " NOT NULL, " +

View file

@ -30,6 +30,7 @@ import com.newsblur.activity.GlobalSharedStoriesItemsList;
import com.newsblur.activity.NewsBlurApplication;
import static com.newsblur.database.DatabaseConstants.getStr;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SocialFeed;
import com.newsblur.util.AppConstants;
import com.newsblur.util.ImageLoader;
@ -48,7 +49,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
private Cursor socialFeedCursor;
private Map<String,List<String>> folderFeedMap = Collections.emptyMap();
private Map<String,Folder> folders = Collections.emptyMap();
private List<String> activeFolderNames;
private List<List<Feed>> activeFolderChildren;
private List<Integer> neutCounts;
@ -308,18 +309,23 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
notifyDataSetChanged();
}
public synchronized void setFolderFeedMapCursor(Cursor cursor) {
public synchronized void setFoldersCursor(Cursor cursor) {
if ((cursor.getCount() < 1) || (!cursor.isBeforeFirst())) return;
folderFeedMap = newCustomSortedMap();
folders = new LinkedHashMap<String,Folder>(cursor.getCount());
while (cursor.moveToNext()) {
String folderName = getStr(cursor, DatabaseConstants.FEED_FOLDER_FOLDER_NAME);
String feedId = getStr(cursor, DatabaseConstants.FEED_FOLDER_FEED_ID);
if (! folderFeedMap.containsKey(folderName)) folderFeedMap.put(folderName, new ArrayList<String>());
folderFeedMap.get(folderName).add(feedId);
Folder folder = Folder.fromCursor(cursor);
folders.put(folder.flatName(), folder);
}
if (!folderFeedMap.containsKey(AppConstants.ROOT_FOLDER)) {
folderFeedMap.put(AppConstants.ROOT_FOLDER, new ArrayList<String>());
/*
if (!folders.containsKey(AppConstants.ROOT_FOLDER)) {
Folder fakeRoot = new Folder();
fakeRoot.name = AppConstants.ROOT_FOLDER;
fakeRoot.parents = Collections.emptyList();
fakeRoot.children = Collections.emptyList();
fakeRoot.feedIds = Collections.emptyList();
folders.put(AppConstants.ROOT_FOLDER, fakeRoot);
}
*/
recountFeeds();
notifyDataSetChanged();
}
@ -344,18 +350,22 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
}
private void recountFeeds() {
if ((folderFeedMap == null) || (feeds == null)) return;
int c = folderFeedMap.keySet().size();
activeFolderNames = new ArrayList<String>(c);
activeFolderChildren = new ArrayList<List<Feed>>(c);
neutCounts = new ArrayList<Integer>(c);
posCounts = new ArrayList<Integer>(c);
for (String folderName : folderFeedMap.keySet()) {
if ((folders == null) || (feeds == null)) return;
// re-init our local vars
activeFolderNames = new ArrayList<String>();
activeFolderChildren = new ArrayList<List<Feed>>();
neutCounts = new ArrayList<Integer>();
posCounts = new ArrayList<Integer>();
// create a sorted list of folder display names
List<String> sortedFolderNames = new ArrayList<String>(folders.keySet());
customSortList(sortedFolderNames);
// inspect folders to see if the are active for display
for (String folderName : sortedFolderNames) {
List<Feed> activeFeeds = new ArrayList<Feed>();
int neutCount = 0;
int posCount = 0;
for (String feedId : folderFeedMap.get(folderName)) {
Feed f = feeds.get(feedId);
for (Long feedId : folders.get(folderName).feedIds) {
Feed f = feeds.get(feedId.toString());
if (f != null) {
if (((currentState == StateFilter.BEST) && (f.positiveCount > 0)) ||
((currentState == StateFilter.SOME) && ((f.positiveCount + f.neutralCount > 0))) ||
@ -492,18 +502,20 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
* folder on top, and also the expectation that *despite locale*, folders
* starting with an underscore should show up on top.
*/
private Map<String,List<String>> newCustomSortedMap() {
Comparator<String> c = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
if (TextUtils.equals(s1, s2)) return 0;
if (s1.equals(AppConstants.ROOT_FOLDER)) return -1;
if (s2.equals(AppConstants.ROOT_FOLDER)) return 1;
if (s1.startsWith("_")) return -1;
if (s2.startsWith("_")) return 1;
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
};
return new TreeMap<String,List<String>>(c);
private void customSortList(List<String> list) {
Collections.sort(list, CustomComparator);
}
private static Comparator<String> CustomComparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
if (TextUtils.equals(s1, s2)) return 0;
if (s1.equals(AppConstants.ROOT_FOLDER)) return -1;
if (s2.equals(AppConstants.ROOT_FOLDER)) return 1;
if (s1.startsWith("_")) return -1;
if (s2.startsWith("_")) return 1;
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
};
}

View file

@ -4,38 +4,67 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import com.newsblur.database.DatabaseConstants;
public class Folder {
public final ContentValues values = new ContentValues();
/** Actual unique name of the folder. */
public String name;
/** List, drilling down from root to this folder of containing folders. NOTE: this is a path! */
public List<String> parents;
/** Set of any children folders contained in this folder. NOTE: this is a one-to-many set! */
public List<String> children;
/** Set of any feeds contained in this folder. */
public List<Long> feedIds;
public void setId(final String id) {
values.put(DatabaseConstants.FOLDER_ID, id);
}
public void setName(final String name) {
values.put(DatabaseConstants.FOLDER_NAME, name);
}
public String getId() {
return values.getAsString(DatabaseConstants.FOLDER_ID);
}
public String getName() {
return values.getAsString(DatabaseConstants.FOLDER_NAME);
}
public static Folder fromCursor(Cursor folderCursor) {
final Folder folder = new Folder();
folder.setId(folderCursor.getString(folderCursor.getColumnIndex(DatabaseConstants.FOLDER_ID)));
folder.setName(folderCursor.getString(folderCursor.getColumnIndex(DatabaseConstants.FOLDER_NAME)));
public static Folder fromCursor(Cursor c) {
if (c.isBeforeFirst()) {
c.moveToFirst();
}
Folder folder = new Folder();
folder.name = c.getString(c.getColumnIndex(DatabaseConstants.FOLDER_NAME));
String parents = c.getString(c.getColumnIndex(DatabaseConstants.FOLDER_PARENT_NAMES));
folder.parents = new ArrayList<String>();
for (String name : TextUtils.split(parents, ",")) { folder.parents.add(name);}
String children = c.getString(c.getColumnIndex(DatabaseConstants.FOLDER_CHILDREN_NAMES));
folder.children = new ArrayList<String>();
for (String name : TextUtils.split(children, ",")) { folder.children.add(name);}
String feeds = c.getString(c.getColumnIndex(DatabaseConstants.FOLDER_FEED_IDS));
folder.feedIds = new ArrayList<Long>();
for (String id : TextUtils.split(feeds, ",")) { folder.feedIds.add(Long.valueOf(id));}
return folder;
}
public ContentValues getValues() {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.FOLDER_NAME, name);
values.put(DatabaseConstants.FOLDER_PARENT_NAMES, TextUtils.join(",", parents));
values.put(DatabaseConstants.FOLDER_CHILDREN_NAMES, TextUtils.join(",", children));
values.put(DatabaseConstants.FOLDER_FEED_IDS, TextUtils.join(",", feedIds));
return values;
}
public String flatName() {
StringBuilder builder = new StringBuilder();
for (String parentName : parents) {
builder.append(parentName);
builder.append(" - ");
}
builder.append(name);
return builder.toString();
}
@Override
public boolean equals(Object otherFolder) {
return TextUtils.equals(((Folder) otherFolder).getId(), getId());
return TextUtils.equals(((Folder) otherFolder).name, name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}

View file

@ -44,7 +44,7 @@ import com.newsblur.util.UIUtils;
public class FolderListFragment extends NbFragment implements OnGroupClickListener, OnChildClickListener, OnCreateContextMenuListener, LoaderManager.LoaderCallbacks<Cursor> {
private static final int SOCIALFEEDS_LOADER = 1;
private static final int FOLDERFEEDMAP_LOADER = 2;
private static final int FOLDERS_LOADER = 2;
private static final int FEEDS_LOADER = 3;
private static final int SAVEDCOUNT_LOADER = 4;
@ -64,9 +64,9 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
public synchronized void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
sharedPreferences = getActivity().getSharedPreferences(PrefConstants.PREFERENCES, 0);
if (getLoaderManager().getLoader(FOLDERFEEDMAP_LOADER) == null) {
if (getLoaderManager().getLoader(FOLDERS_LOADER) == null) {
getLoaderManager().initLoader(SOCIALFEEDS_LOADER, null, this);
getLoaderManager().initLoader(FOLDERFEEDMAP_LOADER, null, this);
getLoaderManager().initLoader(FOLDERS_LOADER, null, this);
getLoaderManager().initLoader(FEEDS_LOADER, null, this);
getLoaderManager().initLoader(SAVEDCOUNT_LOADER, null, this);
}
@ -77,8 +77,8 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
switch (id) {
case SOCIALFEEDS_LOADER:
return FeedUtils.dbHelper.getSocialFeedsLoader(currentState);
case FOLDERFEEDMAP_LOADER:
return FeedUtils.dbHelper.getFolderFeedMapLoader();
case FOLDERS_LOADER:
return FeedUtils.dbHelper.getFoldersLoader();
case FEEDS_LOADER:
return FeedUtils.dbHelper.getFeedsLoader(currentState);
case SAVEDCOUNT_LOADER:
@ -96,8 +96,8 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
case SOCIALFEEDS_LOADER:
adapter.setSocialFeedCursor(cursor);
break;
case FOLDERFEEDMAP_LOADER:
adapter.setFolderFeedMapCursor(cursor);
case FOLDERS_LOADER:
adapter.setFoldersCursor(cursor);
break;
case FEEDS_LOADER:
adapter.setFeedCursor(cursor);
@ -124,7 +124,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
public void hasUpdated() {
if (isAdded()) {
getLoaderManager().restartLoader(SOCIALFEEDS_LOADER, null, this);
getLoaderManager().restartLoader(FOLDERFEEDMAP_LOADER, null, this);
getLoaderManager().restartLoader(FOLDERS_LOADER, null, this);
getLoaderManager().restartLoader(FEEDS_LOADER, null, this);
getLoaderManager().restartLoader(SAVEDCOUNT_LOADER, null, this);
}
@ -215,7 +215,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
} else if (item.getItemId() == R.id.menu_mark_folder_as_read) {
if (!adapter.isFolderRoot(groupPosition)) {
String folderName = adapter.getGroup(groupPosition);
FeedUtils.markFeedsRead(FeedUtils.feedSetFromFolderName(folderName, getActivity()), null, null, getActivity());
FeedUtils.markFeedsRead(FeedUtils.feedSetFromFolderName(folderName), null, null, getActivity());
} else {
FeedUtils.markFeedsRead(FeedSet.allFeeds(), null, null, getActivity());
}

View file

@ -1,12 +1,11 @@
package com.newsblur.network.domain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
import android.util.Log;
import com.google.gson.Gson;
@ -17,32 +16,26 @@ import com.google.gson.JsonParser;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SocialFeed;
import com.newsblur.util.AppConstants;
public class FeedFolderResponse {
/** Helper variable so users of the parser can pass along how long it took to read the JSON stream, for instrumentation. */
public long readTime;
@SerializedName("starred_count")
public int starredCount;
public Set<Folder> folders;
public Set<Feed> feeds;
public Set<SocialFeed> socialFeeds;
@SerializedName("feeds")
public Map<String, Feed> feeds;
@SerializedName("flat_folders")
public Map<String, List<Long>> folders;
@SerializedName("social_feeds")
public SocialFeed[] socialFeeds;
public boolean isAuthenticated;
public boolean isPremium;
public boolean isStaff;
public int starredCount;
public FeedFolderResponse(String json, Gson gson) {
// TODO: is there really any good reason the default GSON parser doesn't work here?
JsonParser parser = new JsonParser();
JsonObject asJsonObject = parser.parse(json).getAsJsonObject();
@ -55,23 +48,19 @@ public class FeedFolderResponse {
this.isPremium = profile.get("is_premium").getAsBoolean();
}
JsonArray jsonFoldersArray = (JsonArray) asJsonObject.get("folders");
ArrayList<String> nestedFolderList = new ArrayList<String>();
folders = new HashMap<String, List<Long>>();
parseFeedArray(nestedFolderList, folders, null, jsonFoldersArray);
JsonElement starredCountElement = asJsonObject.get("starred_count");
if(starredCountElement != null) {
starredCount = gson.fromJson(starredCountElement, int.class);
}
folders = new HashSet<Folder>();
JsonArray jsonFoldersArray = (JsonArray) asJsonObject.get("folders");
// recursively parse folders
parseFolderArray(new ArrayList<String>(0), null, jsonFoldersArray);
// Inconsistent server response here. When user has no feeds we get
// "feeds": []
// and other times we get
// "feeds": {"309667": {
// So support both I guess
// Inconsistent server response here. When user has no feeds we get an empty array, otherwise an object
JsonElement feedsElement = asJsonObject.get("feeds");
feeds = new HashMap<String, Feed>();
feeds = new HashSet<Feed>();
if(feedsElement instanceof JsonObject) {
JsonObject feedsObject = (JsonObject) asJsonObject.get("feeds");
if(feedsObject != null) {
@ -80,48 +69,43 @@ public class FeedFolderResponse {
while(iterator.hasNext()) {
Entry<String, JsonElement> feedElement = iterator.next();
Feed feed = gson.fromJson(feedElement.getValue(), Feed.class);
feeds.put(feedElement.getKey(), feed);
feeds.add(feed);
}
}
} // else server sent back '"feeds": []'
socialFeeds = new SocialFeed[0];
socialFeeds = new HashSet<SocialFeed>();
JsonArray socialFeedsArray = (JsonArray) asJsonObject.get("social_feeds");
if(socialFeedsArray != null) {
List<SocialFeed> socialFeedsList = new ArrayList<SocialFeed>();
for(int i=0;i<socialFeedsArray.size();i++) {
JsonElement jsonElement = socialFeedsArray.get(i);
SocialFeed socialFeed = gson.fromJson(jsonElement, SocialFeed.class);
socialFeedsList.add(socialFeed);
socialFeeds.add(socialFeed);
}
socialFeeds = socialFeedsList.toArray(new SocialFeed[socialFeedsArray.size()]);
}
// sometimes the API won't declare the top-level/root folder, but most of the
// codebase expects it to exist. Declare it as empty if missing.
if (!folders.containsKey(AppConstants.ROOT_FOLDER)) {
folders.put(AppConstants.ROOT_FOLDER, new ArrayList<Long>());
Folder emptyRootFolder = new Folder();
emptyRootFolder.name = AppConstants.ROOT_FOLDER;
// equality is based on folder name, so contains() will work
if (!folders.contains(emptyRootFolder)) {
folders.add(emptyRootFolder);
Log.d( this.getClass().getName(), "root folder was missing. added it.");
}
}
/**
* Parses a folder, which is a list of feeds and/or more folders. Nested folders
* are flattened into a single list, with names that are heirarchical.
* Parses a folder, which is a list of feeds and/or more folders.
*
* @param nestedFolderList a list of any parent folders that surrounded this folder.
* @param folders the sink
* @param name the name of this folder.
* @param arrayValue the actual contents to be parsed.
* @param parentName folder that surrounded this folder.
* @param name the name of this folder or null if root.
* @param arrayValue the contents to be parsed.
*/
private void parseFeedArray(List<String> nestedFolderList,
Map<String, List<Long>> folders, String name, JsonArray arrayValue) {
// determine our text name, like "grandparent - parent - me"
String fullFolderName = getFolderName(name, nestedFolderList);
// sink for any feeds found in this folder
ArrayList<Long> feedIds = new ArrayList<Long>();
private void parseFolderArray(List<String> parentNames, String name, JsonArray arrayValue) {
if (name == null) name = AppConstants.ROOT_FOLDER;
List<String> children = new ArrayList<String>();
List<Long> feedIds = new ArrayList<Long>();
for (JsonElement jsonElement : arrayValue) {
// a folder array contains either feed IDs or nested folder objects
if(jsonElement.isJsonPrimitive()) {
@ -129,35 +113,23 @@ public class FeedFolderResponse {
} else {
// if it wasn't a feed ID, it is a nested folder object
Set<Entry<String, JsonElement>> entrySet = ((JsonObject) jsonElement).entrySet();
List<String> nestedFolderListCopy = new ArrayList<String>(nestedFolderList);
if(name != null) {
nestedFolderListCopy.add(name);
}
// recurse - nested folders are just objects with (usually one) field named for the folder
// that is a list of contained feeds or additional folders
for (Entry<String, JsonElement> next : entrySet) {
parseFeedArray( nestedFolderListCopy, folders, next.getKey(), (JsonArray) next.getValue() );
children.add(next.getKey());
List<String> appendedParentList = new ArrayList<String>(parentNames);
appendedParentList.add(name);
parseFolderArray(appendedParentList, next.getKey(), (JsonArray) next.getValue());
}
}
}
folders.put(fullFolderName, feedIds);
//Log.d( this.getClass().getName(), "parsed folder '" + fullFolderName + "' with " + feedIds.size() + " feeds" );
Folder folder = new Folder();
folder.name = name;
folder.parents = parentNames;
folder.children = children;
folder.feedIds = feedIds;
folders.add(folder);
Log.d(this.getClass().getName(), String.format("folder %s has parents %s and children %s and %d feeds", name, parentNames.toString(), children.toString(), feedIds.size()));
}
private String getFolderName(String key, List<String> parentFeedNames) {
StringBuilder builder = new StringBuilder();
for(String parentFolder: parentFeedNames) {
builder.append(parentFolder);
builder.append(" - ");
}
if(key != null) {
builder.append(key);
} else {
// a null key means we are at the root. give these a pseudo-folder name, since the DB and many
// classes would be very unhappy with a null foldername.
builder.append(AppConstants.ROOT_FOLDER);
}
return builder.toString();
}
}

View file

@ -18,6 +18,8 @@ import com.newsblur.activity.NbActivity;
import com.newsblur.database.BlurDatabaseHelper;
import static com.newsblur.database.BlurDatabaseHelper.closeQuietly;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.Story;
import com.newsblur.network.APIConstants;
@ -385,7 +387,7 @@ public class NBSyncService extends Service {
NbActivity.updateAllActivities(false);
// there is a rare issue with feeds that have no folder. capture them for workarounds.
Set<String> debugFeedIds = new HashSet<String>();
Set<Long> debugFeedIds = new HashSet<Long>();
orphanFeedIds = new HashSet<String>();
try {
@ -420,42 +422,27 @@ public class NBSyncService extends Service {
// data for the folder and folder-feed-mapping tables
List<ContentValues> folderValues = new ArrayList<ContentValues>();
List<ContentValues> ffmValues = new ArrayList<ContentValues>();
for (Entry<String, List<Long>> entry : feedResponse.folders.entrySet()) {
if (!TextUtils.isEmpty(entry.getKey())) {
String folderName = entry.getKey().trim();
if (!TextUtils.isEmpty(folderName)) {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.FOLDER_NAME, folderName);
folderValues.add(values);
}
for (Long feedId : entry.getValue()) {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.FEED_FOLDER_FEED_ID, feedId);
values.put(DatabaseConstants.FEED_FOLDER_FOLDER_NAME, folderName);
ffmValues.add(values);
// note all feeds that belong to some folder
debugFeedIds.add(Long.toString(feedId));
}
}
for (Folder folder : feedResponse.folders) {
folderValues.add(folder.getValues());
// note all feeds that belong to some folder
debugFeedIds.addAll(folder.feedIds);
}
// data for the feeds table
List<ContentValues> feedValues = new ArrayList<ContentValues>();
feedaddloop: for (String feedId : feedResponse.feeds.keySet()) {
feedaddloop: for (Feed feed : feedResponse.feeds) {
// sanity-check that the returned feeds actually exist in a folder or at the root
// if they do not, they should neither display nor count towards unread numbers
if (! debugFeedIds.contains(feedId)) {
Log.w(this.getClass().getName(), "Found and ignoring un-foldered feed: " + feedId );
orphanFeedIds.add(feedId);
if (! debugFeedIds.contains(feed.feedId)) {
Log.w(this.getClass().getName(), "Found and ignoring un-foldered feed: " + feed.feedId );
orphanFeedIds.add(feed.feedId);
continue feedaddloop;
}
if (! feedResponse.feeds.get(feedId).active) {
if (! feed.active) {
// the feed is disabled/hidden, pretend it doesn't exist
continue feedaddloop;
}
feedValues.add(feedResponse.feeds.get(feedId).getValues());
feedValues.add(feed.getValues());
}
// data for the the social feeds table
@ -464,7 +451,7 @@ public class NBSyncService extends Service {
socialFeedValues.add(feed.getValues());
}
dbHelper.insertFeedsFolders(feedValues, folderValues, ffmValues, socialFeedValues);
dbHelper.insertFeedsFolders(folderValues, feedValues, socialFeedValues);
// populate the starred stories count table
dbHelper.updateStarredStoriesCount(feedResponse.starredCount);

View file

@ -5,7 +5,7 @@ public class AppConstants {
// Enables high-volume logging that may be useful for debugging. This should
// never be enabled for releases, as it not only slows down the app considerably,
// it will log sensitive info such as passwords!
public static final boolean VERBOSE_LOG = false;
public static final boolean VERBOSE_LOG = true;
public static final boolean VERBOSE_LOG_DB = false;
public static final boolean VERBOSE_LOG_NET = false;

View file

@ -21,6 +21,7 @@ import com.newsblur.activity.NbActivity;
import com.newsblur.database.BlurDatabaseHelper;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.Story;
import com.newsblur.network.APIManager;
@ -200,8 +201,10 @@ public class FeedUtils {
context.startActivity(Intent.createChooser(intent, "Share using"));
}
public static FeedSet feedSetFromFolderName(String folderName, Context context) {
Set<String> feedIds = dbHelper.getFeedsForFolder(folderName);
public static FeedSet feedSetFromFolderName(String folderName) {
Folder folder = dbHelper.getFolder(folderName);
Set<String> feedIds = new HashSet<String>(folder.feedIds.size());
for (Long id : folder.feedIds) feedIds.add(Long.toString(id));
return FeedSet.folder(folderName, feedIds);
}