basic support for notifications (#966)

This commit is contained in:
dosiecki 2016-12-01 05:56:48 -08:00
parent ed198a1e07
commit a85a6099db
9 changed files with 142 additions and 9 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -977,6 +977,18 @@ public class BlurDatabaseHelper {
return c;
}
public Cursor getNotifyStoriesCursor() {
return rawQuery(DatabaseConstants.NOTIFY_STORY_QUERY_BASE, null, null);
}
public void markNotifications() {
synchronized (RW_MUTEX) {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.STORY_NOTIFIED, 1);
dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_NOTIFY + " > 0", null);
}
}
public Loader<Cursor> getActiveStoriesLoader(final FeedSet fs) {
final StoryOrder order = PrefsUtils.getStoryOrder(context, fs);
return new QueryCursorLoader(context) {
@ -1003,7 +1015,7 @@ public class BlurDatabaseHelper {
// stories aren't actually queried directly via the FeedSet and filters set in the UI. rather,
// those filters are use to push live or cached story hashes into the reading session table, and
// those hashes are used to pull story data from the story table
StringBuilder q = new StringBuilder(DatabaseConstants.STORY_QUERY_BASE);
StringBuilder q = new StringBuilder(DatabaseConstants.SESSION_STORY_QUERY_BASE);
if (fs.isAllRead()) {
q.append(" ORDER BY " + DatabaseConstants.READ_STORY_ORDER);

View file

@ -97,6 +97,8 @@ public class DatabaseConstants {
public static final String STORY_LAST_READ_DATE = "last_read_date";
public static final String STORY_SEARCH_HIT = "search_hit";
public static final String STORY_THUMBNAIL_URL = "thumbnail_url";
public static final String STORY_NOTIFY = "notify";
public static final String STORY_NOTIFIED = "notified";
public static final String READING_SESSION_TABLE = "reading_session";
public static final String READING_SESSION_STORY_HASH = "session_story_hash";
@ -233,6 +235,8 @@ public class DatabaseConstants {
STORY_PERMALINK + TEXT + ", " +
STORY_READ + INTEGER + ", " +
STORY_STARRED + INTEGER + ", " +
STORY_NOTIFY + INTEGER + ", " +
STORY_NOTIFIED + INTEGER + ", " +
STORY_STARRED_DATE + INTEGER + ", " +
STORY_TITLE + TEXT + ", " +
STORY_IMAGE_URLS + TEXT + ", " +
@ -291,24 +295,36 @@ public class DatabaseConstants {
STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS, STORY_INTELLIGENCE_TOTAL,
STORY_INTELLIGENCE_TITLE, STORY_PERMALINK, STORY_READ, STORY_STARRED, STORY_STARRED_DATE, STORY_TAGS, STORY_USER_TAGS, STORY_TITLE,
STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_HASH,
STORY_LAST_READ_DATE, STORY_THUMBNAIL_URL,
STORY_LAST_READ_DATE, STORY_THUMBNAIL_URL, STORY_NOTIFY, STORY_NOTIFIED,
};
private static final String STORY_COLUMNS =
TextUtils.join(",", BASE_STORY_COLUMNS) + ", " +
FEED_TITLE + ", " + FEED_FAVICON_URL + ", " + FEED_FAVICON_COLOR + ", " + FEED_FAVICON_BORDER + ", " + FEED_FAVICON_FADE + ", " + FEED_FAVICON_TEXT;
public static final String STORY_QUERY_BASE =
public static final String STORY_QUERY_BASE_1 =
"SELECT " +
STORY_COLUMNS +
" FROM " + STORY_TABLE +
" INNER JOIN " + FEED_TABLE +
" ON " + STORY_TABLE + "." + STORY_FEED_ID + " = " + FEED_TABLE + "." + FEED_ID +
" WHERE " + STORY_HASH + " IN (" +
" SELECT DISTINCT " + READING_SESSION_STORY_HASH +
" FROM " + READING_SESSION_TABLE + ")" +
" WHERE ";
public static final String STORY_QUERY_BASE_2 =
" GROUP BY " + STORY_HASH;
public static final String SESSION_STORY_QUERY_BASE =
STORY_QUERY_BASE_1 +
STORY_HASH + " IN (" +
" SELECT DISTINCT " + READING_SESSION_STORY_HASH +
" FROM " + READING_SESSION_TABLE +
")" +
STORY_QUERY_BASE_2;
public static final String NOTIFY_STORY_QUERY_BASE =
STORY_QUERY_BASE_1 +
STORY_NOTIFY + " > 0 AND " + STORY_NOTIFIED + " < 1" +
STORY_QUERY_BASE_2;
public static final String JOIN_STORIES_ON_SOCIALFEED_MAP =
" INNER JOIN " + STORY_TABLE + " ON " + STORY_TABLE + "." + STORY_ID + " = " + SOCIALFEED_STORY_MAP_TABLE + "." + SOCIALFEED_STORY_STORYID;

View file

@ -135,4 +135,13 @@ public class Feed implements Comparable<Feed>, Serializable {
return title.compareToIgnoreCase(f.title);
}
public boolean isNotify() {
// the API vends more info on notifications than we need. distill it to a boolean
if (notificationTypes == null) return false;
for (String type : notificationTypes) {
if (type.equals("android")) return true;
}
return false;
}
}

View file

@ -96,6 +96,10 @@ public class Story implements Serializable {
// non-API, though it probably could/should be. populated on first story ingest if thumbnails are turned on
public String thumbnailUrl;
// non-API
public boolean notify;
public boolean notified;
public ContentValues getValues() {
final ContentValues values = new ContentValues();
@ -126,6 +130,8 @@ public class Story implements Serializable {
values.put(DatabaseConstants.STORY_LAST_READ_DATE, lastReadTimestamp);
values.put(DatabaseConstants.STORY_SEARCH_HIT, searchHit);
values.put(DatabaseConstants.STORY_THUMBNAIL_URL, thumbnailUrl);
values.put(DatabaseConstants.STORY_NOTIFY, notify);
values.put(DatabaseConstants.STORY_NOTIFIED, notified);
return values;
}
@ -157,6 +163,8 @@ public class Story implements Serializable {
story.storyHash = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_HASH));
story.lastReadTimestamp = cursor.getLong(cursor.getColumnIndex(DatabaseConstants.STORY_LAST_READ_DATE));
story.thumbnailUrl = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_THUMBNAIL_URL));
story.notify = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_NOTIFY)) > 0;
story.notified = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_NOTIFIED)) > 0;
return story;
}

