2014-06-04 13:46:10 -07:00
|
|
|
package com.newsblur.database;
|
|
|
|
|
|
2014-06-04 17:09:40 -07:00
|
|
|
import android.content.ContentValues;
|
2014-06-04 13:46:10 -07:00
|
|
|
import android.content.Context;
|
2014-07-27 00:39:40 -07:00
|
|
|
import android.content.Loader;
|
2014-06-04 17:09:40 -07:00
|
|
|
import android.database.Cursor;
|
|
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
2014-10-01 02:41:06 -07:00
|
|
|
import android.os.AsyncTask;
|
2014-06-05 04:04:14 -07:00
|
|
|
import android.text.TextUtils;
|
2014-06-05 15:51:29 -07:00
|
|
|
import android.util.Log;
|
2014-06-04 13:46:10 -07:00
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
import com.newsblur.domain.Classifier;
|
|
|
|
|
import com.newsblur.domain.Comment;
|
|
|
|
|
import com.newsblur.domain.Feed;
|
|
|
|
|
import com.newsblur.domain.FeedResult;
|
|
|
|
|
import com.newsblur.domain.Reply;
|
|
|
|
|
import com.newsblur.domain.SocialFeed;
|
|
|
|
|
import com.newsblur.domain.Story;
|
|
|
|
|
import com.newsblur.domain.UserProfile;
|
|
|
|
|
import com.newsblur.network.domain.StoriesResponse;
|
2014-06-04 13:46:10 -07:00
|
|
|
import com.newsblur.util.AppConstants;
|
2014-08-14 17:46:16 -07:00
|
|
|
import com.newsblur.util.FeedSet;
|
2014-07-10 15:11:43 -07:00
|
|
|
import com.newsblur.util.FeedUtils;
|
2014-08-14 17:46:16 -07:00
|
|
|
import com.newsblur.util.PrefsUtils;
|
2014-09-17 20:04:01 -07:00
|
|
|
import com.newsblur.util.ReadingAction;
|
2014-08-14 17:46:16 -07:00
|
|
|
import com.newsblur.util.ReadFilter;
|
2014-09-24 17:16:13 -07:00
|
|
|
import com.newsblur.util.StateFilter;
|
2014-08-14 17:46:16 -07:00
|
|
|
import com.newsblur.util.StoryOrder;
|
2014-06-04 13:46:10 -07:00
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
import java.util.ArrayList;
|
2014-08-25 16:37:37 -07:00
|
|
|
import java.util.HashSet;
|
2014-06-04 17:09:40 -07:00
|
|
|
import java.util.List;
|
2014-07-28 15:41:17 -07:00
|
|
|
import java.util.Map;
|
2014-08-25 16:37:37 -07:00
|
|
|
import java.util.Set;
|
2014-06-04 17:09:40 -07:00
|
|
|
|
2014-06-04 13:46:10 -07:00
|
|
|
/**
|
|
|
|
|
* Utility class for executing DB operations on the local, private NB database.
|
|
|
|
|
*
|
|
|
|
|
* It is the intent of this class to be the single location of SQL executed on
|
|
|
|
|
* our DB, replacing the deprecated ContentProvider access pattern.
|
|
|
|
|
*/
|
|
|
|
|
public class BlurDatabaseHelper {
|
|
|
|
|
|
2014-07-27 00:39:40 -07:00
|
|
|
private Context context;
|
2014-10-01 02:41:06 -07:00
|
|
|
private final BlurDatabase dbWrapper;
|
2014-06-04 17:09:40 -07:00
|
|
|
private SQLiteDatabase dbRO;
|
|
|
|
|
private SQLiteDatabase dbRW;
|
2014-06-04 13:46:10 -07:00
|
|
|
|
|
|
|
|
public BlurDatabaseHelper(Context context) {
|
2014-07-27 00:39:40 -07:00
|
|
|
this.context = context;
|
2014-06-04 17:09:40 -07:00
|
|
|
dbWrapper = new BlurDatabase(context);
|
|
|
|
|
dbRO = dbWrapper.getRO();
|
|
|
|
|
dbRW = dbWrapper.getRW();
|
2014-06-04 13:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void close() {
|
2014-10-01 02:41:06 -07:00
|
|
|
// when asked to close, do so via an AsyncTask. This is so that (since becoming serial in android 4.0)
|
|
|
|
|
// the closure will happen after other async tasks are done using the conn
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
|
@Override
|
|
|
|
|
protected Void doInBackground(Void... arg) {
|
|
|
|
|
dbWrapper.close();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}.execute();
|
2014-06-04 13:46:10 -07:00
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:02:25 -07:00
|
|
|
public boolean isOpen() {
|
|
|
|
|
return dbRW.isOpen();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 14:17:44 -07:00
|
|
|
private List<String> getAllFeeds() {
|
2014-06-28 20:59:24 -07:00
|
|
|
String q1 = "SELECT " + DatabaseConstants.FEED_ID +
|
|
|
|
|
" FROM " + DatabaseConstants.FEED_TABLE;
|
|
|
|
|
Cursor c = dbRO.rawQuery(q1, null);
|
|
|
|
|
List<String> feedIds = new ArrayList<String>(c.getCount());
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
feedIds.add(c.getString(c.getColumnIndexOrThrow(DatabaseConstants.FEED_ID)));
|
|
|
|
|
}
|
|
|
|
|
c.close();
|
2014-09-25 14:17:44 -07:00
|
|
|
return feedIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<String> getAllSocialFeeds() {
|
|
|
|
|
String q1 = "SELECT " + DatabaseConstants.SOCIAL_FEED_ID +
|
|
|
|
|
" FROM " + DatabaseConstants.SOCIALFEED_TABLE;
|
|
|
|
|
Cursor c = dbRO.rawQuery(q1, null);
|
|
|
|
|
List<String> feedIds = new ArrayList<String>(c.getCount());
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
feedIds.add(c.getString(c.getColumnIndexOrThrow(DatabaseConstants.SOCIAL_FEED_ID)));
|
|
|
|
|
}
|
|
|
|
|
c.close();
|
|
|
|
|
return feedIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void cleanupStories(boolean keepOldStories) {
|
|
|
|
|
for (String feedId : getAllFeeds()) {
|
2014-06-28 20:59:24 -07:00
|
|
|
String q = "DELETE FROM " + DatabaseConstants.STORY_TABLE +
|
|
|
|
|
" WHERE " + DatabaseConstants.STORY_ID + " IN " +
|
|
|
|
|
"( SELECT " + DatabaseConstants.STORY_ID + " FROM " + DatabaseConstants.STORY_TABLE +
|
2014-06-29 16:11:43 -07:00
|
|
|
" WHERE " + DatabaseConstants.STORY_READ + " = 1" +
|
|
|
|
|
" AND " + DatabaseConstants.STORY_FEED_ID + " = " + feedId +
|
2014-06-28 20:59:24 -07:00
|
|
|
" ORDER BY " + DatabaseConstants.STORY_TIMESTAMP + " DESC" +
|
2014-06-30 19:58:04 -07:00
|
|
|
" LIMIT -1 OFFSET " + (keepOldStories ? AppConstants.MAX_READ_STORIES_STORED : 0) +
|
2014-06-28 20:59:24 -07:00
|
|
|
")";
|
|
|
|
|
dbRW.execSQL(q);
|
|
|
|
|
}
|
2014-06-04 17:09:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void cleanupFeedsFolders() {
|
|
|
|
|
dbRW.delete(DatabaseConstants.FEED_TABLE, null, null);
|
|
|
|
|
dbRW.delete(DatabaseConstants.FOLDER_TABLE, null, null);
|
|
|
|
|
dbRW.delete(DatabaseConstants.FEED_FOLDER_MAP_TABLE, null, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void bulkInsertValues(String table, List<ContentValues> valuesList) {
|
2014-06-05 04:04:14 -07:00
|
|
|
if (valuesList.size() < 1) return;
|
2014-06-04 17:09:40 -07:00
|
|
|
dbRW.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
for(ContentValues values: valuesList) {
|
|
|
|
|
dbRW.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
|
|
|
|
}
|
|
|
|
|
dbRW.setTransactionSuccessful();
|
|
|
|
|
} finally {
|
|
|
|
|
dbRW.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void insertFeedsFolders(List<ContentValues> feedValues,
|
|
|
|
|
List<ContentValues> folderValues,
|
|
|
|
|
List<ContentValues> ffmValues,
|
|
|
|
|
List<ContentValues> socialFeedValues) {
|
|
|
|
|
bulkInsertValues(DatabaseConstants.FEED_TABLE, feedValues);
|
|
|
|
|
bulkInsertValues(DatabaseConstants.FOLDER_TABLE, folderValues);
|
|
|
|
|
bulkInsertValues(DatabaseConstants.FEED_FOLDER_MAP_TABLE, ffmValues);
|
|
|
|
|
bulkInsertValues(DatabaseConstants.SOCIALFEED_TABLE, socialFeedValues);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void updateStarredStoriesCount(int count) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.STARRED_STORY_COUNT_COUNT, count);
|
|
|
|
|
// this DB just has one row and one column. blow it away and replace it.
|
|
|
|
|
dbRW.delete(DatabaseConstants.STARRED_STORY_COUNT_TABLE, null, null);
|
|
|
|
|
dbRW.insert(DatabaseConstants.STARRED_STORY_COUNT_TABLE, null, values);
|
2014-06-04 13:46:10 -07:00
|
|
|
}
|
2014-06-05 04:04:14 -07:00
|
|
|
|
|
|
|
|
public List<String> getStoryHashesForFeed(String feedId) {
|
|
|
|
|
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
|
|
|
|
" FROM " + DatabaseConstants.STORY_TABLE +
|
|
|
|
|
" WHERE " + DatabaseConstants.STORY_FEED_ID + " = ?";
|
|
|
|
|
Cursor c = dbRO.rawQuery(q, new String[]{feedId});
|
|
|
|
|
List<String> hashes = new ArrayList<String>(c.getCount());
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
hashes.add(c.getString(c.getColumnIndexOrThrow(DatabaseConstants.STORY_HASH)));
|
|
|
|
|
}
|
|
|
|
|
c.close();
|
|
|
|
|
return hashes;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 15:51:29 -07:00
|
|
|
public List<String> getUnreadStoryHashes() {
|
|
|
|
|
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
|
|
|
|
" FROM " + DatabaseConstants.STORY_TABLE +
|
|
|
|
|
" WHERE " + DatabaseConstants.STORY_READ + " = 0" ;
|
|
|
|
|
Cursor c = dbRO.rawQuery(q, null);
|
|
|
|
|
List<String> hashes = new ArrayList<String>(c.getCount());
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
hashes.add(c.getString(c.getColumnIndexOrThrow(DatabaseConstants.STORY_HASH)));
|
|
|
|
|
}
|
|
|
|
|
c.close();
|
|
|
|
|
return hashes;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
public void insertStories(StoriesResponse apiResponse) {
|
2014-07-05 16:21:41 -07:00
|
|
|
// to insert classifiers, we need to determine the feed ID of the stories in this
|
|
|
|
|
// response, so sniff one out.
|
|
|
|
|
String impliedFeedId = null;
|
|
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
// handle users
|
2014-07-13 19:32:40 -07:00
|
|
|
if (apiResponse.users != null) {
|
|
|
|
|
List<ContentValues> userValues = new ArrayList<ContentValues>(apiResponse.users.length);
|
|
|
|
|
for (UserProfile user : apiResponse.users) {
|
|
|
|
|
userValues.add(user.getValues());
|
|
|
|
|
}
|
|
|
|
|
bulkInsertValues(DatabaseConstants.USER_TABLE, userValues);
|
2014-06-05 04:04:14 -07:00
|
|
|
}
|
|
|
|
|
|
2014-07-08 18:08:36 -07:00
|
|
|
// handle supplemental feed data that may have been included (usually in social requests)
|
2014-07-09 17:01:09 -07:00
|
|
|
if (apiResponse.feeds != null) {
|
2014-07-27 00:39:40 -07:00
|
|
|
List<ContentValues> feedValues = new ArrayList<ContentValues>(apiResponse.feeds.size());
|
2014-07-09 17:01:09 -07:00
|
|
|
for (Feed feed : apiResponse.feeds) {
|
|
|
|
|
feedValues.add(feed.getValues());
|
|
|
|
|
}
|
|
|
|
|
bulkInsertValues(DatabaseConstants.FEED_TABLE, feedValues);
|
2014-07-08 18:08:36 -07:00
|
|
|
}
|
|
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
// handle story content
|
|
|
|
|
List<ContentValues> storyValues = new ArrayList<ContentValues>(apiResponse.stories.length);
|
2014-07-08 18:08:36 -07:00
|
|
|
List<ContentValues> socialStoryValues = new ArrayList<ContentValues>();
|
2014-06-05 04:04:14 -07:00
|
|
|
for (Story story : apiResponse.stories) {
|
2014-07-08 18:08:36 -07:00
|
|
|
ContentValues values = story.getValues();
|
|
|
|
|
// 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
|
|
|
|
|
for (String sharedUserId : story.sharedUserIds) {
|
2014-07-09 17:01:09 -07:00
|
|
|
ContentValues socialValues = new ContentValues();
|
|
|
|
|
socialValues.put(DatabaseConstants.SOCIALFEED_STORY_USER_ID, sharedUserId);
|
|
|
|
|
socialValues.put(DatabaseConstants.SOCIALFEED_STORY_STORYID, values.getAsString(DatabaseConstants.STORY_ID));
|
|
|
|
|
socialStoryValues.add(socialValues);
|
2014-07-08 18:08:36 -07:00
|
|
|
}
|
2014-07-05 16:21:41 -07:00
|
|
|
impliedFeedId = story.feedId;
|
2014-06-05 04:04:14 -07:00
|
|
|
}
|
|
|
|
|
bulkInsertValues(DatabaseConstants.STORY_TABLE, storyValues);
|
2014-07-09 17:01:09 -07:00
|
|
|
bulkInsertValues(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, socialStoryValues);
|
2014-07-05 16:21:41 -07:00
|
|
|
|
|
|
|
|
// handle classifiers
|
|
|
|
|
if (apiResponse.classifiers != null) {
|
2014-07-28 15:41:17 -07:00
|
|
|
for (Map.Entry<String,Classifier> entry : apiResponse.classifiers.entrySet()) {
|
|
|
|
|
// the API might not have included a feed ID, in which case it deserialized as -1 and must be implied
|
|
|
|
|
String classifierFeedId = entry.getKey();
|
|
|
|
|
if (classifierFeedId.equals("-1")) {
|
|
|
|
|
classifierFeedId = impliedFeedId;
|
|
|
|
|
}
|
|
|
|
|
List<ContentValues> classifierValues = entry.getValue().getContentValues();
|
|
|
|
|
for (ContentValues values : classifierValues) {
|
|
|
|
|
values.put(DatabaseConstants.CLASSIFIER_ID, classifierFeedId);
|
|
|
|
|
}
|
|
|
|
|
dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", new String[] { classifierFeedId });
|
|
|
|
|
bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifierValues);
|
2014-07-05 16:21:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 04:04:14 -07:00
|
|
|
// handle comments
|
|
|
|
|
List<ContentValues> commentValues = new ArrayList<ContentValues>();
|
|
|
|
|
List<ContentValues> replyValues = new ArrayList<ContentValues>();
|
|
|
|
|
for (Story story : apiResponse.stories) {
|
|
|
|
|
for (Comment comment : story.publicComments) {
|
|
|
|
|
comment.storyId = story.id;
|
|
|
|
|
comment.id = TextUtils.concat(story.id, story.feedId, comment.userId).toString();
|
|
|
|
|
commentValues.add(comment.getValues());
|
|
|
|
|
for (Reply reply : comment.replies) {
|
|
|
|
|
reply.commentId = comment.id;
|
|
|
|
|
replyValues.add(reply.getValues());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (Comment comment : story.friendsComments) {
|
|
|
|
|
comment.storyId = story.id;
|
|
|
|
|
comment.id = TextUtils.concat(story.id, story.feedId, comment.userId).toString();
|
|
|
|
|
commentValues.add(comment.getValues());
|
|
|
|
|
for (Reply reply : comment.replies) {
|
|
|
|
|
reply.commentId = comment.id;
|
|
|
|
|
replyValues.add(reply.getValues());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bulkInsertValues(DatabaseConstants.COMMENT_TABLE, commentValues);
|
|
|
|
|
bulkInsertValues(DatabaseConstants.REPLY_TABLE, replyValues);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 03:04:55 -07:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 15:51:29 -07:00
|
|
|
public void markStoryHashesRead(List<String> hashes) {
|
|
|
|
|
// NOTE: attempting to wrap these updates in a transaction for speed makes them silently fail
|
|
|
|
|
for (String hash : hashes) {
|
2014-08-14 17:46:16 -07:00
|
|
|
setStoryReadState(hash, true);
|
2014-06-05 15:51:29 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 17:46:16 -07:00
|
|
|
public void setStoryReadState(String hash, boolean read) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.STORY_READ, read);
|
|
|
|
|
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, read);
|
|
|
|
|
dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 16:37:37 -07:00
|
|
|
/**
|
|
|
|
|
* Marks a story (un)read and also adjusts unread counts for it.
|
|
|
|
|
*/
|
|
|
|
|
public void setStoryReadState(Story story, boolean read) {
|
|
|
|
|
setStoryReadState(story.storyHash, read);
|
|
|
|
|
// non-social feed count
|
2014-09-25 14:17:44 -07:00
|
|
|
refreshFeedCounts(FeedSet.singleFeed(story.feedId));
|
2014-08-25 16:37:37 -07:00
|
|
|
// social feed counts
|
|
|
|
|
Set<String> socialIds = new HashSet<String>();
|
|
|
|
|
if (!TextUtils.isEmpty(story.socialUserId)) {
|
|
|
|
|
socialIds.add(story.socialUserId);
|
|
|
|
|
}
|
|
|
|
|
if (story.friendUserIds != null) {
|
|
|
|
|
for (String id : story.friendUserIds) {
|
|
|
|
|
socialIds.add(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-25 14:17:44 -07:00
|
|
|
if (socialIds.size() > 0) {
|
|
|
|
|
refreshFeedCounts(FeedSet.multipleSocialFeeds(socialIds));
|
2014-08-25 16:37:37 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
public void markStoriesRead(FeedSet fs, Long olderThan, Long newerThan) {
|
2014-09-09 16:51:26 -07:00
|
|
|
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.");
|
2014-09-05 03:04:55 -07:00
|
|
|
}
|
2014-09-09 16:51:26 -07:00
|
|
|
dbRW.update(DatabaseConstants.STORY_TABLE, values, conjoinSelections(feedSelection, rangeSelection), null);
|
|
|
|
|
|
2014-09-25 14:17:44 -07:00
|
|
|
refreshFeedCounts(fs);
|
2014-09-05 03:04:55 -07:00
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
/**
|
|
|
|
|
* Refreshes the counts in the feeds/socialfeeds tables by counting stories in the story table.
|
|
|
|
|
*/
|
|
|
|
|
public void refreshFeedCounts(FeedSet fs) {
|
|
|
|
|
// decompose the FeedSet into a list of single feeds that need to be recounted
|
|
|
|
|
List<String> feedIds = new ArrayList<String>();
|
|
|
|
|
List<String> socialFeedIds = new ArrayList<String>();
|
2014-09-05 03:04:55 -07:00
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
if (fs.isAllNormal()) {
|
2014-09-25 14:17:44 -07:00
|
|
|
feedIds.addAll(getAllFeeds());
|
|
|
|
|
socialFeedIds.addAll(getAllSocialFeeds());
|
2014-09-24 17:16:13 -07:00
|
|
|
} else if (fs.getMultipleFeeds() != null) {
|
2014-09-25 14:17:44 -07:00
|
|
|
feedIds.addAll(fs.getMultipleFeeds());
|
2014-09-24 17:16:13 -07:00
|
|
|
} else if (fs.getSingleFeed() != null) {
|
|
|
|
|
feedIds.add(fs.getSingleFeed());
|
|
|
|
|
} else if (fs.getSingleSocialFeed() != null) {
|
|
|
|
|
socialFeedIds.add(fs.getSingleSocialFeed().getKey());
|
2014-09-25 14:17:44 -07:00
|
|
|
} else if (fs.getMultipleSocialFeeds() != null) {
|
|
|
|
|
socialFeedIds.addAll(fs.getMultipleSocialFeeds().keySet());
|
2014-09-24 17:16:13 -07:00
|
|
|
} else {
|
|
|
|
|
throw new IllegalStateException("Asked to refresh story counts for FeedSet of unknown type.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now recount the number of unreads in each feed, one by one
|
|
|
|
|
for (String feedId : feedIds) {
|
|
|
|
|
FeedSet singleFs = FeedSet.singleFeed(feedId);
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.FEED_NEGATIVE_COUNT, getUnreadCount(singleFs, StateFilter.NEG));
|
|
|
|
|
values.put(DatabaseConstants.FEED_NEUTRAL_COUNT, getUnreadCount(singleFs, StateFilter.NEUT));
|
|
|
|
|
values.put(DatabaseConstants.FEED_POSITIVE_COUNT, getUnreadCount(singleFs, StateFilter.BEST));
|
|
|
|
|
dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (String socialId : socialFeedIds) {
|
|
|
|
|
FeedSet singleFs = FeedSet.singleSocialFeed(socialId, "");
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.SOCIAL_FEED_NEGATIVE_COUNT, getUnreadCount(singleFs, StateFilter.NEG));
|
|
|
|
|
values.put(DatabaseConstants.SOCIAL_FEED_NEUTRAL_COUNT, getUnreadCount(singleFs, StateFilter.NEUT));
|
|
|
|
|
values.put(DatabaseConstants.SOCIAL_FEED_POSITIVE_COUNT, getUnreadCount(singleFs, StateFilter.BEST));
|
|
|
|
|
dbRW.update(DatabaseConstants.SOCIALFEED_TABLE, values, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[]{socialId});
|
|
|
|
|
}
|
2014-09-05 03:04:55 -07:00
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
public int getUnreadCount(FeedSet fs, StateFilter stateFilter) {
|
2014-09-24 03:54:31 -07:00
|
|
|
Cursor c = getStoriesCursor(fs, stateFilter, ReadFilter.PURE_UNREAD, null);
|
|
|
|
|
int count = c.getCount();
|
|
|
|
|
c.close();
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-17 20:04:01 -07:00
|
|
|
public void enqueueAction(ReadingAction ra) {
|
|
|
|
|
dbRW.insertOrThrow(DatabaseConstants.ACTION_TABLE, null, ra.toContentValues());
|
2014-09-05 03:04:55 -07:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 16:37:37 -07:00
|
|
|
public Cursor getActions(boolean includeDone) {
|
2014-09-17 20:04:01 -07:00
|
|
|
String q = "SELECT * FROM " + DatabaseConstants.ACTION_TABLE;
|
2014-08-25 16:37:37 -07:00
|
|
|
return dbRO.rawQuery(q, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void clearAction(String actionId) {
|
|
|
|
|
dbRW.delete(DatabaseConstants.ACTION_TABLE, DatabaseConstants.ACTION_ID + " = ?", new String[]{actionId});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Cursor getStory(String hash) {
|
|
|
|
|
String q = "SELECT * FROM " + DatabaseConstants.STORY_TABLE +
|
|
|
|
|
" WHERE " + DatabaseConstants.STORY_HASH + " = ?";
|
|
|
|
|
return dbRO.rawQuery(q, new String[]{hash});
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 04:30:33 -07:00
|
|
|
public void setStoryStarred(String hash, boolean starred) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.STORY_STARRED, starred);
|
|
|
|
|
dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-14 17:46:16 -07:00
|
|
|
/**
|
2014-09-24 03:18:21 -07:00
|
|
|
* Tags all saved stories with the reading session flag so they don't disappear if unsaved.
|
|
|
|
|
*/
|
|
|
|
|
public void markSavedReadingSession() {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, true);
|
|
|
|
|
dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_STARRED + " = 1", null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clears the read_this_session flag for all stories so they won't be displayed.
|
2014-08-14 17:46:16 -07:00
|
|
|
*/
|
|
|
|
|
public void clearReadingSession() {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, false);
|
|
|
|
|
dbRW.update(DatabaseConstants.STORY_TABLE, values, null, null);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
public Loader<Cursor> getStoriesLoader(final FeedSet fs, final StateFilter stateFilter) {
|
2014-07-27 00:39:40 -07:00
|
|
|
return new QueryCursorLoader(context) {
|
2014-08-14 17:46:16 -07:00
|
|
|
protected Cursor createCursor() {return getStoriesCursor(fs, stateFilter);}
|
2014-07-27 00:39:40 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
public Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter) {
|
2014-08-14 17:46:16 -07:00
|
|
|
ReadFilter readFilter = PrefsUtils.getReadFilter(context, fs);
|
2014-09-24 03:54:31 -07:00
|
|
|
StoryOrder order = PrefsUtils.getStoryOrder(context, fs);
|
|
|
|
|
return getStoriesCursor(fs, stateFilter, readFilter, order);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 17:16:13 -07:00
|
|
|
public Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, StoryOrder order) {
|
2014-08-14 17:46:16 -07:00
|
|
|
|
|
|
|
|
if (fs.getSingleFeed() != null) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder("SELECT ");
|
|
|
|
|
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);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), new String[]{fs.getSingleFeed()});
|
|
|
|
|
|
|
|
|
|
} else if (fs.getMultipleFeeds() != null) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder(DatabaseConstants.MULTIFEED_STORIES_QUERY_BASE);
|
|
|
|
|
q.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
|
|
|
|
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);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), null);
|
|
|
|
|
|
|
|
|
|
} else if (fs.getSingleSocialFeed() != null) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder(DatabaseConstants.MULTIFEED_STORIES_QUERY_BASE);
|
|
|
|
|
q.append(" FROM " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
|
|
|
|
|
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);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), new String[]{fs.getSingleSocialFeed().getKey()});
|
|
|
|
|
|
|
|
|
|
} else if (fs.isAllNormal()) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder(DatabaseConstants.MULTIFEED_STORIES_QUERY_BASE);
|
|
|
|
|
q.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
|
|
|
|
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
|
|
|
|
|
q.append(" WHERE 1");
|
|
|
|
|
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, null);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), null);
|
|
|
|
|
|
|
|
|
|
} else if (fs.isAllSocial()) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder(DatabaseConstants.MULTIFEED_STORIES_QUERY_BASE);
|
|
|
|
|
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);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), null);
|
|
|
|
|
|
|
|
|
|
} else if (fs.isAllSaved()) {
|
|
|
|
|
|
|
|
|
|
StringBuilder q = new StringBuilder(DatabaseConstants.MULTIFEED_STORIES_QUERY_BASE);
|
|
|
|
|
q.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
|
|
|
|
q.append(DatabaseConstants.JOIN_FEEDS_ON_STORIES);
|
2014-09-24 03:18:21 -07:00
|
|
|
q.append(" WHERE ((" + DatabaseConstants.STORY_STARRED + " = 1)");
|
|
|
|
|
q.append(" OR (" + DatabaseConstants.STORY_READ_THIS_SESSION + " = 1))");
|
2014-08-14 17:46:16 -07:00
|
|
|
q.append(" ORDER BY " + DatabaseConstants.STARRED_STORY_ORDER);
|
|
|
|
|
return dbRO.rawQuery(q.toString(), null);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
throw new IllegalStateException("Asked to get stories for FeedSet of unknown type.");
|
|
|
|
|
}
|
2014-07-27 00:39:40 -07:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 16:37:37 -07:00
|
|
|
public static void closeQuietly(Cursor c) {
|
|
|
|
|
if (c == null) return;
|
|
|
|
|
try {c.close();} catch (Exception e) {;}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 16:51:26 -07:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-04 13:46:10 -07:00
|
|
|
}
|