mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-31 13:35:58 +00:00
basic support for notifications (#966)
This commit is contained in:
parent
ed198a1e07
commit
a85a6099db
9 changed files with 142 additions and 9 deletions
BIN
clients/android/NewsBlur/res/drawable/logo_monochrome.png
Executable file
BIN
clients/android/NewsBlur/res/drawable/logo_monochrome.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue