From 95e9f3d58cd7b9f60e9a0713cd50d53a280edaeb Mon Sep 17 00:00:00 2001 From: dosiecki Date: Tue, 9 Sep 2014 16:51:26 -0700 Subject: [PATCH 01/10] Mark stories read locally before remote refresh. (#575) Fix time-bound marking. (#576) --- .../newsblur/database/BlurDatabaseHelper.java | 59 ++++++++++++++++--- .../newsblur/database/DatabaseConstants.java | 6 ++ .../com/newsblur/network/APIConstants.java | 5 +- .../src/com/newsblur/network/APIManager.java | 13 +++- .../com/newsblur/service/NBSyncService.java | 9 ++- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java index b3436aef0..5e4d07111 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java @@ -292,16 +292,16 @@ public class BlurDatabaseHelper { } public void markFeedsRead(FeedSet fs, Long olderThan, Long newerThan) { - // TODO: impl at least the older/newer than cases + // split this into two steps, since the double-check feature needs to + // redo the second step separately + markFeedsRead_feedCounts(fs, olderThan, newerThan); + markFeedsRead_storyCounts(fs, olderThan, newerThan); + } - // TODO: stories - - // feed counts + public void markFeedsRead_feedCounts(FeedSet fs, Long olderThan, Long newerThan) { if (fs.isAllNormal()) { setFeedUnreadCount(0, null, null); - } else if (fs.isAllSocial()) { - // TODO: oddly, the client never supported this before, so there is no button to invoke it. The API call - // works, though, so adding an impl. here should let us enable the button. + setSocialFeedUnreadCount(0, null, null); } else if (fs.getMultipleFeeds() != null) { for (String feedId : fs.getMultipleFeeds()) { setFeedUnreadCount(0, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId}); @@ -311,10 +311,38 @@ public class BlurDatabaseHelper { } else if (fs.getSingleSocialFeed() != null) { setSocialFeedUnreadCount(0, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[]{fs.getSingleSocialFeed().getKey()}); } else { - throw new IllegalStateException("Asked to get stories for FeedSet of unknown type."); + // TODO: fs.isAllSocial() was never supported by the UI, but the API has it, and it would be + // easy enough to add here. Should we? + throw new IllegalStateException("Asked to mark stories for FeedSet of unknown type."); } } + public void markFeedsRead_storyCounts(FeedSet fs, Long olderThan, Long newerThan) { + ContentValues values = new ContentValues(); + values.put(DatabaseConstants.STORY_READ, true); + String rangeSelection = null; + if (olderThan != null) rangeSelection = DatabaseConstants.STORY_TIMESTAMP + " <= " + olderThan.toString(); + if (newerThan != null) rangeSelection = DatabaseConstants.STORY_TIMESTAMP + " >= " + newerThan.toString(); + StringBuilder feedSelection = null; + if (fs.isAllNormal()) { + // a null selection is fine for all stories + } else if (fs.getMultipleFeeds() != null) { + feedSelection = new StringBuilder(DatabaseConstants.STORY_FEED_ID + " IN ( "); + feedSelection.append(TextUtils.join(",", fs.getMultipleFeeds())); + feedSelection.append(")"); + } else if (fs.getSingleFeed() != null) { + feedSelection= new StringBuilder(DatabaseConstants.STORY_FEED_ID + " = "); + feedSelection.append(fs.getSingleFeed()); + } else if (fs.getSingleSocialFeed() != null) { + feedSelection= new StringBuilder(DatabaseConstants.STORY_SOCIAL_USER_ID + " = "); + feedSelection.append(fs.getSingleSocialFeed().getKey()); + } else { + throw new IllegalStateException("Asked to mark stories for FeedSet of unknown type."); + } + dbRW.update(DatabaseConstants.STORY_TABLE, values, conjoinSelections(feedSelection, rangeSelection), null); + + } + private void setFeedUnreadCount(int count, String whereClause, String[] whereArgs) { ContentValues values = new ContentValues(); values.put(DatabaseConstants.FEED_NEGATIVE_COUNT, count); @@ -485,4 +513,19 @@ public class BlurDatabaseHelper { try {c.close();} catch (Exception e) {;} } + private static String conjoinSelections(CharSequence... args) { + StringBuilder s = null; + for (CharSequence c : args) { + if (c == null) continue; + if (s == null) { + s = new StringBuilder(c); + } else { + s.append(" AND "); + s.append(c); + } + } + if (s == null) return null; + return s.toString(); + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java index 5080361b2..faf801534 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java @@ -468,4 +468,10 @@ public class DatabaseConstants { } } + public static Long nullIfZero(Long l) { + if (l == null) return null; + if (l.longValue() == 0L) return null; + return l; + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java b/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java index 04bed9c98..7f0258bdc 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java @@ -68,12 +68,15 @@ public class APIConstants { public static final String PARAMETER_URL = "url"; public static final String PARAMETER_DAYS = "days"; public static final String PARAMETER_UPDATE_COUNTS = "update_counts"; - + public static final String PARAMETER_CUTOFF_TIME = "cutoff_timestamp"; + public static final String PARAMETER_DIRECTION = "direction"; public static final String PARAMETER_PAGE_NUMBER = "page"; public static final String PARAMETER_ORDER = "order"; public static final String PARAMETER_READ_FILTER = "read_filter"; public static final String VALUE_ALLSOCIAL = "river:blurblogs"; // the magic value passed to the mark-read API for all social feeds + public static final String VALUE_OLDER = "older"; + public static final String VALUE_NEWER = "newer"; public static final String URL_CONNECT_FACEBOOK = NEWSBLUR_URL + "/oauth/facebook_connect/"; public static final String URL_CONNECT_TWITTER = NEWSBLUR_URL + "/oauth/twitter_connect/"; diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index 22e4c175b..97bb89850 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -122,7 +122,18 @@ public class APIManager { for (String feedId : feedIds) { values.put(APIConstants.PARAMETER_FEEDID, feedId); } - // TODO: handle older/newer + if (includeOlder != null) { + // the API vends microsecond timestamps but for this call it requires milliseconds + long cut = includeOlder.longValue(); + values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); + values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_OLDER); + } + if (includeNewer != null) { + // the API vends microsecond timestamps but for this call it requires milliseconds + long cut = includeNewer.longValue(); + values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); + values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_NEWER); + } APIResponse response = post(APIConstants.URL_MARK_FEED_AS_READ, values, false); // TODO: these calls use a different return format than others: the errors field is an array, not an object diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 8bfe6a548..de19c2e0b 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -195,8 +195,8 @@ public class NBSyncService extends Service { if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_READ)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); String feedIds = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID)); - Long includeOlder = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); - Long includeNewer = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); + Long includeOlder = DatabaseConstants.nullIfZero(c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_INCLUDE_OLDER))); + Long includeNewer = DatabaseConstants.nullIfZero(c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_INCLUDE_NEWER))); if (hash != null) { response = apiManager.markStoryAsRead(hash); } else if (feedIds != null) { @@ -253,7 +253,10 @@ public class NBSyncService extends Service { String id = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_ID)); if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_READ)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); - dbHelper.setStoryReadState(hash, true); + if (hash != null ) { + dbHelper.setStoryReadState(hash, true); + } + // TODO: double-check stories from feed-marks } else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_UNREAD)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); dbHelper.setStoryReadState(hash, false); From f083354110a323193caedbadc0e1c81940a4f4b6 Mon Sep 17 00:00:00 2001 From: dosiecki Date: Tue, 9 Sep 2014 16:59:53 -0700 Subject: [PATCH 02/10] Refetch story load state after story list is left so stories will be refreshed on next view. (#575) --- .../NewsBlur/src/com/newsblur/service/NBSyncService.java | 6 ++++++ .../android/NewsBlur/src/com/newsblur/util/FeedUtils.java | 1 + 2 files changed, 7 insertions(+) diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index de19c2e0b..45278a319 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -655,6 +655,12 @@ public class NBSyncService extends Service { FeedStoriesSeen.put(fs, 0); } + public static void resetAllFeeds() { + ExhaustedFeeds.clear(); + FeedPagesSeen.clear(); + FeedStoriesSeen.clear(); + } + public static void softInterrupt() { Log.d(NBSyncService.class.getName(), "soft stop"); HaltNow = true; diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 3690c9fb9..5747e4c73 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -117,6 +117,7 @@ public class FeedUtils { } catch (Exception e) { ; // this one call can evade the on-upgrade DB wipe and throw exceptions } + NBSyncService.resetAllFeeds(); return null; } }.execute(); From df8cdbf795152f317d6e713ccfc06a2dd75fa347 Mon Sep 17 00:00:00 2001 From: dosiecki Date: Tue, 9 Sep 2014 17:46:18 -0700 Subject: [PATCH 03/10] Further tweak unread-only mode to reset session after mode switch. (#564) --- .../src/com/newsblur/activity/ItemsList.java | 4 ++-- .../src/com/newsblur/service/NBSyncService.java | 12 +----------- .../NewsBlur/src/com/newsblur/util/FeedUtils.java | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java b/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java index 1c173e680..e900a02f8 100644 --- a/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java +++ b/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java @@ -179,7 +179,7 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen @Override public void storyOrderChanged(StoryOrder newValue) { updateStoryOrderPreference(newValue); - NBSyncService.resetFeed(fs); + FeedUtils.clearReadingSession(this); itemListFragment.resetEmptyState(); itemListFragment.hasUpdated(); itemListFragment.scrollToTop(); @@ -191,7 +191,7 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen @Override public void readFilterChanged(ReadFilter newValue) { updateReadFilterPreference(newValue); - NBSyncService.resetFeed(fs); + FeedUtils.clearReadingSession(this); itemListFragment.resetEmptyState(); itemListFragment.hasUpdated(); itemListFragment.scrollToTop(); diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 45278a319..003804803 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -645,17 +645,7 @@ public class NBSyncService extends Service { return true; } - /** - * Resets pagination and exhaustion flags for the given feedset, so that it can be requested fresh - * from the beginning with new parameters. - */ - public static void resetFeed(FeedSet fs) { - ExhaustedFeeds.remove(fs); - FeedPagesSeen.put(fs, 0); - FeedStoriesSeen.put(fs, 0); - } - - public static void resetAllFeeds() { + public static void resetFeeds() { ExhaustedFeeds.clear(); FeedPagesSeen.clear(); FeedStoriesSeen.clear(); diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 5747e4c73..f6a341782 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -117,7 +117,7 @@ public class FeedUtils { } catch (Exception e) { ; // this one call can evade the on-upgrade DB wipe and throw exceptions } - NBSyncService.resetAllFeeds(); + NBSyncService.resetFeeds(); return null; } }.execute(); From 06ca37cd25d131d8bd4ea32b5fbd1c7db9d4092f Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 14:26:59 -0700 Subject: [PATCH 04/10] Handle missing authors in story view. (#574) --- .../src/com/newsblur/view/SocialItemViewBinder.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java b/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java index 12cc89054..6699772c9 100644 --- a/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java +++ b/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java @@ -70,9 +70,12 @@ public class SocialItemViewBinder implements ViewBinder { return true; } else if (TextUtils.equals(columnName, DatabaseConstants.STORY_AUTHORS)) { String authors = cursor.getString(columnIndex); - if (!TextUtils.isEmpty(authors)) { - ((TextView) view).setText(authors.toUpperCase()); - } + if (TextUtils.isEmpty(authors)) { + view.setVisibility(View.GONE); + } else { + ((TextView) view).setText(authors.toUpperCase()); + view.setVisibility(View.VISIBLE); + } return true; } else if (TextUtils.equals(columnName, DatabaseConstants.STORY_TITLE)) { ((TextView) view).setText(Html.fromHtml(cursor.getString(columnIndex))); From f8ad205e9fb99e485f108d8107a27e6564ad35ab Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 14:33:19 -0700 Subject: [PATCH 05/10] Clarify comment. --- .../android/NewsBlur/src/com/newsblur/network/APIManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index 97bb89850..67f8e09ba 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -123,13 +123,13 @@ public class APIManager { values.put(APIConstants.PARAMETER_FEEDID, feedId); } if (includeOlder != null) { - // the API vends microsecond timestamps but for this call it requires milliseconds + // the app uses milliseconds but the API wants seconds long cut = includeOlder.longValue(); values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_OLDER); } if (includeNewer != null) { - // the API vends microsecond timestamps but for this call it requires milliseconds + // the app uses milliseconds but the API wants seconds long cut = includeNewer.longValue(); values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_NEWER); From a22d77274e5b4d94629398588d83df9371d1c24f Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 16:02:32 -0700 Subject: [PATCH 06/10] Give users a way to send app feedback. --- clients/android/NewsBlur/res/menu/main.xml | 4 +++ .../android/NewsBlur/res/values/strings.xml | 1 + .../src/com/newsblur/activity/Main.java | 22 ++++++++++++++-- .../src/com/newsblur/util/AppConstants.java | 6 +++++ .../src/com/newsblur/util/PrefsUtils.java | 25 ++++++++++++++++--- 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/clients/android/NewsBlur/res/menu/main.xml b/clients/android/NewsBlur/res/menu/main.xml index 5d56c1687..b806f4dc6 100644 --- a/clients/android/NewsBlur/res/menu/main.xml +++ b/clients/android/NewsBlur/res/menu/main.xml @@ -18,6 +18,10 @@ + + Search Mark all as read Log out + Send app feedback Loading… No stories to read diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java index 2e6d25d25..d4139641f 100644 --- a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java +++ b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java @@ -6,7 +6,9 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.app.DialogFragment; import android.app.FragmentManager; +import android.net.Uri; import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -20,6 +22,7 @@ import com.newsblur.fragment.FolderListFragment; import com.newsblur.fragment.LogoutDialogFragment; import com.newsblur.service.BootReceiver; import com.newsblur.service.NBSyncService; +import com.newsblur.util.AppConstants; import com.newsblur.util.FeedUtils; import com.newsblur.util.PrefsUtils; import com.newsblur.util.UIUtils; @@ -30,7 +33,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre private ActionBar actionBar; private FolderListFragment folderFeedList; private FragmentManager fragmentManager; - private Menu menu; private TextView overlayStatusText; private boolean isLightTheme; private SwipeRefreshLayout swipeLayout; @@ -91,7 +93,14 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); - this.menu = menu; + + MenuItem feedbackItem = menu.findItem(R.id.menu_feedback); + if (AppConstants.ENABLE_FEEDBACK) { + feedbackItem.setTitle(feedbackItem.getTitle() + " (v" + PrefsUtils.getVersion(this) + ")"); + } else { + feedbackItem.setVisible(false); + } + return true; } @@ -116,6 +125,15 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre Intent settingsIntent = new Intent(this, Settings.class); startActivity(settingsIntent); return true; + } else if (item.getItemId() == R.id.menu_feedback) { + try { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this))); + startActivity(i); + } catch (Exception e) { + Log.wtf(this.getClass().getName(), "device cannot even open URLs to report feedback"); + } + return true; } return super.onOptionsItemSelected(item); } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java index 5fcd746d4..0e3a0d63c 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java @@ -58,4 +58,10 @@ public class AppConstants { // how many images to prefetch before updating the countdown UI public static final int IMAGE_PREFETCH_BATCH_SIZE = 10; + // should the feedback link be enabled (read: is this a beta?) + public static final boolean ENABLE_FEEDBACK = true; + + // link to app feedback page + public static final String FEEDBACK_URL = "https://getsatisfaction.com/newsblur/topics/new?topic[style]=question&from=company&product=NewsBlur+Android+App&topic[additional_detail]="; + } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java index 4f9214eda..39c1855fc 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java @@ -19,6 +19,7 @@ import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.Build; import android.util.Log; import com.newsblur.R; @@ -45,10 +46,8 @@ public class PrefsUtils { SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0); - String version; - try { - version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; - } catch (NameNotFoundException nnfe) { + String version = getVersion(context); + if (version == null) { Log.w(PrefsUtils.class.getName(), "could not determine app version"); return; } @@ -72,6 +71,24 @@ public class PrefsUtils { } + public static String getVersion(Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + } catch (NameNotFoundException nnfe) { + Log.w(PrefsUtils.class.getName(), "could not determine app version"); + return null; + } + } + + public static String createFeedbackLink(Context context) { + StringBuilder s = new StringBuilder(AppConstants.FEEDBACK_URL); + s.append("%0A%0A"); + s.append("%0Aapp version: ").append(getVersion(context)); + s.append("%0Aandroid version: ").append(Build.VERSION.RELEASE); + s.append("%0Adevice: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")"); + return s.toString(); + } + public static void logout(Context context) { NBSyncService.softInterrupt(); From 3905ab81dc24125a2a0cba952748b6567646e41c Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 16:20:29 -0700 Subject: [PATCH 07/10] Be even more respectful of low device memory. --- .../com/newsblur/service/NBSyncService.java | 20 ++++++++++++++----- .../src/com/newsblur/util/PrefsUtils.java | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 003804803..9c849d95f 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -69,6 +69,8 @@ public class NBSyncService extends Service { would annoy a user who is on the story list or paging through stories. */ private volatile static boolean HoldStories = false; private volatile static boolean DoFeedsFolders = false; + private volatile static boolean isMemoryLow = false; + private volatile static boolean HaltNow = false; /** Feed sets that we need to sync and how many stories the UI wants for them. */ private static Map PendingFeeds; @@ -84,8 +86,6 @@ public class NBSyncService extends Service { private static Set ImageQueue; static { ImageQueue = new HashSet(); } - private volatile static boolean HaltNow; - private PowerManager.WakeLock wl = null; private ExecutorService executor; private APIManager apiManager; @@ -171,6 +171,10 @@ public class NBSyncService extends Service { if (wl != null) wl.release(); Log.d(this.getClass().getName(), " . . . sync done"); } + + if (isMemoryLow && (NbActivity.getActiveActivityCount() < 1)) { + stopSelf(startId); + } } /** @@ -564,12 +568,14 @@ public class NBSyncService extends Service { } public void onTrimMemory (int level) { - // if the UI is still active, definitely don't stop - if (NbActivity.getActiveActivityCount() > 0) return; - // be nice and stop if memory is even a tiny bit pressured and we aren't visible; // the OS penalises long-running processes, and it is reasonably cheap to re-create ourself. if (level > ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { + isMemoryLow = true; + + // if the UI is still active, definitely don't stop + if (NbActivity.getActiveActivityCount() > 0) return; + if (lastStartIdCompleted != -1) { stopSelf(lastStartIdCompleted); } @@ -670,4 +676,8 @@ public class NBSyncService extends Service { return null; } + public static boolean isMemoryLow() { + return isMemoryLow; + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java index 39c1855fc..743348163 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java @@ -86,6 +86,7 @@ public class PrefsUtils { s.append("%0Aapp version: ").append(getVersion(context)); s.append("%0Aandroid version: ").append(Build.VERSION.RELEASE); s.append("%0Adevice: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")"); + s.append("%0Amemory: ").append(NBSyncService.isMemoryLow() ? "low" : "normal"); return s.toString(); } From 1fe9fa967e2827e24d2bb00a3bcc7ddb313734b2 Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 16:29:06 -0700 Subject: [PATCH 08/10] Fix sync hang on logout/login. (#572) --- .../NewsBlur/src/com/newsblur/service/NBSyncService.java | 4 ++++ .../android/NewsBlur/src/com/newsblur/util/PrefsUtils.java | 1 + 2 files changed, 5 insertions(+) diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 9c849d95f..4dafe81bb 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -662,6 +662,10 @@ public class NBSyncService extends Service { HaltNow = true; } + public static void resumeFromInterrupt() { + HaltNow = false; + } + @Override public void onDestroy() { Log.d(this.getClass().getName(), "onDestroy"); diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java index 743348163..61198a19f 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java @@ -31,6 +31,7 @@ import com.newsblur.service.NBSyncService; public class PrefsUtils { public static void saveLogin(final Context context, final String userName, final String cookie) { + NBSyncService.resumeFromInterrupt(); final SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0); final Editor edit = preferences.edit(); edit.putString(PrefConstants.PREF_COOKIE, cookie); From 54140ae345247de96a07976cd993a5e4492d61d3 Mon Sep 17 00:00:00 2001 From: dosiecki Date: Wed, 10 Sep 2014 16:39:39 -0700 Subject: [PATCH 09/10] Add barebones profiling info to feedback reports. --- .../src/com/newsblur/service/NBSyncService.java | 14 ++++++++++++++ .../NewsBlur/src/com/newsblur/util/PrefsUtils.java | 1 + 2 files changed, 15 insertions(+) diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 4dafe81bb..ff4a72049 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -72,6 +72,9 @@ public class NBSyncService extends Service { private volatile static boolean isMemoryLow = false; private volatile static boolean HaltNow = false; + private static long lastFeedCount = 0L; + private static long lastFFWriteMillis = 0L; + /** Feed sets that we need to sync and how many stories the UI wants for them. */ private static Map PendingFeeds; static { PendingFeeds = new HashMap(); } @@ -331,6 +334,8 @@ public class NBSyncService extends Service { return; } + long startTime = System.currentTimeMillis(); + isPremium = feedResponse.isPremium; // clean out the feed / folder tables @@ -382,6 +387,9 @@ public class NBSyncService extends Service { // populate the starred stories count table dbHelper.updateStarredStoriesCount(feedResponse.starredCount); + lastFFWriteMillis = System.currentTimeMillis() - startTime; + lastFeedCount = feedValues.size(); + } finally { FFSyncRunning = false; NbActivity.updateAllActivities(); @@ -684,4 +692,10 @@ public class NBSyncService extends Service { return isMemoryLow; } + public static String getSpeedInfo() { + StringBuilder s = new StringBuilder(); + s.append(lastFeedCount).append(" in ").append(lastFFWriteMillis); + return s.toString(); + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java index 61198a19f..686542363 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java @@ -88,6 +88,7 @@ public class PrefsUtils { s.append("%0Aandroid version: ").append(Build.VERSION.RELEASE); s.append("%0Adevice: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")"); s.append("%0Amemory: ").append(NBSyncService.isMemoryLow() ? "low" : "normal"); + s.append("%0Aspeed: ").append(NBSyncService.getSpeedInfo()); return s.toString(); } From de773435d802c470d278345982f0b2ccf3c5416c Mon Sep 17 00:00:00 2001 From: dosiecki Date: Tue, 16 Sep 2014 14:21:51 -0700 Subject: [PATCH 10/10] Refine debug logging. --- .../NewsBlur/src/com/newsblur/database/FeedProvider.java | 8 ++++---- .../NewsBlur/src/com/newsblur/service/NBSyncService.java | 7 ++++++- .../NewsBlur/src/com/newsblur/util/AppConstants.java | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java b/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java index 92aac2de9..5ece211a6 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java @@ -309,24 +309,24 @@ public class FeedProvider extends ContentProvider { mdb = db; } public Cursor rawQuery(String sql, String[] selectionArgs) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "rawQuery: " + sql); Log.d(LoggingDatabase.class.getName(), "selArgs : " + Arrays.toString(selectionArgs)); } Cursor cursor = mdb.rawQuery(sql, selectionArgs); - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "result rows: " + cursor.getCount()); } return cursor; } public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "selection: " + selection); } return mdb.query(table, columns, selection, selectionArgs, groupBy, having, orderBy); } public void execSQL(String sql) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "execSQL: " + sql); } mdb.execSQL(sql); diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index ff4a72049..cef29f092 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -141,7 +141,12 @@ public class NBSyncService extends Service { */ private synchronized void doSync(int startId) { try { - if (HaltNow) return; + if (HaltNow) { + if (AppConstants.VERBOSE_LOG) { + Log.d(this.getClass().getName(), "skipping sync, soft interrupt set."); + } + return; + } Log.d(this.getClass().getName(), "starting sync . . ."); diff --git a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java index 0e3a0d63c..e755a2f20 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java @@ -5,7 +5,8 @@ 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 int STATE_ALL = 0; public static final int STATE_SOME = 1;