From 37ebbb4fd51c7c4974201904f0d07f0bde1537d1 Mon Sep 17 00:00:00 2001 From: ojiikun Date: Mon, 8 Jul 2013 09:21:43 +0000 Subject: [PATCH 1/4] More rework of feed item list to deal with missing feeds. --- .../fragment/FeedItemListFragment.java | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/FeedItemListFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/FeedItemListFragment.java index b3131fb09..2e9210366 100644 --- a/clients/android/NewsBlur/src/com/newsblur/fragment/FeedItemListFragment.java +++ b/clients/android/NewsBlur/src/com/newsblur/fragment/FeedItemListFragment.java @@ -38,15 +38,12 @@ public class FeedItemListFragment extends StoryItemListFragment implements Loade private ContentResolver contentResolver; private String feedId; private FeedItemsAdapter adapter; - private Uri storiesUri; private int currentState; private int currentPage = 1; private boolean requestedPage = false; - private boolean doRequest = true; public static int ITEMLIST_LOADER = 0x01; private int READING_RETURNED = 0x02; - private Feed feed; private StoryOrder storyOrder; @@ -68,51 +65,43 @@ public class FeedItemListFragment extends StoryItemListFragment implements Loade currentState = getArguments().getInt("currentState"); feedId = getArguments().getString("feedId"); storyOrder = (StoryOrder)getArguments().getSerializable("storyOrder"); - - if (!NetworkUtils.isOnline(getActivity())) { - doRequest = false; - } } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_itemlist, null); - ListView itemList = (ListView) v.findViewById(R.id.itemlistfragment_list); + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_itemlist, null); + ListView itemList = (ListView) v.findViewById(R.id.itemlistfragment_list); - itemList.setEmptyView(v.findViewById(R.id.empty_view)); + itemList.setEmptyView(v.findViewById(R.id.empty_view)); - contentResolver = getActivity().getContentResolver(); - storiesUri = FeedProvider.FEED_STORIES_URI.buildUpon().appendPath(feedId).build(); - Cursor cursor = contentResolver.query(storiesUri, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder)); - - setupFeed(); - - String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_READ, DatabaseConstants.STORY_SHORTDATE, DatabaseConstants.STORY_INTELLIGENCE_AUTHORS }; - int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_author, R.id.row_item_title, R.id.row_item_date, R.id.row_item_sidebar }; - - getLoaderManager().initLoader(ITEMLIST_LOADER , null, this); - - adapter = new FeedItemsAdapter(getActivity(), feed, R.layout.row_item, cursor, groupFrom, groupTo, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); - - itemList.setOnScrollListener(this); - - adapter.setViewBinder(new FeedItemViewBinder(getActivity())); - itemList.setAdapter(adapter); - itemList.setOnItemClickListener(this); - itemList.setOnCreateContextMenuListener(this); - - return v; - } - - private void setupFeed() { + contentResolver = getActivity().getContentResolver(); + Uri storiesUri = FeedProvider.FEED_STORIES_URI.buildUpon().appendPath(feedId).build(); + Cursor storiesCursor = contentResolver.query(storiesUri, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder)); Uri feedUri = FeedProvider.FEEDS_URI.buildUpon().appendPath(feedId).build(); Cursor feedCursor = contentResolver.query(feedUri, null, null, null, null); + if (feedCursor.getCount() > 0) { feedCursor.moveToFirst(); - feed = Feed.fromCursor(feedCursor); + Feed feed = Feed.fromCursor(feedCursor); + + String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_READ, DatabaseConstants.STORY_SHORTDATE, DatabaseConstants.STORY_INTELLIGENCE_AUTHORS }; + int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_author, R.id.row_item_title, R.id.row_item_date, R.id.row_item_sidebar }; + + getLoaderManager().initLoader(ITEMLIST_LOADER , null, this); + + adapter = new FeedItemsAdapter(getActivity(), feed, R.layout.row_item, storiesCursor, groupFrom, groupTo, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + + itemList.setOnScrollListener(this); + + adapter.setViewBinder(new FeedItemViewBinder(getActivity())); + itemList.setAdapter(adapter); + itemList.setOnItemClickListener(this); + itemList.setOnCreateContextMenuListener(this); } else { Log.w(this.getClass().getName(), "Feed not found in DB, can't load."); } + + return v; } @Override @@ -130,7 +119,6 @@ public class FeedItemListFragment extends StoryItemListFragment implements Loade } public void hasUpdated() { - setupFeed(); getLoaderManager().restartLoader(ITEMLIST_LOADER , null, this); requestedPage = false; } @@ -154,12 +142,12 @@ public class FeedItemListFragment extends StoryItemListFragment implements Loade refreshStories(); } - @Override - protected void refreshStories() { - final String selection = DatabaseConstants.getStorySelectionFromState(currentState); - Cursor cursor = contentResolver.query(storiesUri, null, selection, null, DatabaseConstants.getStorySortOrder(storyOrder)); - adapter.swapCursor(cursor); - } + @Override + protected void refreshStories() { + Uri storiesUri = FeedProvider.FEED_STORIES_URI.buildUpon().appendPath(feedId).build(); + Cursor cursor = contentResolver.query(storiesUri, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder)); + adapter.swapCursor(cursor); + } @Override public void onScroll(AbsListView view, int firstVisible, int visibleCount, int totalCount) { From ebc7120f54ad0d7cd42a184caa9e30f96df6ee80 Mon Sep 17 00:00:00 2001 From: ojiikun Date: Wed, 10 Jul 2013 21:56:15 +0000 Subject: [PATCH 2/4] More error handling improvements. Added developer debug mode. --- .../NewsBlur/src/com/newsblur/network/APIManager.java | 3 +++ .../NewsBlur/src/com/newsblur/network/APIResponse.java | 7 +++++-- .../src/com/newsblur/network/domain/NewsBlurResponse.java | 4 ++-- .../NewsBlur/src/com/newsblur/util/AppConstants.java | 6 ++++++ .../android/NewsBlur/src/com/newsblur/util/FeedUtils.java | 4 ++-- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index d2a0c99d9..1e244ca8e 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -761,6 +761,9 @@ public class APIManager { try { URL url = new URL(urlString); Log.d(this.getClass().getName(), "API POST " + url ); + if (AppConstants.VERBOSE_LOG) { + Log.d(this.getClass().getName(), "post body: " + postBodyString); + } HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("POST"); diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIResponse.java b/clients/android/NewsBlur/src/com/newsblur/network/APIResponse.java index 789d833e5..4f3a05370 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIResponse.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIResponse.java @@ -15,6 +15,7 @@ import org.apache.http.HttpStatus; import com.newsblur.R; import com.newsblur.network.domain.NewsBlurResponse; +import com.newsblur.util.AppConstants; /** * A JSON-encoded response from the API servers. This class encodes the possible outcomes of @@ -74,7 +75,9 @@ public class APIResponse { return; } - //Log.d(this.getClass().getName(), "received API response: \n" + this.responseBody); + if (AppConstants.VERBOSE_LOG) { + Log.d(this.getClass().getName(), "received API response: \n" + this.responseBody); + } try { connection.disconnect(); @@ -112,7 +115,7 @@ public class APIResponse { return ((T) response); } catch (Exception e) { // this should never fail unless the constructor of the base response bean fails - Log.wtf(this.getClass().getName(), "" + classOfT); + Log.wtf(this.getClass().getName(), "Failed to load class: " + classOfT); return null; } } else { diff --git a/clients/android/NewsBlur/src/com/newsblur/network/domain/NewsBlurResponse.java b/clients/android/NewsBlur/src/com/newsblur/network/domain/NewsBlurResponse.java index 5d466ec4d..92e5503f9 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/domain/NewsBlurResponse.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/domain/NewsBlurResponse.java @@ -12,7 +12,7 @@ public class NewsBlurResponse { public String result; public boolean isError() { - if (message != null) return true; + if ((message != null) && (!message.equals(""))) return true; if ((errors != null) && (errors.message.length > 0) && (errors.message[0] != null)) return true; return false; } @@ -21,7 +21,7 @@ public class NewsBlurResponse { * Gets the error message returned by the API, or defaultMessage if none was found. */ public String getErrorMessage(String defaultMessage) { - if (message != null) return message; + if ((message != null) && (!message.equals(""))) return message; if ((errors != null) &&(errors.message != null) && (errors.message.length > 0) && (errors.message[0] != null)) return errors.message[0]; return defaultMessage; } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java index af1920eff..694829404 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java @@ -1,6 +1,12 @@ package com.newsblur.util; 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 int STATE_ALL = 0; public static final int STATE_SOME = 1; public static final int STATE_BEST = 2; diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 89bc9c78b..8c0569a8d 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -46,7 +46,7 @@ public class FeedUtils { if (!result.isError()) { Toast.makeText(context, R.string.toast_story_saved, Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(context, result.getErrorMessage(), Toast.LENGTH_LONG).show(); + Toast.makeText(context, result.getErrorMessage(context.getString(R.string.toast_story_save_error)), Toast.LENGTH_LONG).show(); } } }.execute(); @@ -67,7 +67,7 @@ public class FeedUtils { if (!result.isError()) { Toast.makeText(context, R.string.toast_story_unread, Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(context, result.getErrorMessage(), Toast.LENGTH_LONG).show(); + Toast.makeText(context, result.getErrorMessage(context.getString(R.string.toast_story_unread_error)), Toast.LENGTH_LONG).show(); } } }.execute(); From ab4c8bea153c2cc9d27df783e8cda08d123d338a Mon Sep 17 00:00:00 2001 From: ojiikun Date: Fri, 12 Jul 2013 05:11:48 +0000 Subject: [PATCH 3/4] Switch mark-unread task to use story hash. --- .../NewsBlur/src/com/newsblur/database/BlurDatabase.java | 1 + .../src/com/newsblur/database/DatabaseConstants.java | 5 +++-- clients/android/NewsBlur/src/com/newsblur/domain/Story.java | 5 +++++ .../android/NewsBlur/src/com/newsblur/util/FeedUtils.java | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabase.java b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabase.java index c5545c916..49bf4f02e 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabase.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabase.java @@ -83,6 +83,7 @@ public class BlurDatabase extends SQLiteOpenHelper { ")"; private final String STORY_TABLES_COLS = + DatabaseConstants.STORY_HASH + TEXT + ", " + DatabaseConstants.STORY_AUTHORS + TEXT + ", " + DatabaseConstants.STORY_CONTENT + TEXT + ", " + DatabaseConstants.STORY_DATE + TEXT + ", " + diff --git a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java index 7b92acbdc..26c142a8f 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java @@ -92,6 +92,7 @@ public class DatabaseConstants { public static final String STORY_SOCIAL_USER_ID = "socialUserId"; public static final String STORY_SOURCE_USER_ID = "sourceUserId"; public static final String STORY_TAGS = "tags"; + public static final String STORY_HASH = "story_hash"; public static final String COMMENT_ID = BaseColumns._ID; public static final String COMMENT_STORYID = "comment_storyid"; @@ -166,11 +167,11 @@ public class DatabaseConstants { public static final String[] STORY_COLUMNS = { STORY_AUTHORS, STORY_COMMENT_COUNT, STORY_CONTENT, STORY_DATE, STORY_SHARED_DATE, STORY_SHORTDATE, STORY_LONGDATE, STORY_TABLE + "." + STORY_FEED_ID, STORY_TABLE + "." + STORY_ID, STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS, STORY_INTELLIGENCE_TITLE, - STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL + STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH }; public static final String[] STARRED_STORY_COLUMNS = { STORY_AUTHORS, STORY_COMMENT_COUNT, STORY_CONTENT, STORY_DATE, STORY_SHARED_DATE, STORY_SHORTDATE, STORY_LONGDATE, STARRED_STORIES_TABLE + "." + STORY_FEED_ID, STARRED_STORIES_TABLE + "." + STORY_ID, STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS, STORY_INTELLIGENCE_TITLE, - STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL + STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH }; /** diff --git a/clients/android/NewsBlur/src/com/newsblur/domain/Story.java b/clients/android/NewsBlur/src/com/newsblur/domain/Story.java index 257a6b1fe..cc38cf847 100644 --- a/clients/android/NewsBlur/src/com/newsblur/domain/Story.java +++ b/clients/android/NewsBlur/src/com/newsblur/domain/Story.java @@ -83,6 +83,9 @@ public class Story implements Serializable { @SerializedName("long_parsed_date") public String longDate; + @SerializedName("story_hash") + public String storyHash; + public ContentValues getValues() { final ContentValues values = new ContentValues(); values.put(DatabaseConstants.STORY_ID, id); @@ -108,6 +111,7 @@ public class Story implements Serializable { values.put(DatabaseConstants.STORY_TAGS, TextUtils.join(",", tags)); values.put(DatabaseConstants.STORY_READ, read); values.put(DatabaseConstants.STORY_FEED_ID, feedId); + values.put(DatabaseConstants.STORY_HASH, storyHash); return values; } @@ -136,6 +140,7 @@ public class Story implements Serializable { story.tags = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_TAGS)), ","); story.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_FEED_ID)); story.id = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_ID)); + story.storyHash = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_HASH)); return story; } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 8c0569a8d..7304d80b3 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -60,7 +60,7 @@ public class FeedUtils { new AsyncTask() { @Override protected NewsBlurResponse doInBackground(Void... arg) { - return apiManager.markStoryAsUnread(story.feedId, story.id); + return apiManager.markStoryAsUnread(story.feedId, story.storyHash); } @Override protected void onPostExecute(NewsBlurResponse result) { From ad58aadd98e6ecc7025eac9485b7968bc6887792 Mon Sep 17 00:00:00 2001 From: ojiikun Date: Sat, 13 Jul 2013 01:16:03 +0000 Subject: [PATCH 4/4] Change save-story to use story hash. --- clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 7304d80b3..0b26de835 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -34,12 +34,10 @@ public class FeedUtils { public static void saveStory(final Story story, final Context context, final APIManager apiManager) { if (story != null) { - final String feedId = story.feedId; - final String storyId = story.id; new AsyncTask() { @Override protected NewsBlurResponse doInBackground(Void... arg) { - return apiManager.markStoryAsStarred(feedId, storyId); + return apiManager.markStoryAsStarred(story.feedId, story.storyHash); } @Override protected void onPostExecute(NewsBlurResponse result) {