mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
Merge branch 'master' into 5.1
* master: (30 commits)
Tune fetch batch sizing, remove debug.
Fix missing search icon for saved stories.
Fix DB optimization crashes, remove O(n) search tagging, instrument live memory.
Fix intel state filtering and reading session resets on filter changes.
Adding include_inactive=true to flat feeds to only give inactive feeds. I hope this doesn't break someday.
Separating flat_folders and flat_folders_with_inactive.
Adding inactive feeds to flat folders. This shouldn't break anything, since clients should be able to handle missing feeds anyhow.
Adding to /reader/feeds?flat=true (for @dejal).
Disable mark folder read context menu for all shared stories (#866)
Android v4.7.1.
Disable folder context menu for global shared stories row (#866)
Fix search queries with special characters. (#868)
I cannot believe it was a string-to-int type conversion that wasn't happening when deleting feeds. *smacks forehead* Stupid json.decode. Sorry to 85136323e2
.
Handling search queries with just spaces in them.
Handling update_fields=[] when saving feeds.
Fix new reading session tracker (partial!).
Replace story activation with reading session table. (partial!)
Fix search queries with special characters. (#868)
Replace story activation with filtering by fetch time.
Optimise story insert times.
...
This commit is contained in:
commit
e9158d1550
47 changed files with 580 additions and 548 deletions
|
@ -21,6 +21,14 @@
|
|||
"user": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "reader.usersubscriptionfolders",
|
||||
"fields": {
|
||||
"folders": "[5299728, 644144, 1187026, {\"Brainiacs & Opinion\": [569, 38, 3581, 183139, 1186180, 15]}, {\"Science & Technology\": [731503, 140145, 1272495, 76, 161, 39, {\"Hacker\": [5985150, 3323431]}]}, {\"Humor\": [212379, 3530, 5994357]}, {\"Videos\": [3240, 5168]}]",
|
||||
"user": 2
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 2,
|
||||
|
@ -161,6 +169,24 @@
|
|||
"email": "samuel@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "Dejal",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"is_active": 1,
|
||||
"is_superuser": 1,
|
||||
"is_staff": 1,
|
||||
"last_login": "2009-04-07 19:22:24",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
|
||||
"email": "dejal@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1206,
|
||||
|
|
|
@ -1288,13 +1288,17 @@ class UserSubscriptionFolders(models.Model):
|
|||
|
||||
return _arrange_folder(user_sub_folders)
|
||||
|
||||
def flatten_folders(self, feeds=None):
|
||||
def flatten_folders(self, feeds=None, inactive_feeds=None):
|
||||
folders = json.decode(self.folders)
|
||||
flat_folders = {" ": []}
|
||||
if feeds and not inactive_feeds:
|
||||
inactive_feeds = []
|
||||
|
||||
def _flatten_folders(items, parent_folder="", depth=0):
|
||||
for item in items:
|
||||
if isinstance(item, int) and ((not feeds) or (feeds and item in feeds)):
|
||||
if (isinstance(item, int) and
|
||||
(not feeds or
|
||||
(item in feeds or item in inactive_feeds))):
|
||||
if not parent_folder:
|
||||
parent_folder = ' '
|
||||
if parent_folder in flat_folders:
|
||||
|
@ -1317,6 +1321,7 @@ class UserSubscriptionFolders(models.Model):
|
|||
return flat_folders
|
||||
|
||||
def delete_feed(self, feed_id, in_folder, commit_delete=True):
|
||||
feed_id = int(feed_id)
|
||||
def _find_feed_in_folders(old_folders, folder_name='', multiples_found=False, deleted=False):
|
||||
new_folders = []
|
||||
for k, folder in enumerate(old_folders):
|
||||
|
@ -1462,6 +1467,7 @@ class UserSubscriptionFolders(models.Model):
|
|||
logging.user(self.user, "~FBMoving ~SB%s~SN feeds to folder: ~SB%s" % (
|
||||
len(feeds_by_folder), to_folder))
|
||||
for feed_id, in_folder in feeds_by_folder:
|
||||
feed_id = int(feed_id)
|
||||
self.move_feed_to_folder(feed_id, in_folder, to_folder)
|
||||
|
||||
return self
|
||||
|
|
|
@ -96,6 +96,22 @@ class ReaderTest(TestCase):
|
|||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, {'Blogs': [8, 9]}])
|
||||
|
||||
def test_move_feeds_by_folder(self):
|
||||
self.client.login(username='Dejal', password='test')
|
||||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [5299728, 644144, 1187026, {"Brainiacs & Opinion": [569, 38, 3581, 183139, 1186180, 15]}, {"Science & Technology": [731503, 140145, 1272495, 76, 161, 39, {"Hacker": [5985150, 3323431]}]}, {"Humor": [212379, 3530, 5994357]}, {"Videos": [3240, 5168]}])
|
||||
|
||||
# Move feeds by folder
|
||||
response = self.client.post(reverse('move-feeds-by-folder-to-folder'), {u'feeds_by_folder': u'[\n [\n "5994357",\n "Humor"\n ],\n [\n "3530",\n "Humor"\n ]\n]', u'to_folder': u'Brainiacs & Opinion'})
|
||||
response = json.decode(response.content)
|
||||
self.assertEquals(response['code'], 1)
|
||||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [5299728, 644144, 1187026, {"Brainiacs & Opinion": [569, 38, 3581, 183139, 1186180, 15, 5994357, 3530]}, {"Science & Technology": [731503, 140145, 1272495, 76, 161, 39, {"Hacker": [5985150, 3323431]}]}, {"Humor": [212379]}, {"Videos": [3240, 5168]}])
|
||||
|
||||
def test_load_single_feed(self):
|
||||
# from django.conf import settings
|
||||
|
|
|
@ -322,8 +322,10 @@ def load_feeds_flat(request):
|
|||
user = request.user
|
||||
include_favicons = is_true(request.REQUEST.get('include_favicons', False))
|
||||
update_counts = is_true(request.REQUEST.get('update_counts', True))
|
||||
include_inactive = is_true(request.REQUEST.get('include_inactive', False))
|
||||
|
||||
feeds = {}
|
||||
inactive_feeds = {}
|
||||
day_ago = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||
scheduled_feeds = []
|
||||
iphone_version = "2.1" # Preserved forever. Don't change.
|
||||
|
@ -345,7 +347,9 @@ def load_feeds_flat(request):
|
|||
if not user_subs and folders:
|
||||
folders.auto_activate()
|
||||
user_subs = UserSubscription.objects.select_related('feed').filter(user=user, active=True)
|
||||
|
||||
if include_inactive:
|
||||
inactive_subs = UserSubscription.objects.select_related('feed').filter(user=user, active=False)
|
||||
|
||||
for sub in user_subs:
|
||||
if update_counts and sub.needs_unread_recalc:
|
||||
sub.calculate_feed_scores(silent=True)
|
||||
|
@ -357,14 +361,21 @@ def load_feeds_flat(request):
|
|||
elif sub.feed.next_scheduled_update < day_ago:
|
||||
scheduled_feeds.append(sub.feed.pk)
|
||||
|
||||
if include_inactive:
|
||||
for sub in inactive_subs:
|
||||
inactive_feeds[sub.feed_id] = sub.canonical(include_favicon=include_favicons)
|
||||
|
||||
if len(scheduled_feeds) > 0 and request.user.is_authenticated():
|
||||
logging.user(request, "~SN~FMTasking the scheduling immediate fetch of ~SB%s~SN feeds..." %
|
||||
len(scheduled_feeds))
|
||||
ScheduleImmediateFetches.apply_async(kwargs=dict(feed_ids=scheduled_feeds, user_id=user.pk))
|
||||
|
||||
flat_folders = []
|
||||
flat_folders_with_inactive = []
|
||||
if folders:
|
||||
flat_folders = folders.flatten_folders(feeds=feeds)
|
||||
flat_folders_with_inactive = folders.flatten_folders(feeds=feeds,
|
||||
inactive_feeds=inactive_feeds)
|
||||
|
||||
social_params = {
|
||||
'user_id': user.pk,
|
||||
|
@ -382,12 +393,14 @@ def load_feeds_flat(request):
|
|||
if not user_subs:
|
||||
categories = MCategory.serialize()
|
||||
|
||||
logging.user(request, "~FB~SBLoading ~FY%s~FB/~FM%s~FB feeds/socials ~FMflat~FB%s" % (
|
||||
len(feeds.keys()), len(social_feeds), '. ~FCUpdating counts.' if update_counts else ''))
|
||||
logging.user(request, "~FB~SBLoading ~FY%s~FB/~FM%s~FB/~FR%s~FB feeds/socials/inactive ~FMflat~FB%s" % (
|
||||
len(feeds.keys()), len(social_feeds), len(inactive_feeds), '. ~FCUpdating counts.' if update_counts else ''))
|
||||
|
||||
data = {
|
||||
"flat_folders": flat_folders,
|
||||
"feeds": feeds,
|
||||
"flat_folders_with_inactive": flat_folders_with_inactive,
|
||||
"feeds": feeds if not include_inactive else {"0": "Don't include `include_inactive=true` if you want active feeds."},
|
||||
"inactive_feeds": inactive_feeds if include_inactive else {"0": "Include `include_inactive=true`"},
|
||||
"social_feeds": social_feeds,
|
||||
"social_profile": social_profile,
|
||||
"social_services": social_services,
|
||||
|
@ -541,7 +554,7 @@ def load_single_feed(request, feed_id):
|
|||
offset = limit * (page-1)
|
||||
order = request.REQUEST.get('order', 'newest')
|
||||
read_filter = request.REQUEST.get('read_filter', 'all')
|
||||
query = request.REQUEST.get('query')
|
||||
query = request.REQUEST.get('query', '').strip()
|
||||
include_story_content = is_true(request.REQUEST.get('include_story_content', True))
|
||||
include_hidden = is_true(request.REQUEST.get('include_hidden', False))
|
||||
message = None
|
||||
|
@ -793,7 +806,7 @@ def load_starred_stories(request):
|
|||
offset = int(request.REQUEST.get('offset', 0))
|
||||
limit = int(request.REQUEST.get('limit', 10))
|
||||
page = int(request.REQUEST.get('page', 0))
|
||||
query = request.REQUEST.get('query')
|
||||
query = request.REQUEST.get('query', '').strip()
|
||||
order = request.REQUEST.get('order', 'newest')
|
||||
tag = request.REQUEST.get('tag')
|
||||
story_hashes = request.REQUEST.getlist('h')[:100]
|
||||
|
@ -1097,7 +1110,7 @@ def load_read_stories(request):
|
|||
limit = int(request.REQUEST.get('limit', 10))
|
||||
page = int(request.REQUEST.get('page', 0))
|
||||
order = request.REQUEST.get('order', 'newest')
|
||||
query = request.REQUEST.get('query')
|
||||
query = request.REQUEST.get('query', '').strip()
|
||||
now = localtime_for_timezone(datetime.datetime.now(), user.profile.timezone)
|
||||
message = None
|
||||
if page: offset = limit * (page - 1)
|
||||
|
@ -1185,7 +1198,7 @@ def load_river_stories__redis(request):
|
|||
page = int(request.REQUEST.get('page', 1))
|
||||
order = request.REQUEST.get('order', 'newest')
|
||||
read_filter = request.REQUEST.get('read_filter', 'unread')
|
||||
query = request.REQUEST.get('query')
|
||||
query = request.REQUEST.get('query', '').strip()
|
||||
include_hidden = is_true(request.REQUEST.get('include_hidden', False))
|
||||
now = localtime_for_timezone(datetime.datetime.now(), user.profile.timezone)
|
||||
usersubs = []
|
||||
|
|
|
@ -20,6 +20,7 @@ from django.db import models
|
|||
from django.db import IntegrityError
|
||||
from django.conf import settings
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.utils import DatabaseError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
|
@ -226,6 +227,9 @@ class Feed(models.Model):
|
|||
|
||||
try:
|
||||
super(Feed, self).save(*args, **kwargs)
|
||||
except DatabaseError, e:
|
||||
logging.debug(" ---> ~FBFeed update failed, no change: %s / %s..." % (kwargs.get('update_fields', None), e))
|
||||
pass
|
||||
except IntegrityError, e:
|
||||
logging.debug(" ---> ~FRFeed save collision (%s), checking dupe..." % e)
|
||||
duplicate_feeds = Feed.objects.filter(feed_address=self.feed_address,
|
||||
|
@ -2484,8 +2488,11 @@ class MStarredStoryCounts(mongo.Document):
|
|||
|
||||
if not total_only:
|
||||
cls.objects(user_id=user_id).delete()
|
||||
user_tags = cls.count_tags_for_user(user_id)
|
||||
user_feeds = cls.count_feeds_for_user(user_id)
|
||||
try:
|
||||
user_tags = cls.count_tags_for_user(user_id)
|
||||
user_feeds = cls.count_feeds_for_user(user_id)
|
||||
except pymongo.errors.OperationFailure, e:
|
||||
logging.debug(" ---> ~FBOperationError on mongo: ~SB%s" % e)
|
||||
|
||||
total_stories_count = MStarredStory.objects(user_id=user_id).count()
|
||||
cls.objects(user_id=user_id, tag=None, feed_id=None).update_one(set__count=total_stories_count,
|
||||
|
|
|
@ -47,7 +47,7 @@ def load_social_stories(request, user_id, username=None):
|
|||
page = request.REQUEST.get('page')
|
||||
order = request.REQUEST.get('order', 'newest')
|
||||
read_filter = request.REQUEST.get('read_filter', 'all')
|
||||
query = request.REQUEST.get('query')
|
||||
query = request.REQUEST.get('query', '').strip()
|
||||
stories = []
|
||||
message = None
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.newsblur"
|
||||
android:versionCode="116"
|
||||
android:versionName="4.7.0" >
|
||||
android:versionCode="117"
|
||||
android:versionName="4.7.1" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
|
|
|
@ -44,6 +44,13 @@
|
|||
android:entries="@array/default_read_filter_entries"
|
||||
android:entryValues="@array/default_read_filter_values"
|
||||
android:defaultValue="@string/default_read_filter_value" />
|
||||
<ListPreference
|
||||
android:key="pref_confirm_mark_all_read"
|
||||
android:title="@string/settings_confirm_mark_all_read"
|
||||
android:dialogTitle="@string/settings_confirm_mark_all_read"
|
||||
android:entries="@array/confirm_mark_all_read_entries"
|
||||
android:entryValues="@array/confirm_mark_all_read_values"
|
||||
android:defaultValue="@string/confirm_mark_all_read_value" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_auto_open_first_unread"
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
android:showAsAction="never" />
|
||||
<item android:id="@+id/menu_search_stories"
|
||||
android:title="@string/menu_search_stories"
|
||||
android:showAsAction="never" />
|
||||
android:showAsAction="ifRoom" android:icon="@drawable/search" />
|
||||
</menu>
|
||||
|
|
|
@ -224,7 +224,7 @@
|
|||
<string name="default_read_filter_value">ALL</string>
|
||||
|
||||
<string-array name="mark_all_read_options">
|
||||
<item>Mark entire folder read</item>
|
||||
<item>Mark all read</item>
|
||||
<item>Cancel</item>
|
||||
</string-array>
|
||||
|
||||
|
@ -279,4 +279,20 @@
|
|||
<item>DOWN_NEXT</item>
|
||||
</string-array>
|
||||
<string name="default_volume_key_navigation_value">OFF</string>
|
||||
|
||||
<string name="settings_confirm_mark_all_read">Confirm Mark All Read</string>
|
||||
<string name="none">None</string>
|
||||
<string name="feed_and_folder">Feeds and Folders</string>
|
||||
<string name="folder_only">Folders Only</string>
|
||||
<string-array name="confirm_mark_all_read_entries">
|
||||
<item>@string/feed_and_folder</item>
|
||||
<item>@string/folder_only</item>
|
||||
<item>@string/none</item>
|
||||
</string-array>
|
||||
<string-array name="confirm_mark_all_read_values">
|
||||
<item>FEED_AND_FOLDER</item>
|
||||
<item>FOLDER_ONLY</item>
|
||||
<item>NONE</item>
|
||||
</string-array>
|
||||
<string name="confirm_mark_all_read_value">FOLDER_ONLY</string>
|
||||
</resources>
|
||||
|
|
|
@ -2,23 +2,19 @@ package com.newsblur.activity;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.fragment.AllStoriesItemListFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment.MarkAllReadDialogListener;
|
||||
import com.newsblur.util.DefaultFeedView;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class AllStoriesItemsList extends ItemsList implements MarkAllReadDialogListener {
|
||||
public class AllStoriesItemsList extends ItemsList {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
|
@ -40,17 +36,6 @@ public class AllStoriesItemsList extends ItemsList implements MarkAllReadDialogL
|
|||
protected FeedSet createFeedSet() {
|
||||
return FeedSet.allFeeds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markItemListAsRead() {
|
||||
MarkAllReadDialogFragment dialog = MarkAllReadDialogFragment.newInstance(getResources().getString(R.string.all_stories));
|
||||
dialog.show(fragmentManager, "dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMarkAllRead() {
|
||||
super.markItemListAsRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
|
@ -77,10 +62,4 @@ public class AllStoriesItemsList extends ItemsList implements MarkAllReadDialogL
|
|||
itemListFragment.setDefaultFeedView(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.newsblur.util.DefaultFeedView;
|
|||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class FeedItemsList extends ItemsList {
|
||||
|
|
|
@ -4,21 +4,17 @@ import android.os.Bundle;
|
|||
import android.app.FragmentTransaction;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.util.Log;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.fragment.FolderItemListFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment.MarkAllReadDialogListener;
|
||||
import com.newsblur.util.DefaultFeedView;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class FolderItemsList extends ItemsList implements MarkAllReadDialogListener {
|
||||
public class FolderItemsList extends ItemsList {
|
||||
|
||||
public static final String EXTRA_FOLDER_NAME = "folderName";
|
||||
private String folderName;
|
||||
|
@ -54,17 +50,6 @@ public class FolderItemsList extends ItemsList implements MarkAllReadDialogListe
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markItemListAsRead() {
|
||||
MarkAllReadDialogFragment dialog = MarkAllReadDialogFragment.newInstance(folderName);
|
||||
dialog.show(fragmentManager, "dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMarkAllRead() {
|
||||
super.markItemListAsRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateReadFilterPreference(ReadFilter newValue) {
|
||||
PrefsUtils.setReadFilterForFolder(this, folderName, newValue);
|
||||
|
@ -82,10 +67,4 @@ public class FolderItemsList extends ItemsList implements MarkAllReadDialogListe
|
|||
itemListFragment.setDefaultFeedView(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,13 +17,17 @@ import butterknife.FindView;
|
|||
import com.newsblur.R;
|
||||
import com.newsblur.fragment.DefaultFeedViewDialogFragment;
|
||||
import com.newsblur.fragment.ItemListFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment.MarkAllReadDialogListener;
|
||||
import com.newsblur.fragment.ReadFilterDialogFragment;
|
||||
import com.newsblur.fragment.StoryOrderDialogFragment;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.DefaultFeedView;
|
||||
import com.newsblur.util.DefaultFeedViewChangedListener;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.MarkAllReadConfirmation;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.ReadFilterChangedListener;
|
||||
|
@ -32,7 +36,7 @@ import com.newsblur.util.StoryOrder;
|
|||
import com.newsblur.util.StoryOrderChangedListener;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public abstract class ItemsList extends NbActivity implements StoryOrderChangedListener, ReadFilterChangedListener, DefaultFeedViewChangedListener {
|
||||
public abstract class ItemsList extends NbActivity implements StoryOrderChangedListener, ReadFilterChangedListener, DefaultFeedViewChangedListener, MarkAllReadDialogListener {
|
||||
|
||||
private static final String STORY_ORDER = "storyOrder";
|
||||
private static final String READ_FILTER = "readFilter";
|
||||
|
@ -64,7 +68,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +99,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) {
|
||||
|
@ -126,6 +131,17 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
}
|
||||
|
||||
public void markItemListAsRead() {
|
||||
MarkAllReadConfirmation confirmation = PrefsUtils.getMarkAllReadConfirmation(this);
|
||||
if (confirmation.feedSetRequiresConfirmation(fs)) {
|
||||
MarkAllReadDialogFragment dialog = MarkAllReadDialogFragment.newInstance(fs);
|
||||
dialog.show(fragmentManager, "dialog");
|
||||
} else {
|
||||
onMarkAllRead(fs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMarkAllRead(FeedSet feedSet) {
|
||||
if (itemListFragment != null) {
|
||||
// since v6.0 of Android, the ListView in the fragment likes to crash if the underlying
|
||||
// dataset changes rapidly as happens when marking-all-read and when the fragment is
|
||||
|
@ -205,6 +221,9 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
if (overlayStatusText != null) {
|
||||
String syncStatus = NBSyncService.getSyncStatusMessage(this, true);
|
||||
if (syncStatus != null) {
|
||||
if (AppConstants.VERBOSE_LOG) {
|
||||
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
|
||||
}
|
||||
overlayStatusText.setText(syncStatus);
|
||||
overlayStatusText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -231,7 +250,6 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
@Override
|
||||
public void storyOrderChanged(StoryOrder newValue) {
|
||||
updateStoryOrderPreference(newValue);
|
||||
FeedUtils.clearReadingSession();
|
||||
itemListFragment.resetEmptyState();
|
||||
itemListFragment.hasUpdated();
|
||||
itemListFragment.scrollToTop();
|
||||
|
@ -240,7 +258,6 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
@Override
|
||||
public void readFilterChanged(ReadFilter newValue) {
|
||||
updateReadFilterPreference(newValue);
|
||||
FeedUtils.clearReadingSession();
|
||||
itemListFragment.resetEmptyState();
|
||||
itemListFragment.hasUpdated();
|
||||
itemListFragment.scrollToTop();
|
||||
|
|
|
@ -28,16 +28,18 @@ import com.newsblur.fragment.FeedIntelligenceSelectorFragment;
|
|||
import com.newsblur.fragment.FolderListFragment;
|
||||
import com.newsblur.fragment.LoginAsDialogFragment;
|
||||
import com.newsblur.fragment.LogoutDialogFragment;
|
||||
import com.newsblur.fragment.MarkAllReadDialogFragment.MarkAllReadDialogListener;
|
||||
import com.newsblur.service.BootReceiver;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.StateFilter;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.view.StateToggleButton.StateChangedListener;
|
||||
|
||||
public class Main extends NbActivity implements StateChangedListener, SwipeRefreshLayout.OnRefreshListener, AbsListView.OnScrollListener, PopupMenu.OnMenuItemClickListener {
|
||||
public class Main extends NbActivity implements StateChangedListener, SwipeRefreshLayout.OnRefreshListener, AbsListView.OnScrollListener, PopupMenu.OnMenuItemClickListener, MarkAllReadDialogListener {
|
||||
|
||||
private FolderListFragment folderFeedList;
|
||||
private FragmentManager fragmentManager;
|
||||
|
@ -100,8 +102,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
|
||||
NBSyncService.clearPendingStoryRequest();
|
||||
NBSyncService.flushRecounts();
|
||||
NBSyncService.setActivationMode(NBSyncService.ActivationMode.ALL);
|
||||
FeedUtils.activateAllStories();
|
||||
FeedUtils.clearReadingSession();
|
||||
|
||||
updateStatusIndicators();
|
||||
|
@ -172,6 +172,9 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
if (overlayStatusText != null) {
|
||||
String syncStatus = NBSyncService.getSyncStatusMessage(this, false);
|
||||
if (syncStatus != null) {
|
||||
if (AppConstants.VERBOSE_LOG) {
|
||||
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
|
||||
}
|
||||
overlayStatusText.setText(syncStatus);
|
||||
overlayStatusText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -278,4 +281,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMarkAllRead(FeedSet feedSet) {
|
||||
FeedUtils.markFeedsRead(feedSet, null, null, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,7 +220,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
finish();
|
||||
return null;
|
||||
}
|
||||
return FeedUtils.dbHelper.getStoriesLoader(fs, intelState);
|
||||
return FeedUtils.dbHelper.getActiveStoriesLoader(fs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -281,7 +281,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
}
|
||||
}
|
||||
// if the story wasn't found, try to get more stories into the cursor
|
||||
FeedUtils.activateAllStories();
|
||||
this.checkStoryCount(readingAdapter.getCount()+1);
|
||||
}
|
||||
|
||||
|
@ -396,6 +395,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
if (overlayStatusText != null) {
|
||||
String syncStatus = NBSyncService.getSyncStatusMessage(this, true);
|
||||
if (syncStatus != null) {
|
||||
if (AppConstants.VERBOSE_LOG) {
|
||||
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
|
||||
}
|
||||
overlayStatusText.setText(syncStatus);
|
||||
overlayStatusText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
@ -677,13 +679,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
*/
|
||||
private void nextUnread() {
|
||||
unreadSearchActive = true;
|
||||
|
||||
// the first time an unread search is triggered, also trigger an activation of unreads, so
|
||||
// we don't search for a story that doesn't exist in the cursor
|
||||
if (!unreadSearchStarted) {
|
||||
FeedUtils.activateAllStories();
|
||||
unreadSearchStarted = true;
|
||||
}
|
||||
unreadSearchStarted = true;
|
||||
|
||||
// if we somehow got tapped before construction or are running during destruction, stop and
|
||||
// let either finish. search will happen when the cursor is pushed.
|
||||
|
|
|
@ -21,10 +21,4 @@ public class SavedStoriesReading extends Reading {
|
|||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
// every time we see a set of saved stories, tag them so they don't disappear during this reading session
|
||||
FeedUtils.dbHelper.markSavedReadingSession();
|
||||
super.onLoadFinished(loader, cursor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,9 @@ import com.newsblur.util.UIUtils;
|
|||
|
||||
public class SocialFeedReading extends Reading {
|
||||
|
||||
public static final String EXTRA_IGNORE_FILTERS = "ignore_filters";
|
||||
private boolean ignoreFilters;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
super.onCreate(savedInstanceBundle);
|
||||
ignoreFilters = getIntent().hasExtra(EXTRA_IGNORE_FILTERS);
|
||||
SocialFeed socialFeed = FeedUtils.dbHelper.getSocialFeed(fs.getSingleSocialFeed().getKey());
|
||||
if (socialFeed == null) finish(); // don't open fatally stale intents
|
||||
UIUtils.setCustomActionBar(this, socialFeed.photoUrl, socialFeed.feedTitle);
|
||||
|
@ -25,14 +21,4 @@ public class SocialFeedReading extends Reading {
|
|||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
// 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);
|
||||
} else {
|
||||
return super.onCreateLoader(loaderId, bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
db.execSQL(DatabaseConstants.FOLDER_SQL);
|
||||
db.execSQL(DatabaseConstants.USER_SQL);
|
||||
db.execSQL(DatabaseConstants.STORY_SQL);
|
||||
db.execSQL(DatabaseConstants.READING_SESSION_SQL);
|
||||
db.execSQL(DatabaseConstants.STORY_TEXT_SQL);
|
||||
db.execSQL(DatabaseConstants.COMMENT_SQL);
|
||||
db.execSQL(DatabaseConstants.REPLY_SQL);
|
||||
|
@ -36,6 +37,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
db.execSQL(drop + DatabaseConstants.SOCIALFEED_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.FOLDER_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.STORY_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.READING_SESSION_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.STORY_TEXT_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.USER_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.COMMENT_TABLE);
|
||||
|
|
|
@ -136,10 +136,6 @@ public class BlurDatabaseHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public void cleanupAllStories() {
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, null, null);}
|
||||
}
|
||||
|
||||
public void cleanupStoryText() {
|
||||
String q = "DELETE FROM " + DatabaseConstants.STORY_TEXT_TABLE +
|
||||
" WHERE " + DatabaseConstants.STORY_TEXT_STORY_HASH + " NOT IN " +
|
||||
|
@ -266,149 +262,136 @@ public class BlurDatabaseHelper {
|
|||
return urls;
|
||||
}
|
||||
|
||||
public void insertStories(StoriesResponse apiResponse, NBSyncService.ActivationMode actMode, long modeCutoff) {
|
||||
// to insert classifiers, we need to determine the feed ID of the stories in this
|
||||
// response, so sniff one out.
|
||||
String impliedFeedId = null;
|
||||
public void insertStories(StoriesResponse apiResponse, boolean forImmediateReading) {
|
||||
StateFilter intelState = PrefsUtils.getStateFilter(context);
|
||||
synchronized (RW_MUTEX) {
|
||||
// do not attempt to use beginTransactionNonExclusive() to reduce lock time for this very heavy set
|
||||
// of calls. most versions of Android incorrectly implement the underlying SQLite calls and will
|
||||
// result in crashes that poison the DB beyond repair
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
|
||||
// to insert classifiers, we need to determine the feed ID of the stories in this
|
||||
// response, so sniff one out.
|
||||
String impliedFeedId = null;
|
||||
|
||||
// handle users
|
||||
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);
|
||||
}
|
||||
|
||||
// handle supplemental feed data that may have been included (usually in social requests)
|
||||
if (apiResponse.feeds != null) {
|
||||
List<ContentValues> feedValues = new ArrayList<ContentValues>(apiResponse.feeds.size());
|
||||
for (Feed feed : apiResponse.feeds) {
|
||||
feedValues.add(feed.getValues());
|
||||
}
|
||||
bulkInsertValues(DatabaseConstants.FEED_TABLE, feedValues);
|
||||
}
|
||||
|
||||
// handle story content
|
||||
List<ContentValues> storyValues = new ArrayList<ContentValues>(apiResponse.stories.length);
|
||||
List<ContentValues> socialStoryValues = new ArrayList<ContentValues>();
|
||||
for (Story story : apiResponse.stories) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
impliedFeedId = story.feedId;
|
||||
}
|
||||
if (storyValues.size() > 0) {
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
bulkInsertValuesExtSync(DatabaseConstants.STORY_TABLE, storyValues);
|
||||
markStoriesActive(actMode, modeCutoff);
|
||||
dbRW.setTransactionSuccessful();
|
||||
} finally {
|
||||
dbRW.endTransaction();
|
||||
// handle users
|
||||
if (apiResponse.users != null) {
|
||||
List<ContentValues> userValues = new ArrayList<ContentValues>(apiResponse.users.length);
|
||||
for (UserProfile user : apiResponse.users) {
|
||||
userValues.add(user.getValues());
|
||||
}
|
||||
bulkInsertValuesExtSync(DatabaseConstants.USER_TABLE, userValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socialStoryValues.size() > 0) {
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
|
||||
// handle supplemental feed data that may have been included (usually in social requests)
|
||||
if (apiResponse.feeds != null) {
|
||||
List<ContentValues> feedValues = new ArrayList<ContentValues>(apiResponse.feeds.size());
|
||||
for (Feed feed : apiResponse.feeds) {
|
||||
feedValues.add(feed.getValues());
|
||||
}
|
||||
bulkInsertValuesExtSync(DatabaseConstants.FEED_TABLE, feedValues);
|
||||
}
|
||||
|
||||
// handle story content
|
||||
List<ContentValues> socialStoryValues = new ArrayList<ContentValues>();
|
||||
for (Story story : apiResponse.stories) {
|
||||
ContentValues values = story.getValues();
|
||||
// immediate insert the story data
|
||||
dbRW.insertWithOnConflict(DatabaseConstants.STORY_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
// if a story was shared by a user, also insert it into the social table under their userid, too
|
||||
for (String sharedUserId : story.sharedUserIds) {
|
||||
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);
|
||||
}
|
||||
// if the story is being fetched for the immediate session, also add the hash to the session table
|
||||
if (forImmediateReading && story.isStoryVisibileInState(intelState)) {
|
||||
ContentValues sessionHashValues = new ContentValues();
|
||||
sessionHashValues.put(DatabaseConstants.READING_SESSION_STORY_HASH, story.storyHash);
|
||||
dbRW.insert(DatabaseConstants.READING_SESSION_TABLE, null, sessionHashValues);
|
||||
}
|
||||
impliedFeedId = story.feedId;
|
||||
}
|
||||
if (socialStoryValues.size() > 0) {
|
||||
for(ContentValues values: socialStoryValues) {
|
||||
dbRW.insertWithOnConflict(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
markStoriesActive(actMode, modeCutoff);
|
||||
dbRW.setTransactionSuccessful();
|
||||
} finally {
|
||||
dbRW.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle classifiers
|
||||
if (apiResponse.classifiers != null) {
|
||||
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;
|
||||
// handle classifiers
|
||||
if (apiResponse.classifiers != null) {
|
||||
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 });
|
||||
bulkInsertValuesExtSync(DatabaseConstants.CLASSIFIER_TABLE, classifierValues);
|
||||
}
|
||||
}
|
||||
List<ContentValues> classifierValues = entry.getValue().getContentValues();
|
||||
for (ContentValues values : classifierValues) {
|
||||
values.put(DatabaseConstants.CLASSIFIER_ID, classifierFeedId);
|
||||
}
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", new String[] { classifierFeedId });}
|
||||
bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifierValues);
|
||||
}
|
||||
}
|
||||
|
||||
// handle comments
|
||||
List<ContentValues> commentValues = new ArrayList<ContentValues>();
|
||||
List<ContentValues> replyValues = new ArrayList<ContentValues>();
|
||||
// track which comments were seen, so replies can be cleared before re-insertion. there isn't
|
||||
// enough data to de-dupe them for an insert/update operation
|
||||
List<String> freshCommentIds = new ArrayList<String>();
|
||||
for (Story story : apiResponse.stories) {
|
||||
for (Comment comment : story.publicComments) {
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
// handle comments
|
||||
List<ContentValues> commentValues = new ArrayList<ContentValues>();
|
||||
List<ContentValues> replyValues = new ArrayList<ContentValues>();
|
||||
// track which comments were seen, so replies can be cleared before re-insertion. there isn't
|
||||
// enough data to de-dupe them for an insert/update operation
|
||||
List<String> freshCommentIds = new ArrayList<String>();
|
||||
for (Story story : apiResponse.stories) {
|
||||
for (Comment comment : story.publicComments) {
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsComments) {
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsShares) {
|
||||
comment.isPseudo = true;
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsComments) {
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsShares) {
|
||||
comment.isPseudo = true;
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
}
|
||||
deleteRepliesForComments(freshCommentIds);
|
||||
bulkInsertValues(DatabaseConstants.COMMENT_TABLE, commentValues);
|
||||
bulkInsertValues(DatabaseConstants.REPLY_TABLE, replyValues);
|
||||
}
|
||||
|
||||
private void deleteRepliesForComments(Collection<String> commentIds) {
|
||||
// NB: attempting to do this with a "WHERE col IN (vector)" for speed can cause errors on some versions of sqlite
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
for (String commentId : commentIds) {
|
||||
// before inserting new replies, remove existing ones for the fetched comments
|
||||
// NB: attempting to do this with a "WHERE col IN (vector)" for speed can cause errors on some versions of sqlite
|
||||
for (String commentId : freshCommentIds) {
|
||||
dbRW.delete(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_COMMENTID + " = ?", new String[]{commentId});
|
||||
}
|
||||
bulkInsertValuesExtSync(DatabaseConstants.COMMENT_TABLE, commentValues);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.REPLY_TABLE, replyValues);
|
||||
|
||||
dbRW.setTransactionSuccessful();
|
||||
} finally {
|
||||
dbRW.endTransaction();
|
||||
|
@ -498,16 +481,15 @@ public class BlurDatabaseHelper {
|
|||
// update the story's read state
|
||||
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[]{story.storyHash});
|
||||
// which column to inc/dec depends on story intel
|
||||
String impactedCol;
|
||||
String impactedSocialCol;
|
||||
if (story.intelTotal < 0) {
|
||||
if (story.intelligence.calcTotalIntel() < 0) {
|
||||
// negative stories don't affect counts
|
||||
dbRW.setTransactionSuccessful();
|
||||
return impactedFeeds;
|
||||
} else if (story.intelTotal == 0 ) {
|
||||
} else if (story.intelligence.calcTotalIntel() == 0 ) {
|
||||
impactedCol = DatabaseConstants.FEED_NEUTRAL_COUNT;
|
||||
impactedSocialCol = DatabaseConstants.SOCIAL_FEED_NEUTRAL_COUNT;
|
||||
} else {
|
||||
|
@ -672,7 +654,11 @@ 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);
|
||||
StringBuilder sel = new StringBuilder();
|
||||
ArrayList<String> selArgs = new ArrayList<String>();
|
||||
getLocalStorySelectionAndArgs(sel, selArgs, fs, stateFilter, ReadFilter.UNREAD);
|
||||
|
||||
Cursor c = dbRO.rawQuery(sel.toString(), selArgs.toArray(new String[selArgs.size()]));
|
||||
int count = c.getCount();
|
||||
c.close();
|
||||
return count;
|
||||
|
@ -796,41 +782,6 @@ public class BlurDatabaseHelper {
|
|||
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.STORY_TEXT_TABLE, null, values);}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_STARRED + " = 1", null);}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the read_this_session and search_hit flags for all stories.
|
||||
*/
|
||||
public void clearReadingSession() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, false);
|
||||
values.put(DatabaseConstants.STORY_SEARCHIT, false);
|
||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, null, null);}
|
||||
}
|
||||
|
||||
public void markStoriesActive(NBSyncService.ActivationMode actMode, long modeCutoff) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_ACTIVE, true);
|
||||
|
||||
String selection = null;
|
||||
if (actMode == NBSyncService.ActivationMode.ALL) {
|
||||
// leave the selection null to mark all
|
||||
} else if (actMode == NBSyncService.ActivationMode.OLDER) {
|
||||
selection = DatabaseConstants.STORY_TIMESTAMP + " <= " + Long.toString(modeCutoff);
|
||||
} else if (actMode == NBSyncService.ActivationMode.NEWER) {
|
||||
selection = DatabaseConstants.STORY_TIMESTAMP + " >= " + Long.toString(modeCutoff);
|
||||
}
|
||||
|
||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, selection, null);}
|
||||
}
|
||||
|
||||
public Loader<Cursor> getSocialFeedsLoader(final StateFilter stateFilter) {
|
||||
return new QueryCursorLoader(context) {
|
||||
protected Cursor createCursor() {return getSocialFeedsCursor(stateFilter, cancellationSignal);}
|
||||
|
@ -891,112 +842,118 @@ 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> getActiveStoriesLoader(final FeedSet fs) {
|
||||
final StoryOrder order = PrefsUtils.getStoryOrder(context, fs);
|
||||
return new QueryCursorLoader(context) {
|
||||
protected Cursor createCursor() {
|
||||
ReadFilter readFilter = PrefsUtils.getReadFilter(context, fs);
|
||||
return getStoriesCursor(fs, stateFilter, readFilter, cancellationSignal);
|
||||
return getActiveStoriesCursor(fs, order, cancellationSignal);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Cursor getActiveStoriesCursor(FeedSet fs, StoryOrder order, CancellationSignal cancellationSignal) {
|
||||
// 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);
|
||||
|
||||
if (fs.isAllRead()) {
|
||||
q.append(" ORDER BY " + DatabaseConstants.READ_STORY_ORDER);
|
||||
} else if (fs.isAllSaved()) {
|
||||
q.append(" ORDER BY " + DatabaseConstants.getSavedStoriesSortOrder(order));
|
||||
} else {
|
||||
q.append(" ORDER BY ").append(DatabaseConstants.getStorySortOrder(order));
|
||||
}
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
}
|
||||
|
||||
public void clearStorySession() {
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.READING_SESSION_TABLE, null, null);}
|
||||
}
|
||||
|
||||
public void prepareReadingSession(FeedSet fs, StateFilter stateFilter) {
|
||||
ReadFilter readFilter = PrefsUtils.getReadFilter(context, fs);
|
||||
prepareReadingSession(fs, stateFilter, readFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Populates the reading session table with hashes of already-fetched stories that meet the
|
||||
* criteria for the given FeedSet and filters; these hashes will be supplemented by hashes
|
||||
* fetched via the API and used to actually select story data when rendering story lists.
|
||||
*/
|
||||
public Loader<Cursor> getStoriesLoaderIgnoreFilters(final FeedSet fs) {
|
||||
return new QueryCursorLoader(context) {
|
||||
protected Cursor createCursor() {return getStoriesCursor(fs, StateFilter.ALL, ReadFilter.ALL, cancellationSignal);}
|
||||
};
|
||||
private void prepareReadingSession(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter) {
|
||||
// a selection filter that will be used to pull active story hashes from the stories table into the reading session table
|
||||
StringBuilder sel = new StringBuilder();
|
||||
// any selection args that need to be used within the inner select statement
|
||||
ArrayList<String> selArgs = new ArrayList<String>();
|
||||
|
||||
getLocalStorySelectionAndArgs(sel, selArgs, fs, stateFilter, readFilter);
|
||||
|
||||
// use the inner select statement to push the active hashes into the session table
|
||||
StringBuilder q = new StringBuilder("INSERT INTO " + DatabaseConstants.READING_SESSION_TABLE);
|
||||
q.append(" (" + DatabaseConstants.READING_SESSION_STORY_HASH + ") ");
|
||||
q.append(sel);
|
||||
|
||||
dbRW.execSQL(q.toString(), selArgs.toArray(new String[selArgs.size()]));
|
||||
}
|
||||
|
||||
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, CancellationSignal cancellationSignal) {
|
||||
if (fs == null) return null;
|
||||
StoryOrder order = PrefsUtils.getStoryOrder(context, fs);
|
||||
return getStoriesCursor(fs, stateFilter, readFilter, order, cancellationSignal);
|
||||
}
|
||||
|
||||
private Cursor getStoriesCursor(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter, StoryOrder order, CancellationSignal cancellationSignal) {
|
||||
if (fs == null) return null;
|
||||
|
||||
/**
|
||||
* Gets hashes of already-fetched stories that satisfy the given FeedSet and filters. Can be used
|
||||
* both to populate a reading session or to count local unreads.
|
||||
*/
|
||||
private void getLocalStorySelectionAndArgs(StringBuilder sel, List<String> selArgs, FeedSet fs, StateFilter stateFilter, ReadFilter readFilter) {
|
||||
sel.append("SELECT " + DatabaseConstants.STORY_HASH);
|
||||
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, (fs.getSearchQuery() != null));
|
||||
return rawQuery(q.toString(), new String[]{fs.getSingleFeed()}, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
||||
sel.append(" WHERE " + DatabaseConstants.STORY_FEED_ID + " = ?");
|
||||
selArgs.add(fs.getSingleFeed());
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} 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, (fs.getSearchQuery() != null));
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
||||
sel.append(" WHERE " + DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_FEED_ID + " IN ( ");
|
||||
sel.append(TextUtils.join(",", fs.getMultipleFeeds()) + ")");
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} 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, (fs.getSearchQuery() != null));
|
||||
return rawQuery(q.toString(), new String[]{fs.getSingleSocialFeed().getKey()}, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
|
||||
sel.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
|
||||
sel.append(" WHERE " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE + "." + DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ? ");
|
||||
selArgs.add(fs.getSingleSocialFeed().getKey());
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} 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, (fs.getSearchQuery() != null));
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
||||
sel.append(" WHERE 1");
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} 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);
|
||||
q.append(DatabaseConstants.JOIN_SOCIAL_FEEDS_ON_SOCIALFEED_MAP);
|
||||
DatabaseConstants.appendStorySelectionGroupOrder(q, readFilter, order, stateFilter, DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_ID, false);
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
|
||||
sel.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} else if (fs.isAllRead()) {
|
||||
|
||||
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_LAST_READ_DATE + " > 0)");
|
||||
q.append(" ORDER BY " + DatabaseConstants.READ_STORY_ORDER);
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
||||
sel.append(" WHERE (" + DatabaseConstants.STORY_LAST_READ_DATE + " > 0)");
|
||||
|
||||
} 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);
|
||||
q.append(" WHERE ((" + DatabaseConstants.STORY_STARRED + " = 1)");
|
||||
q.append(" OR (" + DatabaseConstants.STORY_READ_THIS_SESSION + " = 1))");
|
||||
if (fs.getSearchQuery() != null) {
|
||||
q.append(" AND (" + DatabaseConstants.STORY_TABLE + "." + DatabaseConstants.STORY_SEARCHIT + " = 1)");
|
||||
}
|
||||
q.append(" ORDER BY " + DatabaseConstants.getSavedStoriesSortOrder(order));
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.STORY_TABLE);
|
||||
sel.append(" WHERE (" + DatabaseConstants.STORY_STARRED + " = 1)");
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, ReadFilter.ALL, StateFilter.ALL, fs.getSearchQuery());
|
||||
|
||||
} else if (fs.isGlobalShared()) {
|
||||
|
||||
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, false);
|
||||
return rawQuery(q.toString(), null, cancellationSignal);
|
||||
sel.append(" FROM " + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
|
||||
sel.append(DatabaseConstants.JOIN_STORIES_ON_SOCIALFEED_MAP);
|
||||
DatabaseConstants.appendStorySelection(sel, selArgs, readFilter, stateFilter, fs.getSearchQuery());
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Asked to get stories for FeedSet of unknown type.");
|
||||
}
|
||||
|
|
|
@ -77,31 +77,29 @@ public class DatabaseConstants {
|
|||
public static final String STORY_SHARED_DATE = "sharedDate";
|
||||
public static final String STORY_CONTENT = "content";
|
||||
public static final String STORY_SHORT_CONTENT = "short_content";
|
||||
public static final String STORY_COMMENT_COUNT = "comment_count";
|
||||
public static final String STORY_FEED_ID = "feed_id";
|
||||
public static final String STORY_INTELLIGENCE_AUTHORS = "intelligence_authors";
|
||||
public static final String STORY_INTELLIGENCE_TAGS = "intelligence_tags";
|
||||
public static final String STORY_INTELLIGENCE_FEED = "intelligence_feed";
|
||||
public static final String STORY_INTELLIGENCE_TITLE = "intelligence_title";
|
||||
public static final String STORY_INTELLIGENCE_TOTAL = "intelligence_total";
|
||||
public static final String STORY_PERMALINK = "permalink";
|
||||
public static final String STORY_READ = "read";
|
||||
public static final String STORY_READ_THIS_SESSION = "read_this_session";
|
||||
public static final String STORY_STARRED = "starred";
|
||||
public static final String STORY_STARRED_DATE = "starred_date";
|
||||
public static final String STORY_SHARE_COUNT = "share_count";
|
||||
public static final String STORY_SHARED_USER_IDS = "shared_user_ids";
|
||||
public static final String STORY_FRIEND_USER_IDS = "comment_user_ids";
|
||||
public static final String STORY_PUBLIC_USER_IDS = "public_user_ids";
|
||||
public static final String STORY_SHORTDATE = "shortDate";
|
||||
public static final String STORY_LONGDATE = "longDate";
|
||||
public static final String STORY_SOCIAL_USER_ID = "socialUserId";
|
||||
public static final String STORY_SOURCE_USER_ID = "sourceUserId";
|
||||
public static final String STORY_TAGS = "tags";
|
||||
public static final String STORY_HASH = "story_hash";
|
||||
public static final String STORY_ACTIVE = "active";
|
||||
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_SEARCH_HIT = "search_hit";
|
||||
|
||||
public static final String READING_SESSION_TABLE = "reading_session";
|
||||
public static final String READING_SESSION_STORY_HASH = "session_story_hash";
|
||||
|
||||
public static final String STORY_TEXT_TABLE = "storytext";
|
||||
public static final String STORY_TEXT_STORY_HASH = "story_hash";
|
||||
|
@ -213,38 +211,37 @@ public class DatabaseConstants {
|
|||
")";
|
||||
|
||||
static final String STORY_SQL = "CREATE TABLE " + STORY_TABLE + " (" +
|
||||
STORY_HASH + TEXT + ", " +
|
||||
STORY_HASH + TEXT + " PRIMARY KEY, " +
|
||||
STORY_AUTHORS + TEXT + ", " +
|
||||
STORY_CONTENT + TEXT + ", " +
|
||||
STORY_SHORT_CONTENT + TEXT + ", " +
|
||||
STORY_TIMESTAMP + INTEGER + ", " +
|
||||
STORY_SHARED_DATE + INTEGER + ", " +
|
||||
STORY_SHORTDATE + TEXT + ", " +
|
||||
STORY_LONGDATE + TEXT + ", " +
|
||||
STORY_FEED_ID + INTEGER + ", " +
|
||||
STORY_ID + TEXT + " PRIMARY KEY, " +
|
||||
STORY_ID + TEXT + ", " +
|
||||
STORY_INTELLIGENCE_AUTHORS + INTEGER + ", " +
|
||||
STORY_INTELLIGENCE_FEED + INTEGER + ", " +
|
||||
STORY_INTELLIGENCE_TAGS + INTEGER + ", " +
|
||||
STORY_INTELLIGENCE_TITLE + INTEGER + ", " +
|
||||
STORY_COMMENT_COUNT + INTEGER + ", " +
|
||||
STORY_SHARE_COUNT + INTEGER + ", " +
|
||||
STORY_INTELLIGENCE_TOTAL + INTEGER + ", " +
|
||||
STORY_SOCIAL_USER_ID + TEXT + ", " +
|
||||
STORY_SOURCE_USER_ID + TEXT + ", " +
|
||||
STORY_SHARED_USER_IDS + TEXT + ", " +
|
||||
STORY_PUBLIC_USER_IDS + TEXT + ", " +
|
||||
STORY_FRIEND_USER_IDS + TEXT + ", " +
|
||||
STORY_TAGS + TEXT + ", " +
|
||||
STORY_PERMALINK + TEXT + ", " +
|
||||
STORY_READ + INTEGER + ", " +
|
||||
STORY_READ_THIS_SESSION + INTEGER + ", " +
|
||||
STORY_STARRED + INTEGER + ", " +
|
||||
STORY_STARRED_DATE + INTEGER + ", " +
|
||||
STORY_TITLE + TEXT + ", " +
|
||||
STORY_ACTIVE + INTEGER + " DEFAULT 0, " +
|
||||
STORY_IMAGE_URLS + TEXT + ", " +
|
||||
STORY_LAST_READ_DATE + INTEGER + ", " +
|
||||
STORY_SEARCHIT + INTEGER + " DEFAULT 0" +
|
||||
STORY_SEARCH_HIT + TEXT +
|
||||
")";
|
||||
|
||||
static final String READING_SESSION_SQL = "CREATE TABLE " + READING_SESSION_TABLE + " (" +
|
||||
READING_SESSION_STORY_HASH + TEXT +
|
||||
")";
|
||||
|
||||
static final String STORY_TEXT_SQL = "CREATE TABLE " + STORY_TEXT_TABLE + " (" +
|
||||
|
@ -300,53 +297,41 @@ public class DatabaseConstants {
|
|||
SOCIAL_FEED_ID, SOCIAL_FEED_USERNAME, SOCIAL_FEED_TITLE, SOCIAL_FEED_ICON, SOCIAL_FEED_POSITIVE_COUNT, SOCIAL_FEED_NEUTRAL_COUNT, SOCIAL_FEED_NEGATIVE_COUNT,
|
||||
};
|
||||
|
||||
public static final String SUM_STORY_TOTAL = "storyTotal";
|
||||
private static String STORY_SUM_TOTAL = " CASE " +
|
||||
"WHEN MAX(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") > 0 " +
|
||||
"THEN MAX(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") " +
|
||||
"WHEN MIN(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") < 0 " +
|
||||
"THEN MIN(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") " +
|
||||
"ELSE " + STORY_INTELLIGENCE_FEED + " " +
|
||||
"END AS " + SUM_STORY_TOTAL;
|
||||
private static final String STORY_INTELLIGENCE_BEST = SUM_STORY_TOTAL + " > 0 ";
|
||||
private static final String STORY_INTELLIGENCE_SOME = SUM_STORY_TOTAL + " >= 0 ";
|
||||
private static final String STORY_INTELLIGENCE_NEUT = SUM_STORY_TOTAL + " = 0 ";
|
||||
private static final String STORY_INTELLIGENCE_NEG = SUM_STORY_TOTAL + " < 0 ";
|
||||
|
||||
public static final String[] STORY_COLUMNS = {
|
||||
STORY_AUTHORS, STORY_COMMENT_COUNT, STORY_SHORT_CONTENT, STORY_TIMESTAMP, STORY_SHARED_DATE, STORY_SHORTDATE, STORY_LONGDATE,
|
||||
STORY_TABLE + "." + STORY_FEED_ID, STORY_TABLE + "." + STORY_ID, STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS,
|
||||
STORY_INTELLIGENCE_TITLE, STORY_PERMALINK, STORY_READ, STORY_STARRED, STORY_STARRED_DATE, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE,
|
||||
STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH,
|
||||
STORY_LAST_READ_DATE, STORY_SEARCHIT,
|
||||
private static final String[] BASE_STORY_COLUMNS = {
|
||||
STORY_AUTHORS, STORY_SHORT_CONTENT, STORY_TIMESTAMP, STORY_SHARED_DATE, STORY_LONGDATE,
|
||||
STORY_TABLE + "." + STORY_FEED_ID, STORY_TABLE + "." + STORY_ID,
|
||||
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_TITLE,
|
||||
STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_HASH,
|
||||
STORY_LAST_READ_DATE,
|
||||
};
|
||||
|
||||
public static final String MULTIFEED_STORIES_QUERY_BASE =
|
||||
"SELECT " + TextUtils.join(",", STORY_COLUMNS) + ", " +
|
||||
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 JOIN_FEEDS_ON_STORIES =
|
||||
" INNER JOIN " + FEED_TABLE + " ON " + STORY_TABLE + "." + STORY_FEED_ID + " = " + FEED_TABLE + "." + FEED_ID;
|
||||
public static final String STORY_QUERY_BASE =
|
||||
"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 + ")" +
|
||||
" GROUP BY " + STORY_HASH;
|
||||
|
||||
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;
|
||||
|
||||
public static final String JOIN_SOCIAL_FEEDS_ON_SOCIALFEED_MAP =
|
||||
" INNER JOIN " + SOCIALFEED_TABLE + " ON " + SOCIALFEED_TABLE + "." + SOCIAL_FEED_ID + " = " + SOCIALFEED_STORY_MAP_TABLE + "." + SOCIALFEED_STORY_USER_ID;
|
||||
|
||||
public static final String READ_STORY_ORDER = STORY_LAST_READ_DATE + " DESC";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* filtration parameters.
|
||||
*/
|
||||
public static void appendStorySelectionGroupOrder(StringBuilder q, ReadFilter readFilter, StoryOrder order, StateFilter stateFilter, String dedupCol, boolean requireQueryHit) {
|
||||
public static void appendStorySelection(StringBuilder q, List<String> selArgs, ReadFilter readFilter, StateFilter stateFilter, String requireQueryHit) {
|
||||
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!
|
||||
q.append(" AND ((" + STORY_READ + " = 0) OR (" + STORY_READ_THIS_SESSION + " = 1))");
|
||||
} else if (readFilter == ReadFilter.PURE_UNREAD) {
|
||||
// This means really just unreads, useful for getting counts
|
||||
q.append(" AND (" + STORY_READ + " = 0)");
|
||||
}
|
||||
|
||||
|
@ -355,18 +340,9 @@ public class DatabaseConstants {
|
|||
q.append(" AND " + stateSelection);
|
||||
}
|
||||
|
||||
if (requireQueryHit) {
|
||||
q.append(" AND (" + STORY_TABLE + "." + STORY_SEARCHIT + " = 1)");
|
||||
}
|
||||
|
||||
q.append(" AND (" + STORY_TABLE + "." + STORY_ACTIVE + " = 1)");
|
||||
|
||||
if (dedupCol != null) {
|
||||
q.append( " GROUP BY " + dedupCol);
|
||||
}
|
||||
|
||||
if (order != null) {
|
||||
q.append(" ORDER BY " + getStorySortOrder(order));
|
||||
if (requireQueryHit != null) {
|
||||
q.append(" AND (" + STORY_TABLE + "." + STORY_SEARCH_HIT + " = ?)");
|
||||
selArgs.add(requireQueryHit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,13 +354,13 @@ public class DatabaseConstants {
|
|||
case ALL:
|
||||
return null;
|
||||
case SOME:
|
||||
return STORY_INTELLIGENCE_SOME;
|
||||
return STORY_INTELLIGENCE_TOTAL + " >= 0 ";
|
||||
case NEUT:
|
||||
return STORY_INTELLIGENCE_NEUT;
|
||||
return STORY_INTELLIGENCE_TOTAL + " = 0 ";
|
||||
case BEST:
|
||||
return STORY_INTELLIGENCE_BEST;
|
||||
return STORY_INTELLIGENCE_TOTAL + " > 0 ";
|
||||
case NEG:
|
||||
return STORY_INTELLIGENCE_NEG;
|
||||
return STORY_INTELLIGENCE_TOTAL + " < 0 ";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import android.database.Cursor;
|
|||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.util.StateFilter;
|
||||
|
||||
public class Story implements Serializable {
|
||||
|
||||
|
@ -18,21 +20,12 @@ public class Story implements Serializable {
|
|||
@SerializedName("story_permalink")
|
||||
public String permalink;
|
||||
|
||||
@SerializedName("share_count")
|
||||
public String shareCount;
|
||||
|
||||
@SerializedName("share_user_ids")
|
||||
public String[] sharedUserIds;
|
||||
|
||||
@SerializedName("shared_by_friends")
|
||||
public String[] friendUserIds = new String[]{};
|
||||
|
||||
@SerializedName("shared_by_public")
|
||||
public String[] publicUserIds = new String[]{};
|
||||
|
||||
@SerializedName("comment_count")
|
||||
public int commentCount;
|
||||
|
||||
@SerializedName("read_status")
|
||||
public boolean read;
|
||||
|
||||
|
@ -83,11 +76,6 @@ public class Story implements Serializable {
|
|||
@SerializedName("intelligence")
|
||||
public Intelligence intelligence = new Intelligence();
|
||||
|
||||
public int intelTotal;
|
||||
|
||||
@SerializedName("short_parsed_date")
|
||||
public String shortDate;
|
||||
|
||||
@SerializedName("long_parsed_date")
|
||||
public String longDate;
|
||||
|
||||
|
@ -101,30 +89,28 @@ public class Story implements Serializable {
|
|||
// not yet vended by the API, but tracked locally and fudged (see SyncService) for remote stories
|
||||
public long lastReadTimestamp = 0L;
|
||||
|
||||
public boolean isSearchHit = false;
|
||||
// non-API and only set once when story is pushed to DB so it can be selected upon
|
||||
public String searchHit = "";
|
||||
|
||||
public ContentValues getValues() {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_ID, id);
|
||||
values.put(DatabaseConstants.STORY_TITLE, title.replace("\n", " ").replace("\r", " "));
|
||||
values.put(DatabaseConstants.STORY_TIMESTAMP, timestamp);
|
||||
values.put(DatabaseConstants.STORY_SHORTDATE, shortDate);
|
||||
values.put(DatabaseConstants.STORY_LONGDATE, longDate);
|
||||
values.put(DatabaseConstants.STORY_CONTENT, content);
|
||||
values.put(DatabaseConstants.STORY_SHORT_CONTENT, shortContent);
|
||||
values.put(DatabaseConstants.STORY_PERMALINK, permalink);
|
||||
values.put(DatabaseConstants.STORY_COMMENT_COUNT, commentCount);
|
||||
values.put(DatabaseConstants.STORY_SHARE_COUNT, shareCount);
|
||||
values.put(DatabaseConstants.STORY_AUTHORS, authors);
|
||||
values.put(DatabaseConstants.STORY_SOCIAL_USER_ID, socialUserId);
|
||||
values.put(DatabaseConstants.STORY_SOURCE_USER_ID, sourceUserId);
|
||||
values.put(DatabaseConstants.STORY_SHARED_USER_IDS, TextUtils.join(",", sharedUserIds));
|
||||
values.put(DatabaseConstants.STORY_FRIEND_USER_IDS, TextUtils.join(",", friendUserIds));
|
||||
values.put(DatabaseConstants.STORY_PUBLIC_USER_IDS, TextUtils.join(",", publicUserIds));
|
||||
values.put(DatabaseConstants.STORY_INTELLIGENCE_AUTHORS, intelligence.intelligenceAuthors);
|
||||
values.put(DatabaseConstants.STORY_INTELLIGENCE_FEED, intelligence.intelligenceFeed);
|
||||
values.put(DatabaseConstants.STORY_INTELLIGENCE_TAGS, intelligence.intelligenceTags);
|
||||
values.put(DatabaseConstants.STORY_INTELLIGENCE_TITLE, intelligence.intelligenceTitle);
|
||||
values.put(DatabaseConstants.STORY_INTELLIGENCE_TOTAL, intelligence.calcTotalIntel());
|
||||
values.put(DatabaseConstants.STORY_TAGS, TextUtils.join(",", tags));
|
||||
values.put(DatabaseConstants.STORY_READ, read);
|
||||
values.put(DatabaseConstants.STORY_STARRED, starred);
|
||||
|
@ -133,7 +119,7 @@ public class Story implements Serializable {
|
|||
values.put(DatabaseConstants.STORY_HASH, storyHash);
|
||||
values.put(DatabaseConstants.STORY_IMAGE_URLS, TextUtils.join(",", imageUrls));
|
||||
values.put(DatabaseConstants.STORY_LAST_READ_DATE, lastReadTimestamp);
|
||||
values.put(DatabaseConstants.STORY_SEARCHIT, isSearchHit);
|
||||
values.put(DatabaseConstants.STORY_SEARCH_HIT, searchHit);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -146,21 +132,16 @@ public class Story implements Serializable {
|
|||
story.shortContent = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SHORT_CONTENT));
|
||||
story.title = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_TITLE));
|
||||
story.timestamp = cursor.getLong(cursor.getColumnIndex(DatabaseConstants.STORY_TIMESTAMP));
|
||||
story.shortDate = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SHORTDATE));
|
||||
story.longDate = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_LONGDATE));
|
||||
story.shareCount = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SHARE_COUNT));
|
||||
story.commentCount = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_COMMENT_COUNT));
|
||||
story.socialUserId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SOCIAL_USER_ID));
|
||||
story.sourceUserId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SOURCE_USER_ID));
|
||||
story.permalink = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_PERMALINK));
|
||||
story.sharedUserIds = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_SHARED_USER_IDS)), ",");
|
||||
story.friendUserIds = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_FRIEND_USER_IDS)), ",");
|
||||
story.publicUserIds = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_PUBLIC_USER_IDS)), ",");
|
||||
story.intelligence.intelligenceAuthors = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_AUTHORS));
|
||||
story.intelligence.intelligenceFeed = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_FEED));
|
||||
story.intelligence.intelligenceTags = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_TAGS));
|
||||
story.intelligence.intelligenceTitle = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_TITLE));
|
||||
story.intelTotal = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.SUM_STORY_TOTAL));
|
||||
story.read = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_READ)) > 0;
|
||||
story.starred = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_STARRED)) > 0;
|
||||
story.starredTimestamp = cursor.getLong(cursor.getColumnIndex(DatabaseConstants.STORY_STARRED_DATE));
|
||||
|
@ -169,7 +150,6 @@ public class Story implements Serializable {
|
|||
story.id = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_ID));
|
||||
story.storyHash = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_HASH));
|
||||
story.lastReadTimestamp = cursor.getLong(cursor.getColumnIndex(DatabaseConstants.STORY_LAST_READ_DATE));
|
||||
story.isSearchHit = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_SEARCHIT)) > 0;
|
||||
return story;
|
||||
}
|
||||
|
||||
|
@ -187,8 +167,42 @@ public class Story implements Serializable {
|
|||
|
||||
@SerializedName("title")
|
||||
public int intelligenceTitle = 0;
|
||||
|
||||
public int calcTotalIntel() {
|
||||
int max = 0;
|
||||
max = Math.max(max, intelligenceAuthors);
|
||||
max = Math.max(max, intelligenceTags);
|
||||
max = Math.max(max, intelligenceTitle);
|
||||
if (max > 0) return max;
|
||||
|
||||
int min = 0;
|
||||
min = Math.min(min, intelligenceAuthors);
|
||||
min = Math.min(min, intelligenceTags);
|
||||
min = Math.min(min, intelligenceTitle);
|
||||
if (min < 0) return min;
|
||||
|
||||
return intelligenceFeed;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStoryVisibileInState(StateFilter state) {
|
||||
int score = intelligence.calcTotalIntel();
|
||||
switch (state) {
|
||||
case ALL:
|
||||
return true;
|
||||
case SOME:
|
||||
return (score >= 0);
|
||||
case NEUT:
|
||||
return (score == 0);
|
||||
case BEST:
|
||||
return (score > 0);
|
||||
case NEG:
|
||||
return (score < 0);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom equality based on storyID/feedID equality so that a Set can de-duplicate story objects.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -40,7 +41,7 @@ public class ValueMultimap implements Serializable {
|
|||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(key);
|
||||
builder.append("=");
|
||||
builder.append(value);
|
||||
builder.append(URLEncoder.encode(value));
|
||||
parameters.add(builder.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public class AllSharedStoriesItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_feedtitle };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity()));
|
||||
|
|
|
@ -14,7 +14,7 @@ public class AllStoriesItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_feedtitle };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity()));
|
||||
|
|
|
@ -31,7 +31,7 @@ public class FeedItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar };
|
||||
adapter = new FeedItemsAdapter(getActivity(), feed, R.layout.row_item, cursor, groupFrom, groupTo);
|
||||
adapter.setViewBinder(new FeedItemViewBinder(getActivity()));
|
||||
|
|
|
@ -21,7 +21,7 @@ public class FolderItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.FEED_TITLE, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.STORY_AUTHORS };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.FEED_TITLE, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.STORY_AUTHORS };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_feedtitle, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_author };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo);
|
||||
adapter.setViewBinder(new FeedItemViewBinder(getActivity()));
|
||||
|
|
|
@ -39,6 +39,7 @@ import com.newsblur.domain.SocialFeed;
|
|||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.MarkAllReadConfirmation;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.StateFilter;
|
||||
|
@ -211,6 +212,8 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
case ExpandableListView.PACKED_POSITION_TYPE_GROUP:
|
||||
if (adapter.isRowSavedStories(groupPosition)) break;
|
||||
if (adapter.isRowReadStories(groupPosition)) break;
|
||||
if (groupPosition == FolderListAdapter.GLOBAL_SHARED_STORIES_GROUP_POSITION) break;
|
||||
if (groupPosition == FolderListAdapter.ALL_SHARED_STORIES_GROUP_POSITION) break;
|
||||
inflater.inflate(R.menu.context_folder, menu);
|
||||
break;
|
||||
|
||||
|
@ -244,20 +247,25 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_mark_feed_as_read) {
|
||||
String feedId = adapter.getChild(groupPosition, childPosition);
|
||||
FeedSet fs = null;
|
||||
if (groupPosition == FolderListAdapter.ALL_SHARED_STORIES_GROUP_POSITION) {
|
||||
SocialFeed socialFeed = adapter.getSocialFeed(feedId);
|
||||
FeedUtils.markFeedsRead(FeedSet.singleSocialFeed(socialFeed.userId, socialFeed.username), null, null, getActivity());
|
||||
fs = FeedSet.singleSocialFeed(socialFeed.userId, socialFeed.username);
|
||||
} else {
|
||||
FeedUtils.markFeedsRead(FeedSet.singleFeed(feedId), null, null, getActivity());
|
||||
fs = FeedSet.singleFeed(feedId);
|
||||
}
|
||||
|
||||
markFeedsAsRead(fs);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_mark_folder_as_read) {
|
||||
if (!adapter.isFolderRoot(groupPosition)) {
|
||||
FeedSet fs = null;
|
||||
if (!adapter.isFolderRoot(groupPosition)) {
|
||||
String folderName = adapter.getGroup(groupPosition);
|
||||
FeedUtils.markFeedsRead(FeedUtils.feedSetFromFolderName(folderName), null, null, getActivity());
|
||||
fs = FeedUtils.feedSetFromFolderName(folderName);
|
||||
} else {
|
||||
FeedUtils.markFeedsRead(FeedSet.allFeeds(), null, null, getActivity());
|
||||
fs = FeedSet.allFeeds();
|
||||
}
|
||||
markFeedsAsRead(fs);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_choose_folders) {
|
||||
DialogFragment chooseFoldersFragment = ChooseFoldersFragment.newInstance(adapter.getFeed(adapter.getChild(groupPosition, childPosition)));
|
||||
|
@ -267,6 +275,16 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
private void markFeedsAsRead(FeedSet fs) {
|
||||
MarkAllReadConfirmation confirmation = PrefsUtils.getMarkAllReadConfirmation(getActivity());
|
||||
if (confirmation.feedSetRequiresConfirmation(fs)) {
|
||||
MarkAllReadDialogFragment dialog = MarkAllReadDialogFragment.newInstance(fs);
|
||||
dialog.show(getFragmentManager(), "dialog");
|
||||
} else {
|
||||
FeedUtils.markFeedsRead(fs, null, null, getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
public void changeState(StateFilter state) {
|
||||
currentState = state;
|
||||
PrefsUtils.setStateFilter(getActivity(), state);
|
||||
|
|
|
@ -24,7 +24,7 @@ public class GlobalSharedStoriesItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_feedtitle };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo, true);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity(), true));
|
||||
|
|
|
@ -55,7 +55,6 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
protected DefaultFeedView defaultFeedView;
|
||||
protected StateFilter intelState;
|
||||
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)
|
||||
|
@ -124,10 +123,15 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
super.onActivityCreated(savedInstanceState);
|
||||
stopLoading = false;
|
||||
if (getLoaderManager().getLoader(ITEMLIST_LOADER) == null) {
|
||||
initReadingSession();
|
||||
getLoaderManager().initLoader(ITEMLIST_LOADER, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void initReadingSession() {
|
||||
FeedUtils.prepareReadingSession(getFeedSet(), intelState);
|
||||
}
|
||||
|
||||
private void triggerRefresh(int desiredStoryCount, int totalSeen) {
|
||||
boolean gotSome = NBSyncService.requestMoreForFeed(getFeedSet(), desiredStoryCount, totalSeen);
|
||||
if (gotSome) triggerSync();
|
||||
|
@ -145,7 +149,8 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
*/
|
||||
public void resetEmptyState() {
|
||||
cursorSeenYet = false;
|
||||
firstStorySeenYet = false;
|
||||
FeedUtils.dbHelper.clearStorySession();
|
||||
initReadingSession();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,7 +235,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.getActiveStoriesLoader(getFeedSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -240,20 +245,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 +335,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());
|
||||
}
|
||||
|
||||
protected void setupBezelSwipeDetector(View v) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
@ -10,23 +12,22 @@ import android.os.Bundle;
|
|||
import android.app.DialogFragment;
|
||||
|
||||
public class MarkAllReadDialogFragment extends DialogFragment {
|
||||
private static final String FOLDER_NAME = "folder_name";
|
||||
private static final String FEED_SET = "feed_set";
|
||||
|
||||
public interface MarkAllReadDialogListener {
|
||||
public void onMarkAllRead();
|
||||
public void onCancel();
|
||||
void onMarkAllRead(FeedSet feedSet);
|
||||
}
|
||||
|
||||
private MarkAllReadDialogListener listener;
|
||||
|
||||
public static MarkAllReadDialogFragment newInstance(String folderName) {
|
||||
public static MarkAllReadDialogFragment newInstance(FeedSet feedSet) {
|
||||
MarkAllReadDialogFragment fragment = new MarkAllReadDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(FOLDER_NAME, folderName);
|
||||
args.putSerializable(FEED_SET, feedSet);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
@ -36,15 +37,25 @@ public class MarkAllReadDialogFragment extends DialogFragment {
|
|||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(getArguments().getString(FOLDER_NAME))
|
||||
|
||||
final FeedSet feedSet = (FeedSet)getArguments().getSerializable(FEED_SET);
|
||||
String title = null;
|
||||
if (feedSet.isAllNormal()) {
|
||||
title = getResources().getString(R.string.all_stories);
|
||||
} else if (feedSet.isFolder()) {
|
||||
title = feedSet.getFolderName();
|
||||
} else if (feedSet.isSingleSocial()) {
|
||||
title = FeedUtils.getSocialFeed(feedSet.getSingleSocialFeed().getKey()).feedTitle;
|
||||
} else {
|
||||
title = FeedUtils.getFeed(feedSet.getSingleFeed()).title;
|
||||
}
|
||||
|
||||
builder.setTitle(title)
|
||||
.setItems(R.array.mark_all_read_options, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == 0) {
|
||||
listener.onMarkAllRead();
|
||||
} else {
|
||||
listener.onCancel();
|
||||
listener.onMarkAllRead(feedSet);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
|
|
|
@ -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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ReadStoriesItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_feedtitle };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity(), true));
|
||||
|
|
|
@ -30,15 +30,13 @@ public class SavedStoriesItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.SUM_STORY_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
String[] groupFrom = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_INTELLIGENCE_TOTAL, DatabaseConstants.FEED_TITLE };
|
||||
int[] groupTo = new int[] { R.id.row_item_title, R.id.row_item_content, R.id.row_item_author, R.id.row_item_date, R.id.row_item_sidebar, R.id.row_item_feedtitle };
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFrom, groupTo, true);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity(), true));
|
||||
itemList.setAdapter(adapter);
|
||||
}
|
||||
super.onLoadFinished(loader, cursor);
|
||||
// every time we see a set of saved stories, tag them so they don't disappear during this reading session
|
||||
FeedUtils.dbHelper.markSavedReadingSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,7 +21,7 @@ public class SocialFeedItemListFragment extends ItemListFragment {
|
|||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if ((adapter == null) && (cursor != null)) {
|
||||
String[] groupFroms = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.FEED_FAVICON_URL, DatabaseConstants.FEED_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.SUM_STORY_TOTAL};
|
||||
String[] groupFroms = new String[] { DatabaseConstants.STORY_TITLE, DatabaseConstants.FEED_FAVICON_URL, DatabaseConstants.FEED_TITLE, DatabaseConstants.STORY_SHORT_CONTENT, DatabaseConstants.STORY_TIMESTAMP, DatabaseConstants.STORY_AUTHORS, DatabaseConstants.STORY_INTELLIGENCE_TOTAL};
|
||||
int[] groupTos = new int[] { R.id.row_item_title, R.id.row_item_feedicon, R.id.row_item_feedtitle, R.id.row_item_content, R.id.row_item_date, R.id.row_item_author, R.id.row_item_sidebar};
|
||||
adapter = new MultipleFeedItemsAdapter(getActivity(), R.layout.row_folderitem, cursor, groupFroms, groupTos);
|
||||
adapter.setViewBinder(new SocialItemViewBinder(getActivity()));
|
||||
|
|
|
@ -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
|
||||
|
@ -615,9 +605,9 @@ public class NBSyncService extends Service {
|
|||
if (FlushRecounts) return;
|
||||
// don't let the page loop block actions
|
||||
if (dbHelper.getActions(false).getCount() > 0) return;
|
||||
|
||||
// bail if the active view has changed
|
||||
if (!fs.equals(PendingFeed)) {
|
||||
// the active view has changed
|
||||
if (fs == null) finished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -629,16 +619,14 @@ public class NBSyncService extends Service {
|
|||
|
||||
if (! isStoryResponseGood(apiResponse)) return;
|
||||
|
||||
if (!fs.equals(PendingFeed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FeedPagesSeen.put(fs, pageNumber);
|
||||
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);
|
||||
|
||||
|
@ -707,15 +695,15 @@ public class NBSyncService extends Service {
|
|||
// If this set of stories was found in response to the active search query, note
|
||||
// them as such in the DB so the UI can filter for them
|
||||
for (Story story : apiResponse.stories) {
|
||||
story.isSearchHit = true;
|
||||
story.searchHit = fs.getSearchQuery();
|
||||
}
|
||||
}
|
||||
|
||||
dbHelper.insertStories(apiResponse, ActMode, ModeCutoff);
|
||||
dbHelper.insertStories(apiResponse, true);
|
||||
}
|
||||
|
||||
void insertStories(StoriesResponse apiResponse) {
|
||||
dbHelper.insertStories(apiResponse, ActMode, ModeCutoff);
|
||||
dbHelper.insertStories(apiResponse, false);
|
||||
}
|
||||
|
||||
void incrementRunningChild() {
|
||||
|
@ -838,18 +826,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.
|
||||
|
|
|
@ -52,27 +52,28 @@ public class UnreadsService extends SubService {
|
|||
NavigableMap<String,String> sortingMap = new TreeMap<String,String>();
|
||||
UnreadStoryHashesResponse unreadHashes = parent.apiManager.getUnreadStoryHashes();
|
||||
|
||||
if (parent.stopSync()) return;
|
||||
// note all the stories we thought were unread before. if any fail to appear in
|
||||
// the API request for unreads, we will mark them as read
|
||||
List<String> oldUnreadHashes = parent.dbHelper.getUnreadStoryHashes();
|
||||
|
||||
// process the api response, both bookkeeping no-longer-unread stories and populating
|
||||
// the sortation map we will use to create the fetch list for step two
|
||||
for (Entry<String, List<String[]>> entry : unreadHashes.unreadHashes.entrySet()) {
|
||||
feedloop: for (Entry<String, List<String[]>> entry : unreadHashes.unreadHashes.entrySet()) {
|
||||
String feedId = entry.getKey();
|
||||
// ignore unreads from orphaned feeds
|
||||
if( ! parent.orphanFeedIds.contains(feedId)) {
|
||||
if(parent.orphanFeedIds.contains(feedId)) continue feedloop;
|
||||
for (String[] newHash : entry.getValue()) {
|
||||
// only fetch the reported unreads if we don't already have them
|
||||
List<String> existingHashes = parent.dbHelper.getStoryHashesForFeed(feedId);
|
||||
for (String[] newHash : entry.getValue()) {
|
||||
if (!existingHashes.contains(newHash[0])) {
|
||||
sortingMap.put(newHash[1]+newHash[0], newHash[0]);
|
||||
}
|
||||
if (!oldUnreadHashes.contains(newHash[0])) {
|
||||
sortingMap.put(newHash[1]+newHash[0], newHash[0]);
|
||||
} else {
|
||||
oldUnreadHashes.remove(newHash[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent.stopSync()) return;
|
||||
// now that we have the sorted set of hashes, turn them into a list over which we
|
||||
// can iterate to fetch them
|
||||
if (PrefsUtils.getDefaultStoryOrder(parent) == StoryOrder.NEWEST) {
|
||||
|
@ -90,6 +91,7 @@ public class UnreadsService extends SubService {
|
|||
}
|
||||
|
||||
private void getNewUnreadStories() {
|
||||
int totalCount = StoryHashQueue.size();
|
||||
unreadsyncloop: while (StoryHashQueue.size() > 0) {
|
||||
if (parent.stopSync()) return;
|
||||
if(!PrefsUtils.isOfflineEnabled(parent)) return;
|
||||
|
|
|
@ -56,7 +56,7 @@ public class AppConstants {
|
|||
public static final int MAX_READ_STORIES_STORED = 500;
|
||||
|
||||
// how many unread stories to fetch via hash at a time
|
||||
public static final int UNREAD_FETCH_BATCH_SIZE = 100;
|
||||
public static final int UNREAD_FETCH_BATCH_SIZE = 50;
|
||||
|
||||
// how many images to prefetch before updating the countdown UI
|
||||
public static final int IMAGE_PREFETCH_BATCH_SIZE = 6;
|
||||
|
|
|
@ -172,6 +172,10 @@ public class FeedSet implements Serializable {
|
|||
return (((savedFeeds != null) && (savedFeeds.size() < 1)) || ((savedTags != null) && (savedTags.size() < 1)));
|
||||
}
|
||||
|
||||
public boolean isSingleSocial() {
|
||||
return ((socialFeeds != null) && (socialFeeds.size() == 1));
|
||||
}
|
||||
|
||||
public boolean isGlobalShared() {
|
||||
return this.isGlobalShared;
|
||||
}
|
||||
|
|
|
@ -94,33 +94,30 @@ public class FeedUtils {
|
|||
}.execute();
|
||||
}
|
||||
|
||||
public static void prepareReadingSession(final FeedSet fs, final StateFilter state) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... arg) {
|
||||
dbHelper.prepareReadingSession(fs, state);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void clearReadingSession() {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... arg) {
|
||||
// TODO: this reset might no longer be necessary after every FeedSet switch and could save a lot of API calls
|
||||
NBSyncService.resetFeeds();
|
||||
try {
|
||||
dbHelper.clearReadingSession();
|
||||
dbHelper.clearStorySession();
|
||||
} catch (Exception e) {
|
||||
; // this one call can evade the on-upgrade DB wipe and throw exceptions
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
public static void activateAllStories() {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... arg) {
|
||||
try {
|
||||
dbHelper.markStoriesActive(NBSyncService.ActivationMode.ALL, 0L);
|
||||
} catch (Exception e) {
|
||||
; // this call can evade the on-upgrade DB wipe and throw exceptions
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void markStoryUnread(final Story story, final Context context) {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.newsblur.util;
|
||||
|
||||
/**
|
||||
* Enum to represent mark all read confirmation preference.
|
||||
* @author mark
|
||||
*/
|
||||
public enum MarkAllReadConfirmation {
|
||||
|
||||
FEED_AND_FOLDER("feed_and_folder"),
|
||||
FOLDER_ONLY("folder_only"),
|
||||
NONE("none");
|
||||
|
||||
private String parameterValue;
|
||||
|
||||
MarkAllReadConfirmation(String parameterValue) {
|
||||
this.parameterValue = parameterValue;
|
||||
}
|
||||
|
||||
public boolean feedSetRequiresConfirmation(FeedSet fs) {
|
||||
if (fs.isFolder() || fs.isAllNormal()) {
|
||||
return this != NONE;
|
||||
} else {
|
||||
return this == FEED_AND_FOLDER;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,4 +67,5 @@ public class PrefConstants {
|
|||
public static final String LAST_CLEANUP_TIME = "last_cleanup_time";
|
||||
|
||||
public static final String VOLUME_KEY_NAVIGATION = "volume_key_navigation";
|
||||
public static final String MARK_ALL_READ_CONFIRMATION = "pref_confirm_mark_all_read";
|
||||
}
|
||||
|
|
|
@ -567,4 +567,9 @@ public class PrefsUtils {
|
|||
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
|
||||
return VolumeKeyNavigation.valueOf(prefs.getString(PrefConstants.VOLUME_KEY_NAVIGATION, VolumeKeyNavigation.OFF.toString()));
|
||||
}
|
||||
|
||||
public static MarkAllReadConfirmation getMarkAllReadConfirmation(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
|
||||
return MarkAllReadConfirmation.valueOf(prefs.getString(PrefConstants.MARK_ALL_READ_CONFIRMATION, MarkAllReadConfirmation.FOLDER_ONLY.toString()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ package com.newsblur.util;
|
|||
*/
|
||||
public enum ReadFilter {
|
||||
ALL("all"),
|
||||
UNREAD("unread"), // in the app, this often means "unread and read since switching activities"
|
||||
PURE_UNREAD("unread");
|
||||
UNREAD("unread");
|
||||
|
||||
private String parameterValue;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
Class activityClass;
|
||||
if (fs.isAllSaved()) {
|
||||
activityClass = SavedStoriesReading.class;
|
||||
|
@ -189,9 +189,15 @@ public class UIUtils {
|
|||
Intent i = new Intent(context, activityClass);
|
||||
i.putExtra(Reading.EXTRA_FEEDSET, fs);
|
||||
i.putExtra(Reading.EXTRA_STORY_HASH, startingHash);
|
||||
if (ignoreFilters) {
|
||||
i.putExtra(SocialFeedReading.EXTRA_IGNORE_FILTERS, true);
|
||||
}
|
||||
context.startActivity(i);
|
||||
}
|
||||
|
||||
public static String getMemoryUsageDebug(Context context) {
|
||||
String memInfo = " (";
|
||||
android.app.ActivityManager activityManager = (android.app.ActivityManager) context.getSystemService(android.app.Activity.ACTIVITY_SERVICE);
|
||||
int[] pids = new int[]{android.os.Process.myPid()};
|
||||
android.os.Debug.MemoryInfo[] mi = activityManager.getProcessMemoryInfo(pids);
|
||||
memInfo = memInfo + (mi[0].getTotalPss() / 1024) + "MB used)";
|
||||
return memInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class FeedItemViewBinder implements ViewBinder {
|
|||
((TextView) view).setText(cursor.getString(columnIndex).toUpperCase());
|
||||
}
|
||||
return true;
|
||||
} else if (TextUtils.equals(columnName, DatabaseConstants.SUM_STORY_TOTAL)) {
|
||||
} else if (TextUtils.equals(columnName, DatabaseConstants.STORY_INTELLIGENCE_TOTAL)) {
|
||||
int score = cursor.getInt(columnIndex);
|
||||
Drawable icon;
|
||||
if (score > 0) {
|
||||
|
|
|
@ -39,7 +39,7 @@ public class SocialItemViewBinder implements ViewBinder {
|
|||
String faviconUrl = cursor.getString(columnIndex);
|
||||
FeedUtils.imageLoader.displayImage(faviconUrl, ((ImageView) view), true);
|
||||
return true;
|
||||
} else if (TextUtils.equals(columnName, DatabaseConstants.SUM_STORY_TOTAL)) {
|
||||
} else if (TextUtils.equals(columnName, DatabaseConstants.STORY_INTELLIGENCE_TOTAL)) {
|
||||
if (! this.ignoreIntel) {
|
||||
int score = cursor.getInt(columnIndex);
|
||||
Drawable icon;
|
||||
|
|
Loading…
Add table
Reference in a new issue