Replace story activation with filtering by fetch time.

This commit is contained in:
dosiecki 2016-01-27 03:25:58 -08:00
parent a9bab35c6f
commit 41bb192c51
10 changed files with 55 additions and 72 deletions

View file

@ -64,7 +64,7 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
if (PrefsUtils.isAutoOpenFirstUnread(this)) {
if (FeedUtils.dbHelper.getUnreadCount(fs, intelState) > 0) {
UIUtils.startReadingActivity(fs, Reading.FIND_FIRST_UNREAD, this, false);
UIUtils.startReadingActivity(fs, Reading.FIND_FIRST_UNREAD, this, false, System.currentTimeMillis());
}
}
@ -95,6 +95,7 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (searchQueryInput != null) {
String q = searchQueryInput.getText().toString().trim();
if (q.length() > 0) {

View file

@ -100,7 +100,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
NBSyncService.clearPendingStoryRequest();
NBSyncService.flushRecounts();
NBSyncService.setActivationMode(NBSyncService.ActivationMode.ALL);
FeedUtils.clearReadingSession();
updateStatusIndicators();

View file

@ -53,6 +53,7 @@ import com.newsblur.view.NonfocusScrollview.ScrollChangeListener;
public abstract class Reading extends NbActivity implements OnPageChangeListener, OnSeekBarChangeListener, ScrollChangeListener, LoaderManager.LoaderCallbacks<Cursor> {
public static final String EXTRA_FEEDSET = "feed_set";
public static final String EXTRA_SESSION_START = "session_start_time";
public static final String EXTRA_POSITION = "feed_position";
public static final String EXTRA_STORY_HASH = "story_hash";
private static final String BUNDLE_POSITION = "position";
@ -97,6 +98,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
protected ReadingAdapter readingAdapter;
private boolean stopLoading;
protected FeedSet fs;
protected long readingSessionStart;
// unread count for the circular progress overlay. set to nonzero to activate the progress indicator overlay
protected int startingUnreadCount = 0;
@ -124,6 +126,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
fragmentManager = getFragmentManager();
fs = (FeedSet)getIntent().getSerializableExtra(EXTRA_FEEDSET);
readingSessionStart = getIntent().getLongExtra(EXTRA_SESSION_START, 0L);
if ((savedInstanceBundle != null) && savedInstanceBundle.containsKey(BUNDLE_STARTING_UNREAD)) {
startingUnreadCount = savedInstanceBundle.getInt(BUNDLE_STARTING_UNREAD);
@ -220,7 +223,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
finish();
return null;
}
return FeedUtils.dbHelper.getStoriesLoader(fs, intelState);
return FeedUtils.dbHelper.getStoriesLoader(fs, intelState, readingSessionStart);
}
@Override

View file

@ -30,7 +30,7 @@ public class SocialFeedReading extends Reading {
// If we have navigated from the profile we want to ignore the StateFilter and ReadFilter settings
// for the feed to ensure we can find the story.
if (ignoreFilters) {
return FeedUtils.dbHelper.getStoriesLoaderIgnoreFilters(fs);
return FeedUtils.dbHelper.getStoriesLoaderIgnoreFilters(fs, readingSessionStart);
} else {
return super.onCreateLoader(loaderId, bundle);
}

View file

@ -266,7 +266,7 @@ public class BlurDatabaseHelper {
return urls;
}
public void insertStories(StoriesResponse apiResponse, NBSyncService.ActivationMode actMode, long modeCutoff) {
public void insertStories(StoriesResponse apiResponse, long fetchtime) {
long startTime = System.currentTimeMillis();
synchronized (RW_MUTEX) {
dbRW.beginTransactionNonExclusive();
@ -299,6 +299,7 @@ public class BlurDatabaseHelper {
List<ContentValues> socialStoryValues = new ArrayList<ContentValues>();
for (Story story : apiResponse.stories) {
ContentValues values = story.getValues();
values.put(DatabaseConstants.STORY_FETCHTIME, fetchtime);
// the basic columns are fine for the stories table
storyValues.add(values);
// if a story was shared by a user, also insert it into the social table under their userid, too
@ -657,7 +658,7 @@ public class BlurDatabaseHelper {
* Get the unread count for the given feedset based on local story state.
*/
public int getLocalUnreadCount(FeedSet fs, StateFilter stateFilter) {
Cursor c = getStoriesCursor(fs, stateFilter, ReadFilter.PURE_UNREAD, null, null);
Cursor c = getStoriesCursor(fs, stateFilter, ReadFilter.PURE_UNREAD, null, System.currentTimeMillis(), null);
int count = c.getCount();
c.close();
return count;
@ -860,11 +861,11 @@ public class BlurDatabaseHelper {
return dbRO.query(DatabaseConstants.STARRED_STORY_COUNT_TABLE, null, null, null, null, null, null);
}
public Loader<Cursor> getStoriesLoader(final FeedSet fs, final StateFilter stateFilter) {
public Loader<Cursor> getStoriesLoader(final FeedSet fs, final StateFilter stateFilter, final long readingSessionStart) {
return new QueryCursorLoader(context) {
protected Cursor createCursor() {
ReadFilter readFilter = PrefsUtils.getReadFilter(context, fs);
return getStoriesCursor(fs, stateFilter, readFilter, cancellationSignal);
return getStoriesCursor(fs, stateFilter, readFilter, readingSessionStart, cancellationSignal);
}
};
}
@ -873,19 +874,19 @@ public class BlurDatabaseHelper {
* When navigating to a social story from an interaction/activity we want to ignore
* the any state so we can be sure we find the selected story.
*/
public Loader<Cursor> getStoriesLoaderIgnoreFilters(final FeedSet fs) {
public Loader<Cursor> getStoriesLoaderIgnoreFilters(final FeedSet fs, final long readingSessionStart) {
return new QueryCursorLoader(context) {
protected Cursor createCursor() {return getStoriesCursor(fs, StateFilter.ALL, ReadFilter.ALL, cancellationSignal);}
protected Cursor createCursor() {return getStoriesCursor(fs, StateFilter.ALL, ReadFilter.ALL, readingSessionStart, cancellationSignal);}
};
}
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, CancellationSignal cancellationSignal) {
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, long readingSessionStart, CancellationSignal cancellationSignal) {
if (fs == null) return null;
StoryOrder order = PrefsUtils.getStoryOrder(context, fs);
return getStoriesCursor(fs, stateFilter, readFilter, order, cancellationSignal);
return getStoriesCursor(fs, stateFilter, readFilter, order, readingSessionStart, cancellationSignal);
}
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, StoryOrder order, CancellationSignal cancellationSignal) {
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, StoryOrder order, long readingSessionStart, CancellationSignal cancellationSignal) {
if (fs == null) return null;
if (fs.getSingleFeed() != null) {
@ -894,7 +895,7 @@ public class BlurDatabaseHelper {
q.append(TextUtils.join(",", DatabaseConstants.STORY_COLUMNS));
q.append(" FROM " + DatabaseConstants.STORY_TABLE);
q.append(" WHERE " + DatabaseConstants.STORY_FEED_ID + " = ?");
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null));
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null), readingSessionStart);
return rawQuery(q.toString(), new String[]{fs.getSingleFeed()}, cancellationSignal);
} else if (fs.getMultipleFeeds() != null) {
@ -904,7 +905,7 @@ public class BlurDatabaseHelper {
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
q.append(" WHERE " + DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_FEED_ID + " IN ( ");
q.append(TextUtils.join(",", fs.getMultipleFeeds()) + ")");
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null));
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null), readingSessionStart);
return rawQuery(q.toString(), null, cancellationSignal);
} else if (fs.getSingleSocialFeed() != null) {
@ -914,7 +915,7 @@ public class BlurDatabaseHelper {
q.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
q.append(" WHERE " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE + "." + DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ? ");
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null));
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null), readingSessionStart);
return rawQuery(q.toString(), new String[]{fs.getSingleSocialFeed().getKey()}, cancellationSignal);
} else if (fs.isAllNormal()) {
@ -923,7 +924,7 @@ public class BlurDatabaseHelper {
q.append(" FROM " + DatabaseConstants.STORY_TABLE);
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
q.append(" WHERE 1");
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null));
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null, (fs.getSearchQuery() != null), readingSessionStart);
return rawQuery(q.toString(), null, cancellationSignal);
} else if (fs.isAllSocial()) {
@ -933,7 +934,7 @@ public class BlurDatabaseHelper {
q.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
q.append(DatabaseConstants.JOIN_SOCIAL_FEEDS_ON_SOCIALFEED_MAP);
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_ID, false);
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_ID, false, readingSessionStart);
return rawQuery(q.toString(), null, cancellationSignal);
} else if (fs.isAllRead()) {
@ -964,7 +965,7 @@ public class BlurDatabaseHelper {
q.append(" FROM " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
q.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_ID, false);
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_ID, false, readingSessionStart);
return rawQuery(q.toString(), null, cancellationSignal);
} else {
throw new IllegalStateException("Asked to get stories for FeedSet of unknown type.");

View file

@ -97,6 +97,7 @@ public class DatabaseConstants {
public static final String STORY_IMAGE_URLS = "image_urls";
public static final String STORY_LAST_READ_DATE = "last_read_date";
public static final String STORY_SEARCHIT = "search_hit";
public static final String STORY_FETCHTIME = "fetch_time";
public static final String STORY_TEXT_TABLE = "storytext";
public static final String STORY_TEXT_STORY_HASH = "story_hash";
@ -234,7 +235,8 @@ public class DatabaseConstants {
STORY_TITLE + TEXT + ", " +
STORY_IMAGE_URLS + TEXT + ", " +
STORY_LAST_READ_DATE + INTEGER + ", " +
STORY_SEARCHIT + INTEGER + " DEFAULT 0" +
STORY_SEARCHIT + INTEGER + " DEFAULT 0," +
STORY_FETCHTIME + INTEGER + " DEFAULT 0" +
")";
static final String STORY_TEXT_SQL = "CREATE TABLE " + STORY_TEXT_TABLE + " (" +
@ -330,7 +332,7 @@ public class DatabaseConstants {
* Appends to the given story query any and all selection statements that are required to satisfy the specified
* filtration parameters, dedup column, and ordering requirements.
*/
public static void appendStorySelectionGroupOrder(StringBuilder q, ReadFilter readFilter, StoryOrder order, StateFilter stateFilter, String dedupCol, boolean requireQueryHit) {
public static void appendStorySelectionGroupOrder(StringBuilder q, ReadFilter readFilter, StoryOrder order, StateFilter stateFilter, String dedupCol, boolean requireQueryHit, long readingSessionStart) {
if (readFilter == ReadFilter.UNREAD) {
// When a user is viewing "unread only" stories, what they really want are stories that were unread when they started reading,
// or else the selection set will constantly change as they see things!
@ -348,6 +350,8 @@ public class DatabaseConstants {
if (requireQueryHit) {
q.append(" AND (" + STORY_TABLE + "." + STORY_SEARCHIT + " = 1)");
}
q.append(" AND (" + STORY_FETCHTIME + " < ").append(readingSessionStart).append(")");
if (dedupCol != null) {
q.append( " GROUP BY " + dedupCol);

View file

@ -49,13 +49,16 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
public static int ITEMLIST_LOADER = 0x01;
private static final String BUNDLE_SESSION_START = "bundle_session_start_time";
protected ItemsList activity;
@FindView(R.id.itemlistfragment_list) ListView itemList;
protected StoryItemsAdapter adapter;
protected DefaultFeedView defaultFeedView;
protected StateFilter intelState;
/** The time this story list / reading session began. */
private long readingSessionStart;
private boolean cursorSeenYet = false;
private boolean firstStorySeenYet = false;
private boolean stopLoading = false;
// loading indicator for when stories are present but stale (at top of list)
@ -71,6 +74,17 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
intelState = PrefsUtils.getStateFilter(getActivity());
defaultFeedView = (DefaultFeedView)getArguments().getSerializable("defaultFeedView");
activity = (ItemsList) getActivity();
if ((savedInstanceState != null) && savedInstanceState.containsKey(BUNDLE_SESSION_START)) {
readingSessionStart = savedInstanceState.getLong(BUNDLE_SESSION_START);
} else {
readingSessionStart = System.currentTimeMillis();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(BUNDLE_SESSION_START, readingSessionStart);
}
@Override
@ -145,7 +159,6 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
*/
public void resetEmptyState() {
cursorSeenYet = false;
firstStorySeenYet = false;
}
/**
@ -230,7 +243,7 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
try { getActivity().finish(); } catch (Exception e) {;}
return null;
}
return FeedUtils.dbHelper.getStoriesLoader(getFeedSet(), intelState);
return FeedUtils.dbHelper.getStoriesLoader(getFeedSet(), intelState, readingSessionStart);
}
@Override
@ -240,20 +253,6 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
cursorSeenYet = true;
if (cursor.getCount() < 1) {
triggerRefresh(1, 0);
} else {
if (!firstStorySeenYet) {
// once we have at least a single story, we can instruct the sync service as to how to safely
// activate new stories we recieve
firstStorySeenYet = true;
cursor.moveToFirst();
long cutoff = cursor.getLong(cursor.getColumnIndex(DatabaseConstants.STORY_TIMESTAMP));
cursor.moveToPosition(-1);
if (activity.getStoryOrder() == StoryOrder.NEWEST) {
NBSyncService.setActivationMode(NBSyncService.ActivationMode.OLDER, cutoff);
} else {
NBSyncService.setActivationMode(NBSyncService.ActivationMode.NEWER, cutoff);
}
}
}
adapter.swapCursor(cursor);
}
@ -344,7 +343,7 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
int truePosition = position - 1;
Story story = adapter.getStory(truePosition);
if (getActivity().isFinishing()) return;
UIUtils.startReadingActivity(getFeedSet(), story.storyHash, getActivity(), false);
UIUtils.startReadingActivity(getFeedSet(), story.storyHash, getActivity(), false, readingSessionStart);
}
protected void setupBezelSwipeDetector(View v) {

View file

@ -144,7 +144,7 @@ public abstract class ProfileActivityDetailsFragment extends Fragment implements
context.startActivity(intent);
}
} else if (activity.category == Category.STAR) {
UIUtils.startReadingActivity(FeedSet.allSaved(), activity.storyHash, context, false);
UIUtils.startReadingActivity(FeedSet.allSaved(), activity.storyHash, context, false, System.currentTimeMillis());
} else if (isSocialFeedCategory(activity)) {
// Strip the social: prefix from feedId
String socialFeedId = activity.feedId.substring(7);
@ -152,7 +152,7 @@ public abstract class ProfileActivityDetailsFragment extends Fragment implements
if (feed == null) {
Toast.makeText(context, R.string.profile_do_not_follow, Toast.LENGTH_SHORT).show();
} else {
UIUtils.startReadingActivity(FeedSet.singleSocialFeed(feed.userId, feed.username), activity.storyHash, context, true);
UIUtils.startReadingActivity(FeedSet.singleSocialFeed(feed.userId, feed.username), activity.storyHash, context, true, System.currentTimeMillis());
}
}
}

View file

@ -65,12 +65,6 @@ import java.util.concurrent.TimeUnit;
*/
public class NBSyncService extends Service {
/**
* Mode switch for which newly received stories are suitable for display so
* that they don't disrupt actively visible pager and list offsets.
*/
public enum ActivationMode { ALL, OLDER, NEWER };
private static final Object WAKELOCK_MUTEX = new Object();
private static final Object PENDING_FEED_MUTEX = new Object();
@ -83,8 +77,6 @@ public class NBSyncService extends Service {
private volatile static boolean DoFeedsFolders = false;
private volatile static boolean DoUnreads = false;
private volatile static boolean HaltNow = false;
private volatile static ActivationMode ActMode = ActivationMode.ALL;
private volatile static long ModeCutoff = 0L;
/** Informational flag only, as to whether we were offline last time we cycled. */
public volatile static boolean OfflineNow = false;
@ -398,7 +390,6 @@ public class NBSyncService extends Service {
if (stopSync()) return;
if (backoffBackgroundCalls()) return;
if (ActMode != ActivationMode.ALL) return;
if (dbHelper.getActions(false).getCount() > 0) return;
FFSyncRunning = true;
@ -425,7 +416,6 @@ public class NBSyncService extends Service {
}
if (stopSync()) return;
if (ActMode != ActivationMode.ALL) return;
if (dbHelper.getActions(false).getCount() > 0) return;
// a metadata sync invalidates pagination and feed status
@ -633,12 +623,6 @@ public class NBSyncService extends Service {
totalStoriesSeen += apiResponse.stories.length;
FeedStoriesSeen.put(fs, totalStoriesSeen);
// lock in the activation cutoff based upon the timestamp of the first
// story received for a given pagination session. it will be the newest
// or oldest story for the feedset, as dictated by order.
if ((pageNumber == 1) && (apiResponse.stories.length > 0)) {
ModeCutoff = apiResponse.stories[0].timestamp;
}
insertStories(apiResponse, fs);
NbActivity.updateAllActivities(NbActivity.UPDATE_STORY);
@ -711,11 +695,14 @@ public class NBSyncService extends Service {
}
}
dbHelper.insertStories(apiResponse, ActMode, ModeCutoff);
// stories fetched for a particular feed set have an always-visible fetch time of arbitrarily long ago
dbHelper.insertStories(apiResponse, 0L);
}
void insertStories(StoriesResponse apiResponse) {
dbHelper.insertStories(apiResponse, ActMode, ModeCutoff);
// stories inserted by the prefetch service have their fetch time noted so they don't show up before requested
long fetchtime = System.currentTimeMillis();
dbHelper.insertStories(apiResponse, fetchtime);
}
void incrementRunningChild() {
@ -838,18 +825,6 @@ public class NBSyncService extends Service {
FlushRecounts = true;
}
/**
* Tell the service which stories can be activated if received. See ActivationMode.
*/
public static void setActivationMode(ActivationMode actMode) {
ActMode = actMode;
}
public static void setActivationMode(ActivationMode actMode, long modeCutoff) {
ActMode = actMode;
ModeCutoff = modeCutoff;
}
/**
* Requests that the service fetch additional stories for the specified feed/folder. Returns
* true if more will be fetched as a result of this request.

View file

@ -164,7 +164,7 @@ public class UIUtils {
});
}
public static void startReadingActivity(FeedSet fs, String startingHash, Context context, boolean ignoreFilters) {
public static void startReadingActivity(FeedSet fs, String startingHash, Context context, boolean ignoreFilters, long sessionStartTime) {
Class activityClass;
if (fs.isAllSaved()) {
activityClass = SavedStoriesReading.class;
@ -188,6 +188,7 @@ public class UIUtils {
}
Intent i = new Intent(context, activityClass);
i.putExtra(Reading.EXTRA_FEEDSET, fs);
i.putExtra(Reading.EXTRA_SESSION_START, sessionStartTime);
i.putExtra(Reading.EXTRA_STORY_HASH, startingHash);
if (ignoreFilters) {
i.putExtra(SocialFeedReading.EXTRA_IGNORE_FILTERS, true);