View file

@ -124,6 +124,8 @@ public class NBSyncService extends Service {
Set<String> orphanFeedIds;
Set<String> disabledFeedIds;
Set<String> notifyFeedIds;
private ExecutorService primaryExecutor;
CleanupService cleanupService;
OriginalTextService originalTextService;
@ -415,6 +417,7 @@ public class NBSyncService extends Service {
Set<String> debugFeedIdsFromFeeds = new HashSet<String>();
orphanFeedIds = new HashSet<String>();
disabledFeedIds = new HashSet<String>();
notifyFeedIds = new HashSet<String>();
try {
FeedFolderResponse feedResponse = apiManager.getFolderFeedMapping(true);
@ -470,6 +473,9 @@ public class NBSyncService extends Service {
disabledFeedIds.add(feed.feedId);
}
feedValues.add(feed.getValues());
if (feed.isNotify()) {
notifyFeedIds.add(feed.feedId);
}
}
// also add the implied zero-id feed
feedValues.add(Feed.getZeroFeed().getValues());

View file

@ -1,12 +1,15 @@
package com.newsblur.service;
import android.database.Cursor;
import android.util.Log;
import static com.newsblur.database.BlurDatabaseHelper.closeQuietly;
import com.newsblur.domain.Story;
import com.newsblur.network.domain.StoriesResponse;
import com.newsblur.network.domain.UnreadStoryHashesResponse;
import com.newsblur.util.AppConstants;
import com.newsblur.util.DefaultFeedView;
import com.newsblur.util.NotificationUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.StoryOrder;
@ -39,9 +42,13 @@ public class UnreadsService extends SubService {
doMetadata = false;
}
if (StoryHashQueue.size() < 1) return;
if (StoryHashQueue.size() > 0) {
getNewUnreadStories();
}
getNewUnreadStories();
if (StoryHashQueue.size() < 1) {
notifyStories();
}
}
private void syncUnreadList() {
@ -133,6 +140,16 @@ public class UnreadsService extends SubService {
Log.e(this.getClass().getName(), "error fetching unreads batch, abandoning sync.");
break unreadsyncloop;
}
for (Story story : response.stories) {
if (parent.notifyFeedIds.contains(story.feedId)) {
// for now, only notify stories with 1+ intel
if (story.intelligence.calcTotalIntel() > 0) {
story.notify = true;
}
}
}
parent.insertStories(response);
for (String hash : hashBatch) {
StoryHashQueue.remove(hash);
@ -169,6 +186,15 @@ public class UnreadsService extends SubService {
return true;
}
private void notifyStories() {
Cursor c = parent.dbHelper.getNotifyStoriesCursor();
if (c.getCount() > 0 ) {
NotificationUtils.notifyStories(c, parent);
parent.dbHelper.markNotifications();
}
closeQuietly(c);
}
public static void clear() {
StoryHashQueue.clear();
}

View file

@ -29,7 +29,7 @@ public class AppConstants {
public static final String LAST_SYNC_TIME = "LAST_SYNC_TIME";
// how long to wait before auto-syncing the feed/folder list
public static final long AUTO_SYNC_TIME_MILLIS = 20L * 60L * 1000L;
public static final long AUTO_SYNC_TIME_MILLIS = 15L * 60L * 1000L;
// how often to rebuild the DB
public static final long VACUUM_TIME_MILLIS = 12L * 60L * 60L * 1000L;

View file

@ -0,0 +1,56 @@
package com.newsblur.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import com.newsblur.R;
import com.newsblur.activity.Main;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Story;
public class NotificationUtils {
private NotificationUtils() {} // util class - no instances
public static void notifyStories(Cursor stories, Context context) {
String title = stories.getCount() + " New Stories";
if (stories.getCount() == 1) title = "1 New Story";
StringBuilder content = new StringBuilder();
while (stories.moveToNext()) {
String feedTitle = stories.getString(stories.getColumnIndex(DatabaseConstants.FEED_TITLE));
content.append(feedTitle);
if (! stories.isLast()) {
content.append(", ");
}
}
Intent appIntent = new Intent(context, Main.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, appIntent, 0);
Notification n = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(content.toString())
.setSmallIcon(R.drawable.logo_monochrome)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setNumber(stories.getCount())
.build();
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(1, n);
}
}