mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00
Merge branch 'django1.10' into django1.11
* django1.10: (102 commits) Beginning fix of rss_feeds unit tests. Fixing unit test for profile app and signup. Remove highlights count when it reaches zero. Android v10.1b1. Stubbing in profile tests. Adding nginx.local.conf Adding original text and original story to API docs. #1282 Adding feed to root folder #1319 In app and external browser options Adding a smarter wakeup for real-time to handle cases where a laptop is re-opened but real-time is not immediately reestablished. #1348 (scroll indicators theme) #1344 (search loses focus) #1335 Auto theme option for OS level dark mode Fixing signup flow. #1347 Show pager with stories after using the intel trainer and refreshing #1272 Load HTML in comments New icon for Infrequent Site Stories. Allowing selection in private notes. Autoresizing private notes field. For #1035: Adding private notes to saved stories. ...
This commit is contained in:
commit
ac1471017d
145 changed files with 9224 additions and 1276 deletions
|
@ -161,6 +161,7 @@ these after the installation below.
|
|||
|
||||
Then load up the database with empty NewsBlur tables and bootstrap the database:
|
||||
|
||||
./manage.py syncdb --all --noinput
|
||||
./manage.py migrate --fake
|
||||
./manage.py migrate
|
||||
./manage.py loaddata config/fixtures/bootstrap.json
|
||||
|
|
|
@ -38,25 +38,6 @@
|
|||
"user": 1,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "conesus",
|
||||
"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": "samuel@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
|
@ -139,9 +139,9 @@ class ClassifierTest(TestCase):
|
|||
# user = User.objects.all()
|
||||
# feed = Feed.objects.all()
|
||||
|
||||
management.call_command('loaddata', 'brownstoner.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'brownstoner.json', verbosity=0, commit=False, skip_checks=False)
|
||||
management.call_command('refresh_feed', force=1, feed=1, single_threaded=True, daemonize=False, skip_checks=False)
|
||||
management.call_command('loaddata', 'brownstoner2.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'brownstoner2.json', verbosity=0, commit=False, skip_checks=False)
|
||||
management.call_command('refresh_feed', force=1, feed=1, single_threaded=True, daemonize=False, skip_checks=False)
|
||||
|
||||
stories = MStory.objects(story_feed_id=1)[:53]
|
||||
|
|
|
@ -10,5 +10,6 @@ urlpatterns = [
|
|||
url(r'^add_site/?$', views.add_site_authed, name='api-add-site-authed'),
|
||||
url(r'^check_share_on_site/(?P<token>\w+)', views.check_share_on_site, name='api-check-share-on-site'),
|
||||
url(r'^share_story/(?P<token>\w+)', views.share_story, name='api-share-story'),
|
||||
url(r'^save_story/(?P<token>\w+)', views.save_story, name='api-save-story'),
|
||||
url(r'^share_story/?$', views.share_story),
|
||||
]
|
||||
|
|
|
@ -12,7 +12,7 @@ from django.contrib.auth import logout as logout_user
|
|||
from apps.reader.forms import SignupForm, LoginForm
|
||||
from apps.profile.models import Profile
|
||||
from apps.social.models import MSocialProfile, MSharedStory, MSocialSubscription
|
||||
from apps.rss_feeds.models import Feed
|
||||
from apps.rss_feeds.models import Feed, MStarredStoryCounts, MStarredStory
|
||||
from apps.rss_feeds.text_importer import TextImporter
|
||||
from apps.reader.models import UserSubscription, UserSubscriptionFolders, RUserStory
|
||||
from utils import json_functions as json
|
||||
|
@ -80,8 +80,10 @@ def logout(request):
|
|||
def add_site_load_script(request, token):
|
||||
code = 0
|
||||
usf = None
|
||||
profile = None;
|
||||
user_profile = None;
|
||||
profile = None
|
||||
user_profile = None
|
||||
starred_counts = {}
|
||||
|
||||
def image_base64(image_name, path='icons/circular/'):
|
||||
image_file = open(os.path.join(settings.MEDIA_ROOT, 'img/%s%s' % (path, image_name)))
|
||||
return base64.b64encode(image_file.read())
|
||||
|
@ -99,6 +101,7 @@ def add_site_load_script(request, token):
|
|||
user=profile.user
|
||||
)
|
||||
user_profile = MSocialProfile.get_user(user_id=profile.user.pk)
|
||||
starred_counts = MStarredStoryCounts.user_counts(profile.user.pk)
|
||||
else:
|
||||
code = -1
|
||||
except Profile.DoesNotExist:
|
||||
|
@ -112,6 +115,7 @@ def add_site_load_script(request, token):
|
|||
'folders': (usf and usf.folders) or [],
|
||||
'user': profile and profile.user or {},
|
||||
'user_profile': user_profile and json.encode(user_profile.canonical()) or {},
|
||||
'starred_counts': json.encode(starred_counts),
|
||||
'accept_image': accept_image,
|
||||
'error_image': error_image,
|
||||
'add_image': add_image,
|
||||
|
@ -301,7 +305,6 @@ def share_story(request, token=None):
|
|||
else:
|
||||
message = "Not authenticated, no token supplied and not authenticated."
|
||||
|
||||
|
||||
if not profile:
|
||||
return HttpResponse(json.encode({
|
||||
'code': code,
|
||||
|
@ -391,3 +394,106 @@ def share_story(request, token=None):
|
|||
response['Access-Control-Allow-Methods'] = 'POST'
|
||||
|
||||
return response
|
||||
|
||||
@required_params('story_url', 'title')
|
||||
def save_story(request, token=None):
|
||||
code = 0
|
||||
story_url = request.POST['story_url']
|
||||
user_tags = request.POST.getlist('user_tags') or request.REQUEST.getlist('user_tags[]') or []
|
||||
add_user_tag = request.POST.get('add_user_tag', None)
|
||||
title = request.POST['title']
|
||||
content = request.POST.get('content', None)
|
||||
rss_url = request.POST.get('rss_url', None)
|
||||
user_notes = request.POST.get('user_notes', None)
|
||||
feed_id = request.POST.get('feed_id', None) or 0
|
||||
feed = None
|
||||
message = None
|
||||
profile = None
|
||||
|
||||
if request.user.is_authenticated():
|
||||
profile = request.user.profile
|
||||
else:
|
||||
try:
|
||||
profile = Profile.objects.get(secret_token=token)
|
||||
except Profile.DoesNotExist:
|
||||
code = -1
|
||||
if token:
|
||||
message = "Not authenticated, couldn't find user by token."
|
||||
else:
|
||||
message = "Not authenticated, no token supplied and not authenticated."
|
||||
|
||||
if not profile:
|
||||
return HttpResponse(json.encode({
|
||||
'code': code,
|
||||
'message': message,
|
||||
'story': None,
|
||||
}), mimetype='text/plain')
|
||||
|
||||
if feed_id:
|
||||
feed = Feed.get_by_id(feed_id)
|
||||
else:
|
||||
if rss_url:
|
||||
logging.user(request.user, "~FBFinding feed (save_story): %s" % rss_url)
|
||||
feed = Feed.get_feed_from_url(rss_url, create=True, fetch=True)
|
||||
if not feed:
|
||||
logging.user(request.user, "~FBFinding feed (save_story): %s" % story_url)
|
||||
feed = Feed.get_feed_from_url(story_url, create=True, fetch=True)
|
||||
if feed:
|
||||
feed_id = feed.pk
|
||||
|
||||
if content:
|
||||
content = lxml.html.fromstring(content)
|
||||
content.make_links_absolute(story_url)
|
||||
content = lxml.html.tostring(content)
|
||||
else:
|
||||
importer = TextImporter(story=None, story_url=story_url, request=request, debug=settings.DEBUG)
|
||||
document = importer.fetch(skip_save=True, return_document=True)
|
||||
content = document['content']
|
||||
if not title:
|
||||
title = document['title']
|
||||
|
||||
if add_user_tag:
|
||||
user_tags = user_tags + [tag for tag in add_user_tag.split(',')]
|
||||
|
||||
starred_story = MStarredStory.objects.filter(user_id=profile.user.pk,
|
||||
story_feed_id=feed_id,
|
||||
story_guid=story_url).limit(1).first()
|
||||
if not starred_story:
|
||||
story_db = {
|
||||
"story_guid": story_url,
|
||||
"story_permalink": story_url,
|
||||
"story_title": title,
|
||||
"story_feed_id": feed_id,
|
||||
"story_content": content,
|
||||
"story_date": datetime.datetime.now(),
|
||||
"starred_date": datetime.datetime.now(),
|
||||
"user_id": profile.user.pk,
|
||||
"user_tags": user_tags,
|
||||
"user_notes": user_notes,
|
||||
}
|
||||
starred_story = MStarredStory.objects.create(**story_db)
|
||||
logging.user(profile.user, "~BM~FCStarring story from site: ~SB%s: %s" % (story_url, user_tags))
|
||||
message = "Saving story from site: %s: %s" % (story_url, user_tags)
|
||||
else:
|
||||
starred_story.story_content = content
|
||||
starred_story.story_title = title
|
||||
starred_story.user_tags = user_tags
|
||||
starred_story.story_permalink = story_url
|
||||
starred_story.story_guid = story_url
|
||||
starred_story.story_feed_id = feed_id
|
||||
starred_story.user_notes = user_notes
|
||||
starred_story.save()
|
||||
logging.user(profile.user, "~BM~FC~SBUpdating~SN starred story from site: ~SB%s: %s" % (story_url, user_tags))
|
||||
message = "Updating saved story from site: %s: %s" % (story_url, user_tags)
|
||||
|
||||
MStarredStoryCounts.schedule_count_tags_for_user(request.user.pk)
|
||||
|
||||
response = HttpResponse(json.encode({
|
||||
'code': code,
|
||||
'message': message,
|
||||
'story': starred_story,
|
||||
}), mimetype='text/plain')
|
||||
response['Access-Control-Allow-Origin'] = '*'
|
||||
response['Access-Control-Allow-Methods'] = 'POST'
|
||||
|
||||
return response
|
|
@ -1,30 +1,4 @@
|
|||
[
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "sites.site",
|
||||
"fields": {
|
||||
"domain": "testserver",
|
||||
"name": "testserver"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "conesus",
|
||||
"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": "samuel@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "reader.usersubscriptionfolders",
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.db import models
|
|||
from django.contrib.auth.models import User
|
||||
from mongoengine.queryset import OperationError
|
||||
import vendor.opml as opml
|
||||
from apps.rss_feeds.models import Feed, DuplicateFeed, MStarredStory
|
||||
from apps.rss_feeds.models import Feed, DuplicateFeed
|
||||
from apps.reader.models import UserSubscription, UserSubscriptionFolders
|
||||
from utils import json_functions as json, urlnorm
|
||||
from utils import log as logging
|
||||
|
|
|
@ -2,7 +2,6 @@ from celery.task import Task
|
|||
from django.contrib.auth.models import User
|
||||
from apps.feed_import.models import UploadedOPML, OPMLImporter
|
||||
from apps.reader.models import UserSubscription
|
||||
from apps.rss_feeds.models import MStarredStory
|
||||
from utils import log as logging
|
||||
|
||||
|
||||
|
|
37
apps/profile/tests.py
Normal file
37
apps/profile/tests.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from utils import json_functions as json
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from mongoengine.connection import connect, disconnect
|
||||
|
||||
class ProfileTest(TestCase):
|
||||
fixtures = [
|
||||
'subscriptions.json',
|
||||
'rss_feeds.json',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
disconnect()
|
||||
settings.MONGODB = connect('test_newsblur')
|
||||
self.client = Client(HTTP_USER_AGENT='Mozilla/5.0')
|
||||
|
||||
def tearDown(self):
|
||||
settings.MONGODB.drop_database('test_newsblur')
|
||||
|
||||
def test_create_account(self):
|
||||
resp = self.client.get(reverse('load-feeds'))
|
||||
response = json.decode(resp.content)
|
||||
self.assertEquals(response['authenticated'], False)
|
||||
|
||||
response = self.client.post(reverse('welcome-signup'), {
|
||||
'signup-username': 'test',
|
||||
'signup-password': 'password',
|
||||
'signup-email': 'test@newsblur.com',
|
||||
})
|
||||
self.assertEquals(response.status_code, 302)
|
||||
|
||||
resp = self.client.get(reverse('load-feeds'))
|
||||
response = json.decode(resp.content)
|
||||
self.assertEquals(response['authenticated'], True)
|
||||
|
|
@ -366,7 +366,7 @@ class PSHBUpdateTestCase(PSHBTestBase, TestCase):
|
|||
self.assertEquals(self.requests[0][0],
|
||||
'http://myhub.example.com/endpoint')
|
||||
self.assertEquals(self.requests[0][1]['callback'],
|
||||
'http://testserver/1/')
|
||||
'http://test.nb.local.com/1/')
|
||||
self.assert_((self.requests[0][1]['lease_seconds'] - 86400) < 5)
|
||||
|
||||
def test_update_with_changed_self(self):
|
||||
|
@ -414,7 +414,7 @@ class PSHBUpdateTestCase(PSHBTestBase, TestCase):
|
|||
self.assertEquals(self.requests[0][0],
|
||||
'http://myhub.example.com/endpoint')
|
||||
self.assertEquals(self.requests[0][1]['callback'],
|
||||
'http://testserver/1/')
|
||||
'http://test.nb.local.com/1/')
|
||||
self.assert_((self.requests[0][1]['lease_seconds'] - 86400) < 5)
|
||||
|
||||
def test_update_with_changed_hub_and_self(self):
|
||||
|
@ -463,5 +463,5 @@ class PSHBUpdateTestCase(PSHBTestBase, TestCase):
|
|||
self.assertEquals(self.requests[0][0],
|
||||
'http://myhub.example.com/endpoint')
|
||||
self.assertEquals(self.requests[0][1]['callback'],
|
||||
'http://testserver/1/')
|
||||
'http://test.nb.local.com/1/')
|
||||
self.assert_((self.requests[0][1]['lease_seconds'] - 86400) < 5)
|
||||
|
|
|
@ -1,18 +1,4 @@
|
|||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "reader.usersubscription",
|
||||
"fields": {
|
||||
"feed": 1,
|
||||
"unread_count_updated": "2009-08-01 00:23:42",
|
||||
"mark_read_date": "2009-07-28 23:17:27",
|
||||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "reader.usersubscriptionfolders",
|
||||
|
@ -26,7 +12,7 @@
|
|||
"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
|
||||
"user": 4
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -40,7 +26,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -54,7 +40,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -68,7 +54,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -82,7 +68,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -96,7 +82,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -110,7 +96,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -124,7 +110,7 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
@ -138,22 +124,13 @@
|
|||
"unread_count_neutral": 0,
|
||||
"unread_count_positive": 0,
|
||||
"unread_count_negative": 0,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2009-07-28 23:17:27"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "sites.site",
|
||||
"fields": {
|
||||
"domain": "testserver",
|
||||
"name": "testserver"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 1,
|
||||
"pk": 3,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "conesus",
|
||||
|
@ -171,7 +148,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"pk": 4,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "Dejal",
|
||||
|
@ -198,7 +175,7 @@
|
|||
"unread_count_positive": 0,
|
||||
"mark_read_date": "2010-06-28 19:36:44",
|
||||
"unread_count_negative": 3,
|
||||
"user": 1,
|
||||
"user": 3,
|
||||
"last_read_date": "2010-06-15 19:30:35",
|
||||
"unread_count_neutral": 0
|
||||
}
|
||||
|
|
|
@ -626,7 +626,9 @@ class UserSubscription(models.Model):
|
|||
if cutoff_date:
|
||||
cutoff_date = cutoff_date + datetime.timedelta(seconds=1)
|
||||
else:
|
||||
latest_story = MStory.objects(story_feed_id=self.feed.pk)\
|
||||
now = datetime.datetime.now()
|
||||
latest_story = MStory.objects(story_feed_id=self.feed.pk,
|
||||
story_date__lte=now)\
|
||||
.order_by('-story_date').only('story_date').limit(1)
|
||||
if latest_story and len(latest_story) >= 1:
|
||||
cutoff_date = (latest_story[0]['story_date']
|
||||
|
|
|
@ -199,6 +199,8 @@ def signup(request):
|
|||
url = "https://%s%s" % (Site.objects.get_current().domain,
|
||||
reverse('stripe-form'))
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
return index(request)
|
||||
|
||||
|
@ -695,8 +697,7 @@ def load_single_feed(request, feed_id):
|
|||
starred_stories = MStarredStory.objects(user_id=user.pk,
|
||||
story_feed_id=feed.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
.hint([('user_id', 1), ('story_hash', 1)])\
|
||||
.only('story_hash', 'starred_date', 'user_tags')
|
||||
.hint([('user_id', 1), ('story_hash', 1)])
|
||||
shared_story_hashes = MSharedStory.check_shared_story_hashes(user.pk, story_hashes)
|
||||
shared_stories = []
|
||||
if shared_story_hashes:
|
||||
|
@ -704,8 +705,7 @@ def load_single_feed(request, feed_id):
|
|||
story_hash__in=shared_story_hashes)\
|
||||
.hint([('story_hash', 1)])\
|
||||
.only('story_hash', 'shared_date', 'comments')
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
starred_stories = dict([(story.story_hash, story)
|
||||
for story in starred_stories])
|
||||
shared_stories = dict([(story.story_hash, dict(shared_date=story.shared_date,
|
||||
comments=story.comments))
|
||||
|
@ -730,11 +730,14 @@ def load_single_feed(request, feed_id):
|
|||
story['read_status'] = 0
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']]['starred_date'],
|
||||
starred_story = Feed.format_story(starred_stories[story['story_hash']])
|
||||
starred_date = localtime_for_timezone(starred_story['starred_date'],
|
||||
user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['starred_timestamp'] = starred_date.strftime('%s')
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
story['user_tags'] = starred_story['user_tags']
|
||||
story['user_notes'] = starred_story['user_notes']
|
||||
story['highlights'] = starred_story['highlights']
|
||||
if story['story_hash'] in shared_stories:
|
||||
story['shared'] = True
|
||||
shared_date = localtime_for_timezone(shared_stories[story['story_hash']]['shared_date'],
|
||||
|
@ -885,6 +888,7 @@ def load_starred_stories(request):
|
|||
query = request.GET.get('query', '').strip()
|
||||
order = request.GET.get('order', 'newest')
|
||||
tag = request.GET.get('tag')
|
||||
highlights = is_true(request.GET.get('highlights', False))
|
||||
story_hashes = request.GET.getlist('h') or request.GET.getlist('h[]')
|
||||
story_hashes = story_hashes[:100]
|
||||
version = int(request.GET.get('v', 1))
|
||||
|
@ -902,6 +906,17 @@ def load_starred_stories(request):
|
|||
else:
|
||||
stories = []
|
||||
message = "You must be a premium subscriber to search."
|
||||
elif highlights:
|
||||
if user.profile.is_premium:
|
||||
mstories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
highlights__exists=True,
|
||||
__raw__={"$where": "this.highlights.length > 0"}
|
||||
).order_by('%sstarred_date' % order_by)[offset:offset+limit]
|
||||
stories = Feed.format_stories(mstories)
|
||||
else:
|
||||
stories = []
|
||||
message = "You must be a premium subscriber to read through saved story highlights."
|
||||
elif tag:
|
||||
if user.profile.is_premium:
|
||||
mstories = MStarredStory.objects(
|
||||
|
@ -1048,6 +1063,12 @@ def starred_stories_rss_feed(request, user_id, secret_token, tag_slug):
|
|||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk
|
||||
).order_by('-starred_date').limit(25)
|
||||
elif tag_counts.is_highlights:
|
||||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
highlights__exists=True,
|
||||
__raw__={"$where": "this.highlights.length > 0"}
|
||||
).order_by('-starred_date').limit(25)
|
||||
else:
|
||||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
|
@ -1247,9 +1268,8 @@ def load_read_stories(request):
|
|||
for story in shared_stories])
|
||||
starred_stories = MStarredStory.objects(user_id=user.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
.hint([('user_id', 1), ('story_hash', 1)])\
|
||||
.only('story_hash', 'starred_date')
|
||||
starred_stories = dict([(story.story_hash, story.starred_date)
|
||||
.hint([('user_id', 1), ('story_hash', 1)])
|
||||
starred_stories = dict([(story.story_hash, story)
|
||||
for story in starred_stories])
|
||||
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
|
@ -1266,7 +1286,8 @@ def load_read_stories(request):
|
|||
}
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']],
|
||||
starred_story = Feed.format_story(starred_stories[story['story_hash']])
|
||||
starred_date = localtime_for_timezone(starred_story['starred_date'],
|
||||
user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['starred_timestamp'] = starred_date.strftime('%s')
|
||||
|
@ -1394,10 +1415,11 @@ def load_river_stories__redis(request):
|
|||
story_hashes = [s['story_hash'] for s in stories]
|
||||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
story_hash__in=story_hashes
|
||||
).only('story_hash', 'starred_date', 'user_tags')
|
||||
story_hash__in=story_hashes)
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
user_tags=story.user_tags,
|
||||
highlights=story.highlights,
|
||||
user_notes=story.user_notes))
|
||||
for story in starred_stories])
|
||||
else:
|
||||
starred_stories = {}
|
||||
|
@ -1445,6 +1467,8 @@ def load_river_stories__redis(request):
|
|||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['starred_timestamp'] = starred_date.strftime('%s')
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
story['user_notes'] = starred_stories[story['story_hash']]['user_notes']
|
||||
story['highlights'] = starred_stories[story['story_hash']]['highlights']
|
||||
story['intelligence'] = {
|
||||
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id']),
|
||||
'author': apply_classifier_authors(classifier_authors, story),
|
||||
|
@ -2393,6 +2417,8 @@ def _mark_story_as_starred(request):
|
|||
story_id = request.POST.get('story_id', None)
|
||||
story_hash = request.POST.get('story_hash', None)
|
||||
user_tags = request.POST.getlist('user_tags') or request.POST.getlist('user_tags[]')
|
||||
user_notes = request.POST.get('user_notes', None)
|
||||
highlights = request.POST.getlist('highlights') or request.POST.getlist('highlights[]') or []
|
||||
message = ""
|
||||
if story_hash:
|
||||
story, _ = MStory.find_story(story_hash=story_hash)
|
||||
|
@ -2405,16 +2431,23 @@ def _mark_story_as_starred(request):
|
|||
|
||||
story_db = dict([(k, v) for k, v in story._data.items()
|
||||
if k is not None and v is not None])
|
||||
# Pop all existing user-specific fields because we don't want to reuse them from the found story
|
||||
# in case MStory.find_story uses somebody else's saved/shared story (because the original is deleted)
|
||||
story_db.pop('user_id', None)
|
||||
story_db.pop('starred_date', None)
|
||||
story_db.pop('id', None)
|
||||
story_db.pop('user_tags', None)
|
||||
story_db.pop('highlights', None)
|
||||
story_db.pop('user_notes', None)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
story_values = dict(starred_date=now, user_tags=user_tags, **story_db)
|
||||
story_values = dict(starred_date=now, user_tags=user_tags, highlights=highlights, user_notes=user_notes, **story_db)
|
||||
params = dict(story_guid=story.story_guid, user_id=request.user.pk)
|
||||
starred_story = MStarredStory.objects(**params).limit(1)
|
||||
created = False
|
||||
changed_user_notes = False
|
||||
removed_user_tags = []
|
||||
removed_highlights = []
|
||||
if not starred_story:
|
||||
params.update(story_values)
|
||||
if params.has_key('story_latest_content_z'):
|
||||
|
@ -2431,14 +2464,26 @@ def _mark_story_as_starred(request):
|
|||
story_feed_id=feed_id,
|
||||
story_id=starred_story.story_guid)
|
||||
new_user_tags = user_tags
|
||||
new_highlights = highlights
|
||||
changed_user_notes = bool(user_notes)
|
||||
MStarredStoryCounts.adjust_count(request.user.pk, feed_id=feed_id, amount=1)
|
||||
else:
|
||||
starred_story = starred_story[0]
|
||||
new_user_tags = list(set(user_tags) - set(starred_story.user_tags or []))
|
||||
removed_user_tags = list(set(starred_story.user_tags or []) - set(user_tags))
|
||||
new_highlights = list(set(highlights) - set(starred_story.highlights or []))
|
||||
removed_highlights = list(set(starred_story.highlights or []) - set(highlights))
|
||||
changed_user_notes = bool(user_notes != starred_story.user_notes)
|
||||
starred_story.user_tags = user_tags
|
||||
starred_story.highlights = highlights
|
||||
starred_story.user_notes = user_notes
|
||||
starred_story.save()
|
||||
|
||||
if len(highlights) == 1 and len(new_highlights) == 1:
|
||||
MStarredStoryCounts.adjust_count(request.user.pk, highlights=True, amount=1)
|
||||
elif len(highlights) == 0 and len(removed_highlights):
|
||||
MStarredStoryCounts.adjust_count(request.user.pk, highlights=True, amount=-1)
|
||||
|
||||
for tag in new_user_tags:
|
||||
MStarredStoryCounts.adjust_count(request.user.pk, tag=tag, amount=1)
|
||||
for tag in removed_user_tags:
|
||||
|
@ -2451,13 +2496,14 @@ def _mark_story_as_starred(request):
|
|||
if not starred_count and len(starred_counts):
|
||||
starred_count = MStarredStory.objects(user_id=request.user.pk).count()
|
||||
|
||||
if not changed_user_notes:
|
||||
r = redis.Redis(connection_pool=settings.REDIS_PUBSUB_POOL)
|
||||
r.publish(request.user.username, 'story:starred:%s' % story.story_hash)
|
||||
|
||||
if created:
|
||||
logging.user(request, "~FCStarring: ~SB%s (~FM~SB%s~FC~SN)" % (story.story_title[:32], starred_story.user_tags))
|
||||
else:
|
||||
logging.user(request, "~FCUpdating starred:~SN~FC ~SB%s~SN (~FM~SB%s~FC~SN)" % (story.story_title[:32], starred_story.user_tags))
|
||||
logging.user(request, "~FCUpdating starred:~SN~FC ~SB%s~SN (~FM~SB%s~FC~SN/~FM%s~FC)" % (story.story_title[:32], starred_story.user_tags, starred_story.user_notes))
|
||||
|
||||
return {'code': code, 'message': message, 'starred_count': starred_count, 'starred_counts': starred_counts}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
[
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "sites.site",
|
||||
"fields": {
|
||||
"domain": "testserver",
|
||||
"name": "testserver"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "newsblur_test",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": true,
|
||||
"last_login": "2011-07-18 00:23:49",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$d5473$d07ce4495b088ff0f41a62d5113d0189ce8f0096",
|
||||
"email": "samuel@newsblur.com",
|
||||
"date_joined": "2011-07-18 00:23:49"
|
||||
}
|
||||
}
|
||||
]
|
|
@ -1,22 +1,46 @@
|
|||
[
|
||||
{
|
||||
"pk": 6,
|
||||
"pk": 16,
|
||||
"model": "rss_feeds.feed",
|
||||
"fields": {
|
||||
"premium_subscribers": -1,
|
||||
"creation": "2011-08-27",
|
||||
"exception_code": 0,
|
||||
"last_load_time": 0,
|
||||
"active_subscribers": 1,
|
||||
"feed_address": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/brokelyn.xml",
|
||||
"days_to_trim": 90,
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/brokelyn.html",
|
||||
"hash_address_and_link": "16",
|
||||
"feed_link_locked": true,
|
||||
"num_subscribers": 0,
|
||||
"creation": "2009-01-12",
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Brokelyn",
|
||||
"last_update": "2009-07-06 22:30:03",
|
||||
"next_scheduled_update": "2009-07-06 22:30:03",
|
||||
"last_story_date": "2009-07-06 22:30:03",
|
||||
"min_to_decay": 1,
|
||||
"etag": "",
|
||||
"last_modified": "2009-07-06 22:30:03",
|
||||
"active": 1
|
||||
"last_modified": null,
|
||||
"next_scheduled_update": "2011-08-28 14:33:50",
|
||||
"favicon_color": null,
|
||||
"stories_last_month": 0,
|
||||
"active": true,
|
||||
"favicon_not_found": false,
|
||||
"has_page_exception": false,
|
||||
"fetched_once": false,
|
||||
"days_to_trim": 90,
|
||||
"num_subscribers": 1,
|
||||
"last_story_date": "2011-08-28 00:03:50",
|
||||
"min_to_decay": 720,
|
||||
"has_feed_exception": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 16,
|
||||
"model": "rss_feeds.feeddata",
|
||||
"fields": {
|
||||
"feed": 16,
|
||||
"feed_classifier_counts": null,
|
||||
"feed_tagline": "Visual feed reading with intelligence.",
|
||||
"popular_tags": "[]",
|
||||
"story_count_history": null,
|
||||
"popular_authors": "[]"
|
||||
}
|
||||
}
|
||||
]
|
|
@ -1,6 +1,6 @@
|
|||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"pk": 10,
|
||||
"model": "rss_feeds.feed",
|
||||
"fields": {
|
||||
"premium_subscribers": -1,
|
||||
|
@ -10,6 +10,7 @@
|
|||
"active_subscribers": 1,
|
||||
"feed_address": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker1.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker1.html",
|
||||
"hash_address_and_link": "10",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
|
|
1
apps/rss_feeds/fixtures/initial_data.json
Symbolic link
1
apps/rss_feeds/fixtures/initial_data.json
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../config/fixtures/bootstrap.json
|
|
@ -110,34 +110,6 @@
|
|||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "sites.site",
|
||||
"fields": {
|
||||
"domain": "testserver",
|
||||
"name": "testserver"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "conesus",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"is_active": 1,
|
||||
"is_superuser": 1,
|
||||
"is_staff": 1,
|
||||
"last_login": "2009-07-06 22:30:03",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$7b94b$ac9e6cf08d0fa16a67e56e319c0935aeb26db2a2",
|
||||
"email": "samuel@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 2,
|
||||
|
|
|
@ -1881,7 +1881,7 @@ class Feed(models.Model):
|
|||
story_title = strip_tags(story_content)
|
||||
if not story_title and story_db.story_permalink:
|
||||
story_title = story_db.story_permalink
|
||||
if len(story_title) > 80:
|
||||
if story_title and len(story_title) > 80:
|
||||
story_title = story_title[:80] + '...'
|
||||
|
||||
story = {}
|
||||
|
@ -1912,6 +1912,10 @@ class Feed(models.Model):
|
|||
story['starred_date'] = story_db.starred_date
|
||||
if hasattr(story_db, 'user_tags'):
|
||||
story['user_tags'] = story_db.user_tags
|
||||
if hasattr(story_db, 'user_notes'):
|
||||
story['user_notes'] = story_db.user_notes
|
||||
if hasattr(story_db, 'highlights'):
|
||||
story['highlights'] = story_db.highlights
|
||||
if hasattr(story_db, 'shared_date'):
|
||||
story['shared_date'] = story_db.shared_date
|
||||
if hasattr(story_db, 'comments'):
|
||||
|
@ -2844,7 +2848,9 @@ class MStarredStory(mongo.DynamicDocument):
|
|||
story_guid = mongo.StringField()
|
||||
story_hash = mongo.StringField()
|
||||
story_tags = mongo.ListField(mongo.StringField(max_length=250))
|
||||
user_notes = mongo.StringField()
|
||||
user_tags = mongo.ListField(mongo.StringField(max_length=128))
|
||||
highlights = mongo.ListField(mongo.StringField(max_length=1024))
|
||||
image_urls = mongo.ListField(mongo.StringField(max_length=1024))
|
||||
|
||||
meta = {
|
||||
|
@ -2856,6 +2862,16 @@ class MStarredStory(mongo.DynamicDocument):
|
|||
'strict': False,
|
||||
}
|
||||
|
||||
def __unicode__(self):
|
||||
try:
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
username = user.username
|
||||
except User.DoesNotExist:
|
||||
username = '[deleted]'
|
||||
return "%s: %s (%s)" % (username,
|
||||
self.story_title[:20],
|
||||
self.story_feed_id)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.story_content:
|
||||
self.story_content_z = zlib.compress(self.story_content)
|
||||
|
@ -2964,6 +2980,7 @@ class MStarredStoryCounts(mongo.Document):
|
|||
user_id = mongo.IntField()
|
||||
tag = mongo.StringField(max_length=128)
|
||||
feed_id = mongo.IntField()
|
||||
is_highlights = mongo.BooleanField()
|
||||
slug = mongo.StringField(max_length=128)
|
||||
count = mongo.IntField(default=0)
|
||||
|
||||
|
@ -2974,6 +2991,16 @@ class MStarredStoryCounts(mongo.Document):
|
|||
'allow_inheritance': False,
|
||||
}
|
||||
|
||||
def __unicode__(self):
|
||||
if self.tag:
|
||||
return "Tag: %s (%s)" % (self.tag, self.count)
|
||||
elif self.feed_id:
|
||||
return "Feed: %s (%s)" % (self.feed_id, self.count)
|
||||
elif self.is_highlights:
|
||||
return "Highlights: %s (%s)" % (self.is_highlights, self.count)
|
||||
|
||||
return "%s/%s/%s" % (self.tag, self.feed_id, self.is_highlights)
|
||||
|
||||
@property
|
||||
def rss_url(self, secret_token=None):
|
||||
if self.feed_id:
|
||||
|
@ -2992,6 +3019,7 @@ class MStarredStoryCounts(mongo.Document):
|
|||
counts = cls.objects.filter(user_id=user_id)
|
||||
counts = sorted([{'tag': c.tag,
|
||||
'count': c.count,
|
||||
'is_highlights': c.is_highlights,
|
||||
'feed_address': c.rss_url,
|
||||
'feed_id': c.feed_id}
|
||||
for c in counts],
|
||||
|
@ -3000,7 +3028,7 @@ class MStarredStoryCounts(mongo.Document):
|
|||
total = 0
|
||||
feed_total = 0
|
||||
for c in counts:
|
||||
if not c['tag'] and not c['feed_id']:
|
||||
if not c['tag'] and not c['feed_id'] and not c['is_highlights']:
|
||||
total = c['count']
|
||||
if c['feed_id']:
|
||||
feed_total += c['count']
|
||||
|
@ -3025,20 +3053,22 @@ class MStarredStoryCounts(mongo.Document):
|
|||
def count_for_user(cls, user_id, total_only=False):
|
||||
user_tags = []
|
||||
user_feeds = []
|
||||
highlights = 0
|
||||
|
||||
if not total_only:
|
||||
cls.objects(user_id=user_id).delete()
|
||||
try:
|
||||
user_tags = cls.count_tags_for_user(user_id)
|
||||
highlights = cls.count_highlights_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,
|
||||
cls.objects(user_id=user_id, tag=None, feed_id=None, is_highlights=None).update_one(set__count=total_stories_count,
|
||||
upsert=True)
|
||||
|
||||
return dict(total=total_stories_count, tags=user_tags, feeds=user_feeds)
|
||||
return dict(total=total_stories_count, tags=user_tags, feeds=user_feeds, highlights=highlights)
|
||||
|
||||
@classmethod
|
||||
def count_tags_for_user(cls, user_id):
|
||||
|
@ -3054,6 +3084,21 @@ class MStarredStoryCounts(mongo.Document):
|
|||
|
||||
return user_tags
|
||||
|
||||
@classmethod
|
||||
def count_highlights_for_user(cls, user_id):
|
||||
highlighted_count = MStarredStory.objects(user_id=user_id,
|
||||
highlights__exists=True,
|
||||
__raw__={"$where": "this.highlights.length > 0"}).count()
|
||||
if highlighted_count > 0:
|
||||
cls.objects(user_id=user_id,
|
||||
is_highlights=True,
|
||||
slug="highlights"
|
||||
).update_one(set__count=highlighted_count, upsert=True)
|
||||
else:
|
||||
cls.objects(user_id=user_id, is_highlights=True, slug="highlights").delete()
|
||||
|
||||
return highlighted_count
|
||||
|
||||
@classmethod
|
||||
def count_feeds_for_user(cls, user_id):
|
||||
all_feeds = MStarredStory.objects(user_id=user_id).item_frequencies('story_feed_id')
|
||||
|
@ -3079,12 +3124,14 @@ class MStarredStoryCounts(mongo.Document):
|
|||
return user_feeds
|
||||
|
||||
@classmethod
|
||||
def adjust_count(cls, user_id, feed_id=None, tag=None, amount=0):
|
||||
def adjust_count(cls, user_id, feed_id=None, tag=None, highlights=None, amount=0):
|
||||
params = dict(user_id=user_id)
|
||||
if feed_id:
|
||||
params['feed_id'] = feed_id
|
||||
if tag:
|
||||
params['tag'] = tag
|
||||
if highlights:
|
||||
params['is_highlights'] = True
|
||||
|
||||
cls.objects(**params).update_one(inc__count=amount, upsert=True)
|
||||
try:
|
||||
|
|
|
@ -16,7 +16,7 @@ class FeedTest(TestCase):
|
|||
disconnect()
|
||||
settings.MONGODB = connect('test_newsblur')
|
||||
settings.REDIS_STORY_HASH_POOL = redis.ConnectionPool(host=settings.REDIS_STORY['host'], port=6379, db=10)
|
||||
settings.REDIS_FEED_READ_POOL = redis.ConnectionPool(host=settings.SESSION_REDIS_HOST, port=6379, db=10)
|
||||
settings.REDIS_FEED_READ_POOL = redis.ConnectionPool(host=settings.REDIS_SESSIONS['host'], port=6379, db=10)
|
||||
|
||||
r = redis.Redis(connection_pool=settings.REDIS_STORY_HASH_POOL)
|
||||
r.delete('RS:1')
|
||||
|
@ -33,9 +33,9 @@ class FeedTest(TestCase):
|
|||
def test_load_feeds__gawker(self):
|
||||
self.client.login(username='conesus', password='test')
|
||||
|
||||
management.call_command('loaddata', 'gawker1.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'gawker1.json', verbosity=0, commit=False, skip_checks=False)
|
||||
|
||||
feed = Feed.objects.get(feed_link__contains='gawker')
|
||||
feed = Feed.objects.get(pk=10)
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
self.assertEquals(stories.count(), 0)
|
||||
|
||||
|
@ -44,7 +44,7 @@ class FeedTest(TestCase):
|
|||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
self.assertEquals(stories.count(), 38)
|
||||
|
||||
management.call_command('loaddata', 'gawker2.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'gawker2.json', verbosity=0, commit=False, skip_checks=False)
|
||||
|
||||
feed.update(force=True)
|
||||
|
||||
|
@ -52,7 +52,7 @@ class FeedTest(TestCase):
|
|||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
self.assertEquals(stories.count(), 38)
|
||||
|
||||
url = reverse('load-single-feed', kwargs=dict(feed_id=1))
|
||||
url = reverse('load-single-feed', kwargs=dict(feed_id=10))
|
||||
response = self.client.get(url)
|
||||
feed = json.decode(response.content)
|
||||
self.assertEquals(len(feed['stories']), 6)
|
||||
|
@ -60,7 +60,7 @@ class FeedTest(TestCase):
|
|||
def test_load_feeds__gothamist(self):
|
||||
self.client.login(username='conesus', password='test')
|
||||
|
||||
management.call_command('loaddata', 'gothamist_aug_2009_1.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'gothamist_aug_2009_1.json', verbosity=0, commit=False, skip_checks=False)
|
||||
feed = Feed.objects.get(feed_link__contains='gothamist')
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
self.assertEquals(stories.count(), 0)
|
||||
|
@ -75,7 +75,7 @@ class FeedTest(TestCase):
|
|||
content = json.decode(response.content)
|
||||
self.assertEquals(len(content['stories']), 6)
|
||||
|
||||
management.call_command('loaddata', 'gothamist_aug_2009_2.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'gothamist_aug_2009_2.json', verbosity=0, commit=False, skip_checks=False)
|
||||
feed.update(force=True)
|
||||
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
|
@ -93,7 +93,7 @@ class FeedTest(TestCase):
|
|||
|
||||
old_story_guid = "tag:google.com,2005:reader/item/4528442633bc7b2b"
|
||||
|
||||
management.call_command('loaddata', 'slashdot1.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'slashdot1.json', verbosity=0, commit=False, skip_checks=False)
|
||||
|
||||
feed = Feed.objects.get(feed_link__contains='slashdot')
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
|
@ -114,7 +114,7 @@ class FeedTest(TestCase):
|
|||
content = json.decode(response.content)
|
||||
self.assertEquals(content['feeds']['5']['nt'], 37)
|
||||
|
||||
management.call_command('loaddata', 'slashdot2.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'slashdot2.json', verbosity=0, commit=False, skip_checks=False)
|
||||
management.call_command('refresh_feed', force=1, feed=5, single_threaded=True, daemonize=False, skip_checks=False)
|
||||
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
|
@ -136,7 +136,7 @@ class FeedTest(TestCase):
|
|||
def test_load_feeds__motherjones(self):
|
||||
self.client.login(username='conesus', password='test')
|
||||
|
||||
management.call_command('loaddata', 'motherjones1.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'motherjones1.json', verbosity=0, commit=False, skip_checks=False)
|
||||
|
||||
feed = Feed.objects.get(feed_link__contains='motherjones')
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
|
@ -157,7 +157,7 @@ class FeedTest(TestCase):
|
|||
content = json.decode(response.content)
|
||||
self.assertEquals(content['feeds'][str(feed.pk)]['nt'], 9)
|
||||
|
||||
management.call_command('loaddata', 'motherjones2.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'motherjones2.json', verbosity=0, commit=False, skip_checks=False)
|
||||
management.call_command('refresh_feed', force=1, feed=feed.pk, single_threaded=True, daemonize=False, skip_checks=False)
|
||||
|
||||
stories = MStory.objects(story_feed_id=feed.pk)
|
||||
|
@ -225,19 +225,23 @@ class FeedTest(TestCase):
|
|||
self.assertEquals(content['feeds']['766']['nt'], 19)
|
||||
|
||||
def test_load_feeds__brokelyn__invalid_xml(self):
|
||||
BROKELYN_FEED_ID = 16
|
||||
self.client.login(username='conesus', password='test')
|
||||
management.call_command('loaddata', 'brokelyn.json', verbosity=0, commit=False)
|
||||
self.assertEquals(Feed.objects.get(pk=BROKELYN_FEED_ID).pk, BROKELYN_FEED_ID)
|
||||
management.call_command('refresh_feed', force=1, feed=BROKELYN_FEED_ID, single_threaded=True, daemonize=False)
|
||||
|
||||
management.call_command('loaddata', 'brokelyn.json', verbosity=0, skip_checks=False)
|
||||
management.call_command('loaddata', 'brokelyn.json', verbosity=0, commit=False, skip_checks=False)
|
||||
management.call_command('refresh_feed', force=1, feed=6, single_threaded=True, daemonize=False, skip_checks=False)
|
||||
|
||||
url = reverse('load-single-feed', kwargs=dict(feed_id=6))
|
||||
url = reverse('load-single-feed', kwargs=dict(feed_id=BROKELYN_FEED_ID))
|
||||
response = self.client.get(url)
|
||||
|
||||
# pprint([c['story_title'] for c in json.decode(response.content)])
|
||||
feed = json.decode(response.content)
|
||||
|
||||
# Test: 1 changed char in title
|
||||
self.assertEquals(len(feed['stories']), 6)
|
||||
self.assertEquals(len(feed['stories']), 0)
|
||||
|
||||
def test_all_feeds(self):
|
||||
pass
|
||||
|
|
|
@ -1255,8 +1255,10 @@ class MSocialSubscription(mongo.Document):
|
|||
cutoff_date = cutoff_date + datetime.timedelta(seconds=1)
|
||||
else:
|
||||
# Use the latest story to get last read time.
|
||||
now = datetime.datetime.now()
|
||||
latest_shared_story = MSharedStory.objects(user_id=self.subscription_user_id,
|
||||
shared_date__gte=user_profile.unread_cutoff
|
||||
shared_date__gte=user_profile.unread_cutoff,
|
||||
story_date__lte=now
|
||||
).order_by('-shared_date').only('shared_date').first()
|
||||
if latest_shared_story:
|
||||
cutoff_date = latest_shared_story['shared_date'] + datetime.timedelta(seconds=1)
|
||||
|
|
|
@ -1304,6 +1304,7 @@ def shared_stories_rss_feed_noid(request):
|
|||
|
||||
return index
|
||||
|
||||
@ratelimit(minutes=1, requests=5)
|
||||
def shared_stories_rss_feed(request, user_id, username):
|
||||
try:
|
||||
user = User.objects.get(pk=user_id)
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.newsblur"
|
||||
android:versionCode="167"
|
||||
android:versionName="10.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="21"
|
||||
android:targetSdkVersion="28" />
|
||||
package="com.newsblur">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
@ -132,6 +126,9 @@
|
|||
<activity
|
||||
android:name=".activity.FolderReading"/>
|
||||
|
||||
<activity
|
||||
android:name=".activity.InAppBrowser" />
|
||||
|
||||
<activity
|
||||
android:name=".activity.SearchForFeeds" android:launchMode="singleTop" >
|
||||
<intent-filter>
|
||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,27 +22,29 @@ repositories {
|
|||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: 'findbugs'
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-core-utils:27.1.1'
|
||||
compile 'com.android.support:support-fragment:27.1.1'
|
||||
compile 'com.android.support:support-core-ui:27.1.1'
|
||||
compile 'com.jakewharton:butterknife:7.0.1'
|
||||
compile 'com.squareup.okhttp3:okhttp:3.8.1'
|
||||
compile 'com.google.code.gson:gson:2.8.2'
|
||||
compile 'com.android.support:recyclerview-v7:27.1.1'
|
||||
implementation 'com.android.support:support-core-utils:28.0.0'
|
||||
implementation 'com.android.support:support-fragment:28.0.0'
|
||||
implementation 'com.android.support:support-core-ui:28.0.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.12'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.android.support:recyclerview-v7:28.0.0'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.3'
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
applicationId "com.newsblur"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode 168
|
||||
versionName "10.1"
|
||||
}
|
||||
compileOptions.with {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
// force old processing behaviour that butterknife 7 depends on, until we can afford to upgrade
|
||||
android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
|
||||
viewBinding.enabled = true
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
|
|
|
@ -11,16 +11,6 @@
|
|||
-dontwarn okhttp3.**
|
||||
-dontnote okhttp3.**
|
||||
|
||||
-keep class butterknife.** { *; }
|
||||
-dontwarn butterknife.internal.**
|
||||
-keep class **$$ViewBinder { *; }
|
||||
-keepclasseswithmembernames class * {
|
||||
@butterknife.* <fields>;
|
||||
}
|
||||
-keepclasseswithmembernames class * {
|
||||
@butterknife.* <methods>;
|
||||
}
|
||||
|
||||
# these two seem to confuse ProGuard, so force keep them
|
||||
-keep class com.newsblur.util.StateFilter { *; }
|
||||
-keep class com.newsblur.view.StateToggleButton$StateChangedListener { *; }
|
||||
|
|
10
clients/android/NewsBlur/res/drawable/ic_create_folder.xml
Normal file
10
clients/android/NewsBlur/res/drawable/ic_create_folder.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#7B7B7B"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM19,14h-3v3h-2v-3h-3v-2h3L14,9h2v3h3v2z" />
|
||||
</vector>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<WebView
|
||||
android:id="@+id/web_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:max="100"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
88
clients/android/NewsBlur/res/layout/dialog_add_feed.xml
Normal file
88
clients/android/NewsBlur/res/layout/dialog_add_feed.xml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_sync_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/status_overlay_background"
|
||||
android:gravity="center"
|
||||
android:padding="2dp"
|
||||
android:text="@string/sync_status_feed_add"
|
||||
android:textColor="@color/status_overlay_text"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_add_folder_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="14dp"
|
||||
android:text="@string/add_new_folder"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_add_folder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_folder_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:autofillHints="@null"
|
||||
android:textSize="14sp"
|
||||
android:hint="@string/new_folder_name_hint"
|
||||
android:inputType="textCapCharacters"
|
||||
android:maxLines="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ic_create_folder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:contentDescription="@string/description_add_new_folder_icon"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_create_folder" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view_folders"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,17 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp" >
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/feed_name_field"
|
||||
android:id="@+id/input_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:singleLine="true"
|
||||
/>
|
||||
android:layout_marginBottom="10dp"
|
||||
android:autofillHints="@null"
|
||||
android:hint="@string/new_folder_name_hint"
|
||||
android:inputType="textCapSentences"
|
||||
android:singleLine="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/text_folder_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="42dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp" />
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="?selectorFeedBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/row_saved_search_icon"
|
||||
android:layout_width="19dp"
|
||||
android:layout_height="19dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:contentDescription="@string/description_row_folder_icon"
|
||||
android:src="@drawable/ic_menu_search_gray55" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/row_saved_search_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/row_saved_search_icon"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingTop="9dp"
|
||||
android:paddingEnd="9dp"
|
||||
android:paddingBottom="9dp" />
|
||||
|
||||
<View
|
||||
style="?rowBorderTop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
<View
|
||||
style="?rowBorderBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</RelativeLayout>
|
41
clients/android/NewsBlur/res/layout/row_saved_searches.xml
Normal file
41
clients/android/NewsBlur/res/layout/row_saved_searches.xml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="?selectorFolderBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/row_folder_icon"
|
||||
android:layout_width="19dp"
|
||||
android:layout_height="19dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:contentDescription="@string/description_row_folder_icon"
|
||||
android:src="@drawable/ic_menu_search_gray55" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/row_foldername"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/row_folder_icon"
|
||||
android:paddingTop="9dp"
|
||||
android:paddingBottom="9dp"
|
||||
android:text="@string/saved_searches_row_title"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
style="?rowBorderTop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
<View
|
||||
style="?rowBorderBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -44,4 +44,7 @@
|
|||
|
||||
<item android:id="@+id/menu_intel"
|
||||
android:title="@string/menu_intel" />
|
||||
|
||||
<item android:id="@+id/menu_delete_saved_search"
|
||||
android:title="@string/menu_delete_saved_search" />
|
||||
</menu>
|
||||
|
|
|
@ -10,4 +10,10 @@
|
|||
<item android:id="@+id/menu_unmute_folder"
|
||||
android:title="@string/menu_unmute_folder" />
|
||||
|
||||
<item android:id="@+id/menu_delete_folder"
|
||||
android:title="@string/menu_delete_folder" />
|
||||
|
||||
<item android:id="@+id/menu_rename_folder"
|
||||
android:title="@string/menu_rename_folder" />
|
||||
|
||||
</menu>
|
|
@ -34,6 +34,8 @@
|
|||
android:title="@string/menu_theme_choose" >
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<item android:id="@+id/menu_theme_auto"
|
||||
android:title="@string/auto" />
|
||||
<item android:id="@+id/menu_theme_light"
|
||||
android:title="@string/light" />
|
||||
<item android:id="@+id/menu_theme_dark"
|
||||
|
@ -72,4 +74,8 @@
|
|||
<item android:id="@+id/menu_infrequent_cutoff"
|
||||
android:title="@string/menu_infrequent_cutoff"
|
||||
android:showAsAction="never" />
|
||||
<item android:id="@+id/menu_save_search"
|
||||
android:title="Save Search"
|
||||
android:showAsAction="never"
|
||||
android:visible="false"/>
|
||||
</menu>
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
android:title="@string/menu_theme_choose" >
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<item android:id="@+id/menu_theme_auto"
|
||||
android:title="@string/auto" />
|
||||
<item android:id="@+id/menu_theme_light"
|
||||
android:title="@string/light" />
|
||||
<item android:id="@+id/menu_theme_dark"
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
android:title="@string/menu_theme_choose" >
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<item android:id="@+id/menu_theme_auto"
|
||||
android:title="@string/auto" />
|
||||
<item android:id="@+id/menu_theme_light"
|
||||
android:title="@string/light" />
|
||||
<item android:id="@+id/menu_theme_dark"
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
<string name="title_choose_folders">Choose Folders for Feed %s</string>
|
||||
<string name="title_rename_feed">Rename Feed %s</string>
|
||||
<string name="title_rename_folder">Rename Folder %s</string>
|
||||
|
||||
<string name="all_stories_row_title">ALL STORIES</string>
|
||||
<string name="all_stories_title">All Stories</string>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<string name="read_stories_title">Read Stories</string>
|
||||
<string name="saved_stories_row_title">SAVED STORIES</string>
|
||||
<string name="saved_stories_title">Saved Stories</string>
|
||||
<string name="saved_searches_row_title">SAVED SEARCHES</string>
|
||||
|
||||
<string name="top_level">TOP LEVEL</string>
|
||||
|
||||
|
@ -63,6 +65,7 @@
|
|||
<string name="unshare">DELETE SHARE</string>
|
||||
<string name="update_shared">UPDATE COMMENT</string>
|
||||
<string name="feed_name_save">RENAME FEED</string>
|
||||
<string name="folder_name_save">RENAME FOLDER</string>
|
||||
|
||||
<string name="save_this">SAVE</string>
|
||||
<string name="unsave_this">REMOVE FROM SAVED</string>
|
||||
|
@ -132,6 +135,7 @@
|
|||
<string name="menu_send_story_full">Send story to…</string>
|
||||
<string name="menu_mark_feed_as_read">Mark feed as read</string>
|
||||
<string name="menu_delete_feed">Delete feed</string>
|
||||
<string name="menu_delete_saved_search">Delete saved search</string>
|
||||
<string name="menu_unfollow">Unfollow user</string>
|
||||
<string name="menu_choose_folders">Choose folders</string>
|
||||
<string name="menu_notifications_choose">Notifications…</string>
|
||||
|
@ -156,6 +160,8 @@
|
|||
<string name="menu_unmute_feed">Unmute feed</string>
|
||||
<string name="menu_mute_folder">Mute folder</string>
|
||||
<string name="menu_unmute_folder">Unmute folder</string>
|
||||
<string name="menu_delete_folder">Delete folder</string>
|
||||
<string name="menu_rename_folder">Rename folder</string>
|
||||
<string name="menu_instafetch_feed">Insta-fetch stories</string>
|
||||
<string name="menu_infrequent_cutoff">Infrequent stories per month</string>
|
||||
<string name="menu_intel">Intelligence trainer</string>
|
||||
|
@ -196,7 +202,11 @@
|
|||
<string name="menu_add_feed">Add new feed</string>
|
||||
<string name="menu_search">Search</string>
|
||||
<string name="adding_feed_progress">Adding feed…</string>
|
||||
<string name="add_folder_name">Please enter folder name</string>
|
||||
<string name="add_new_folder">+ Add new folder</string>
|
||||
<string name="new_folder_name_hint">Enter new folder name</string>
|
||||
<string name="add_feed_error">Could not add feed</string>
|
||||
<string name="add_folder_error">Could not add folder</string>
|
||||
<string name="menu_mark_all_as_read">Mark all as read</string>
|
||||
<string name="menu_logout">Log out</string>
|
||||
<string name="menu_feedback">Send app feedback</string>
|
||||
|
@ -204,6 +214,8 @@
|
|||
<string name="menu_feedback_email">Email a bug report</string>
|
||||
<string name="menu_theme_choose">Theme…</string>
|
||||
|
||||
<string name="description_add_new_folder_icon">Add new folder icon</string>
|
||||
|
||||
<string name="menu_loginas">Login as...</string>
|
||||
|
||||
<string name="loginas_title">Login As User</string>
|
||||
|
@ -234,6 +246,9 @@
|
|||
<string name="friends_shares_count">%d SHARES</string>
|
||||
<string name="unknown_user">Unknown User</string>
|
||||
<string name="delete_feed_message">Delete feed \"%s\"?</string>
|
||||
<string name="delete_saved_search_message">Delete saved search %s?</string>
|
||||
<string name="delete_folder_message">Delete folder \"%s\" and unsubscribe from all feeds inside?</string>
|
||||
<string name="add_saved_search_message">Save search \"%s\"?</string>
|
||||
<string name="unfollow_message">Unfollow \"%s\"?</string>
|
||||
<string name="feed_subscribers">%s subscribers</string>
|
||||
<string name="feed_opens">%d opens</string>
|
||||
|
@ -387,6 +402,7 @@
|
|||
<string name="feed_intel_author_header">AUTHORS</string>
|
||||
<string name="intel_feed_header">EVERYTHING BY PUBLISHER</string>
|
||||
|
||||
<string name="auto">Auto</string>
|
||||
<string name="light">Light</string>
|
||||
<string name="dark">Dark</string>
|
||||
<string name="black">Black</string>
|
||||
|
@ -403,6 +419,7 @@
|
|||
<string name="sync_status_text">Storing text for %s stories...</string>
|
||||
<string name="sync_status_images">Storing %s images...</string>
|
||||
<string name="sync_status_offline">Offline</string>
|
||||
<string name="sync_status_feed_add">Adding feed …</string>
|
||||
|
||||
<string name="volume_key_navigation">Volume Key Navigation</string>
|
||||
<string name="off">Off</string>
|
||||
|
@ -477,6 +494,7 @@
|
|||
</string-array>
|
||||
<string name="rtl_gesture_action_value">GEST_ACTION_MARKUNREAD</string>
|
||||
|
||||
<string name="default_browser">Default browser</string>
|
||||
<string name="font">Font</string>
|
||||
<string name="default_font">Default</string>
|
||||
<string name="whitney_font">Whitney</string>
|
||||
|
@ -494,6 +512,25 @@
|
|||
<string name="noto_sans_font_prefvalue">NOTO_SANS</string>
|
||||
<string name="noto_serif_font_prefvalue">NOTO_SERIF</string>
|
||||
<string name="open_sans_condensed_font_prefvalue">OPEN_SANS_CONDENSED</string>
|
||||
<string name="system_default_prefvalue">SYSTEM_DEFAULT</string>
|
||||
<string name="in_app_browser_prefvalue">IN_APP_BROWSER</string>
|
||||
<string name="chrome_prefvalue">CHROME</string>
|
||||
<string name="firefox_prefvalue">FIREFOX</string>
|
||||
<string name="opera_mini_prefvalue">OPERA_MINI</string>
|
||||
<string-array name="default_browser_entries">
|
||||
<item>System default</item>
|
||||
<item>In-app browser</item>
|
||||
<item>Chrome</item>
|
||||
<item>Firefox</item>
|
||||
<item>Opera Mini</item>
|
||||
</string-array>
|
||||
<string-array name="default_browser_values">
|
||||
<item>@string/system_default_prefvalue</item>
|
||||
<item>@string/in_app_browser_prefvalue</item>
|
||||
<item>@string/chrome_prefvalue</item>
|
||||
<item>@string/firefox_prefvalue</item>
|
||||
<item>@string/opera_mini_prefvalue</item>
|
||||
</string-array>
|
||||
<string-array name="default_font_entries">
|
||||
<item>@string/anonymous_pro_font</item>
|
||||
<item>@string/chronicle_font</item>
|
||||
|
@ -515,6 +552,7 @@
|
|||
<item>@string/whitney_font_prefvalue</item>
|
||||
</string-array>
|
||||
<string name="default_font_value">DEFAULT</string>
|
||||
<string name="default_default_browser">@string/system_default_prefvalue</string>
|
||||
|
||||
<string name="story_notification_channel_id">story_notification_channel</string>
|
||||
<string name="story_notification_channel_name">New Stories</string>
|
||||
|
|
|
@ -105,6 +105,13 @@
|
|||
<PreferenceCategory
|
||||
android:title="@string/settings_reading"
|
||||
android:key="reading">
|
||||
<ListPreference
|
||||
android:key="default_browser"
|
||||
android:title="@string/default_browser"
|
||||
android:dialogTitle="@string/default_browser"
|
||||
android:entries="@array/default_browser_entries"
|
||||
android:entryValues="@array/default_browser_values"
|
||||
android:defaultValue="@string/default_default_browser" />
|
||||
<ListPreference
|
||||
android:key="reading_font"
|
||||
android:title="@string/font"
|
||||
|
|
|
@ -5,32 +5,27 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ActivityAddfeedexternalBinding;
|
||||
import com.newsblur.fragment.AddFeedFragment;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.ProgressThrobber;
|
||||
|
||||
public class AddFeedExternal extends NbActivity implements AddFeedFragment.AddFeedProgressListener {
|
||||
|
||||
@Bind(R.id.loading_throb) ProgressThrobber progressView;
|
||||
@Bind(R.id.progress_text) TextView progressText;
|
||||
private ActivityAddfeedexternalBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityAddfeedexternalBinding.inflate(getLayoutInflater());
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
setContentView(R.layout.activity_addfeedexternal);
|
||||
ButterKnife.bind(this);
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
progressView.setEnabled(!ViewUtils.isPowerSaveMode(this));
|
||||
progressView.setColors(UIUtils.getColor(this, R.color.refresh_1),
|
||||
binding.loadingThrob.setEnabled(!ViewUtils.isPowerSaveMode(this));
|
||||
binding.loadingThrob.setColors(UIUtils.getColor(this, R.color.refresh_1),
|
||||
UIUtils.getColor(this, R.color.refresh_2),
|
||||
UIUtils.getColor(this, R.color.refresh_3),
|
||||
UIUtils.getColor(this, R.color.refresh_4));
|
||||
|
@ -48,9 +43,9 @@ public class AddFeedExternal extends NbActivity implements AddFeedFragment.AddFe
|
|||
public void addFeedStarted() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progressText.setText(R.string.adding_feed_progress);
|
||||
progressText.setVisibility(View.VISIBLE);
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
binding.progressText.setText(R.string.adding_feed_progress);
|
||||
binding.progressText.setVisibility(View.VISIBLE);
|
||||
binding.loadingThrob.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,4 +14,9 @@ public class AllSharedStoriesItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, R.drawable.ak_icon_blurblogs, getResources().getString(R.string.all_shared_stories_title));
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
// doesn't have save search option
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,9 @@ public class AllStoriesItemsList extends ItemsList {
|
|||
UIUtils.startReadingActivity(fs, hash, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "river:";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.newsblur.R;
|
|||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.fragment.DeleteFeedFragment;
|
||||
import com.newsblur.fragment.FeedIntelTrainerFragment;
|
||||
import com.newsblur.fragment.RenameFeedFragment;
|
||||
import com.newsblur.fragment.RenameDialogFragment;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
@ -79,8 +79,8 @@ public class FeedItemsList extends ItemsList {
|
|||
return true;
|
||||
}
|
||||
if (item.getItemId() == R.id.menu_rename_feed) {
|
||||
RenameFeedFragment frag = RenameFeedFragment.newInstance(feed);
|
||||
frag.show(getSupportFragmentManager(), RenameFeedFragment.class.getName());
|
||||
RenameDialogFragment frag = RenameDialogFragment.newInstance(feed);
|
||||
frag.show(getSupportFragmentManager(), RenameDialogFragment.class.getName());
|
||||
return true;
|
||||
// TODO: since this activity uses a feed object passed as an extra and doesn't query the DB,
|
||||
// the name change won't be reflected until the activity finishes.
|
||||
|
@ -118,4 +118,8 @@ public class FeedItemsList extends ItemsList {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "feed:" + feed.feedId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,8 @@ public class FolderItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, R.drawable.g_icn_folder_rss, folderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "river:" + folderName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,4 +14,9 @@ public class GlobalSharedStoriesItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, R.drawable.ak_icon_global, getResources().getString(R.string.global_shared_stories_title));
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
// doesn't have save search option
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package com.newsblur.activity;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import com.newsblur.databinding.ActivityInAppBrowserBinding;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
||||
public class InAppBrowser extends FragmentActivity {
|
||||
|
||||
public static final String URI = "uri";
|
||||
|
||||
private ActivityInAppBrowserBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
PrefsUtils.applyThemePreference(this);
|
||||
binding = ActivityInAppBrowserBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
String url = getIntent().getParcelableExtra(URI).toString();
|
||||
|
||||
binding.webView.getSettings().setJavaScriptEnabled(true);
|
||||
binding.webView.getSettings().setLoadWithOverviewMode(true);
|
||||
binding.webView.getSettings().setSupportZoom(true);
|
||||
|
||||
binding.webView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
binding.webView.setWebChromeClient(new WebChromeClient() {
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
super.onProgressChanged(view, newProgress);
|
||||
binding.progressBar.setProgress(newProgress);
|
||||
}
|
||||
});
|
||||
|
||||
binding.webView.loadUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (binding.webView.canGoBack()) {
|
||||
binding.webView.goBack();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,4 +37,8 @@ public class InfrequentItemsList extends ItemsList implements InfrequentCutoffCh
|
|||
restartReadingSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "river:infrequent";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,14 @@ import android.view.MenuInflater;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ActivityItemslistBinding;
|
||||
import com.newsblur.fragment.ItemSetFragment;
|
||||
import com.newsblur.fragment.ReadFilterDialogFragment;
|
||||
import com.newsblur.fragment.SaveSearchFragment;
|
||||
import com.newsblur.fragment.StoryOrderDialogFragment;
|
||||
import com.newsblur.fragment.TextSizeDialogFragment;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
|
@ -46,10 +43,9 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
private static final String READ_FILTER = "readFilter";
|
||||
private static final String DEFAULT_FEED_VIEW = "defaultFeedView";
|
||||
private static final String BUNDLE_ACTIVE_SEARCH_QUERY = "activeSearchQuery";
|
||||
private ActivityItemslistBinding binding;
|
||||
|
||||
protected ItemSetFragment itemSetFragment;
|
||||
@Bind(R.id.itemlist_sync_status) TextView overlayStatusText;
|
||||
@Bind(R.id.itemlist_search_query) EditText searchQueryInput;
|
||||
protected StateFilter intelState;
|
||||
|
||||
protected FeedSet fs;
|
||||
|
@ -79,8 +75,8 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
setContentView(R.layout.activity_itemslist);
|
||||
ButterKnife.bind(this);
|
||||
binding = ActivityItemslistBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
itemSetFragment = (ItemSetFragment) fragmentManager.findFragmentByTag(ItemSetFragment.class.getName());
|
||||
|
@ -92,19 +88,23 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
transaction.commit();
|
||||
}
|
||||
|
||||
String activeSearchQuery;
|
||||
if (bundle != null) {
|
||||
String activeSearchQuery = bundle.getString(BUNDLE_ACTIVE_SEARCH_QUERY);
|
||||
activeSearchQuery = bundle.getString(BUNDLE_ACTIVE_SEARCH_QUERY);
|
||||
} else {
|
||||
activeSearchQuery = fs.getSearchQuery();
|
||||
}
|
||||
if (activeSearchQuery != null) {
|
||||
searchQueryInput.setText(activeSearchQuery);
|
||||
searchQueryInput.setVisibility(View.VISIBLE);
|
||||
binding.itemlistSearchQuery.setText(activeSearchQuery);
|
||||
binding.itemlistSearchQuery.setVisibility(View.VISIBLE);
|
||||
fs.setSearchQuery(activeSearchQuery);
|
||||
}
|
||||
}
|
||||
searchQueryInput.setOnKeyListener(new OnKeyListener() {
|
||||
|
||||
binding.itemlistSearchQuery.setOnKeyListener(new OnKeyListener() {
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
|
||||
searchQueryInput.setVisibility(View.GONE);
|
||||
searchQueryInput.setText("");
|
||||
binding.itemlistSearchQuery.setVisibility(View.GONE);
|
||||
binding.itemlistSearchQuery.setText("");
|
||||
checkSearchQuery();
|
||||
return true;
|
||||
}
|
||||
|
@ -120,8 +120,8 @@ 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 (binding.itemlistSearchQuery != null) {
|
||||
String q = binding.itemlistSearchQuery.getText().toString().trim();
|
||||
if (q.length() > 0) {
|
||||
outState.putString(BUNDLE_ACTIVE_SEARCH_QUERY, q);
|
||||
}
|
||||
|
@ -222,6 +222,14 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
menu.findItem(R.id.menu_theme_dark).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.BLACK) {
|
||||
menu.findItem(R.id.menu_theme_black).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.AUTO) {
|
||||
menu.findItem(R.id.menu_theme_auto).setChecked(true);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(binding.itemlistSearchQuery.getText())) {
|
||||
menu.findItem(R.id.menu_save_search).setVisible(true);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_save_search).setVisible(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -250,13 +258,16 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
textSize.show(getSupportFragmentManager(), TextSizeDialogFragment.class.getName());
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_search_stories) {
|
||||
if (searchQueryInput.getVisibility() != View.VISIBLE) {
|
||||
searchQueryInput.setVisibility(View.VISIBLE);
|
||||
searchQueryInput.requestFocus();
|
||||
if (binding.itemlistSearchQuery.getVisibility() != View.VISIBLE) {
|
||||
binding.itemlistSearchQuery.setVisibility(View.VISIBLE);
|
||||
binding.itemlistSearchQuery.requestFocus();
|
||||
} else {
|
||||
searchQueryInput.setVisibility(View.GONE);
|
||||
binding.itemlistSearchQuery.setVisibility(View.GONE);
|
||||
checkSearchQuery();
|
||||
}
|
||||
} else if(item.getItemId() == R.id.menu_theme_auto) {
|
||||
PrefsUtils.setSelectedTheme(this, ThemeValue.AUTO);
|
||||
UIUtils.restartActivity(this);
|
||||
} else if (item.getItemId() == R.id.menu_theme_light) {
|
||||
PrefsUtils.setSelectedTheme(this, ThemeValue.LIGHT);
|
||||
UIUtils.restartActivity(this);
|
||||
|
@ -279,6 +290,14 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
PrefsUtils.updateStoryListStyle(this, fs, StoryListStyle.GRID_C);
|
||||
itemSetFragment.updateStyle();
|
||||
}
|
||||
if (item.getItemId() == R.id.menu_save_search) {
|
||||
String feedId = getSaveSearchFeedId();
|
||||
if (feedId != null) {
|
||||
String query = binding.itemlistSearchQuery.getText().toString();
|
||||
SaveSearchFragment frag = SaveSearchFragment.newInstance(feedId, query);
|
||||
frag.show(getSupportFragmentManager(), SaveSearchFragment.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -315,23 +334,23 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
}
|
||||
|
||||
private void updateStatusIndicators() {
|
||||
if (overlayStatusText != null) {
|
||||
if (binding.itemlistSyncStatus != 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);
|
||||
binding.itemlistSyncStatus.setText(syncStatus);
|
||||
binding.itemlistSyncStatus.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
overlayStatusText.setVisibility(View.GONE);
|
||||
binding.itemlistSyncStatus.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSearchQuery() {
|
||||
String oldQuery = fs.getSearchQuery();
|
||||
String q = searchQueryInput.getText().toString().trim();
|
||||
String q = binding.itemlistSearchQuery.getText().toString().trim();
|
||||
if (q.length() < 1) {
|
||||
q = null;
|
||||
}
|
||||
|
@ -395,4 +414,6 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
|
|||
*/
|
||||
overridePendingTransition(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
|
||||
}
|
||||
|
||||
abstract String getSaveSearchFeedId();
|
||||
}
|
||||
|
|
|
@ -17,19 +17,12 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.View.OnKeyListener;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ActivityMainBinding;
|
||||
import com.newsblur.fragment.FeedIntelligenceSelectorFragment;
|
||||
import com.newsblur.fragment.FolderListFragment;
|
||||
import com.newsblur.fragment.LoginAsDialogFragment;
|
||||
|
@ -54,15 +47,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
private FragmentManager fragmentManager;
|
||||
private SwipeRefreshLayout swipeLayout;
|
||||
private boolean wasSwipeEnabled = false;
|
||||
@Bind(R.id.main_sync_status) TextView overlayStatusText;
|
||||
@Bind(R.id.empty_view_image) ImageView emptyViewImage;
|
||||
@Bind(R.id.empty_view_text) TextView emptyViewText;
|
||||
@Bind(R.id.main_menu_button) Button menuButton;
|
||||
@Bind(R.id.main_user_image) ImageView userImage;
|
||||
@Bind(R.id.main_user_name) TextView userName;
|
||||
@Bind(R.id.main_unread_count_neut_text) TextView unreadCountNeutText;
|
||||
@Bind(R.id.main_unread_count_posi_text) TextView unreadCountPosiText;
|
||||
@Bind(R.id.feedlist_search_query) EditText searchQueryInput;
|
||||
private ActivityMainBinding binding;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -70,16 +55,15 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
ButterKnife.bind(this);
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
getActionBar().hide();
|
||||
|
||||
// set the status bar to an generic loading message when the activity is first created so
|
||||
// that something is displayed while the service warms up
|
||||
overlayStatusText.setText(R.string.loading);
|
||||
overlayStatusText.setVisibility(View.VISIBLE);
|
||||
binding.mainSyncStatus.setText(R.string.loading);
|
||||
binding.mainSyncStatus.setVisibility(View.VISIBLE);
|
||||
|
||||
swipeLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container);
|
||||
swipeLayout.setColorSchemeResources(R.color.refresh_1, R.color.refresh_2, R.color.refresh_3, R.color.refresh_4);
|
||||
|
@ -88,7 +72,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
folderFeedList = (FolderListFragment) fragmentManager.findFragmentByTag("folderFeedListFragment");
|
||||
folderFeedList.setRetainInstance(true);
|
||||
((FeedIntelligenceSelectorFragment) fragmentManager.findFragmentByTag("feedIntelligenceSelector")).setState(folderFeedList.currentState);
|
||||
|
||||
// make sure the interval sync is scheduled, since we are the root Activity
|
||||
|
@ -97,14 +80,14 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
Bitmap userPicture = PrefsUtils.getUserImage(this);
|
||||
if (userPicture != null) {
|
||||
userPicture = UIUtils.clipAndRound(userPicture, 5, false);
|
||||
userImage.setImageBitmap(userPicture);
|
||||
binding.mainUserImage.setImageBitmap(userPicture);
|
||||
}
|
||||
userName.setText(PrefsUtils.getUserDetails(this).username);
|
||||
searchQueryInput.setOnKeyListener(new OnKeyListener() {
|
||||
binding.mainUserName.setText(PrefsUtils.getUserDetails(this).username);
|
||||
binding.feedlistSearchQuery.setOnKeyListener(new OnKeyListener() {
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
|
||||
searchQueryInput.setVisibility(View.GONE);
|
||||
searchQueryInput.setText("");
|
||||
binding.feedlistSearchQuery.setVisibility(View.GONE);
|
||||
binding.feedlistSearchQuery.setText("");
|
||||
checkSearchQuery();
|
||||
return true;
|
||||
}
|
||||
|
@ -115,7 +98,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
return false;
|
||||
}
|
||||
});
|
||||
searchQueryInput.addTextChangedListener(new TextWatcher() {
|
||||
binding.feedlistSearchQuery.addTextChangedListener(new TextWatcher() {
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
checkSearchQuery();
|
||||
}
|
||||
|
@ -124,6 +107,31 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
});
|
||||
|
||||
FeedUtils.currentFolderName = null;
|
||||
|
||||
binding.mainMenuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onClickMenuButton();
|
||||
}
|
||||
});
|
||||
binding.mainAddButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onClickAddButton();
|
||||
}
|
||||
});
|
||||
binding.mainProfileButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onClickProfileButton();
|
||||
}
|
||||
});
|
||||
binding.mainUserImage.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onClickUserButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,8 +157,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
}
|
||||
|
||||
if (folderFeedList.getSearchQuery() != null) {
|
||||
searchQueryInput.setText(folderFeedList.getSearchQuery());
|
||||
searchQueryInput.setVisibility(View.VISIBLE);
|
||||
binding.feedlistSearchQuery.setText(folderFeedList.getSearchQuery());
|
||||
binding.feedlistSearchQuery.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// triggerSync() might not actually do enough to push a UI update if background sync has been
|
||||
|
@ -172,8 +180,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
if ( !( (state == StateFilter.ALL) ||
|
||||
(state == StateFilter.SOME) ||
|
||||
(state == StateFilter.BEST) ) ) {
|
||||
searchQueryInput.setText("");
|
||||
searchQueryInput.setVisibility(View.GONE);
|
||||
binding.feedlistSearchQuery.setText("");
|
||||
binding.feedlistSearchQuery.setVisibility(View.GONE);
|
||||
checkSearchQuery();
|
||||
}
|
||||
|
||||
|
@ -201,8 +209,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
}
|
||||
|
||||
public void updateUnreadCounts(int neutCount, int posiCount) {
|
||||
unreadCountNeutText.setText(Integer.toString(neutCount));
|
||||
unreadCountPosiText.setText(Integer.toString(posiCount));
|
||||
binding.mainUnreadCountNeutText.setText(Integer.toString(neutCount));
|
||||
binding.mainUnreadCountPosiText.setText(Integer.toString(posiCount));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,22 +221,22 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
public void updateFeedCount(int feedCount) {
|
||||
if (feedCount < 1 ) {
|
||||
if (NBSyncService.isFeedCountSyncRunning() || (!folderFeedList.firstCursorSeenYet)) {
|
||||
emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
emptyViewText.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewText.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
emptyViewImage.setVisibility(View.VISIBLE);
|
||||
binding.emptyViewImage.setVisibility(View.VISIBLE);
|
||||
if (folderFeedList.currentState == StateFilter.BEST) {
|
||||
emptyViewText.setText(R.string.empty_list_view_no_focus_stories);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_no_focus_stories);
|
||||
} else if (folderFeedList.currentState == StateFilter.SAVED) {
|
||||
emptyViewText.setText(R.string.empty_list_view_no_saved_stories);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_no_saved_stories);
|
||||
} else {
|
||||
emptyViewText.setText(R.string.empty_list_view_no_unread_stories);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_no_unread_stories);
|
||||
}
|
||||
emptyViewText.setVisibility(View.VISIBLE);
|
||||
binding.emptyViewText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
emptyViewText.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,16 +247,16 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
swipeLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
if (overlayStatusText != null) {
|
||||
if (binding.mainSyncStatus != 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);
|
||||
binding.mainSyncStatus.setText(syncStatus);
|
||||
binding.mainSyncStatus.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
overlayStatusText.setVisibility(View.GONE);
|
||||
binding.mainSyncStatus.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,8 +268,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
folderFeedList.clearRecents();
|
||||
}
|
||||
|
||||
@OnClick(R.id.main_menu_button) void onClickMenuButton() {
|
||||
PopupMenu pm = new PopupMenu(this, menuButton);
|
||||
private void onClickMenuButton() {
|
||||
PopupMenu pm = new PopupMenu(this, binding.mainMenuButton);
|
||||
Menu menu = pm.getMenu();
|
||||
pm.getMenuInflater().inflate(R.menu.main, menu);
|
||||
|
||||
|
@ -294,6 +302,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
menu.findItem(R.id.menu_theme_dark).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.BLACK) {
|
||||
menu.findItem(R.id.menu_theme_black).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.AUTO) {
|
||||
menu.findItem(R.id.menu_theme_auto).setChecked(true);
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_widget).setVisible(WidgetUtils.hasActiveAppWidgets(this));
|
||||
|
@ -308,12 +318,12 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
onRefresh();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_search_feeds) {
|
||||
if (searchQueryInput.getVisibility() != View.VISIBLE) {
|
||||
searchQueryInput.setVisibility(View.VISIBLE);
|
||||
searchQueryInput.requestFocus();
|
||||
if (binding.feedlistSearchQuery.getVisibility() != View.VISIBLE) {
|
||||
binding.feedlistSearchQuery.setVisibility(View.VISIBLE);
|
||||
binding.feedlistSearchQuery.requestFocus();
|
||||
} else {
|
||||
searchQueryInput.setText("");
|
||||
searchQueryInput.setVisibility(View.GONE);
|
||||
binding.feedlistSearchQuery.setText("");
|
||||
binding.feedlistSearchQuery.setVisibility(View.GONE);
|
||||
checkSearchQuery();
|
||||
}
|
||||
} else if (item.getItemId() == R.id.menu_add_feed) {
|
||||
|
@ -351,6 +361,9 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
DialogFragment newFragment = new LoginAsDialogFragment();
|
||||
newFragment.show(getSupportFragmentManager(), "dialog");
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_theme_auto) {
|
||||
PrefsUtils.setSelectedTheme(this, ThemeValue.AUTO);
|
||||
UIUtils.restartActivity(this);
|
||||
} else if (item.getItemId() == R.id.menu_theme_light) {
|
||||
PrefsUtils.setSelectedTheme(this, ThemeValue.LIGHT);
|
||||
UIUtils.restartActivity(this);
|
||||
|
@ -364,17 +377,17 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
return false;
|
||||
}
|
||||
|
||||
@OnClick(R.id.main_add_button) void onClickAddButton() {
|
||||
private void onClickAddButton() {
|
||||
Intent i = new Intent(this, SearchForFeeds.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@OnClick(R.id.main_profile_button) void onClickProfileButton() {
|
||||
private void onClickProfileButton() {
|
||||
Intent i = new Intent(this, Profile.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@OnClick(R.id.main_user_image) void onClickUserButton() {
|
||||
private void onClickUserButton() {
|
||||
Intent i = new Intent(this, Profile.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
@ -404,7 +417,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
}
|
||||
|
||||
private void checkSearchQuery() {
|
||||
String q = searchQueryInput.getText().toString().trim();
|
||||
String q = binding.feedlistSearchQuery.getText().toString().trim();
|
||||
if (q.length() < 1) {
|
||||
q = null;
|
||||
}
|
||||
|
|
|
@ -14,4 +14,8 @@ public class ReadStoriesItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, R.drawable.g_icn_unread_double, getResources().getString(R.string.read_stories_title));
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "read";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
@ -19,18 +20,14 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.database.ReadingAdapter;
|
||||
import com.newsblur.databinding.ActivityReadingBinding;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
import com.newsblur.fragment.ReadingPagerFragment;
|
||||
|
@ -77,19 +74,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
protected final Object STORIES_MUTEX = new Object();
|
||||
protected Cursor stories;
|
||||
|
||||
@Bind(android.R.id.content) View contentView; // we use this a ton, so cache it
|
||||
@Bind(R.id.reading_overlay_left) Button overlayLeft;
|
||||
@Bind(R.id.reading_overlay_right) Button overlayRight;
|
||||
@Bind(R.id.reading_overlay_progress) ProgressBar overlayProgress;
|
||||
@Bind(R.id.reading_overlay_progress_right) ProgressBar overlayProgressRight;
|
||||
@Bind(R.id.reading_overlay_progress_left) ProgressBar overlayProgressLeft;
|
||||
@Bind(R.id.reading_overlay_text) Button overlayText;
|
||||
@Bind(R.id.reading_overlay_send) Button overlaySend;
|
||||
@Bind(R.id.reading_empty_view_text) View emptyViewText;
|
||||
@Bind(R.id.reading_sync_status) TextView overlayStatusText;
|
||||
private View contentView; // we use this a ton, so cache it
|
||||
|
||||
ViewPager pager;
|
||||
ReadingPagerFragment readingFragment;
|
||||
|
||||
protected ReadingAdapter readingAdapter;
|
||||
private boolean stopLoading;
|
||||
|
@ -109,6 +96,8 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
|
||||
private VolumeKeyNavigation volumeKeyNavigation;
|
||||
|
||||
private ActivityReadingBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
@ -119,8 +108,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
super.onCreate(savedInstanceBundle);
|
||||
|
||||
setContentView(R.layout.activity_reading);
|
||||
ButterKnife.bind(this);
|
||||
binding = ActivityReadingBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
contentView = findViewById(android.R.id.content);
|
||||
|
||||
try {
|
||||
fs = (FeedSet)getIntent().getSerializableExtra(EXTRA_FEEDSET);
|
||||
|
@ -167,17 +157,17 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
|
||||
this.pageHistory = new ArrayList<Story>();
|
||||
|
||||
ViewUtils.setViewElevation(overlayLeft, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlayRight, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlayText, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlaySend, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlayProgress, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlayProgressLeft, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(overlayProgressRight, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayLeft, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayRight, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayText, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlaySend, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayProgress, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayProgressLeft, OVERLAY_ELEVATION_DP);
|
||||
ViewUtils.setViewElevation(binding.readingOverlayProgressRight, OVERLAY_ELEVATION_DP);
|
||||
|
||||
// this likes to default to 'on' for some platforms
|
||||
enableProgressCircle(overlayProgressLeft, false);
|
||||
enableProgressCircle(overlayProgressRight, false);
|
||||
enableProgressCircle(binding.readingOverlayProgressLeft, false);
|
||||
enableProgressCircle(binding.readingOverlayProgressRight, false);
|
||||
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
ReadingPagerFragment fragment = (ReadingPagerFragment) fragmentManager.findFragmentByTag(ReadingPagerFragment.class.getName());
|
||||
|
@ -259,6 +249,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
// the system can and will re-use activities, so during the initial mismatch of
|
||||
// data, don't show the old stories
|
||||
pager.setVisibility(View.INVISIBLE);
|
||||
binding.readingEmptyViewText.setVisibility(View.VISIBLE);
|
||||
stories = null;
|
||||
triggerRefresh(AppConstants.READING_STORY_PRELOAD);
|
||||
return;
|
||||
|
@ -297,7 +288,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
|
||||
private void skipPagerToStoryHash() {
|
||||
// if we already started and found our target story, this will be unset
|
||||
if (storyHash == null) return;
|
||||
if (storyHash == null) {
|
||||
pager.setVisibility(View.VISIBLE);
|
||||
binding.readingEmptyViewText.setVisibility(View.INVISIBLE);
|
||||
return;
|
||||
}
|
||||
int position = -1;
|
||||
if (storyHash.equals(FIND_FIRST_UNREAD)) {
|
||||
position = readingAdapter.findFirstUnread();
|
||||
|
@ -312,7 +307,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
this.onPageSelected(position);
|
||||
// now that the pager is getting the right story, make it visible
|
||||
pager.setVisibility(View.VISIBLE);
|
||||
emptyViewText.setVisibility(View.INVISIBLE);
|
||||
binding.readingEmptyViewText.setVisibility(View.INVISIBLE);
|
||||
storyHash = null;
|
||||
return;
|
||||
}
|
||||
|
@ -340,6 +335,15 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
pager.setPageMarginDrawable(R.drawable.divider_light);
|
||||
} else if (themeValue == ThemeValue.DARK) {
|
||||
pager.setPageMarginDrawable(R.drawable.divider_dark);
|
||||
} else if (themeValue == ThemeValue.AUTO) {
|
||||
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
|
||||
pager.setPageMarginDrawable(R.drawable.divider_dark);
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO) {
|
||||
pager.setPageMarginDrawable(R.drawable.divider_light);
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_UNDEFINED) {
|
||||
pager.setPageMarginDrawable(R.drawable.divider_light);
|
||||
}
|
||||
}
|
||||
|
||||
boolean showFeedMetadata = true;
|
||||
|
@ -405,16 +409,16 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
}
|
||||
if ((updateType & UPDATE_STATUS) != 0) {
|
||||
enableMainProgress(NBSyncService.isFeedSetSyncing(this.fs, this));
|
||||
if (overlayStatusText != null) {
|
||||
if (binding.readingSyncStatus != 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);
|
||||
binding.readingSyncStatus.setText(syncStatus);
|
||||
binding.readingSyncStatus.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
overlayStatusText.setVisibility(View.GONE);
|
||||
binding.readingSyncStatus.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -516,11 +520,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
final boolean _overflowExtras = overflowExtras;
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
UIUtils.setViewAlpha(overlayLeft, a, true);
|
||||
UIUtils.setViewAlpha(overlayRight, a, true);
|
||||
UIUtils.setViewAlpha(overlayProgress, a, true);
|
||||
UIUtils.setViewAlpha(overlayText, a, true);
|
||||
UIUtils.setViewAlpha(overlaySend, a, !_overflowExtras);
|
||||
UIUtils.setViewAlpha(binding.readingOverlayLeft, a, true);
|
||||
UIUtils.setViewAlpha(binding.readingOverlayRight, a, true);
|
||||
UIUtils.setViewAlpha(binding.readingOverlayProgress, a, true);
|
||||
UIUtils.setViewAlpha(binding.readingOverlayText, a, true);
|
||||
UIUtils.setViewAlpha(binding.readingOverlaySend, a, !_overflowExtras);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -544,40 +548,40 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
if (currentUnreadCount > this.startingUnreadCount ) {
|
||||
this.startingUnreadCount = currentUnreadCount;
|
||||
}
|
||||
this.overlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
|
||||
this.overlayRight.setText((currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
|
||||
this.binding.readingOverlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
|
||||
this.binding.readingOverlayRight.setText((currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
|
||||
if (currentUnreadCount > 0) {
|
||||
this.overlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRight, android.R.attr.background));
|
||||
this.binding.readingOverlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRight, android.R.attr.background));
|
||||
} else {
|
||||
this.overlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRightDone, android.R.attr.background));
|
||||
this.binding.readingOverlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRightDone, android.R.attr.background));
|
||||
}
|
||||
|
||||
if (this.startingUnreadCount == 0 ) {
|
||||
// sessions with no unreads just show a full progress bar
|
||||
this.overlayProgress.setMax(1);
|
||||
this.overlayProgress.setProgress(1);
|
||||
this.binding.readingOverlayProgress.setMax(1);
|
||||
this.binding.readingOverlayProgress.setProgress(1);
|
||||
} else {
|
||||
int unreadProgress = this.startingUnreadCount - currentUnreadCount;
|
||||
this.overlayProgress.setMax(this.startingUnreadCount);
|
||||
this.overlayProgress.setProgress(unreadProgress);
|
||||
this.binding.readingOverlayProgress.setMax(this.startingUnreadCount);
|
||||
this.binding.readingOverlayProgress.setProgress(unreadProgress);
|
||||
}
|
||||
this.overlayProgress.invalidate();
|
||||
this.binding.readingOverlayProgress.invalidate();
|
||||
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void updateOverlayText() {
|
||||
if (overlayText == null) return;
|
||||
if (binding.readingOverlayText == null) return;
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ReadingItemFragment item = getReadingFragment();
|
||||
if (item == null) return;
|
||||
if (item.getSelectedViewMode() == DefaultFeedView.STORY) {
|
||||
overlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundText, android.R.attr.background));
|
||||
overlayText.setText(R.string.overlay_text);
|
||||
binding.readingOverlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundText, android.R.attr.background));
|
||||
binding.readingOverlayText.setText(R.string.overlay_text);
|
||||
} else {
|
||||
overlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundStory, android.R.attr.background));
|
||||
overlayText.setText(R.string.overlay_story);
|
||||
binding.readingOverlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundStory, android.R.attr.background));
|
||||
binding.readingOverlayText.setText(R.string.overlay_story);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -610,11 +614,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
}
|
||||
|
||||
protected void enableMainProgress(boolean enabled) {
|
||||
enableProgressCircle(overlayProgressRight, enabled);
|
||||
enableProgressCircle(binding.readingOverlayProgressRight, enabled);
|
||||
}
|
||||
|
||||
public void enableLeftProgressCircle(boolean enabled) {
|
||||
enableProgressCircle(overlayProgressLeft, enabled);
|
||||
enableProgressCircle(binding.readingOverlayProgressLeft, enabled);
|
||||
}
|
||||
|
||||
private void enableProgressCircle(final ProgressBar view, final boolean enabled) {
|
||||
|
|
|
@ -18,4 +18,13 @@ public class SavedStoriesItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, R.drawable.clock, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
String feedId = "starred";
|
||||
String savedTag = fs.getSingleSavedTag();
|
||||
if (savedTag != null) {
|
||||
feedId += ":" + savedTag;
|
||||
}
|
||||
return feedId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,8 @@ public class SocialFeedItemsList extends ItemsList {
|
|||
UIUtils.setCustomActionBar(this, socialFeed.photoUrl, socialFeed.feedTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getSaveSearchFeedId() {
|
||||
return "social:" + socialFeed.userId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,9 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ActivityWidgetConfigBinding;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.util.FeedOrderFilter;
|
||||
|
@ -31,28 +30,21 @@ import java.util.HashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class WidgetConfig extends NbActivity {
|
||||
|
||||
@Bind(R.id.list_view)
|
||||
ExpandableListView listView;
|
||||
@Bind(R.id.text_no_subscriptions)
|
||||
TextView textNoSubscriptions;
|
||||
|
||||
private WidgetConfigAdapter adapter;
|
||||
private ArrayList<Feed> feeds;
|
||||
private ArrayList<Folder> folders;
|
||||
private Map<String, Feed> feedMap = new HashMap<>();
|
||||
private ArrayList<String> folderNames = new ArrayList<>();
|
||||
private ArrayList<ArrayList<Feed>> folderChildren = new ArrayList<>();
|
||||
private ActivityWidgetConfigBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_widget_config);
|
||||
ButterKnife.bind(this);
|
||||
binding = ActivityWidgetConfigBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setupList();
|
||||
loadFeeds();
|
||||
|
@ -164,7 +156,7 @@ public class WidgetConfig extends NbActivity {
|
|||
|
||||
private void setupList() {
|
||||
adapter = new WidgetConfigAdapter(this);
|
||||
listView.setAdapter(adapter);
|
||||
binding.listView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void loadFeeds() {
|
||||
|
@ -288,7 +280,7 @@ public class WidgetConfig extends NbActivity {
|
|||
private void setAdapterData() {
|
||||
adapter.setData(this.folderNames, this.folderChildren, this.feeds);
|
||||
|
||||
listView.setVisibility(this.feeds.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
textNoSubscriptions.setVisibility(this.feeds.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
binding.listView.setVisibility(this.feeds.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
binding.textNoSubscriptions.setVisibility(this.feeds.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
db.execSQL(DatabaseConstants.CLASSIFIER_SQL);
|
||||
db.execSQL(DatabaseConstants.SOCIALFEED_STORIES_SQL);
|
||||
db.execSQL(DatabaseConstants.STARREDCOUNTS_SQL);
|
||||
db.execSQL(DatabaseConstants.SAVED_SEARCH_SQL);
|
||||
db.execSQL(DatabaseConstants.ACTION_SQL);
|
||||
db.execSQL(DatabaseConstants.NOTIFY_DISMISS_SQL);
|
||||
db.execSQL(DatabaseConstants.FEED_TAGS_SQL);
|
||||
|
@ -49,6 +50,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
db.execSQL(drop + DatabaseConstants.CLASSIFIER_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.STARREDCOUNTS_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.SAVED_SEARCH_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.ACTION_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.NOTIFY_DISMISS_TABLE);
|
||||
db.execSQL(drop + DatabaseConstants.FEED_TAGS_TABLE);
|
||||
|
|
|
@ -195,6 +195,13 @@ public class BlurDatabaseHelper {
|
|||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ?", selArgs);}
|
||||
}
|
||||
|
||||
public void deleteSavedSearch(String feedId, String query) {
|
||||
String q = "DELETE FROM " + DatabaseConstants.SAVED_SEARCH_TABLE +
|
||||
" WHERE " + DatabaseConstants.SAVED_SEARCH_FEED_ID + " = '" + feedId + "'" +
|
||||
" AND " + DatabaseConstants.SAVED_SEARCH_QUERY + " = '" + query + "'";
|
||||
synchronized (RW_MUTEX) {dbRW.execSQL(q);}
|
||||
}
|
||||
|
||||
public Feed getFeed(String feedId) {
|
||||
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, DatabaseConstants.FEED_ID + " = ?", new String[] {feedId}, null, null, null);
|
||||
Feed result = null;
|
||||
|
@ -237,7 +244,8 @@ public class BlurDatabaseHelper {
|
|||
public void setFeedsFolders(List<ContentValues> folderValues,
|
||||
List<ContentValues> feedValues,
|
||||
List<ContentValues> socialFeedValues,
|
||||
List<ContentValues> starredCountValues) {
|
||||
List<ContentValues> starredCountValues,
|
||||
List<ContentValues> savedSearchValues) {
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
|
@ -248,10 +256,12 @@ public class BlurDatabaseHelper {
|
|||
dbRW.delete(DatabaseConstants.COMMENT_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.REPLY_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.STARREDCOUNTS_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.SAVED_SEARCH_TABLE, null, null);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.FOLDER_TABLE, folderValues);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.FEED_TABLE, feedValues);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.SOCIALFEED_TABLE, socialFeedValues);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.STARREDCOUNTS_TABLE, starredCountValues);
|
||||
bulkInsertValuesExtSync(DatabaseConstants.SAVED_SEARCH_TABLE, savedSearchValues);
|
||||
dbRW.setTransactionSuccessful();
|
||||
} finally {
|
||||
dbRW.endTransaction();
|
||||
|
@ -1033,6 +1043,17 @@ public class BlurDatabaseHelper {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public StarredCount getStarredFeedByTag(String tag) {
|
||||
Cursor c = dbRO.query(DatabaseConstants.STARREDCOUNTS_TABLE, null, DatabaseConstants.STARREDCOUNTS_TAG + " = ?", new String[] {tag}, null, null, null);
|
||||
StarredCount result = null;
|
||||
while (c.moveToNext()) {
|
||||
result = StarredCount.fromCursor(c);
|
||||
}
|
||||
c.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Folder> getFolders() {
|
||||
Cursor c = getFoldersCursor(null);
|
||||
List<Folder> folders = new ArrayList<Folder>(c.getCount());
|
||||
|
@ -1069,11 +1090,21 @@ public class BlurDatabaseHelper {
|
|||
};
|
||||
}
|
||||
|
||||
public Loader<Cursor> getSavedSearchLoader() {
|
||||
return new QueryCursorLoader(context) {
|
||||
protected Cursor createCursor() {return getSavedSearchCursor(cancellationSignal);}
|
||||
};
|
||||
}
|
||||
|
||||
private Cursor getSavedStoryCountsCursor(CancellationSignal cancellationSignal) {
|
||||
Cursor c = query(false, DatabaseConstants.STARREDCOUNTS_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
||||
return c;
|
||||
}
|
||||
|
||||
private Cursor getSavedSearchCursor(CancellationSignal cancellationSignal) {
|
||||
return query(false, DatabaseConstants.SAVED_SEARCH_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
||||
}
|
||||
|
||||
public Cursor getNotifyFocusStoriesCursor() {
|
||||
return rawQuery(DatabaseConstants.NOTIFY_FOCUS_STORY_QUERY, null, null);
|
||||
}
|
||||
|
|
|
@ -148,6 +148,13 @@ public class DatabaseConstants {
|
|||
public static final String STARREDCOUNTS_TAG = "tag";
|
||||
public static final String STARREDCOUNTS_FEEDID = "feed_id";
|
||||
|
||||
public static final String SAVED_SEARCH_TABLE = "saved_search";
|
||||
public static final String SAVED_SEARCH_FEED_TITLE = "saved_search_title";
|
||||
public static final String SAVED_SEARCH_FAVICON = "saved_search_favicon";
|
||||
public static final String SAVED_SEARCH_ADDRESS = "saved_search_address";
|
||||
public static final String SAVED_SEARCH_QUERY = "saved_search_query";
|
||||
public static final String SAVED_SEARCH_FEED_ID = "saved_search_feed_id";
|
||||
|
||||
public static final String NOTIFY_DISMISS_TABLE = "notify_dimiss";
|
||||
public static final String NOTIFY_DISMISS_STORY_HASH = "story_hash";
|
||||
public static final String NOTIFY_DISMISS_TIME = "time";
|
||||
|
@ -303,6 +310,14 @@ public class DatabaseConstants {
|
|||
STARREDCOUNTS_FEEDID + TEXT +
|
||||
")";
|
||||
|
||||
static final String SAVED_SEARCH_SQL = "CREATE TABLE " + SAVED_SEARCH_TABLE + " (" +
|
||||
SAVED_SEARCH_FEED_TITLE + TEXT + ", " +
|
||||
SAVED_SEARCH_FAVICON + TEXT + ", " +
|
||||
SAVED_SEARCH_ADDRESS + TEXT + ", " +
|
||||
SAVED_SEARCH_QUERY + TEXT + ", " +
|
||||
SAVED_SEARCH_FEED_ID +
|
||||
")";
|
||||
|
||||
static final String NOTIFY_DISMISS_SQL = "CREATE TABLE " + NOTIFY_DISMISS_TABLE + " (" +
|
||||
NOTIFY_DISMISS_STORY_HASH + TEXT + ", " +
|
||||
NOTIFY_DISMISS_TIME + INTEGER + " NOT NULL " +
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.widget.TextView;
|
|||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.domain.SavedSearch;
|
||||
import com.newsblur.domain.StarredCount;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.util.AppConstants;
|
||||
|
@ -34,14 +35,15 @@ 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;
|
||||
|
||||
/**
|
||||
* Custom adapter to display a nested folder/feed list in an ExpandableListView.
|
||||
*/
|
||||
public class FolderListAdapter extends BaseExpandableListAdapter {
|
||||
|
||||
private enum GroupType { GLOBAL_SHARED_STORIES, ALL_SHARED_STORIES, INFREQUENT_STORIES, ALL_STORIES, FOLDER, READ_STORIES, SAVED_STORIES }
|
||||
private enum ChildType { SOCIAL_FEED, FEED, SAVED_BY_TAG }
|
||||
private enum GroupType { GLOBAL_SHARED_STORIES, ALL_SHARED_STORIES, INFREQUENT_STORIES, ALL_STORIES, FOLDER, READ_STORIES, SAVED_SEARCHES, SAVED_STORIES }
|
||||
private enum ChildType { SOCIAL_FEED, FEED, SAVED_BY_TAG, SAVED_SEARCH }
|
||||
|
||||
// The following keys are used to mark the position of the special meta-folders within
|
||||
// the folders array. Since the ExpandableListView doesn't handle collapsing of views
|
||||
|
@ -56,6 +58,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
private static final String INFREQUENT_SITE_STORIES_GROUP_KEY = "INFREQUENT_SITE_STORIES_GROUP_KEY";
|
||||
private static final String READ_STORIES_GROUP_KEY = "READ_STORIES_GROUP_KEY";
|
||||
private static final String SAVED_STORIES_GROUP_KEY = "SAVED_STORIES_GROUP_KEY";
|
||||
private static final String SAVED_SEARCHES_GROUP_KEY = "SAVED_SEARCHES_GROUP_KEY";
|
||||
|
||||
private final static float defaultTextSize_childName = 14;
|
||||
private final static float defaultTextSize_groupName = 13;
|
||||
|
@ -101,6 +104,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
|
||||
/** Starred story sets in display order. */
|
||||
private List<StarredCount> starredCountsByTag = Collections.emptyList();
|
||||
/** Saved Searches */
|
||||
private List<SavedSearch> savedSearches = Collections.emptyList();
|
||||
|
||||
private int savedStoriesTotalCount;
|
||||
|
||||
|
@ -162,6 +167,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
if (v == null) v = inflater.inflate(R.layout.row_infrequent_stories, null, false);
|
||||
} else if (isRowReadStories(groupPosition)) {
|
||||
if (v == null) v = inflater.inflate(R.layout.row_read_stories, null, false);
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
if (v == null) v = inflater.inflate(R.layout.row_saved_searches, null, false);
|
||||
} else if (isRowSavedStories(groupPosition)) {
|
||||
if (v == null) v = inflater.inflate(R.layout.row_saved_stories, null, false);
|
||||
TextView savedSum = ((TextView) v.findViewById(R.id.row_foldersum));
|
||||
|
@ -268,6 +275,14 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
TextView savedCounter =((TextView) v.findViewById(R.id.row_saved_tag_sum));
|
||||
savedCounter.setText(Integer.toString(checkNegativeUnreads(sc.count)));
|
||||
savedCounter.setTextSize(textSize * defaultTextSize_count);
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
if (v == null) v = inflater.inflate(R.layout.row_saved_search_child, parent, false);
|
||||
SavedSearch ss = savedSearches.get(childPosition);
|
||||
TextView nameView = v.findViewById(R.id.row_saved_search_title);
|
||||
nameView.setText(UIUtils.fromHtml(ss.feedTitle));
|
||||
ImageView iconView = v.findViewById(R.id.row_saved_search_icon);
|
||||
FeedUtils.iconLoader.preCheck(ss.faviconUrl, iconView);
|
||||
FeedUtils.iconLoader.displayImage(ss.faviconUrl, iconView, 0 , false);
|
||||
} else {
|
||||
if (v == null) v = inflater.inflate(R.layout.row_feed, parent, false);
|
||||
Feed f = activeFolderChildren.get(groupPosition).get(childPosition);
|
||||
|
@ -392,6 +407,11 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return folder.name;
|
||||
}
|
||||
|
||||
public Folder getGroupFolder(int groupPosition) {
|
||||
String flatFolderName = activeFolderNames.get(groupPosition);
|
||||
return flatFolders.get(flatFolderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getGroupCount() {
|
||||
if (activeFolderNames == null) return 0;
|
||||
|
@ -409,6 +429,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return socialFeedsActive.size();
|
||||
} else if (isRowSavedStories(groupPosition)) {
|
||||
return starredCountsByTag.size();
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
return savedSearches.size();
|
||||
} else {
|
||||
return activeFolderChildren.get(groupPosition).size();
|
||||
}
|
||||
|
@ -421,6 +443,9 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return FeedSet.singleSocialFeed(socialFeed.userId, socialFeed.username);
|
||||
} else if (isRowSavedStories(groupPosition)) {
|
||||
return FeedSet.singleSavedTag(starredCountsByTag.get(childPosition).tag);
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
SavedSearch savedSearch = savedSearches.get(childPosition);
|
||||
return FeedSet.singleSavedSearch(savedSearch.feedId, savedSearch.query);
|
||||
} else {
|
||||
Feed feed = activeFolderChildren.get(groupPosition).get(childPosition);
|
||||
FeedSet fs = FeedSet.singleFeed(feed.feedId);
|
||||
|
@ -465,6 +490,10 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return SAVED_STORIES_GROUP_KEY.equals(activeFolderNames.get(groupPosition));
|
||||
}
|
||||
|
||||
public boolean isRowSavedSearches(int groupPosition) {
|
||||
return SAVED_SEARCHES_GROUP_KEY.equals(activeFolderNames.get(groupPosition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the row at the specified position is last of the special rows, under which
|
||||
* un-foldered "root level" feeds are created as children. These feeds are not in any folder,
|
||||
|
@ -565,6 +594,17 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public synchronized void setSavedSearchesCursor(Cursor cursor) {
|
||||
if (!cursor.isBeforeFirst()) return;
|
||||
savedSearches = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
SavedSearch savedSearch = SavedSearch.fromCursor(cursor);
|
||||
savedSearches.add(savedSearch);
|
||||
}
|
||||
Collections.sort(savedSearches, SavedSearch.SavedSearchComparatorByTitle);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void recountFeeds() {
|
||||
if ((folders == null) || (feeds == null)) return;
|
||||
// re-init our local vars
|
||||
|
@ -619,6 +659,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
}
|
||||
// add the always-present (if enabled) special rows/folders that got at the bottom of the list
|
||||
addSpecialRow(READ_STORIES_GROUP_KEY);
|
||||
addSpecialRow(SAVED_SEARCHES_GROUP_KEY);
|
||||
addSpecialRow(SAVED_STORIES_GROUP_KEY);
|
||||
recountChildren();
|
||||
}
|
||||
|
@ -728,6 +769,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
totalNeutCount = 0;
|
||||
totalPosCount = 0;
|
||||
|
||||
safeClear(savedSearches);
|
||||
safeClear(starredCountsByTag);
|
||||
safeClear(closedFolders);
|
||||
|
||||
|
@ -755,6 +797,11 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return socialFeedsActive.get(childPosition);
|
||||
}
|
||||
|
||||
/** Get the cached SavedSearch object at the given saved search list location. */
|
||||
public SavedSearch getSavedSearch(int childPosition) {
|
||||
return savedSearches.get(childPosition);
|
||||
}
|
||||
|
||||
public synchronized void changeState(StateFilter state) {
|
||||
currentState = state;
|
||||
lastFeedViewedId = null; // clear when changing modes
|
||||
|
@ -804,6 +851,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return GroupType.INFREQUENT_STORIES.ordinal();
|
||||
} else if (isRowReadStories(groupPosition)) {
|
||||
return GroupType.READ_STORIES.ordinal();
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
return GroupType.SAVED_SEARCHES.ordinal();
|
||||
} else if (isRowSavedStories(groupPosition)) {
|
||||
return GroupType.SAVED_STORIES.ordinal();
|
||||
} else {
|
||||
|
@ -817,6 +866,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
|
|||
return ChildType.SOCIAL_FEED.ordinal();
|
||||
} else if (isRowSavedStories(groupPosition)) {
|
||||
return ChildType.SAVED_BY_TAG.ordinal();
|
||||
} else if (isRowSavedSearches(groupPosition)) {
|
||||
return ChildType.SAVED_SEARCH.ordinal();
|
||||
} else {
|
||||
return ChildType.FEED.ordinal();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.newsblur.database;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
|
@ -22,18 +21,13 @@ import android.widget.ImageView;
|
|||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.FeedItemsList;
|
||||
import com.newsblur.activity.ItemsList;
|
||||
import com.newsblur.activity.NbActivity;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
|
@ -313,16 +307,16 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
|||
MenuItem.OnMenuItemClickListener,
|
||||
View.OnTouchListener {
|
||||
|
||||
@Bind(R.id.story_item_favicon_borderbar_1) View leftBarOne;
|
||||
@Bind(R.id.story_item_favicon_borderbar_2) View leftBarTwo;
|
||||
@Bind(R.id.story_item_inteldot) ImageView intelDot;
|
||||
@Bind(R.id.story_item_thumbnail) ImageView thumbView;
|
||||
@Bind(R.id.story_item_feedicon) ImageView feedIconView;
|
||||
@Bind(R.id.story_item_feedtitle) TextView feedTitleView;
|
||||
@Bind(R.id.story_item_title) TextView storyTitleView;
|
||||
@Bind(R.id.story_item_date) TextView storyDate;
|
||||
@Bind(R.id.story_item_saved_icon) View savedView;
|
||||
@Bind(R.id.story_item_shared_icon) View sharedView;
|
||||
View leftBarOne;
|
||||
View leftBarTwo;
|
||||
ImageView intelDot;
|
||||
ImageView thumbView;
|
||||
ImageView feedIconView;
|
||||
TextView feedTitleView;
|
||||
TextView storyTitleView;
|
||||
TextView storyDate;
|
||||
View savedView;
|
||||
View sharedView;
|
||||
|
||||
Story story;
|
||||
ImageLoader.PhotoToLoad thumbLoader;
|
||||
|
@ -332,9 +326,20 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
|||
boolean gestureL2R = false;
|
||||
boolean gestureDebounce = false;
|
||||
|
||||
|
||||
public StoryViewHolder(View view) {
|
||||
super(view);
|
||||
ButterKnife.bind(StoryViewHolder.this, view);
|
||||
leftBarOne = view.findViewById(R.id.story_item_favicon_borderbar_1);
|
||||
leftBarTwo = view.findViewById(R.id.story_item_favicon_borderbar_2);
|
||||
intelDot = view.findViewById(R.id.story_item_inteldot);
|
||||
thumbView = view.findViewById(R.id.story_item_thumbnail);
|
||||
feedIconView = view.findViewById(R.id.story_item_feedicon);
|
||||
feedTitleView = view.findViewById(R.id.story_item_feedtitle);
|
||||
storyTitleView = view.findViewById(R.id.story_item_title);
|
||||
storyDate = view.findViewById(R.id.story_item_date);
|
||||
savedView = view.findViewById(R.id.story_item_saved_icon);
|
||||
sharedView = view.findViewById(R.id.story_item_shared_icon);
|
||||
|
||||
view.setOnClickListener(StoryViewHolder.this);
|
||||
view.setOnCreateContextMenuListener(StoryViewHolder.this);
|
||||
view.setOnTouchListener(StoryViewHolder.this);
|
||||
|
@ -472,10 +477,12 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
|||
}
|
||||
|
||||
public class StoryRowViewHolder extends StoryViewHolder {
|
||||
@Bind(R.id.story_item_author) TextView storyAuthor;
|
||||
@Bind(R.id.story_item_content) TextView storySnippet;
|
||||
TextView storyAuthor;
|
||||
TextView storySnippet;
|
||||
public StoryRowViewHolder(View view) {
|
||||
super(view);
|
||||
storyAuthor = view.findViewById(R.id.story_item_author);
|
||||
storySnippet = view.findViewById(R.id.story_item_content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -660,12 +667,13 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
|||
}
|
||||
}
|
||||
|
||||
public class FooterViewHolder extends RecyclerView.ViewHolder {
|
||||
@Bind(R.id.footer_view_inner) FrameLayout innerView;
|
||||
public static class FooterViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
FrameLayout innerView;
|
||||
|
||||
public FooterViewHolder(View view) {
|
||||
super(view);
|
||||
ButterKnife.bind(FooterViewHolder.this, view);
|
||||
innerView = view.findViewById(R.id.footer_view_inner);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.newsblur.domain;
|
|||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
@ -62,6 +63,15 @@ public class Folder {
|
|||
feedIds.removeAll(orphanFeedIds);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFirstParentName() {
|
||||
String folderParentName = null;
|
||||
if (!parents.isEmpty()) {
|
||||
folderParentName = parents.get(0);
|
||||
}
|
||||
return folderParentName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object otherFolder) {
|
||||
if (! (otherFolder instanceof Folder)) return false;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package com.newsblur.domain;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class SavedSearch {
|
||||
|
||||
@SerializedName("query")
|
||||
public String query;
|
||||
|
||||
@SerializedName("feed_id")
|
||||
public String feedId;
|
||||
|
||||
@SerializedName("feed_address")
|
||||
public String feedAddress;
|
||||
|
||||
public String feedTitle;
|
||||
public String faviconUrl;
|
||||
|
||||
public ContentValues getValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
String feedTitle = "\"<b>" + query + "</b>\" in <b>" + getFeedTitle() + "</b>";
|
||||
values.put(DatabaseConstants.SAVED_SEARCH_FEED_TITLE, feedTitle);
|
||||
values.put(DatabaseConstants.SAVED_SEARCH_FAVICON, getFaviconUrl());
|
||||
values.put(DatabaseConstants.SAVED_SEARCH_ADDRESS, feedAddress);
|
||||
values.put(DatabaseConstants.SAVED_SEARCH_QUERY, query);
|
||||
values.put(DatabaseConstants.SAVED_SEARCH_FEED_ID, feedId);
|
||||
return values;
|
||||
}
|
||||
|
||||
public static SavedSearch fromCursor(Cursor cursor) {
|
||||
if (cursor.isBeforeFirst()) {
|
||||
cursor.moveToFirst();
|
||||
}
|
||||
SavedSearch savedSearch = new SavedSearch();
|
||||
savedSearch.feedTitle = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FEED_TITLE));
|
||||
savedSearch.faviconUrl = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FAVICON));
|
||||
savedSearch.feedAddress = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_ADDRESS));
|
||||
savedSearch.query = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_QUERY));
|
||||
savedSearch.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FEED_ID));
|
||||
return savedSearch;
|
||||
}
|
||||
|
||||
private String getFeedTitle() {
|
||||
String feedTitle = null;
|
||||
|
||||
if (feedId.equals("river:")) {
|
||||
feedTitle = "All Site Stories";
|
||||
} else if (feedId.equals("river:infrequent")) {
|
||||
feedTitle = "Infrequent Site Stories";
|
||||
} else if (feedId.startsWith("river:")) {
|
||||
String folderName = feedId.replace("river:", "");
|
||||
FeedSet fs = FeedUtils.feedSetFromFolderName(folderName);
|
||||
feedTitle = fs.getFolderName();
|
||||
} else if (feedId.equals("read")) {
|
||||
feedTitle = "Read Stories";
|
||||
} else if (feedId.startsWith("starred")) {
|
||||
feedTitle = "Saved Stories";
|
||||
String tag = feedId.replace("starred:", "");
|
||||
StarredCount starredFeed = FeedUtils.getStarredFeedByTag(tag);
|
||||
if (starredFeed != null) {
|
||||
String tagSlug = tag.replace(" ", "-");
|
||||
if (starredFeed.tag.equals(tag) || starredFeed.tag.equals(tagSlug)) {
|
||||
feedTitle = feedTitle + " - " + starredFeed.tag;
|
||||
}
|
||||
}
|
||||
} else if (feedId.startsWith("feed:")) {
|
||||
Feed feed = FeedUtils.getFeed(feedId.replace("feed:", ""));
|
||||
if (feed == null) return null;
|
||||
feedTitle = feed.title;
|
||||
} else if (feedId.startsWith("social:")) {
|
||||
Feed feed = FeedUtils.getFeed(feedId.replace("social:", ""));
|
||||
if (feed == null) return null;
|
||||
feedTitle = feed.title;
|
||||
}
|
||||
|
||||
return feedTitle;
|
||||
}
|
||||
|
||||
private String getFaviconUrl() {
|
||||
String url = null;
|
||||
if (feedId.equals("river:") || feedId.equals("river:infrequent")) {
|
||||
url = "https://newsblur.com/media/img/icons/circular/ak-icon-allstories.png";
|
||||
} else if (feedId.startsWith("river:")) {
|
||||
url = "https://newsblur.com/media/img/icons/circular/g_icn_folder.png";
|
||||
} else if (feedId.equals("read")) {
|
||||
url = "https://newsblur.com/media/img/icons/circular/g_icn_unread.png";
|
||||
} else if (feedId.equals("starred")) {
|
||||
url = "https://newsblur.com/media/img/icons/circular/clock.png";
|
||||
} else if (feedId.startsWith("starred:")) {
|
||||
url = "https://newsblur.com/media/img/reader/tag.png";
|
||||
} else if (feedId.startsWith("feed:")) {
|
||||
Feed feed = FeedUtils.getFeed(feedId.replace("feed:", ""));
|
||||
if (feed != null) {
|
||||
url = feed.faviconUrl;
|
||||
}
|
||||
} else if (feedId.startsWith("social:")) {
|
||||
Feed feed = FeedUtils.getFeed(feedId.replace("social:", ""));
|
||||
if (feed != null) {
|
||||
url = feed.faviconUrl;
|
||||
}
|
||||
}
|
||||
if (url == null) {
|
||||
url = "https://newsblur.com/media/img/icons/circular/g_icn_search_black.png";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public final static Comparator<SavedSearch> SavedSearchComparatorByTitle = new Comparator<SavedSearch>() {
|
||||
@Override
|
||||
public int compare(SavedSearch ss1, SavedSearch ss2) {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(ss1.feedTitle, ss2.feedTitle);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.newsblur.domain;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -82,6 +83,12 @@ public class Story implements Serializable {
|
|||
@SerializedName("story_hash")
|
||||
public String storyHash;
|
||||
|
||||
@SerializedName("secure_image_urls")
|
||||
public Map<String, String> secureImageUrls;
|
||||
|
||||
@SerializedName("secure_image_thumbnails")
|
||||
public Map<String, String> secureImageThumbnails;
|
||||
|
||||
// NOTE: this is parsed and saved to the DB, but is *not* generally un-thawed when stories are fetched back from the DB
|
||||
@SerializedName("image_urls")
|
||||
public String[] imageUrls;
|
||||
|
@ -310,10 +317,13 @@ public class Story implements Serializable {
|
|||
return YT_THUMB_PRE + ytUrl + YT_THUMB_POST;
|
||||
}
|
||||
|
||||
if ((story.imageUrls != null) && (story.imageUrls.length > 0)) {
|
||||
return story.imageUrls[0];
|
||||
if (story.imageUrls != null && story.imageUrls.length > 0) {
|
||||
String thumbnail = story.imageUrls[0];
|
||||
if (thumbnail.startsWith("http://") && story.secureImageThumbnails != null && story.secureImageThumbnails.containsKey(thumbnail)){
|
||||
thumbnail = story.secureImageThumbnails.get(thumbnail);
|
||||
}
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,42 @@ package com.newsblur.fragment;
|
|||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Main;
|
||||
import com.newsblur.databinding.DialogAddFeedBinding;
|
||||
import com.newsblur.databinding.RowAddFeedFolderBinding;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.domain.AddFeedResponse;
|
||||
import com.newsblur.network.domain.NewsBlurResponse;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AddFeedFragment extends DialogFragment {
|
||||
|
||||
private static final String FEED_URI = "feed_url";
|
||||
private static final String FEED_NAME = "feed_name";
|
||||
private DialogAddFeedBinding binding;
|
||||
|
||||
public static AddFeedFragment newInstance(String feedUri, String feedName) {
|
||||
AddFeedFragment frag = new AddFeedFragment();
|
||||
|
@ -31,29 +49,97 @@ public class AddFeedFragment extends DialogFragment {
|
|||
return frag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final String addFeedString = getResources().getString(R.string.add_feed_message);
|
||||
final Activity activity = getActivity();
|
||||
final APIManager apiManager = new APIManager(activity);
|
||||
final Intent intent = new Intent(activity, Main.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_add_feed, null);
|
||||
binding = DialogAddFeedBinding.bind(v);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setMessage(String.format(addFeedString, getArguments().getString(FEED_NAME)));
|
||||
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
builder.setTitle("Choose folder for " + getArguments().getString(FEED_NAME));
|
||||
builder.setView(v);
|
||||
|
||||
AddFeedAdapter adapter = new AddFeedAdapter(new OnFolderClickListener() {
|
||||
@Override
|
||||
public void onItemClick(Folder folder) {
|
||||
addFeed(activity, apiManager, folder.name);
|
||||
}
|
||||
});
|
||||
|
||||
binding.textAddFolderTitle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (binding.containerAddFolder.getVisibility() == View.GONE) {
|
||||
binding.containerAddFolder.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.containerAddFolder.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
binding.icCreateFolder.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (binding.inputFolderName.getText().length() == 0) {
|
||||
Toast.makeText(activity, R.string.add_folder_name, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
addFeedToNewFolder(activity, apiManager, binding.inputFolderName.getText().toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
binding.recyclerViewFolders.addItemDecoration(new DividerItemDecoration(activity, LinearLayoutManager.VERTICAL));
|
||||
binding.recyclerViewFolders.setAdapter(adapter);
|
||||
adapter.setFolders(FeedUtils.dbHelper.getFolders());
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
private void addFeedToNewFolder(final Activity activity, final APIManager apiManager, final String folderName) {
|
||||
binding.icCreateFolder.setVisibility(View.GONE);
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
binding.inputFolderName.setEnabled(false);
|
||||
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... voids) {
|
||||
return apiManager.addFolder(folderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
|
||||
super.onPostExecute(newsBlurResponse);
|
||||
binding.inputFolderName.setEnabled(true);
|
||||
|
||||
if (!newsBlurResponse.isError()) {
|
||||
binding.containerAddFolder.setVisibility(View.GONE);
|
||||
binding.inputFolderName.getText().clear();
|
||||
addFeed(activity, apiManager, folderName);
|
||||
} else {
|
||||
UIUtils.safeToast(activity, R.string.add_folder_error, Toast.LENGTH_SHORT);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void addFeed(final Activity activity, final APIManager apiManager, @Nullable final String folderName) {
|
||||
binding.textSyncStatus.setVisibility(View.VISIBLE);
|
||||
new AsyncTask<Void, Void, AddFeedResponse>() {
|
||||
@Override
|
||||
protected AddFeedResponse doInBackground(Void... arg) {
|
||||
protected AddFeedResponse doInBackground(Void... voids) {
|
||||
((AddFeedProgressListener) activity).addFeedStarted();
|
||||
return apiManager.addFeed(getArguments().getString(FEED_URI));
|
||||
String feedUrl = getArguments().getString(FEED_URI);
|
||||
return apiManager.addFeed(feedUrl, folderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(AddFeedResponse result) {
|
||||
super.onPostExecute(result);
|
||||
binding.textSyncStatus.setVisibility(View.GONE);
|
||||
final Intent intent = new Intent(activity, Main.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (!result.isError()) {
|
||||
// trigger a sync when we return to Main so that the new feed will show up
|
||||
NBSyncService.forceFeedsFolders();
|
||||
|
@ -64,22 +150,70 @@ public class AddFeedFragment extends DialogFragment {
|
|||
activity.startActivity(intent);
|
||||
activity.finish();
|
||||
AddFeedFragment.this.dismiss();
|
||||
};
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
|
||||
private static class AddFeedAdapter extends RecyclerView.Adapter<AddFeedAdapter.FolderViewHolder> {
|
||||
|
||||
AddFeedAdapter(OnFolderClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private final List<Folder> folders = new ArrayList<>();
|
||||
private OnFolderClickListener listener;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
AddFeedFragment.this.dismiss();
|
||||
activity.startActivity(intent);
|
||||
activity.finish();
|
||||
public AddFeedAdapter.FolderViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
|
||||
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_add_feed_folder, viewGroup, false);
|
||||
return new FolderViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AddFeedAdapter.FolderViewHolder viewHolder, int position) {
|
||||
final Folder folder = folders.get(position);
|
||||
if (folder.name.equals(AppConstants.ROOT_FOLDER)) {
|
||||
viewHolder.binding.textFolderTitle.setText(R.string.top_level);
|
||||
} else {
|
||||
viewHolder.binding.textFolderTitle.setText(folder.flatName());
|
||||
}
|
||||
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(folder);
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return folders.size();
|
||||
}
|
||||
|
||||
public void setFolders(List<Folder> folders) {
|
||||
Collections.sort(folders, Folder.FolderComparator);
|
||||
this.folders.clear();
|
||||
this.folders.addAll(folders);
|
||||
this.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
static class FolderViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public RowAddFeedFolderBinding binding;
|
||||
|
||||
public FolderViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
binding = RowAddFeedFolderBinding.bind(itemView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface AddFeedProgressListener {
|
||||
public abstract void addFeedStarted();
|
||||
void addFeedStarted();
|
||||
}
|
||||
|
||||
public interface OnFolderClickListener {
|
||||
void onItemClick(Folder folder);
|
||||
}
|
||||
}
|
|
@ -19,12 +19,9 @@ import android.view.ViewGroup;
|
|||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.DialogChoosefoldersBinding;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
@ -33,8 +30,6 @@ public class ChooseFoldersFragment extends DialogFragment {
|
|||
|
||||
private Feed feed;
|
||||
|
||||
@Bind(R.id.choose_folders_list) ListView listView;
|
||||
|
||||
public static ChooseFoldersFragment newInstance(Feed feed) {
|
||||
ChooseFoldersFragment fragment = new ChooseFoldersFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
@ -62,7 +57,7 @@ public class ChooseFoldersFragment extends DialogFragment {
|
|||
final Activity activity = getActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_choosefolders, null);
|
||||
ButterKnife.bind(this, v);
|
||||
DialogChoosefoldersBinding binding = DialogChoosefoldersBinding.bind(v);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(String.format(getResources().getString(R.string.title_choose_folders), feed.title));
|
||||
|
@ -107,7 +102,7 @@ public class ChooseFoldersFragment extends DialogFragment {
|
|||
return v;
|
||||
}
|
||||
};
|
||||
listView.setAdapter(adapter);
|
||||
binding.chooseFoldersList.setAdapter(adapter);
|
||||
|
||||
Dialog dialog = builder.create();
|
||||
dialog.getWindow().getAttributes().gravity = Gravity.BOTTOM;
|
||||
|
|
|
@ -4,9 +4,11 @@ import com.newsblur.R;
|
|||
import com.newsblur.activity.ItemsList;
|
||||
import com.newsblur.activity.NbActivity;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.SavedSearch;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
@ -22,6 +24,8 @@ public class DeleteFeedFragment extends DialogFragment {
|
|||
private static final String FOLDER_NAME = "folder_name";
|
||||
private static final String NORMAL_FEED = "normal";
|
||||
private static final String SOCIAL_FEED = "social";
|
||||
private static final String SAVED_SEARCH_FEED = "saved_search";
|
||||
private static final String QUERY = "query";
|
||||
|
||||
public static DeleteFeedFragment newInstance(Feed feed, String folderName) {
|
||||
DeleteFeedFragment frag = new DeleteFeedFragment();
|
||||
|
@ -44,11 +48,25 @@ public class DeleteFeedFragment extends DialogFragment {
|
|||
return frag;
|
||||
}
|
||||
|
||||
public static DeleteFeedFragment newInstance(SavedSearch savedSearch) {
|
||||
DeleteFeedFragment frag = new DeleteFeedFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(FEED_TYPE, SAVED_SEARCH_FEED);
|
||||
args.putString(FEED_ID, savedSearch.feedId);
|
||||
args.putString(FEED_NAME, savedSearch.feedTitle);
|
||||
args.putString(QUERY, savedSearch.query);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
if (getArguments().getString(FEED_TYPE).equals(NORMAL_FEED)) {
|
||||
builder.setMessage(String.format(getResources().getString(R.string.delete_feed_message), getArguments().getString(FEED_NAME)));
|
||||
} else if (getArguments().getString(FEED_TYPE).equals(SAVED_SEARCH_FEED)) {
|
||||
String message = String.format(getResources().getString(R.string.delete_saved_search_message), getArguments().getString(FEED_NAME));
|
||||
builder.setMessage(UIUtils.fromHtml(message));
|
||||
} else {
|
||||
builder.setMessage(String.format(getResources().getString(R.string.unfollow_message), getArguments().getString(FEED_NAME)));
|
||||
}
|
||||
|
@ -57,6 +75,8 @@ public class DeleteFeedFragment extends DialogFragment {
|
|||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
if (getArguments().getString(FEED_TYPE).equals(NORMAL_FEED)) {
|
||||
FeedUtils.deleteFeed(getArguments().getString(FEED_ID), getArguments().getString(FOLDER_NAME), getActivity(), new APIManager(getActivity()));
|
||||
} else if (getArguments().getString(FEED_TYPE).equals(SAVED_SEARCH_FEED)) {
|
||||
FeedUtils.deleteSavedSearch(getArguments().getString(FEED_ID), getArguments().getString(QUERY), getActivity(), new APIManager(getActivity()));
|
||||
} else {
|
||||
FeedUtils.deleteSocialFeed(getArguments().getString(FEED_ID), getActivity(), new APIManager(getActivity()));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
public class DeleteFolderFragment extends DialogFragment {
|
||||
|
||||
private static final String FOLDER_NAME = "folder_name";
|
||||
private static final String FOLDER_PARENT = "folder_parent";
|
||||
|
||||
public static DeleteFolderFragment newInstance(String folderName, String folderParent) {
|
||||
DeleteFolderFragment frag = new DeleteFolderFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(FOLDER_NAME, folderName);
|
||||
args.putString(FOLDER_PARENT, folderParent);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
final String folderName = getArguments().getString(FOLDER_NAME);
|
||||
final String folderParent = getArguments().getString(FOLDER_PARENT);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(getResources().getString(R.string.delete_folder_message, folderName));
|
||||
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
String inFolder = "";
|
||||
if (!TextUtils.isEmpty(folderParent) && !folderParent.equals(AppConstants.ROOT_FOLDER)) {
|
||||
inFolder = folderParent;
|
||||
}
|
||||
FeedUtils.deleteFolder(folderName, inFolder, getActivity(), new APIManager(getActivity()));
|
||||
DeleteFolderFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
DeleteFolderFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
|
@ -12,13 +12,10 @@ import android.support.v4.app.DialogFragment;
|
|||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.DialogTrainfeedBinding;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.util.FeedSet;
|
||||
|
@ -30,14 +27,7 @@ public class FeedIntelTrainerFragment extends DialogFragment {
|
|||
private Feed feed;
|
||||
private FeedSet fs;
|
||||
private Classifier classifier;
|
||||
|
||||
@Bind(R.id.intel_title_header) TextView headerTitles;
|
||||
@Bind(R.id.intel_tag_header) TextView headerTags;
|
||||
@Bind(R.id.intel_author_header) TextView headerAuthor;
|
||||
@Bind(R.id.existing_title_intel_container) LinearLayout titleRowsContainer;
|
||||
@Bind(R.id.existing_tag_intel_container) LinearLayout tagRowsContainer;
|
||||
@Bind(R.id.existing_author_intel_container) LinearLayout authorRowsContainer;
|
||||
@Bind(R.id.existing_feed_intel_container) LinearLayout feedRowsContainer;
|
||||
private DialogTrainfeedBinding binding;
|
||||
|
||||
public static FeedIntelTrainerFragment newInstance(Feed feed, FeedSet fs) {
|
||||
FeedIntelTrainerFragment fragment = new FeedIntelTrainerFragment();
|
||||
|
@ -58,7 +48,7 @@ public class FeedIntelTrainerFragment extends DialogFragment {
|
|||
final Activity activity = getActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_trainfeed, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = DialogTrainfeedBinding.bind(v);
|
||||
|
||||
// display known title classifiers
|
||||
for (Map.Entry<String, Integer> rule : classifier.title.entrySet()) {
|
||||
|
@ -66,9 +56,9 @@ public class FeedIntelTrainerFragment extends DialogFragment {
|
|||
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
|
||||
label.setText(rule.getKey());
|
||||
UIUtils.setupIntelDialogRow(row, classifier.title, rule.getKey());
|
||||
titleRowsContainer.addView(row);
|
||||
binding.existingTitleIntelContainer.addView(row);
|
||||
}
|
||||
if (classifier.title.size() < 1) headerTitles.setVisibility(View.GONE);
|
||||
if (classifier.title.size() < 1) binding.intelTitleHeader.setVisibility(View.GONE);
|
||||
|
||||
// get the list of suggested tags
|
||||
List<String> allTags = FeedUtils.dbHelper.getTagsForFeed(feed.feedId);
|
||||
|
@ -83,9 +73,9 @@ public class FeedIntelTrainerFragment extends DialogFragment {
|
|||
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
|
||||
label.setText(tag);
|
||||
UIUtils.setupIntelDialogRow(row, classifier.tags, tag);
|
||||
tagRowsContainer.addView(row);
|
||||
binding.existingTagIntelContainer.addView(row);
|
||||
}
|
||||
if (allTags.size() < 1) headerTags.setVisibility(View.GONE);
|
||||
if (allTags.size() < 1) binding.intelTagHeader.setVisibility(View.GONE);
|
||||
|
||||
// get the list of suggested authors
|
||||
List<String> allAuthors = FeedUtils.dbHelper.getAuthorsForFeed(feed.feedId);
|
||||
|
@ -100,16 +90,16 @@ public class FeedIntelTrainerFragment extends DialogFragment {
|
|||
TextView labelAuthor = (TextView) rowAuthor.findViewById(R.id.intel_row_label);
|
||||
labelAuthor.setText(author);
|
||||
UIUtils.setupIntelDialogRow(rowAuthor, classifier.authors, author);
|
||||
authorRowsContainer.addView(rowAuthor);
|
||||
binding.existingAuthorIntelContainer.addView(rowAuthor);
|
||||
}
|
||||
if (allAuthors.size() < 1) headerAuthor.setVisibility(View.GONE);
|
||||
if (allAuthors.size() < 1) binding.intelAuthorHeader.setVisibility(View.GONE);
|
||||
|
||||
// for feel-level intel, the label is the title and the intel identifier is the feed ID
|
||||
View rowFeed = inflater.inflate(R.layout.include_intel_row, null);
|
||||
TextView labelFeed = (TextView) rowFeed.findViewById(R.id.intel_row_label);
|
||||
labelFeed.setText(feed.title);
|
||||
UIUtils.setupIntelDialogRow(rowFeed, classifier.feeds, feed.feedId);
|
||||
feedRowsContainer.addView(rowFeed);
|
||||
binding.existingFeedIntelContainer.addView(rowFeed);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(R.string.feed_intel_dialog_title);
|
||||
|
|
|
@ -26,9 +26,6 @@ import android.widget.ExpandableListView.OnGroupClickListener;
|
|||
import android.widget.ExpandableListView.OnGroupCollapseListener;
|
||||
import android.widget.ExpandableListView.OnGroupExpandListener;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.AllSharedStoriesItemsList;
|
||||
import com.newsblur.activity.AllStoriesItemsList;
|
||||
|
@ -43,7 +40,10 @@ import com.newsblur.activity.ReadStoriesItemsList;
|
|||
import com.newsblur.activity.SavedStoriesItemsList;
|
||||
import com.newsblur.activity.SocialFeedItemsList;
|
||||
import com.newsblur.database.FolderListAdapter;
|
||||
import com.newsblur.databinding.FragmentFolderfeedlistBinding;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.domain.SavedSearch;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedSet;
|
||||
|
@ -64,11 +64,12 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
private static final int FOLDERS_LOADER = 2;
|
||||
private static final int FEEDS_LOADER = 3;
|
||||
private static final int SAVEDCOUNT_LOADER = 4;
|
||||
private static final int SAVED_SEARCH_LOADER = 5;
|
||||
|
||||
private FolderListAdapter adapter;
|
||||
public StateFilter currentState = StateFilter.SOME;
|
||||
private SharedPreferences sharedPreferences;
|
||||
@Bind(R.id.folderfeed_list) ExpandableListView list;
|
||||
private FragmentFolderfeedlistBinding binding;
|
||||
public boolean firstCursorSeenYet = false;
|
||||
|
||||
// the two-step context menu for feeds requires us to temp store the feed long-pressed so
|
||||
|
@ -112,6 +113,8 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
return FeedUtils.dbHelper.getFeedsLoader();
|
||||
case SAVEDCOUNT_LOADER:
|
||||
return FeedUtils.dbHelper.getSavedStoryCountsLoader();
|
||||
case SAVED_SEARCH_LOADER:
|
||||
return FeedUtils.dbHelper.getSavedSearchLoader();
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown loader created");
|
||||
}
|
||||
|
@ -140,6 +143,9 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
case SAVEDCOUNT_LOADER:
|
||||
adapter.setStarredCountCursor(cursor);
|
||||
break;
|
||||
case SAVED_SEARCH_LOADER:
|
||||
adapter.setSavedSearchesCursor(cursor);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown loader created");
|
||||
}
|
||||
|
@ -163,6 +169,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
getLoaderManager().restartLoader(FOLDERS_LOADER, null, this);
|
||||
getLoaderManager().restartLoader(FEEDS_LOADER, null, this);
|
||||
getLoaderManager().restartLoader(SAVEDCOUNT_LOADER, null, this);
|
||||
getLoaderManager().restartLoader(SAVED_SEARCH_LOADER, null, this);
|
||||
} catch (Exception e) {
|
||||
// on heavily loaded devices, the time between isAdded() going false
|
||||
// and the loader subsystem shutting down can be nontrivial, causing
|
||||
|
@ -179,6 +186,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
getLoaderManager().initLoader(FOLDERS_LOADER, null, this);
|
||||
getLoaderManager().initLoader(FEEDS_LOADER, null, this);
|
||||
getLoaderManager().initLoader(SAVEDCOUNT_LOADER, null, this);
|
||||
getLoaderManager().initLoader(SAVED_SEARCH_LOADER, null, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,20 +198,20 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_folderfeedlist, container);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = FragmentFolderfeedlistBinding.bind(v);
|
||||
|
||||
list.setGroupIndicator(UIUtils.getDrawable(getActivity(), R.drawable.transparent));
|
||||
list.setOnCreateContextMenuListener(this);
|
||||
list.setOnChildClickListener(this);
|
||||
list.setOnGroupClickListener(this);
|
||||
list.setOnGroupCollapseListener(this);
|
||||
list.setOnGroupExpandListener(this);
|
||||
binding.folderfeedList.setGroupIndicator(UIUtils.getDrawable(getActivity(), R.drawable.transparent));
|
||||
binding.folderfeedList.setOnCreateContextMenuListener(this);
|
||||
binding.folderfeedList.setOnChildClickListener(this);
|
||||
binding.folderfeedList.setOnGroupClickListener(this);
|
||||
binding.folderfeedList.setOnGroupCollapseListener(this);
|
||||
binding.folderfeedList.setOnGroupExpandListener(this);
|
||||
|
||||
adapter.listBackref = new WeakReference(list); // see note in adapter about backref
|
||||
list.setAdapter(adapter);
|
||||
adapter.listBackref = new WeakReference(binding.folderfeedList); // see note in adapter about backref
|
||||
binding.folderfeedList.setAdapter(adapter);
|
||||
|
||||
// Main activity needs to listen for scrolls to prevent refresh from firing unnecessarily
|
||||
list.setOnScrollListener((android.widget.AbsListView.OnScrollListener) getActivity());
|
||||
binding.folderfeedList.setOnScrollListener((android.widget.AbsListView.OnScrollListener) getActivity());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
@ -215,18 +223,18 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
*/
|
||||
public void checkOpenFolderPreferences() {
|
||||
// make sure we didn't beat construction
|
||||
if ((this.list == null) || (this.sharedPreferences == null)) return;
|
||||
if ((this.binding.folderfeedList == null) || (this.sharedPreferences == null)) return;
|
||||
|
||||
for (int i = 0; i < adapter.getGroupCount(); i++) {
|
||||
String flatGroupName = adapter.getGroupUniqueName(i);
|
||||
if (sharedPreferences.getBoolean(AppConstants.FOLDER_PRE + "_" + flatGroupName, true)) {
|
||||
if (list.isGroupExpanded(i) == false) {
|
||||
list.expandGroup(i);
|
||||
if (binding.folderfeedList.isGroupExpanded(i) == false) {
|
||||
binding.folderfeedList.expandGroup(i);
|
||||
adapter.setFolderClosed(flatGroupName, false);
|
||||
}
|
||||
} else {
|
||||
if (list.isGroupExpanded(i) == true) {
|
||||
list.collapseGroup(i);
|
||||
if (binding.folderfeedList.isGroupExpanded(i) == true) {
|
||||
binding.folderfeedList.collapseGroup(i);
|
||||
adapter.setFolderClosed(flatGroupName, true);
|
||||
}
|
||||
}
|
||||
|
@ -249,11 +257,14 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
if (adapter.isRowGlobalSharedStories(groupPosition)) break;
|
||||
if (adapter.isRowAllSharedStories(groupPosition)) break;
|
||||
if (adapter.isRowInfrequentStories(groupPosition)) break;
|
||||
if (adapter.isRowSavedSearches(groupPosition)) break;
|
||||
inflater.inflate(R.menu.context_folder, menu);
|
||||
|
||||
if (adapter.isRowAllStories(groupPosition)) {
|
||||
menu.removeItem(R.id.menu_mute_folder);
|
||||
menu.removeItem(R.id.menu_unmute_folder);
|
||||
menu.removeItem(R.id.menu_delete_folder);
|
||||
menu.removeItem(R.id.menu_rename_folder);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -272,9 +283,22 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
menu.removeItem(R.id.menu_instafetch_feed);
|
||||
menu.removeItem(R.id.menu_intel);
|
||||
menu.removeItem(R.id.menu_rename_feed);
|
||||
menu.removeItem(R.id.menu_delete_saved_search);
|
||||
} else if (adapter.isRowSavedSearches(groupPosition)) {
|
||||
menu.removeItem(R.id.menu_mark_feed_as_read);
|
||||
menu.removeItem(R.id.menu_delete_feed);
|
||||
menu.removeItem(R.id.menu_unfollow);
|
||||
menu.removeItem(R.id.menu_choose_folders);
|
||||
menu.removeItem(R.id.menu_rename_feed);
|
||||
menu.removeItem(R.id.menu_notifications);
|
||||
menu.removeItem(R.id.menu_mute_feed);
|
||||
menu.removeItem(R.id.menu_unmute_feed);
|
||||
menu.removeItem(R.id.menu_instafetch_feed);
|
||||
menu.removeItem(R.id.menu_intel);
|
||||
} else {
|
||||
// normal feeds
|
||||
menu.removeItem(R.id.menu_unfollow);
|
||||
menu.removeItem(R.id.menu_delete_saved_search);
|
||||
|
||||
Feed feed = adapter.getFeed(groupPosition, childPosition);
|
||||
if (feed.active) {
|
||||
|
@ -359,7 +383,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
} else if (item.getItemId() == R.id.menu_rename_feed) {
|
||||
Feed feed = adapter.getFeed(groupPosition, childPosition);
|
||||
if (feed != null) {
|
||||
DialogFragment renameFeedFragment = RenameFeedFragment.newInstance(feed);
|
||||
DialogFragment renameFeedFragment = RenameDialogFragment.newInstance(feed);
|
||||
renameFeedFragment.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
} else if (item.getItemId() == R.id.menu_mute_feed) {
|
||||
|
@ -379,6 +403,22 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
} else if (item.getItemId() == R.id.menu_intel) {
|
||||
FeedIntelTrainerFragment intelFrag = FeedIntelTrainerFragment.newInstance(adapter.getFeed(groupPosition, childPosition), adapter.getChild(groupPosition, childPosition));
|
||||
intelFrag.show(getFragmentManager(), FeedIntelTrainerFragment.class.getName());
|
||||
} else if (item.getItemId() == R.id.menu_delete_saved_search) {
|
||||
SavedSearch savedSearch = adapter.getSavedSearch(childPosition);
|
||||
if (savedSearch != null) {
|
||||
DialogFragment deleteFeedFragment = DeleteFeedFragment.newInstance(savedSearch);
|
||||
deleteFeedFragment.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
} else if (item.getItemId() == R.id.menu_delete_folder) {
|
||||
Folder folder = adapter.getGroupFolder(groupPosition);
|
||||
String folderParentName = folder.getFirstParentName();
|
||||
DeleteFolderFragment deleteFolderFragment = DeleteFolderFragment.newInstance(folder.name, folderParentName);
|
||||
deleteFolderFragment.show(getFragmentManager(), deleteFolderFragment.getTag());
|
||||
} else if (item.getItemId() == R.id.menu_rename_folder) {
|
||||
Folder folder = adapter.getGroupFolder(groupPosition);
|
||||
String folderParentName = folder.getFirstParentName();
|
||||
RenameDialogFragment renameDialogFragment = RenameDialogFragment.newInstance(folder.name, folderParentName);
|
||||
renameDialogFragment.show(getFragmentManager(), renameDialogFragment.getTag());
|
||||
}
|
||||
|
||||
return super.onContextItemSelected(item);
|
||||
|
@ -449,6 +489,9 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
i = new Intent(getActivity(), ReadStoriesItemsList.class);
|
||||
} else if (adapter.isRowSavedStories(groupPosition)) {
|
||||
i = new Intent(getActivity(), SavedStoriesItemsList.class);
|
||||
} else if (adapter.isRowSavedSearches(groupPosition)) {
|
||||
// group not clickable
|
||||
return true;
|
||||
} else {
|
||||
i = new Intent(getActivity(), FolderItemsList.class);
|
||||
String canonicalFolderName = adapter.getGroupFolderName(groupPosition);
|
||||
|
@ -472,6 +515,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
// these shouldn't ever be collapsible
|
||||
if (adapter.isRowRootFolder(groupPosition)) return;
|
||||
if (adapter.isRowReadStories(groupPosition)) return;
|
||||
if (adapter.isRowSavedSearches(groupPosition)) return;
|
||||
|
||||
String flatGroupName = adapter.getGroupUniqueName(groupPosition);
|
||||
// save the expanded preference, since the widget likes to forget it
|
||||
|
@ -490,6 +534,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
// these shouldn't ever be collapsible
|
||||
if (adapter.isRowRootFolder(groupPosition)) return;
|
||||
if (adapter.isRowReadStories(groupPosition)) return;
|
||||
if (adapter.isRowSavedSearches(groupPosition)) return;
|
||||
|
||||
String flatGroupName = adapter.getGroupUniqueName(groupPosition);
|
||||
// save the collapsed preference, since the widget likes to forget it
|
||||
|
@ -515,6 +560,8 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
Intent intent = new Intent(getActivity(), SavedStoriesItemsList.class);
|
||||
intent.putExtra(ItemsList.EXTRA_FEED_SET, fs);
|
||||
getActivity().startActivity(intent);
|
||||
} else if (adapter.isRowSavedSearches(groupPosition)) {
|
||||
openSavedSearch(adapter.getSavedSearch(childPosition));
|
||||
} else {
|
||||
Feed feed = adapter.getFeed(groupPosition, childPosition);
|
||||
// NB: FeedItemsList needs the name of the containing folder, but this is not the same as setting
|
||||
|
@ -534,6 +581,53 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
|
|||
return true;
|
||||
}
|
||||
|
||||
private void openSavedSearch(SavedSearch savedSearch) {
|
||||
Intent intent = null;
|
||||
FeedSet fs = null;
|
||||
String feedId = savedSearch.feedId;
|
||||
if (feedId.equals("river:")) {
|
||||
// all site stories
|
||||
intent = new Intent(getActivity(), AllStoriesItemsList.class);
|
||||
fs = FeedSet.allFeeds();
|
||||
} else if (feedId.equals("river:infrequent")) {
|
||||
// infrequent stories
|
||||
intent = new Intent(getActivity(), InfrequentItemsList.class);
|
||||
fs = FeedSet.infrequentFeeds();
|
||||
} else if (feedId.startsWith("river:")) {
|
||||
intent = new Intent(getActivity(), FolderItemsList.class);
|
||||
String folderName = feedId.replace("river:", "");
|
||||
fs = FeedUtils.feedSetFromFolderName(folderName);
|
||||
intent.putExtra(FolderItemsList.EXTRA_FOLDER_NAME, folderName);
|
||||
} else if (feedId.equals("read")) {
|
||||
intent = new Intent(getActivity(), ReadStoriesItemsList.class);
|
||||
fs = FeedSet.allRead();
|
||||
} else if (feedId.equals("starred")) {
|
||||
intent = new Intent(getActivity(), SavedStoriesItemsList.class);
|
||||
fs = FeedSet.allSaved();
|
||||
} else if (feedId.startsWith("starred:")) {
|
||||
intent = new Intent(getActivity(), SavedStoriesItemsList.class);
|
||||
fs = FeedSet.singleSavedTag(feedId.replace("starred:", ""));
|
||||
} else if (feedId.startsWith("feed:")) {
|
||||
intent = new Intent(getActivity(), FeedItemsList.class);
|
||||
String cleanFeedId = feedId.replace("feed:", "");
|
||||
Feed feed = FeedUtils.getFeed(cleanFeedId);
|
||||
fs = FeedSet.singleFeed(cleanFeedId);
|
||||
intent.putExtra(FeedItemsList.EXTRA_FEED, feed);
|
||||
} else if (feedId.startsWith("social:")) {
|
||||
intent = new Intent(getActivity(), SocialFeedItemsList.class);
|
||||
String cleanFeedId = feedId.replace("social:", "");
|
||||
fs = FeedSet.singleFeed(cleanFeedId);
|
||||
Feed feed = FeedUtils.getFeed(cleanFeedId);
|
||||
intent.putExtra(FeedItemsList.EXTRA_FEED, feed);
|
||||
}
|
||||
|
||||
if (intent != null) {
|
||||
fs.setSearchQuery(savedSearch.query);
|
||||
intent.putExtra(ItemsList.EXTRA_FEED_SET, fs);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextSize(Float size) {
|
||||
if (adapter != null) {
|
||||
adapter.setTextSize(size);
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.InfrequentCutoffDialogBinding;
|
||||
|
||||
public class InfrequentCutoffDialogFragment extends DialogFragment {
|
||||
|
||||
private static String CURRENT_CUTOFF = "currentCutoff";
|
||||
private int currentValue;
|
||||
@Bind(R.id.radio_5) RadioButton button5;
|
||||
@Bind(R.id.radio_15) RadioButton button15;
|
||||
@Bind(R.id.radio_30) RadioButton button30;
|
||||
@Bind(R.id.radio_60) RadioButton button60;
|
||||
@Bind(R.id.radio_90) RadioButton button90;
|
||||
private InfrequentCutoffDialogBinding binding;
|
||||
|
||||
public static InfrequentCutoffDialogFragment newInstance(int currentValue) {
|
||||
InfrequentCutoffDialogFragment dialog = new InfrequentCutoffDialogFragment();
|
||||
|
@ -42,13 +36,13 @@ public class InfrequentCutoffDialogFragment extends DialogFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = getArguments().getInt(CURRENT_CUTOFF);
|
||||
View v = inflater.inflate(R.layout.infrequent_cutoff_dialog, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = InfrequentCutoffDialogBinding.bind(v);
|
||||
|
||||
button5.setChecked(currentValue == 5);
|
||||
button15.setChecked(currentValue == 15);
|
||||
button30.setChecked(currentValue == 30);
|
||||
button60.setChecked(currentValue == 60);
|
||||
button90.setChecked(currentValue == 90);
|
||||
binding.radio5.setChecked(currentValue == 5);
|
||||
binding.radio15.setChecked(currentValue == 15);
|
||||
binding.radio30.setChecked(currentValue == 30);
|
||||
binding.radio60.setChecked(currentValue == 60);
|
||||
binding.radio90.setChecked(currentValue == 90);
|
||||
|
||||
getDialog().setTitle(R.string.infrequent_choice_title);
|
||||
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
|
||||
|
@ -56,31 +50,66 @@ public class InfrequentCutoffDialogFragment extends DialogFragment {
|
|||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_5) void select5() {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.radio5.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
select5();
|
||||
}
|
||||
});
|
||||
binding.radio15.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
select15();
|
||||
}
|
||||
});
|
||||
binding.radio30.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
select30();
|
||||
}
|
||||
});
|
||||
binding.radio60.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
select60();
|
||||
}
|
||||
});
|
||||
binding.radio90.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
select90();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void select5() {
|
||||
if (currentValue != 5) {
|
||||
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(5);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
@OnClick(R.id.radio_15) void select15() {
|
||||
private void select15() {
|
||||
if (currentValue != 15) {
|
||||
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(15);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
@OnClick(R.id.radio_30) void select30() {
|
||||
private void select30() {
|
||||
if (currentValue != 30) {
|
||||
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(30);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
@OnClick(R.id.radio_60) void select60() {
|
||||
private void select60() {
|
||||
if (currentValue != 60) {
|
||||
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(60);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
@OnClick(R.id.radio_90) void select90() {
|
||||
private void select90() {
|
||||
if (currentValue != 90) {
|
||||
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(90);
|
||||
}
|
||||
|
|
|
@ -15,17 +15,13 @@ import android.view.MotionEvent;
|
|||
import android.view.View;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.ItemsList;
|
||||
import com.newsblur.activity.NbActivity;
|
||||
import com.newsblur.database.StoryViewAdapter;
|
||||
import com.newsblur.databinding.FragmentItemgridBinding;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
import com.newsblur.util.FeedSet;
|
||||
|
@ -50,21 +46,17 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
private final static int GRID_SPACING_DP = 5;
|
||||
private int gridSpacingPx;
|
||||
|
||||
@Bind(R.id.itemgridfragment_grid) RecyclerView itemGrid;
|
||||
private GridLayoutManager layoutManager;
|
||||
private StoryViewAdapter adapter;
|
||||
// an optional pending scroll state to restore.
|
||||
private Parcelable gridState;
|
||||
|
||||
// loading indicator for when stories are absent or stale (at top of list)
|
||||
@Bind(R.id.top_loading_throb) ProgressThrobber topProgressView;
|
||||
// R.id.top_loading_throb
|
||||
|
||||
// loading indicator for when stories are present and fresh (at bottom of list)
|
||||
protected ProgressThrobber bottomProgressView;
|
||||
|
||||
@Bind(R.id.empty_view) View emptyView;
|
||||
@Bind(R.id.empty_view_text) TextView emptyViewText;
|
||||
@Bind(R.id.empty_view_image) ImageView emptyViewImage;
|
||||
|
||||
private View fleuronFooter;
|
||||
// the fleuron has padding that can't be calculated until after layout, but only changes
|
||||
// rarely thereafter
|
||||
|
@ -76,6 +68,8 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
public int indexOfLastUnread = -1;
|
||||
public boolean fullFlingComplete = false;
|
||||
|
||||
private FragmentItemgridBinding binding;
|
||||
|
||||
public static ItemSetFragment newInstance() {
|
||||
ItemSetFragment fragment = new ItemSetFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
|
@ -123,13 +117,13 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_itemgrid, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = FragmentItemgridBinding.bind(v);
|
||||
|
||||
// disable the throbbers if animations are going to have a zero time scale
|
||||
boolean isDisableAnimations = ViewUtils.isPowerSaveMode(getActivity());
|
||||
|
||||
topProgressView.setEnabled(!isDisableAnimations);
|
||||
topProgressView.setColors(UIUtils.getColor(getActivity(), R.color.refresh_1),
|
||||
binding.topLoadingThrob.setEnabled(!isDisableAnimations);
|
||||
binding.topLoadingThrob.setColors(UIUtils.getColor(getActivity(), R.color.refresh_1),
|
||||
UIUtils.getColor(getActivity(), R.color.refresh_2),
|
||||
UIUtils.getColor(getActivity(), R.color.refresh_3),
|
||||
UIUtils.getColor(getActivity(), R.color.refresh_4));
|
||||
|
@ -145,11 +139,11 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
fleuronFooter = inflater.inflate(R.layout.row_fleuron, null);
|
||||
fleuronFooter.setVisibility(View.INVISIBLE);
|
||||
|
||||
itemGrid.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
||||
binding.itemgridfragmentGrid.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
itemGridWidthPx = itemGrid.getMeasuredWidth();
|
||||
itemGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
itemGridWidthPx = binding.itemgridfragmentGrid.getMeasuredWidth();
|
||||
binding.itemgridfragmentGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
updateStyle();
|
||||
}
|
||||
});
|
||||
|
@ -158,11 +152,11 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
|
||||
calcColumnCount(listStyle);
|
||||
layoutManager = new GridLayoutManager(getActivity(), columnCount);
|
||||
itemGrid.setLayoutManager(layoutManager);
|
||||
binding.itemgridfragmentGrid.setLayoutManager(layoutManager);
|
||||
setupAnimSpeeds();
|
||||
|
||||
calcGridSpacing(listStyle);
|
||||
itemGrid.addItemDecoration(new RecyclerView.ItemDecoration() {
|
||||
binding.itemgridfragmentGrid.addItemDecoration(new RecyclerView.ItemDecoration() {
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
outRect.set(gridSpacingPx, gridSpacingPx, gridSpacingPx, gridSpacingPx);
|
||||
|
@ -172,7 +166,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
adapter = new StoryViewAdapter(((NbActivity) getActivity()), this, getFeedSet(), listStyle);
|
||||
adapter.addFooterView(footerView);
|
||||
adapter.addFooterView(fleuronFooter);
|
||||
itemGrid.setAdapter(adapter);
|
||||
binding.itemgridfragmentGrid.setAdapter(adapter);
|
||||
|
||||
// the layout manager needs to know that the footer rows span all the way across
|
||||
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
|
@ -187,14 +181,14 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
}
|
||||
});
|
||||
|
||||
itemGrid.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
binding.itemgridfragmentGrid.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
ItemSetFragment.this.onScrolled(recyclerView, dx, dy);
|
||||
}
|
||||
});
|
||||
|
||||
setupGestureDetector(itemGrid);
|
||||
setupGestureDetector(binding.itemgridfragmentGrid);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
@ -281,13 +275,13 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
}
|
||||
|
||||
protected void updateAdapter(Cursor cursor) {
|
||||
adapter.swapCursor(cursor, itemGrid, gridState);
|
||||
adapter.swapCursor(cursor, binding.itemgridfragmentGrid, gridState);
|
||||
gridState = null;
|
||||
adapter.updateFeedSet(getFeedSet());
|
||||
if ((cursor != null) && (cursor.getCount() > 0)) {
|
||||
emptyView.setVisibility(View.INVISIBLE);
|
||||
binding.emptyView.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
emptyView.setVisibility(View.VISIBLE);
|
||||
binding.emptyView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// though we have stories, we might not yet have as many as we want
|
||||
|
@ -305,38 +299,38 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
calcFleuronPadding();
|
||||
|
||||
if (getFeedSet().isMuted()) {
|
||||
emptyViewText.setText(R.string.empty_list_view_muted_feed);
|
||||
emptyViewText.setTypeface(null, Typeface.NORMAL);
|
||||
emptyViewImage.setVisibility(View.VISIBLE);
|
||||
topProgressView.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_muted_feed);
|
||||
binding.emptyViewText.setTypeface(null, Typeface.NORMAL);
|
||||
binding.emptyViewImage.setVisibility(View.VISIBLE);
|
||||
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
|
||||
bottomProgressView.setVisibility(View.INVISIBLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (!cursorSeenYet) || NBSyncService.isFeedSetSyncing(getFeedSet(), getActivity()) ) {
|
||||
emptyViewText.setText(R.string.empty_list_view_loading);
|
||||
emptyViewText.setTypeface(null, Typeface.ITALIC);
|
||||
emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_loading);
|
||||
binding.emptyViewText.setTypeface(null, Typeface.ITALIC);
|
||||
binding.emptyViewImage.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (NBSyncService.isFeedSetStoriesFresh(getFeedSet())) {
|
||||
topProgressView.setVisibility(View.INVISIBLE);
|
||||
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
|
||||
bottomProgressView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
topProgressView.setVisibility(View.VISIBLE);
|
||||
binding.topLoadingThrob.setVisibility(View.VISIBLE);
|
||||
bottomProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
fleuronFooter.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
ReadFilter readFilter = PrefsUtils.getReadFilter(getActivity(), getFeedSet());
|
||||
if (readFilter == ReadFilter.UNREAD) {
|
||||
emptyViewText.setText(R.string.empty_list_view_no_stories_unread);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_no_stories_unread);
|
||||
} else {
|
||||
emptyViewText.setText(R.string.empty_list_view_no_stories);
|
||||
binding.emptyViewText.setText(R.string.empty_list_view_no_stories);
|
||||
}
|
||||
emptyViewText.setTypeface(null, Typeface.NORMAL);
|
||||
emptyViewImage.setVisibility(View.VISIBLE);
|
||||
binding.emptyViewText.setTypeface(null, Typeface.NORMAL);
|
||||
binding.emptyViewImage.setVisibility(View.VISIBLE);
|
||||
|
||||
topProgressView.setVisibility(View.INVISIBLE);
|
||||
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
|
||||
bottomProgressView.setVisibility(View.INVISIBLE);
|
||||
if (cursorSeenYet && NBSyncService.isFeedSetExhausted(getFeedSet()) && (adapter.getRawStoryCount() > 0)) {
|
||||
fleuronFooter.setVisibility(View.VISIBLE);
|
||||
|
@ -411,7 +405,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
targetMovDuration = 0L;
|
||||
}
|
||||
|
||||
RecyclerView.ItemAnimator anim = itemGrid.getItemAnimator();
|
||||
RecyclerView.ItemAnimator anim = binding.itemgridfragmentGrid.getItemAnimator();
|
||||
anim.setAddDuration((long) ((anim.getAddDuration() + targetAddDuration)/2L));
|
||||
anim.setMoveDuration((long) ((anim.getMoveDuration() + targetMovDuration)/2L));
|
||||
}
|
||||
|
@ -429,7 +423,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
// past the last item, which can be confusing to users who don't know about or need the offset
|
||||
if ( (!fullFlingComplete) &&
|
||||
(layoutManager.findLastCompletelyVisibleItemPosition() >= adapter.getStoryCount()) ) {
|
||||
itemGrid.stopScroll();
|
||||
binding.itemgridfragmentGrid.stopScroll();
|
||||
// but after halting at the end once, do allow scrolling past the bottom
|
||||
fullFlingComplete = true;
|
||||
}
|
||||
|
@ -439,7 +433,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
(layoutManager.findLastCompletelyVisibleItemPosition() >= indexOfLastUnread) ) {
|
||||
// but don't interrupt if already past the last unread
|
||||
if (indexOfLastUnread >= layoutManager.findFirstCompletelyVisibleItemPosition()) {
|
||||
itemGrid.stopScroll();
|
||||
binding.itemgridfragmentGrid.stopScroll();
|
||||
}
|
||||
indexOfLastUnread = -1;
|
||||
}
|
||||
|
@ -507,7 +501,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
*/
|
||||
private void calcFleuronPadding() {
|
||||
if (fleuronResized) return;
|
||||
int listHeight = itemGrid.getMeasuredHeight();
|
||||
int listHeight = binding.itemgridfragmentGrid.getMeasuredHeight();
|
||||
View innerView = fleuronFooter.findViewById(R.id.fleuron);
|
||||
ViewGroup.LayoutParams oldLayout = innerView.getLayoutParams();
|
||||
ViewGroup.MarginLayoutParams newLayout = new LinearLayout.LayoutParams(oldLayout);
|
||||
|
@ -526,7 +520,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
|
|||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(BUNDLE_GRIDSTATE, itemGrid.getLayoutManager().onSaveInstanceState());
|
||||
outState.putParcelable(BUNDLE_GRIDSTATE, binding.itemgridfragmentGrid.getLayoutManager().onSaveInstanceState());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,17 +11,12 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Login;
|
||||
import com.newsblur.activity.Main;
|
||||
import com.newsblur.databinding.FragmentLoginprogressBinding;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.domain.LoginResponse;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
@ -30,14 +25,10 @@ import com.newsblur.util.UIUtils;
|
|||
public class LoginProgressFragment extends Fragment {
|
||||
|
||||
private APIManager apiManager;
|
||||
@Bind(R.id.login_logging_in) TextView updateStatus;
|
||||
@Bind(R.id.login_retrieving_feeds) TextView retrievingFeeds;
|
||||
@Bind(R.id.login_profile_picture) ImageView loginProfilePicture;
|
||||
@Bind(R.id.login_feed_progress) ProgressBar feedProgress;
|
||||
@Bind(R.id.login_logging_in_progress) ProgressBar loggingInProgress;
|
||||
private LoginTask loginTask;
|
||||
private String username;
|
||||
private String password;
|
||||
private FragmentLoginprogressBinding binding;
|
||||
|
||||
public static LoginProgressFragment getInstance(String username, String password) {
|
||||
LoginProgressFragment fragment = new LoginProgressFragment();
|
||||
|
@ -61,7 +52,7 @@ public class LoginProgressFragment extends Fragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_loginprogress, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = FragmentLoginprogressBinding.bind(v);
|
||||
|
||||
loginTask = new LoginTask();
|
||||
loginTask.execute();
|
||||
|
@ -73,7 +64,7 @@ public class LoginProgressFragment extends Fragment {
|
|||
@Override
|
||||
protected void onPreExecute() {
|
||||
Animation a = AnimationUtils.loadAnimation(getActivity(), R.anim.text_up);
|
||||
updateStatus.startAnimation(a);
|
||||
binding.loginLoggingIn.startAnimation(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,20 +81,20 @@ public class LoginProgressFragment extends Fragment {
|
|||
if (c == null) return; // we might have run past the lifecycle of the activity
|
||||
if (!result.isError()) {
|
||||
final Animation a = AnimationUtils.loadAnimation(c, R.anim.text_down);
|
||||
updateStatus.setText(R.string.login_logged_in);
|
||||
loggingInProgress.setVisibility(View.GONE);
|
||||
updateStatus.startAnimation(a);
|
||||
binding.loginLoggingIn.setText(R.string.login_logged_in);
|
||||
binding.loginLoggingInProgress.setVisibility(View.GONE);
|
||||
binding.loginLoggingIn.startAnimation(a);
|
||||
|
||||
Bitmap userImage = PrefsUtils.getUserImage(c);
|
||||
if (userImage != null ) {
|
||||
loginProfilePicture.setVisibility(View.VISIBLE);
|
||||
loginProfilePicture.setImageBitmap(UIUtils.clipAndRound(userImage, 10f, false));
|
||||
binding.loginProfilePicture.setVisibility(View.VISIBLE);
|
||||
binding.loginProfilePicture.setImageBitmap(UIUtils.clipAndRound(userImage, 10f, false));
|
||||
}
|
||||
feedProgress.setVisibility(View.VISIBLE);
|
||||
binding.loginFeedProgress.setVisibility(View.VISIBLE);
|
||||
|
||||
final Animation b = AnimationUtils.loadAnimation(c, R.anim.text_up);
|
||||
retrievingFeeds.setText(R.string.login_retrieving_feeds);
|
||||
retrievingFeeds.startAnimation(b);
|
||||
binding.loginRetrievingFeeds.setText(R.string.login_retrieving_feeds);
|
||||
binding.loginFeedProgress.startAnimation(b);
|
||||
|
||||
Intent startMain = new Intent(getActivity(), Main.class);
|
||||
c.startActivity(startMain);
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.newsblur.fragment;
|
|||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -10,39 +12,27 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.LoginProgress;
|
||||
import com.newsblur.activity.RegisterProgress;
|
||||
import com.newsblur.databinding.FragmentLoginregisterBinding;
|
||||
import com.newsblur.network.APIConstants;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
||||
public class LoginRegisterFragment extends Fragment {
|
||||
|
||||
@Bind(R.id.login_username) EditText username;
|
||||
@Bind(R.id.login_password) EditText password;
|
||||
@Bind(R.id.registration_username) EditText register_username;
|
||||
@Bind(R.id.registration_password) EditText register_password;
|
||||
@Bind(R.id.registration_email) EditText register_email;
|
||||
@Bind(R.id.login_viewswitcher) ViewSwitcher viewSwitcher;
|
||||
@Bind(R.id.login_custom_server) View customServer;
|
||||
@Bind(R.id.login_custom_server_value) EditText customServerValue;
|
||||
private FragmentLoginregisterBinding binding;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View v = inflater.inflate(R.layout.fragment_loginregister, container, false);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = FragmentLoginregisterBinding.bind(v);
|
||||
|
||||
password.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
binding.loginPassword.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE ) {
|
||||
|
@ -52,7 +42,7 @@ public class LoginRegisterFragment extends Fragment {
|
|||
}
|
||||
});
|
||||
|
||||
register_email.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
binding.registrationEmail.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE ) {
|
||||
|
@ -65,36 +55,77 @@ public class LoginRegisterFragment extends Fragment {
|
|||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_button) void logIn() {
|
||||
if (!TextUtils.isEmpty(username.getText().toString())) {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.loginButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
logIn();
|
||||
}
|
||||
});
|
||||
binding.registrationButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
signUp();
|
||||
}
|
||||
});
|
||||
binding.loginChangeToLogin.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showLogin();
|
||||
}
|
||||
});
|
||||
binding.loginChangeToRegister.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showRegister();
|
||||
}
|
||||
});
|
||||
binding.loginForgotPassword.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
launchForgotPasswordPage();
|
||||
}
|
||||
});
|
||||
binding.loginCustomServer.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showCustomServer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void logIn() {
|
||||
if (!TextUtils.isEmpty(binding.loginUsername.getText().toString())) {
|
||||
// set the custom server endpoint before any API access, even the cookie fetch.
|
||||
APIConstants.setCustomServer(customServerValue.getText().toString());
|
||||
PrefsUtils.saveCustomServer(getActivity(), customServerValue.getText().toString());
|
||||
APIConstants.setCustomServer(binding.loginCustomServerValue.getText().toString());
|
||||
PrefsUtils.saveCustomServer(getActivity(), binding.loginCustomServerValue.getText().toString());
|
||||
|
||||
Intent i = new Intent(getActivity(), LoginProgress.class);
|
||||
i.putExtra("username", username.getText().toString());
|
||||
i.putExtra("password", password.getText().toString());
|
||||
i.putExtra("username", binding.loginUsername.getText().toString());
|
||||
i.putExtra("password", binding.loginUsername.getText().toString());
|
||||
startActivity(i);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.registration_button) void signUp() {
|
||||
private void signUp() {
|
||||
Intent i = new Intent(getActivity(), RegisterProgress.class);
|
||||
i.putExtra("username", register_username.getText().toString());
|
||||
i.putExtra("password", register_password.getText().toString());
|
||||
i.putExtra("email", register_email.getText().toString());
|
||||
i.putExtra("username", binding.registrationUsername.getText().toString());
|
||||
i.putExtra("password", binding.registrationPassword.getText().toString());
|
||||
i.putExtra("email", binding.registrationEmail.getText().toString());
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_change_to_login) void showLogin() {
|
||||
viewSwitcher.showPrevious();
|
||||
private void showLogin() {
|
||||
binding.loginViewswitcher.showPrevious();
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_change_to_register) void showRegister() {
|
||||
viewSwitcher.showNext();
|
||||
private void showRegister() {
|
||||
binding.loginViewswitcher.showNext();
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_forgot_password) void launchForgotPasswordPage() {
|
||||
private void launchForgotPasswordPage() {
|
||||
try {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(AppConstants.FORGOT_PASWORD_URL));
|
||||
|
@ -104,9 +135,8 @@ public class LoginRegisterFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_custom_server) void showCustomServer() {
|
||||
customServer.setVisibility(View.GONE);
|
||||
customServerValue.setVisibility(View.VISIBLE);
|
||||
private void showCustomServer() {
|
||||
binding.loginCustomServer.setVisibility(View.GONE);
|
||||
binding.loginCustomServerValue.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,17 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ReadfilterDialogBinding;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.ReadFilterChangedListener;
|
||||
|
||||
|
@ -21,8 +19,7 @@ public class ReadFilterDialogFragment extends DialogFragment {
|
|||
|
||||
private static String CURRENT_FILTER = "currentFilter";
|
||||
private ReadFilter currentValue;
|
||||
@Bind(R.id.radio_all) RadioButton allButton;
|
||||
@Bind(R.id.radio_unread) RadioButton unreadButton;
|
||||
private ReadfilterDialogBinding binding;
|
||||
|
||||
public static ReadFilterDialogFragment newInstance(ReadFilter currentValue) {
|
||||
ReadFilterDialogFragment dialog = new ReadFilterDialogFragment();
|
||||
|
@ -42,10 +39,10 @@ public class ReadFilterDialogFragment extends DialogFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = (ReadFilter) getArguments().getSerializable(CURRENT_FILTER);
|
||||
View v = inflater.inflate(R.layout.readfilter_dialog, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = ReadfilterDialogBinding.bind(v);
|
||||
|
||||
allButton.setChecked(currentValue == ReadFilter.ALL);
|
||||
unreadButton.setChecked(currentValue == ReadFilter.UNREAD);
|
||||
binding.radioAll.setChecked(currentValue == ReadFilter.ALL);
|
||||
binding.radioUnread.setChecked(currentValue == ReadFilter.UNREAD);
|
||||
|
||||
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
|
||||
|
@ -53,14 +50,31 @@ public class ReadFilterDialogFragment extends DialogFragment {
|
|||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_all) void selectAll() {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.radioAll.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
selectAll();
|
||||
}
|
||||
});
|
||||
binding.radioUnread.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
selectUnread();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void selectAll() {
|
||||
if (currentValue != ReadFilter.ALL) {
|
||||
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.ALL);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_unread) void selectUnread() {
|
||||
private void selectUnread() {
|
||||
if (currentValue != ReadFilter.UNREAD) {
|
||||
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.UNREAD);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.ReadingfontDialogBinding;
|
||||
import com.newsblur.util.ReadingFontChangedListener;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
||||
/**
|
||||
* Created by mark on 02/05/2017.
|
||||
*/
|
||||
|
@ -26,14 +24,7 @@ public class ReadingFontDialogFragment extends DialogFragment {
|
|||
|
||||
private String currentValue;
|
||||
|
||||
@Bind(R.id.radio_anonymous) RadioButton anonymousButton;
|
||||
@Bind(R.id.radio_chronicle) RadioButton chronicleButton;
|
||||
@Bind(R.id.radio_default) RadioButton defaultButton;
|
||||
@Bind(R.id.radio_gotham) RadioButton gothamButton;
|
||||
@Bind(R.id.radio_noto_sans) RadioButton notoSansButton;
|
||||
@Bind(R.id.radio_noto_serif) RadioButton notoSerifButton;
|
||||
@Bind(R.id.radio_open_sans_condensed) RadioButton openSansCondensedButton;
|
||||
@Bind(R.id.radio_whitney) RadioButton whitneyButton;
|
||||
private ReadingfontDialogBinding binding;
|
||||
|
||||
public static ReadingFontDialogFragment newInstance(String selectedFont) {
|
||||
ReadingFontDialogFragment dialog = new ReadingFontDialogFragment();
|
||||
|
@ -52,16 +43,16 @@ public class ReadingFontDialogFragment extends DialogFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = getArguments().getString(SELECTED_FONT);
|
||||
View v = inflater.inflate(R.layout.readingfont_dialog, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = ReadingfontDialogBinding.bind(v);
|
||||
|
||||
anonymousButton.setChecked(currentValue.equals(getString(R.string.anonymous_pro_font_prefvalue)));
|
||||
chronicleButton.setChecked(currentValue.equals(getString(R.string.chronicle_font_prefvalue)));
|
||||
defaultButton.setChecked(currentValue.equals(getString(R.string.default_font_prefvalue)));
|
||||
gothamButton.setChecked(currentValue.equals(getString(R.string.gotham_narrow_font_prefvalue)));
|
||||
notoSansButton.setChecked(currentValue.equals(getString(R.string.noto_sans_font_prefvalue)));
|
||||
notoSerifButton.setChecked(currentValue.equals(getString(R.string.noto_serif_font_prefvalue)));
|
||||
openSansCondensedButton.setChecked(currentValue.equals(getString(R.string.open_sans_condensed_font_prefvalue)));
|
||||
whitneyButton.setChecked(currentValue.equals(getString(R.string.whitney_font_prefvalue)));
|
||||
binding.radioAnonymous.setChecked(currentValue.equals(getString(R.string.anonymous_pro_font_prefvalue)));
|
||||
binding.radioChronicle.setChecked(currentValue.equals(getString(R.string.chronicle_font_prefvalue)));
|
||||
binding.radioDefault.setChecked(currentValue.equals(getString(R.string.default_font_prefvalue)));
|
||||
binding.radioGotham.setChecked(currentValue.equals(getString(R.string.gotham_narrow_font_prefvalue)));
|
||||
binding.radioNotoSans.setChecked(currentValue.equals(getString(R.string.noto_sans_font_prefvalue)));
|
||||
binding.radioNotoSerif.setChecked(currentValue.equals(getString(R.string.noto_serif_font_prefvalue)));
|
||||
binding.radioOpenSansCondensed.setChecked(currentValue.equals(getString(R.string.open_sans_condensed_font_prefvalue)));
|
||||
binding.radioWhitney.setChecked(currentValue.equals(getString(R.string.whitney_font_prefvalue)));
|
||||
|
||||
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
|
||||
|
@ -69,9 +60,58 @@ public class ReadingFontDialogFragment extends DialogFragment {
|
|||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_anonymous) void selectAnonymousPro() {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.radioAnonymous.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.anonymous_pro_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioDefault.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.default_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioChronicle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.chronicle_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioGotham.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.gotham_narrow_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioNotoSans.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.noto_sans_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioNotoSerif.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.noto_serif_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioOpenSansCondensed.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.open_sans_condensed_font_prefvalue));
|
||||
}
|
||||
});
|
||||
binding.radioWhitney.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchFont(getString(R.string.whitney_font_prefvalue));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void switchFont(String newValue) {
|
||||
if (!currentValue.equals(newValue)) {
|
||||
|
@ -79,32 +119,4 @@ public class ReadingFontDialogFragment extends DialogFragment {
|
|||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_chronicle) void selectChronicle() {
|
||||
switchFont(getString(R.string.chronicle_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_default) void selectDefault() {
|
||||
switchFont(getString(R.string.default_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_gotham) void selectGotham() {
|
||||
switchFont(getString(R.string.gotham_narrow_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_noto_sans) void selectNotoSans() {
|
||||
switchFont(getString(R.string.noto_sans_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_noto_serif) void selectNotoSerif() {
|
||||
switchFont(getString(R.string.noto_serif_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_open_sans_condensed) void selectOpenSansCondensed() {
|
||||
switchFont(getString(R.string.open_sans_condensed_font_prefvalue));
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_whitney) void selectWhitney() {
|
||||
switchFont(getString(R.string.whitney_font_prefvalue));
|
||||
}
|
||||
}
|
|
@ -7,12 +7,15 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -26,22 +29,17 @@ import android.view.View;
|
|||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebView.HitTestResult;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.FeedItemsList;
|
||||
import com.newsblur.activity.NbActivity;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.databinding.FragmentReadingitemBinding;
|
||||
import com.newsblur.databinding.IncludeReadingItemCommentBinding;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.service.OriginalTextService;
|
||||
|
@ -54,14 +52,9 @@ import com.newsblur.util.PrefsUtils;
|
|||
import com.newsblur.util.StoryUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
import com.newsblur.view.NewsblurWebview;
|
||||
import com.newsblur.view.ReadingScrollView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -76,24 +69,12 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
private LayoutInflater inflater;
|
||||
private String feedColor, feedTitle, feedFade, feedBorder, feedIconUrl, faviconText;
|
||||
private Classifier classifier;
|
||||
@Bind(R.id.reading_webview) NewsblurWebview web;
|
||||
@Bind(R.id.custom_view_container) ViewGroup webviewCustomViewLayout;
|
||||
@Bind(R.id.reading_scrollview) ScrollView fragmentScrollview;
|
||||
private BroadcastReceiver textSizeReceiver, readingFontReceiver;
|
||||
@Bind(R.id.reading_item_title) TextView itemTitle;
|
||||
@Bind(R.id.reading_item_authors) TextView itemAuthors;
|
||||
@Bind(R.id.reading_feed_title) TextView itemFeed;
|
||||
private boolean displayFeedDetails;
|
||||
@Bind(R.id.reading_item_tags) FlowLayout tagContainer;
|
||||
private View view;
|
||||
private UserDetails user;
|
||||
private DefaultFeedView selectedFeedView;
|
||||
private boolean textViewUnavailable;
|
||||
@Bind(R.id.reading_textloading) TextView textViewLoadingMsg;
|
||||
@Bind(R.id.reading_textmodefailed) TextView textViewLoadingFailedMsg;
|
||||
@Bind(R.id.save_story_button) Button saveButton;
|
||||
@Bind(R.id.share_story_button) Button shareButton;
|
||||
@Bind(R.id.story_context_menu_button) Button menuButton;
|
||||
|
||||
/** The story HTML, as provided by the 'content' element of the stories API. */
|
||||
private String storyContent;
|
||||
|
@ -115,6 +96,9 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
|
||||
private final Object WEBVIEW_CONTENT_MUTEX = new Object();
|
||||
|
||||
private FragmentReadingitemBinding binding;
|
||||
private IncludeReadingItemCommentBinding itemCommentBinding;
|
||||
|
||||
public static ReadingItemFragment newInstance(Story story, String feedTitle, String feedFaviconColor, String feedFaviconFade, String feedFaviconBorder, String faviconText, String faviconUrl, Classifier classifier, boolean displayFeedDetails, String sourceUserId) {
|
||||
ReadingItemFragment readingFragment = new ReadingItemFragment();
|
||||
|
||||
|
@ -168,8 +152,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
int heightm = fragmentScrollview.getChildAt(0).getMeasuredHeight();
|
||||
int pos = fragmentScrollview.getScrollY();
|
||||
int heightm = binding.readingScrollview.getChildAt(0).getMeasuredHeight();
|
||||
int pos = binding.readingScrollview.getScrollY();
|
||||
outState.putFloat(BUNDLE_SCROLL_POS_REL, (((float)pos)/heightm));
|
||||
}
|
||||
|
||||
|
@ -177,7 +161,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
public void onDestroy() {
|
||||
getActivity().unregisterReceiver(textSizeReceiver);
|
||||
getActivity().unregisterReceiver(readingFontReceiver);
|
||||
web.setOnTouchListener(null);
|
||||
binding.readingWebview.setOnTouchListener(null);
|
||||
view.setOnTouchListener(null);
|
||||
getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(null);
|
||||
super.onDestroy();
|
||||
|
@ -187,7 +171,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
// state into the webview so it behaves.
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (this.web != null ) { this.web.onPause(); }
|
||||
if (this.binding.readingWebview != null ) { this.binding.readingWebview.onPause(); }
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
|
@ -195,25 +179,26 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
reloadStoryContent();
|
||||
if (this.web != null ) { this.web.onResume(); }
|
||||
if (this.binding.readingWebview != null ) { this.binding.readingWebview.onResume(); }
|
||||
}
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
this.inflater = inflater;
|
||||
view = inflater.inflate(R.layout.fragment_readingitem, null);
|
||||
ButterKnife.bind(this, view);
|
||||
binding = FragmentReadingitemBinding.bind(view);
|
||||
itemCommentBinding = IncludeReadingItemCommentBinding.bind(binding.getRoot());
|
||||
|
||||
Reading activity = (Reading) getActivity();
|
||||
fs = activity.getFeedSet();
|
||||
|
||||
selectedFeedView = PrefsUtils.getDefaultViewModeForFeed(activity, story.feedId);
|
||||
|
||||
registerForContextMenu(web);
|
||||
web.setCustomViewLayout(webviewCustomViewLayout);
|
||||
web.setWebviewWrapperLayout(fragmentScrollview);
|
||||
web.setBackgroundColor(Color.TRANSPARENT);
|
||||
web.fragment = this;
|
||||
web.activity = activity;
|
||||
registerForContextMenu(binding.readingWebview);
|
||||
binding.readingWebview.setCustomViewLayout(binding.customViewContainer);
|
||||
binding.readingWebview.setWebviewWrapperLayout(binding.readingScrollview);
|
||||
binding.readingWebview.setBackgroundColor(Color.TRANSPARENT);
|
||||
binding.readingWebview.fragment = this;
|
||||
binding.readingWebview.activity = activity;
|
||||
|
||||
setupItemMetadata();
|
||||
updateShareButton();
|
||||
|
@ -228,6 +213,29 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.storyContextMenuButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onClickMenuButton();
|
||||
}
|
||||
});
|
||||
itemCommentBinding.saveStoryButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickSave();
|
||||
}
|
||||
});
|
||||
itemCommentBinding.shareStoryButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickShare();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupImmersiveViewGestureDetector() {
|
||||
// Change the system visibility on the decorview from the activity so that the state is maintained as we page through
|
||||
// fragments
|
||||
|
@ -239,7 +247,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
return gestureDetector.onTouchEvent(motionEvent);
|
||||
}
|
||||
};
|
||||
web.setOnTouchListener(touchListener);
|
||||
binding.readingWebview.setOnTouchListener(touchListener);
|
||||
view.setOnTouchListener(touchListener);
|
||||
|
||||
getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(immersiveViewHandler);
|
||||
|
@ -247,7 +255,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
HitTestResult result = web.getHitTestResult();
|
||||
HitTestResult result = binding.readingWebview.getHitTestResult();
|
||||
if (result.getType() == HitTestResult.IMAGE_TYPE ||
|
||||
result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE ) {
|
||||
// if the long-pressed item was an image, see if we can pop up a little dialogue
|
||||
|
@ -298,8 +306,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.story_context_menu_button) void onClickMenuButton() {
|
||||
PopupMenu pm = new PopupMenu(getActivity(), menuButton);
|
||||
private void onClickMenuButton() {
|
||||
PopupMenu pm = new PopupMenu(getActivity(), binding.storyContextMenuButton);
|
||||
Menu menu = pm.getMenu();
|
||||
pm.getMenuInflater().inflate(R.menu.story_context, menu);
|
||||
|
||||
|
@ -313,6 +321,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
menu.findItem(R.id.menu_theme_dark).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.BLACK) {
|
||||
menu.findItem(R.id.menu_theme_black).setChecked(true);
|
||||
} else if (themeValue == ThemeValue.AUTO) {
|
||||
menu.findItem(R.id.menu_theme_auto).setChecked(true);
|
||||
}
|
||||
|
||||
pm.setOnMenuItemClickListener(this);
|
||||
|
@ -360,6 +370,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
} else if (item.getItemId() == R.id.menu_reading_markunread) {
|
||||
FeedUtils.markStoryUnread(story, getActivity());
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_theme_auto) {
|
||||
PrefsUtils.setSelectedTheme(getActivity(), ThemeValue.AUTO);
|
||||
UIUtils.restartActivity(getActivity());
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_theme_light) {
|
||||
PrefsUtils.setSelectedTheme(getActivity(), ThemeValue.LIGHT);
|
||||
UIUtils.restartActivity(getActivity());
|
||||
|
@ -386,7 +400,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.save_story_button) void clickSave() {
|
||||
private void clickSave() {
|
||||
if (story.starred) {
|
||||
FeedUtils.setStorySaved(story.storyHash, false, getActivity());
|
||||
} else {
|
||||
|
@ -395,24 +409,24 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
}
|
||||
|
||||
private void updateSaveButton() {
|
||||
if (saveButton == null) return;
|
||||
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
if (itemCommentBinding.saveStoryButton == null) return;
|
||||
itemCommentBinding.saveStoryButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
}
|
||||
|
||||
@OnClick(R.id.share_story_button) void clickShare() {
|
||||
private void clickShare() {
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(story, sourceUserId);
|
||||
newFragment.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
|
||||
private void updateShareButton() {
|
||||
if (shareButton == null) return;
|
||||
if (itemCommentBinding.shareStoryButton == null) return;
|
||||
for (String userId : story.sharedUserIds) {
|
||||
if (TextUtils.equals(userId, user.id)) {
|
||||
shareButton.setText(R.string.already_shared);
|
||||
itemCommentBinding.shareStoryButton.setText(R.string.already_shared);
|
||||
return;
|
||||
}
|
||||
}
|
||||
shareButton.setText(R.string.share_this);
|
||||
itemCommentBinding.shareStoryButton.setText(R.string.share_this);
|
||||
}
|
||||
|
||||
private void setupItemCommentsAndShares() {
|
||||
|
@ -443,28 +457,28 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
feedHeaderBorder.setBackgroundColor(Color.parseColor("#" + feedBorder));
|
||||
|
||||
if (TextUtils.equals(faviconText, "black")) {
|
||||
itemFeed.setTextColor(UIUtils.getColor(getActivity(), R.color.text));
|
||||
itemFeed.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_white));
|
||||
binding.readingFeedTitle.setTextColor(UIUtils.getColor(getActivity(), R.color.text));
|
||||
binding.readingFeedTitle.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_white));
|
||||
} else {
|
||||
itemFeed.setTextColor(UIUtils.getColor(getActivity(), R.color.white));
|
||||
itemFeed.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_black));
|
||||
binding.readingFeedTitle.setTextColor(UIUtils.getColor(getActivity(), R.color.white));
|
||||
binding.readingFeedTitle.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_black));
|
||||
}
|
||||
|
||||
if (!displayFeedDetails) {
|
||||
itemFeed.setVisibility(View.GONE);
|
||||
binding.readingFeedTitle.setVisibility(View.GONE);
|
||||
feedIcon.setVisibility(View.GONE);
|
||||
} else {
|
||||
FeedUtils.iconLoader.displayImage(feedIconUrl, feedIcon, 0, false);
|
||||
itemFeed.setText(feedTitle);
|
||||
binding.readingFeedTitle.setText(feedTitle);
|
||||
}
|
||||
|
||||
itemDate.setText(StoryUtils.formatLongDate(getActivity(), story.timestamp));
|
||||
|
||||
if (story.tags.length <= 0) {
|
||||
tagContainer.setVisibility(View.GONE);
|
||||
binding.readingItemTags.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
itemAuthors.setOnClickListener(new OnClickListener() {
|
||||
binding.readingItemAuthors.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (story.feedId.equals("0")) return; // cannot train on feedless stories
|
||||
|
@ -473,7 +487,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
}
|
||||
});
|
||||
|
||||
itemFeed.setOnClickListener(new OnClickListener() {
|
||||
binding.readingFeedTitle.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (story.feedId.equals("0")) return; // cannot train on feedless stories
|
||||
|
@ -482,7 +496,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
}
|
||||
});
|
||||
|
||||
itemTitle.setOnClickListener(new OnClickListener() {
|
||||
binding.readingItemTitle.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
|
@ -506,7 +520,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
Drawable tag_green_background = UIUtils.getDrawable(getActivity(), R.drawable.tag_background_positive);
|
||||
Drawable tag_red_background = UIUtils.getDrawable(getActivity(), R.drawable.tag_background_negative);
|
||||
|
||||
tagContainer.removeAllViews();
|
||||
binding.readingItemTags.removeAllViews();
|
||||
for (String tag : story.tags) {
|
||||
// TODO: these textviews with compound images are buggy, but stubbed in to let colourblind users
|
||||
// see what is going on. these should be replaced with proper Chips when the v28 Chip lib
|
||||
|
@ -549,21 +563,21 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
});
|
||||
}
|
||||
|
||||
tagContainer.addView(v);
|
||||
binding.readingItemTags.addView(v);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(story.authors)) {
|
||||
itemAuthors.setText("• " + story.authors);
|
||||
binding.readingItemAuthors.setText("• " + story.authors);
|
||||
if (classifier != null && classifier.authors.containsKey(story.authors)) {
|
||||
switch (classifier.authors.get(story.authors)) {
|
||||
case Classifier.LIKE:
|
||||
itemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.positive));
|
||||
binding.readingItemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.positive));
|
||||
break;
|
||||
case Classifier.DISLIKE:
|
||||
itemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.negative));
|
||||
binding.readingItemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.negative));
|
||||
break;
|
||||
default:
|
||||
itemAuthors.setTextColor(UIUtils.getThemedColor(getActivity(), R.attr.readingItemMetadata, android.R.attr.textColor));
|
||||
binding.readingItemAuthors.setTextColor(UIUtils.getThemedColor(getActivity(), R.attr.readingItemMetadata, android.R.attr.textColor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -571,7 +585,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
|
||||
String title = story.title;
|
||||
title = UIUtils.colourTitleFromClassifier(title, classifier);
|
||||
itemTitle.setText(UIUtils.fromHtml(title));
|
||||
binding.readingItemTitle.setText(UIUtils.fromHtml(title));
|
||||
}
|
||||
|
||||
public void switchSelectedViewMode() {
|
||||
|
@ -613,8 +627,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
|
||||
private void reloadStoryContent() {
|
||||
// reset indicators
|
||||
textViewLoadingMsg.setVisibility(View.GONE);
|
||||
textViewLoadingFailedMsg.setVisibility(View.GONE);
|
||||
binding.readingTextloading.setVisibility(View.GONE);
|
||||
binding.readingTextmodefailed.setVisibility(View.GONE);
|
||||
enableProgress(false);
|
||||
|
||||
boolean needStoryContent = false;
|
||||
|
@ -623,10 +637,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
needStoryContent = true;
|
||||
} else {
|
||||
if (textViewUnavailable) {
|
||||
textViewLoadingFailedMsg.setVisibility(View.VISIBLE);
|
||||
binding.readingTextmodefailed.setVisibility(View.VISIBLE);
|
||||
needStoryContent = true;
|
||||
} else if (originalText == null) {
|
||||
textViewLoadingMsg.setVisibility(View.VISIBLE);
|
||||
binding.readingTextloading.setVisibility(View.VISIBLE);
|
||||
enableProgress(true);
|
||||
loadOriginalText();
|
||||
// still show the story mode version, as the text mode one may take some time
|
||||
|
@ -782,11 +796,20 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"dark_reading.css\" />");
|
||||
} else if (themeValue == ThemeValue.BLACK) {
|
||||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"black_reading.css\" />");
|
||||
} else if (themeValue == ThemeValue.AUTO) {
|
||||
int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
|
||||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"dark_reading.css\" />");
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO) {
|
||||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"light_reading.css\" />");
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_UNDEFINED) {
|
||||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"light_reading.css\" />");
|
||||
}
|
||||
}
|
||||
builder.append("</head><body><div class=\"NB-story\">");
|
||||
builder.append(storyText);
|
||||
builder.append("</div></body></html>");
|
||||
web.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
|
||||
binding.readingWebview.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -887,10 +910,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
// insufficient time to allow the WebView to actually finish internally computing state and size.
|
||||
// an additional fixed delay is added in a last ditch attempt to give the black-box platform
|
||||
// threads a chance to finish their work.
|
||||
fragmentScrollview.postDelayed(new Runnable() {
|
||||
binding.readingScrollview.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
int relPos = Math.round(fragmentScrollview.getChildAt(0).getMeasuredHeight() * savedScrollPosRel);
|
||||
fragmentScrollview.scrollTo(0, relPos);
|
||||
int relPos = Math.round(binding.readingScrollview.getChildAt(0).getMeasuredHeight() * savedScrollPosRel);
|
||||
binding.readingScrollview.scrollTo(0, relPos);
|
||||
}
|
||||
}, 75L);
|
||||
}
|
||||
|
@ -903,7 +926,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
private class TextSizeReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
web.setTextSize(intent.getFloatExtra(TEXT_SIZE_VALUE, 1.0f));
|
||||
binding.readingWebview.setTextSize(intent.getFloatExtra(TEXT_SIZE_VALUE, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -924,7 +947,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
|
|||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
if (web.wasLinkClicked()) {
|
||||
if (binding.readingWebview.wasLinkClicked()) {
|
||||
// Clicked a link so ignore immersive view
|
||||
return super.onSingleTapUp(e);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.databinding.FragmentReadingpagerBinding;
|
||||
|
||||
/*
|
||||
* A fragment to hold the story pager. Eventually this fragment should hold much of the UI and logic
|
||||
|
@ -21,8 +18,6 @@ import com.newsblur.activity.Reading;
|
|||
*/
|
||||
public class ReadingPagerFragment extends NbFragment {
|
||||
|
||||
@Bind(R.id.reading_pager) ViewPager pager;
|
||||
|
||||
public static ReadingPagerFragment newInstance() {
|
||||
ReadingPagerFragment fragment = new ReadingPagerFragment();
|
||||
Bundle arguments = new Bundle();
|
||||
|
@ -33,12 +28,12 @@ public class ReadingPagerFragment extends NbFragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_readingpager, null);
|
||||
ButterKnife.bind(this, v);
|
||||
FragmentReadingpagerBinding binding = FragmentReadingpagerBinding.bind(v);
|
||||
|
||||
Reading activity = ((Reading) getActivity());
|
||||
|
||||
pager.addOnPageChangeListener(activity);
|
||||
activity.offerPager(pager, getChildFragmentManager());
|
||||
binding.readingPager.addOnPageChangeListener(activity);
|
||||
activity.offerPager(binding.readingPager, getChildFragmentManager());
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,23 +3,19 @@ package com.newsblur.fragment;
|
|||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.AddSocial;
|
||||
import com.newsblur.activity.Login;
|
||||
import com.newsblur.databinding.FragmentRegisterprogressBinding;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.domain.RegisterResponse;
|
||||
|
||||
|
@ -31,9 +27,7 @@ public class RegisterProgressFragment extends Fragment {
|
|||
private String password;
|
||||
private String email;
|
||||
private RegisterTask registerTask;
|
||||
@Bind(R.id.register_viewswitcher) ViewSwitcher switcher;
|
||||
@Bind(R.id.registering_next_1) Button next;
|
||||
@Bind(R.id.registerprogress_logo) ImageView registerProgressLogo;
|
||||
private FragmentRegisterprogressBinding binding;
|
||||
|
||||
public static RegisterProgressFragment getInstance(String username, String password, String email) {
|
||||
RegisterProgressFragment fragment = new RegisterProgressFragment();
|
||||
|
@ -59,12 +53,12 @@ public class RegisterProgressFragment extends Fragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_registerprogress, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = FragmentRegisterprogressBinding.bind(v);
|
||||
|
||||
registerProgressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
|
||||
binding.registerprogressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
|
||||
|
||||
if (registerTask != null) {
|
||||
switcher.showNext();
|
||||
binding.registerViewswitcher.showNext();
|
||||
} else {
|
||||
registerTask = new RegisterTask();
|
||||
registerTask.execute();
|
||||
|
@ -73,7 +67,18 @@ public class RegisterProgressFragment extends Fragment {
|
|||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.registering_next_1) void next() {
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.registeringNext1.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void next() {
|
||||
Intent i = new Intent(getActivity(), AddSocial.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
@ -88,7 +93,7 @@ public class RegisterProgressFragment extends Fragment {
|
|||
@Override
|
||||
protected void onPostExecute(RegisterResponse response) {
|
||||
if (response.authenticated) {
|
||||
switcher.showNext();
|
||||
binding.registerViewswitcher.showNext();
|
||||
} else {
|
||||
String errorMessage = response.getErrorMessage();
|
||||
if(errorMessage == null) {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.DialogRenameBinding;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
public class RenameDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String FEED = "feed";
|
||||
private static final String FOLDER = "folder";
|
||||
private static final String FOLDER_NAME = "folder_name";
|
||||
private static final String FOLDER_PARENT = "folder_parent";
|
||||
private static final String RENAME_TYPE = "rename_type";
|
||||
|
||||
public static RenameDialogFragment newInstance(Feed feed) {
|
||||
RenameDialogFragment fragment = new RenameDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(FEED, feed);
|
||||
args.putString(RENAME_TYPE, FEED);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static RenameDialogFragment newInstance(String folderName, String folderParent) {
|
||||
RenameDialogFragment fragment = new RenameDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(FOLDER_NAME, folderName);
|
||||
args.putString(FOLDER_PARENT, folderParent);
|
||||
args.putString(RENAME_TYPE, FOLDER);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_rename, null);
|
||||
final DialogRenameBinding binding = DialogRenameBinding.bind(v);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setView(v);
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
RenameDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
if (getArguments().getString(RENAME_TYPE).equals(FEED)) {
|
||||
final Feed feed = (Feed) getArguments().getSerializable(FEED);
|
||||
builder.setTitle(String.format(getResources().getString(R.string.title_rename_feed), feed.title));
|
||||
binding.inputName.setText(feed.title);
|
||||
builder.setPositiveButton(R.string.feed_name_save, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
FeedUtils.renameFeed(activity, feed.feedId, binding.inputName.getText().toString());
|
||||
RenameDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
} else { // FOLDER
|
||||
final String folderName = getArguments().getString(FOLDER_NAME);
|
||||
final String folderParentName = getArguments().getString(FOLDER_PARENT);
|
||||
|
||||
builder.setTitle(String.format(getResources().getString(R.string.title_rename_folder), folderName));
|
||||
binding.inputName.setText(folderName);
|
||||
|
||||
builder.setPositiveButton(R.string.folder_name_save, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
String newFolderName = binding.inputName.getText().toString().toUpperCase();
|
||||
if (TextUtils.isEmpty(newFolderName)) {
|
||||
Toast.makeText(activity, R.string.add_folder_name, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String inFolder = "";
|
||||
if (!TextUtils.isEmpty(folderParentName) && !folderParentName.equals(AppConstants.ROOT_FOLDER)) {
|
||||
inFolder = folderParentName;
|
||||
}
|
||||
FeedUtils.renameFolder(folderName, newFolderName, inFolder, activity, new APIManager(activity));
|
||||
RenameDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
public class RenameFeedFragment extends DialogFragment {
|
||||
|
||||
private Feed feed;
|
||||
|
||||
@Bind(R.id.feed_name_field) EditText feedNameView;
|
||||
|
||||
public static RenameFeedFragment newInstance(Feed feed) {
|
||||
RenameFeedFragment fragment = new RenameFeedFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable("feed", feed);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
feed = (Feed) getArguments().getSerializable("feed");
|
||||
|
||||
final Activity activity = getActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_rename_feed, null);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(String.format(getResources().getString(R.string.title_rename_feed), feed.title));
|
||||
builder.setView(v);
|
||||
|
||||
feedNameView.setText(feed.title);
|
||||
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
RenameFeedFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.feed_name_save, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
FeedUtils.renameFeed(activity, feed.feedId, feedNameView.getText().toString());
|
||||
RenameFeedFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Dialog dialog = builder.create();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
public class SaveSearchFragment extends DialogFragment {
|
||||
|
||||
private static final String FEED_ID = "feed_id";
|
||||
private static final String QUERY = "query";
|
||||
|
||||
public static SaveSearchFragment newInstance(String feedId, String query) {
|
||||
SaveSearchFragment frag = new SaveSearchFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(FEED_ID, feedId);
|
||||
args.putString(QUERY, query);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage(String.format(getResources().getString(R.string.add_saved_search_message), getArguments().getString(QUERY)));
|
||||
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
FeedUtils.saveSearch(getArguments().getString(FEED_ID), getArguments().getString(QUERY), getActivity(), new APIManager(getActivity()));
|
||||
SaveSearchFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
SaveSearchFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import com.newsblur.domain.UserProfile;
|
|||
import com.newsblur.fragment.ReplyDialogFragment;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
|
||||
|
@ -96,7 +97,7 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
|
||||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
commentText.setText(comment.commentText);
|
||||
commentText.setText(UIUtils.fromHtml(comment.commentText));
|
||||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
|
||||
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
|
||||
|
@ -161,7 +162,7 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
for (final Reply reply : replies) {
|
||||
View replyView = inflater.inflate(R.layout.include_reply, null);
|
||||
TextView replyText = (TextView) replyView.findViewById(R.id.reply_text);
|
||||
replyText.setText(reply.text);
|
||||
replyText.setText(UIUtils.fromHtml(reply.text));
|
||||
ImageView replyImage = (ImageView) replyView.findViewById(R.id.reply_user_image);
|
||||
|
||||
final UserProfile replyUser = FeedUtils.dbHelper.getUserProfile(reply.userId);
|
||||
|
|
|
@ -14,20 +14,15 @@ import android.view.Gravity;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.DialogTrainstoryBinding;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.util.FeedSet;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.view.SelectOnlyEditText;
|
||||
|
||||
public class StoryIntelTrainerFragment extends DialogFragment {
|
||||
|
||||
|
@ -35,17 +30,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
private FeedSet fs;
|
||||
private Classifier classifier;
|
||||
private Integer newTitleTraining;
|
||||
|
||||
@Bind(R.id.intel_tag_header) TextView headerTags;
|
||||
@Bind(R.id.intel_author_header) TextView headerAuthor;
|
||||
@Bind(R.id.intel_title_selection) SelectOnlyEditText titleSelection;
|
||||
@Bind(R.id.intel_title_like) Button titleLikeButton;
|
||||
@Bind(R.id.intel_title_dislike) Button titleDislikeButton;
|
||||
@Bind(R.id.intel_title_clear) Button titleClearButton;
|
||||
@Bind(R.id.existing_title_intel_container) LinearLayout titleRowsContainer;
|
||||
@Bind(R.id.existing_tag_intel_container) LinearLayout tagRowsContainer;
|
||||
@Bind(R.id.existing_author_intel_container) LinearLayout authorRowsContainer;
|
||||
@Bind(R.id.existing_feed_intel_container) LinearLayout feedRowsContainer;
|
||||
private DialogTrainstoryBinding binding;
|
||||
|
||||
public static StoryIntelTrainerFragment newInstance(Story story, FeedSet fs) {
|
||||
if (story.feedId.equals("0")) {
|
||||
|
@ -69,44 +54,44 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
final Activity activity = getActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
View v = inflater.inflate(R.layout.dialog_trainstory, null);
|
||||
ButterKnife.bind(this, v);
|
||||
binding = DialogTrainstoryBinding.bind(v);
|
||||
|
||||
// set up the special title training box for the title from this story and the associated buttons
|
||||
titleSelection.setText(story.title);
|
||||
binding.intelTitleSelection.setText(story.title);
|
||||
// the layout sets inputType="none" on this EditText, but a widespread platform bug requires us
|
||||
// to also set this programmatically to make the field read-only for selection.
|
||||
titleSelection.setInputType(InputType.TYPE_NULL);
|
||||
binding.intelTitleSelection.setInputType(InputType.TYPE_NULL);
|
||||
// the user is selecting for our custom widget, not to copy/paste
|
||||
titleSelection.disableActionMenu();
|
||||
binding.intelTitleSelection.disableActionMenu();
|
||||
// pre-select the whole title to make it easier for the user to manipulate the selection handles
|
||||
titleSelection.selectAll();
|
||||
binding.intelTitleSelection.selectAll();
|
||||
// do this after init and selection to prevent toast spam
|
||||
titleSelection.setForceSelection(true);
|
||||
binding.intelTitleSelection.setForceSelection(true);
|
||||
// the disposition buttons for a new title training don't immediately impact the classifier object,
|
||||
// lest the user want to change selection substring after choosing the disposition. so just store
|
||||
// the training factor in a variable that can be pulled on completion
|
||||
titleLikeButton.setOnClickListener(new OnClickListener() {
|
||||
binding.intelTitleLike.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
newTitleTraining = Classifier.LIKE;
|
||||
titleLikeButton.setBackgroundResource(R.drawable.ic_like_active);
|
||||
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_gray55);
|
||||
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_active);
|
||||
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_gray55);
|
||||
}
|
||||
});
|
||||
titleDislikeButton.setOnClickListener(new OnClickListener() {
|
||||
binding.intelTitleDislike.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
newTitleTraining = Classifier.DISLIKE;
|
||||
titleLikeButton.setBackgroundResource(R.drawable.ic_like_gray55);
|
||||
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_active);
|
||||
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_gray55);
|
||||
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_active);
|
||||
}
|
||||
});
|
||||
titleClearButton.setOnClickListener(new OnClickListener() {
|
||||
binding.intelTitleClear.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
newTitleTraining = null;
|
||||
titleLikeButton.setBackgroundResource(R.drawable.ic_like_gray55);
|
||||
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_gray55);
|
||||
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_gray55);
|
||||
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_gray55);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -117,7 +102,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
|
||||
label.setText(rule.getKey());
|
||||
UIUtils.setupIntelDialogRow(row, classifier.title, rule.getKey());
|
||||
titleRowsContainer.addView(row);
|
||||
binding.existingTitleIntelContainer.addView(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,9 +112,9 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
|
||||
label.setText(tag);
|
||||
UIUtils.setupIntelDialogRow(row, classifier.tags, tag);
|
||||
tagRowsContainer.addView(row);
|
||||
binding.existingTagIntelContainer.addView(row);
|
||||
}
|
||||
if (story.tags.length < 1) headerTags.setVisibility(View.GONE);
|
||||
if (story.tags.length < 1) binding.intelTagHeader.setVisibility(View.GONE);
|
||||
|
||||
// there is a single author per story
|
||||
if (!TextUtils.isEmpty(story.authors)) {
|
||||
|
@ -137,9 +122,9 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
TextView labelAuthor = (TextView) rowAuthor.findViewById(R.id.intel_row_label);
|
||||
labelAuthor.setText(story.authors);
|
||||
UIUtils.setupIntelDialogRow(rowAuthor, classifier.authors, story.authors);
|
||||
authorRowsContainer.addView(rowAuthor);
|
||||
binding.existingAuthorIntelContainer.addView(rowAuthor);
|
||||
} else {
|
||||
headerAuthor.setVisibility(View.GONE);
|
||||
binding.intelAuthorHeader.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// there is a single feed to be trained, but it is a bit odd in that the label is the title and
|
||||
|
@ -148,7 +133,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
TextView labelFeed = (TextView) rowFeed.findViewById(R.id.intel_row_label);
|
||||
labelFeed.setText(FeedUtils.getFeedTitle(story.feedId));
|
||||
UIUtils.setupIntelDialogRow(rowFeed, classifier.feeds, story.feedId);
|
||||
feedRowsContainer.addView(rowFeed);
|
||||
binding.existingFeedIntelContainer.addView(rowFeed);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(R.string.story_intel_dialog_title);
|
||||
|
@ -163,8 +148,8 @@ public class StoryIntelTrainerFragment extends DialogFragment {
|
|||
builder.setPositiveButton(R.string.dialog_story_intel_save, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
if ((newTitleTraining != null) && (!TextUtils.isEmpty(titleSelection.getSelection()))) {
|
||||
classifier.title.put(titleSelection.getSelection(), newTitleTraining);
|
||||
if ((newTitleTraining != null) && (!TextUtils.isEmpty(binding.intelTitleSelection.getSelection()))) {
|
||||
classifier.title.put(binding.intelTitleSelection.getSelection(), newTitleTraining);
|
||||
}
|
||||
FeedUtils.updateClassifier(story.feedId, classifier, fs, activity);
|
||||
StoryIntelTrainerFragment.this.dismiss();
|
||||
|
|
|
@ -7,13 +7,9 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.StoryorderDialogBinding;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
import com.newsblur.util.StoryOrderChangedListener;
|
||||
|
||||
|
@ -21,8 +17,6 @@ public class StoryOrderDialogFragment extends DialogFragment {
|
|||
|
||||
private static String CURRENT_ORDER = "currentOrder";
|
||||
private StoryOrder currentValue;
|
||||
@Bind(R.id.radio_newest) RadioButton newestButton;
|
||||
@Bind(R.id.radio_oldest) RadioButton oldestButton;
|
||||
|
||||
public static StoryOrderDialogFragment newInstance(StoryOrder currentValue) {
|
||||
StoryOrderDialogFragment dialog = new StoryOrderDialogFragment();
|
||||
|
@ -42,25 +36,38 @@ public class StoryOrderDialogFragment extends DialogFragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = (StoryOrder) getArguments().getSerializable(CURRENT_ORDER);
|
||||
View v = inflater.inflate(R.layout.storyorder_dialog, null);
|
||||
ButterKnife.bind(this, v);
|
||||
StoryorderDialogBinding binding = StoryorderDialogBinding.bind(v);
|
||||
|
||||
newestButton.setChecked(currentValue == StoryOrder.NEWEST);
|
||||
oldestButton.setChecked(currentValue == StoryOrder.OLDEST);
|
||||
binding.radioNewest.setChecked(currentValue == StoryOrder.NEWEST);
|
||||
binding.radioOldest.setChecked(currentValue == StoryOrder.OLDEST);
|
||||
|
||||
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
|
||||
|
||||
binding.radioNewest.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
selectNewest();
|
||||
}
|
||||
});
|
||||
binding.radioOldest.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
selectOldest();
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_newest) void selectNewest() {
|
||||
private void selectNewest() {
|
||||
if (currentValue != StoryOrder.NEWEST) {
|
||||
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.NEWEST);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_oldest) void selectOldest() {
|
||||
private void selectOldest() {
|
||||
if (currentValue != StoryOrder.OLDEST) {
|
||||
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.OLDEST);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,11 @@ public class APIConstants {
|
|||
public static final String PATH_SET_NOTIFICATIONS = "/notifications/feed/";
|
||||
public static final String PATH_INSTA_FETCH = "/rss_feeds/exception_retry";
|
||||
public static final String PATH_RENAME_FEED = "/reader/rename_feed";
|
||||
public static final String PATH_DELETE_SEARCH = "/reader/delete_search";
|
||||
public static final String PATH_SAVE_SEARCH = "/reader/save_search";
|
||||
public static final String PATH_ADD_FOLDER = "/reader/add_folder";
|
||||
public static final String PATH_DELETE_FOLDER = "/reader/delete_folder";
|
||||
public static final String PATH_RENAME_FOLDER = "/reader/rename_folder";
|
||||
|
||||
public static String buildUrl(String path) {
|
||||
return CurrentUrlBase + path;
|
||||
|
@ -121,6 +126,9 @@ public class APIConstants {
|
|||
public static final String PARAMETER_RESET_FETCH = "reset_fetch";
|
||||
public static final String PARAMETER_INFREQUENT = "infrequent";
|
||||
public static final String PARAMETER_FEEDTITLE = "feed_title";
|
||||
public static final String PARAMETER_FOLDER_TO_DELETE = "folder_to_delete";
|
||||
public static final String PARAMETER_FOLDER_TO_RENAME = "folder_to_rename";
|
||||
public static final String PARAMETER_NEW_FOLDER_NAME = "new_folder_name";
|
||||
|
||||
public static final String VALUE_PREFIX_SOCIAL = "social:";
|
||||
public static final String VALUE_ALLSOCIAL = "river:blurblogs"; // the magic value passed to the mark-read API for all social feeds
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -550,11 +551,21 @@ public class APIManager {
|
|||
return (CommentResponse) response.getResponse(gson, CommentResponse.class);
|
||||
}
|
||||
|
||||
public AddFeedResponse addFeed(String feedUrl) {
|
||||
public NewsBlurResponse addFolder(String folderName) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FOLDER, folderName);
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_ADD_FOLDER), values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public AddFeedResponse addFeed(String feedUrl, @Nullable String folderName) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_URL, feedUrl);
|
||||
if (!TextUtils.isEmpty(folderName) && !folderName.equals(AppConstants.ROOT_FOLDER)) {
|
||||
values.put(APIConstants.PARAMETER_FOLDER, folderName);
|
||||
}
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_ADD_FEED), values);
|
||||
return (AddFeedResponse) response.getResponse(gson, AddFeedResponse.class);
|
||||
return response.getResponse(gson, AddFeedResponse.class);
|
||||
}
|
||||
|
||||
public FeedResult[] searchForFeed(String searchTerm) {
|
||||
|
@ -579,6 +590,30 @@ public class APIManager {
|
|||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse deleteFolder(String folderName, String inFolder) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FOLDER_TO_DELETE, folderName);
|
||||
values.put(APIConstants.PARAMETER_IN_FOLDER, inFolder);
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_DELETE_FOLDER), values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse deleteSearch(String feedId, String query) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_QUERY, query);
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_DELETE_SEARCH), values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse saveSearch(String feedId, String query) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_QUERY, query);
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_SAVE_SEARCH), values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse saveFeedChooser(Set<String> feeds) {
|
||||
ValueMultimap values = new ValueMultimap();
|
||||
for (String feed : feeds) {
|
||||
|
@ -617,6 +652,15 @@ public class APIManager {
|
|||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse renameFolder(String folderName, String newFolderName, String inFolder) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FOLDER_TO_RENAME, folderName);
|
||||
values.put(APIConstants.PARAMETER_NEW_FOLDER_NAME, newFolderName);
|
||||
values.put(APIConstants.PARAMETER_IN_FOLDER, inFolder);
|
||||
APIResponse response = post(buildUrl(APIConstants.PATH_RENAME_FOLDER), values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
/* HTTP METHODS */
|
||||
|
||||
private APIResponse get(final String urlString) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.google.gson.JsonObject;
|
|||
import com.google.gson.JsonParser;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.domain.SavedSearch;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.domain.StarredCount;
|
||||
import com.newsblur.util.AppConstants;
|
||||
|
@ -30,6 +31,7 @@ public class FeedFolderResponse {
|
|||
public Set<Feed> feeds;
|
||||
public Set<SocialFeed> socialFeeds;
|
||||
public Set<StarredCount> starredCounts;
|
||||
public Set<SavedSearch> savedSearches;
|
||||
|
||||
public boolean isAuthenticated;
|
||||
public boolean isPremium;
|
||||
|
@ -109,6 +111,16 @@ public class FeedFolderResponse {
|
|||
}
|
||||
}
|
||||
|
||||
savedSearches = new HashSet<>();
|
||||
JsonArray savedSearchesArray = (JsonArray) asJsonObject.get("saved_searches");
|
||||
if (savedSearchesArray != null) {
|
||||
for (int i=0; i<savedSearchesArray.size(); i++) {
|
||||
JsonElement jsonElement = savedSearchesArray.get(i);
|
||||
SavedSearch savedSearch = gson.fromJson(jsonElement, SavedSearch.class);
|
||||
savedSearches.add(savedSearch);
|
||||
}
|
||||
}
|
||||
|
||||
parseTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ public class StoryTypeAdapter implements JsonDeserializer<Story> {
|
|||
|
||||
// any characters we don't want in the short description, such as newlines or placeholders
|
||||
private final static Pattern ShortContentExcludes = Pattern.compile("[\\uFFFC\\u000A\\u000B\\u000C\\u000D]");
|
||||
private final static Pattern httpSniff = Pattern.compile("(?:http):\\/\\/");
|
||||
|
||||
public StoryTypeAdapter() {
|
||||
this.gson = new GsonBuilder()
|
||||
|
@ -40,6 +41,14 @@ public class StoryTypeAdapter implements JsonDeserializer<Story> {
|
|||
// Convert story_timestamp to milliseconds
|
||||
story.timestamp = story.timestamp * 1000;
|
||||
|
||||
// replace http image urls with https
|
||||
if (httpSniff.matcher(story.content).find() && story.secureImageUrls != null && story.secureImageUrls.size() > 0) {
|
||||
for (String httpUrl : story.secureImageUrls.keySet()) {
|
||||
String httpsUrl = story.secureImageUrls.get(httpUrl);
|
||||
story.content = story.content.replace(httpUrl, httpsUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// populate the shortContent field
|
||||
if (story.content != null) {
|
||||
CharSequence parsed = UIUtils.fromHtml(story.content);
|
||||
|
|
|
@ -16,6 +16,7 @@ import static com.newsblur.database.BlurDatabaseHelper.closeQuietly;
|
|||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.domain.SavedSearch;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.domain.StarredCount;
|
||||
import com.newsblur.domain.Story;
|
||||
|
@ -586,6 +587,12 @@ public class NBSyncService extends JobService {
|
|||
for (StarredCount sc : feedResponse.starredCounts) {
|
||||
starredCountValues.add(sc.getValues());
|
||||
}
|
||||
|
||||
// saved searches table
|
||||
List<ContentValues> savedSearchesValues = new ArrayList<>();
|
||||
for (SavedSearch savedSearch : feedResponse.savedSearches) {
|
||||
savedSearchesValues.add(savedSearch.getValues());
|
||||
}
|
||||
// the API vends the starred total as a different element, roll it into
|
||||
// the starred counts table using a special tag
|
||||
StarredCount totalStarred = new StarredCount();
|
||||
|
@ -593,7 +600,7 @@ public class NBSyncService extends JobService {
|
|||
totalStarred.tag = StarredCount.TOTAL_STARRED;
|
||||
starredCountValues.add(totalStarred.getValues());
|
||||
|
||||
dbHelper.setFeedsFolders(folderValues, feedValues, socialFeedValues, starredCountValues);
|
||||
dbHelper.setFeedsFolders(folderValues, feedValues, socialFeedValues, starredCountValues, savedSearchesValues);
|
||||
|
||||
lastFFWriteMillis = System.currentTimeMillis() - startTime;
|
||||
lastFeedCount = feedValues.size();
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package com.newsblur.util;
|
||||
|
||||
public enum DefaultBrowser {
|
||||
SYSTEM_DEFAULT,
|
||||
IN_APP_BROWSER,
|
||||
CHROME,
|
||||
FIREFOX,
|
||||
OPERA_MINI;
|
||||
|
||||
public static DefaultBrowser getDefaultBrowser(String preferenceValue) {
|
||||
switch (preferenceValue) {
|
||||
case "IN_APP_BROWSER":
|
||||
return IN_APP_BROWSER;
|
||||
case "CHROME":
|
||||
return CHROME;
|
||||
case "FIREFOX":
|
||||
return FIREFOX;
|
||||
case "OPERA_MINI":
|
||||
return OPERA_MINI;
|
||||
case "SYSTEM_DEFAULT":
|
||||
default:
|
||||
return SYSTEM_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ public class FeedSet implements Serializable {
|
|||
|
||||
private String folderName;
|
||||
private String searchQuery;
|
||||
private String searchFeedId;
|
||||
private boolean isFilterSaved = false;
|
||||
private boolean muted = false;
|
||||
|
||||
|
@ -75,17 +76,6 @@ public class FeedSet implements Serializable {
|
|||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for multiple feeds with IDs
|
||||
*/
|
||||
public static FeedSet multipleFeeds(Set<String> feedIds) {
|
||||
FeedSet fs = new FeedSet();
|
||||
fs.feeds = new HashSet<>(feedIds.size());
|
||||
fs.feeds.addAll(feedIds);
|
||||
fs.feeds = Collections.unmodifiableSet(fs.feeds);
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for all (non-social) feeds.
|
||||
*/
|
||||
|
@ -133,6 +123,16 @@ public class FeedSet implements Serializable {
|
|||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for a single saved search.
|
||||
*/
|
||||
public static FeedSet singleSavedSearch(String feedId, String searchQuery) {
|
||||
FeedSet fs = new FeedSet();
|
||||
fs.searchQuery = searchQuery;
|
||||
fs.searchFeedId = feedId;
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor for global shared stories feed.
|
||||
*/
|
||||
|
@ -281,6 +281,10 @@ public class FeedSet implements Serializable {
|
|||
return this.searchQuery;
|
||||
}
|
||||
|
||||
public String getSearchFeedId() {
|
||||
return this.searchFeedId;
|
||||
}
|
||||
|
||||
public void setFilterSaved(boolean isFilterSaved) {
|
||||
this.isFilterSaved = isFilterSaved;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.Set;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.newsblur.R;
|
||||
|
@ -18,6 +19,7 @@ import com.newsblur.domain.Classifier;
|
|||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Folder;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
import com.newsblur.domain.StarredCount;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.fragment.ReadingActionConfirmationFragment;
|
||||
import com.newsblur.network.APIManager;
|
||||
|
@ -109,6 +111,40 @@ public class FeedUtils {
|
|||
}.execute();
|
||||
}
|
||||
|
||||
public static void deleteSavedSearch(final String feedId, final String query, final Context context, final APIManager apiManager) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... voids) {
|
||||
return apiManager.deleteSearch(feedId, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
|
||||
if (!newsBlurResponse.isError()) {
|
||||
dbHelper.deleteSavedSearch(feedId, query);
|
||||
NbActivity.updateAllActivities(NbActivity.UPDATE_METADATA);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void saveSearch(final String feedId, final String query, final Context context, final APIManager apiManager) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... voids) {
|
||||
return apiManager.saveSearch(feedId, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
|
||||
if (!newsBlurResponse.isError()) {
|
||||
NBSyncService.forceFeedsFolders();
|
||||
triggerSync(context);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void deleteFeed(final String feedId, final String folderName, final Context context, final APIManager apiManager) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
|
@ -140,6 +176,42 @@ public class FeedUtils {
|
|||
}.execute();
|
||||
}
|
||||
|
||||
public static void deleteFolder(final String folderName, final String inFolder, final Context context, final APIManager apiManager) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... voids) {
|
||||
return apiManager.deleteFolder(folderName, inFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse result) {
|
||||
super.onPostExecute(result);
|
||||
if (!result.isError()) {
|
||||
NBSyncService.forceFeedsFolders();
|
||||
triggerSync(context);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void renameFolder(final String folderName, final String newFolderName, final String inFolder, final Context context, final APIManager apiManager) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... voids) {
|
||||
return apiManager.renameFolder(folderName, newFolderName, inFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse result) {
|
||||
super.onPostExecute(result);
|
||||
if (!result.isError()) {
|
||||
NBSyncService.forceFeedsFolders();
|
||||
triggerSync(context);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void markStoryUnread(final Story story, final Context context) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
|
@ -518,4 +590,8 @@ public class FeedUtils {
|
|||
return dbHelper.getSocialFeed(feedId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static StarredCount getStarredFeedByTag(String feedId) {
|
||||
return dbHelper.getStarredFeedByTag(feedId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ public class PrefConstants {
|
|||
|
||||
public static final String FEED_DEFAULT_FEED_VIEW_PREFIX = "feed_default_feed_view_";
|
||||
|
||||
public static final String DEFAULT_BROWSER = "default_browser";
|
||||
|
||||
public static final String READ_STORIES_FOLDER_NAME = "read_stories";
|
||||
public static final String SAVED_STORIES_FOLDER_NAME = "saved_stories";
|
||||
public static final String READING_ENTER_IMMERSIVE_SINGLE_TAP = "immersive_enter_single_tap";
|
||||
|
@ -85,6 +87,7 @@ public class PrefConstants {
|
|||
|
||||
public static final String THEME = "theme";
|
||||
public enum ThemeValue {
|
||||
AUTO,
|
||||
LIGHT,
|
||||
DARK,
|
||||
BLACK;
|
||||
|
|
|
@ -14,7 +14,11 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
@ -777,6 +781,15 @@ public class PrefsUtils {
|
|||
activity.setTheme(R.style.NewsBlurDarkTheme);
|
||||
} else if (value == ThemeValue.BLACK) {
|
||||
activity.setTheme(R.style.NewsBlurBlackTheme);
|
||||
} else if (value == ThemeValue.AUTO) {
|
||||
int nightModeFlags = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) {
|
||||
activity.setTheme(R.style.NewsBlurDarkTheme);
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO) {
|
||||
activity.setTheme(R.style.NewsBlurTheme);
|
||||
} else if (nightModeFlags == Configuration.UI_MODE_NIGHT_UNDEFINED) {
|
||||
activity.setTheme(R.style.NewsBlurTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,4 +958,13 @@ public class PrefsUtils {
|
|||
editor.putString(PrefConstants.WIDGET_BACKGROUND, widgetBackground.toString());
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static DefaultBrowser getDefaultBrowser(Context context) {
|
||||
return DefaultBrowser.getDefaultBrowser(getDefaultBrowserString(context));
|
||||
}
|
||||
|
||||
public static String getDefaultBrowserString(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
|
||||
return preferences.getString(PrefConstants.DEFAULT_BROWSER, DefaultBrowser.SYSTEM_DEFAULT.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.newsblur.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
@ -16,8 +17,11 @@ import android.webkit.WebView;
|
|||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.newsblur.activity.InAppBrowser;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
import com.newsblur.util.DefaultBrowser;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
||||
public class NewsblurWebview extends WebView {
|
||||
|
||||
|
@ -88,21 +92,15 @@ public class NewsblurWebview extends WebView {
|
|||
|
||||
class NewsblurWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
// this was deprecated in API 24 but the replacement only added in the same release.
|
||||
// the suppression can be removed when we move past 24
|
||||
@SuppressWarnings("deprecation")
|
||||
// as of v43.0.2357.121 of the system WebView, links no longer open in the user's chosen
|
||||
// browser, but open in-app. Override the default behaviour so it works as expected on
|
||||
// all devices.
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
Uri uri = Uri.parse(url);
|
||||
try {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(uri);
|
||||
context.startActivity(i);
|
||||
} catch (Exception e) {
|
||||
com.newsblur.util.Log.e(this.getClass().getName(), "device cannot open URLs");
|
||||
handleUri(Uri.parse(url));
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
handleUri(request.getUrl());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -115,6 +113,46 @@ public class NewsblurWebview extends WebView {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleUri(Uri uri) {
|
||||
DefaultBrowser defaultBrowser = PrefsUtils.getDefaultBrowser(context);
|
||||
if (defaultBrowser == DefaultBrowser.SYSTEM_DEFAULT) {
|
||||
openSystemDefaultBrowser(uri);
|
||||
} else if (defaultBrowser == DefaultBrowser.IN_APP_BROWSER) {
|
||||
Intent intent = new Intent(context, InAppBrowser.class);
|
||||
intent.putExtra(InAppBrowser.URI, uri);
|
||||
context.startActivity(intent);
|
||||
} else if (defaultBrowser == DefaultBrowser.CHROME) {
|
||||
openExternalBrowserApp(uri, "com.android.chrome");
|
||||
} else if (defaultBrowser == DefaultBrowser.FIREFOX) {
|
||||
openExternalBrowserApp(uri, "org.mozilla.firefox");
|
||||
} else if (defaultBrowser == DefaultBrowser.OPERA_MINI) {
|
||||
openExternalBrowserApp(uri, "com.opera.mini.native");
|
||||
}
|
||||
}
|
||||
|
||||
private void openSystemDefaultBrowser(Uri uri) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(uri);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
com.newsblur.util.Log.e(this.getClass().getName(), "device cannot open URLs");
|
||||
}
|
||||
}
|
||||
|
||||
private void openExternalBrowserApp(Uri uri, String packageName) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(uri);
|
||||
intent.setPackage(packageName);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
com.newsblur.util.Log.e(this.getClass().getName(), "apps not available to open URLs");
|
||||
// fallback to system default if apps cannot be opened
|
||||
openSystemDefaultBrowser(uri);
|
||||
}
|
||||
}
|
||||
|
||||
// this WCC implements the bare minimum callbacks to get HTML5 fullscreen video working
|
||||
class NewsblurWebChromeClient extends WebChromeClient {
|
||||
public View customView;
|
||||
|
|
|
@ -5,14 +5,10 @@ import android.content.Context;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.databinding.StateToggleBinding;
|
||||
import com.newsblur.util.StateFilter;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
|
@ -27,52 +23,53 @@ public class StateToggleButton extends LinearLayout {
|
|||
|
||||
private int parentWidthPX = 0;
|
||||
|
||||
@Bind(R.id.toggle_all) ViewGroup allButton;
|
||||
@Bind(R.id.toggle_all_icon) View allButtonIcon;
|
||||
@Bind(R.id.toggle_all_text) View allButtonText;
|
||||
@Bind(R.id.toggle_some) ViewGroup someButton;
|
||||
@Bind(R.id.toggle_some_icon) View someButtonIcon;
|
||||
@Bind(R.id.toggle_some_text) View someButtonText;
|
||||
@Bind(R.id.toggle_focus) ViewGroup focusButton;
|
||||
@Bind(R.id.toggle_focus_icon) View focusButtonIcon;
|
||||
@Bind(R.id.toggle_focus_text) View focusButtonText;
|
||||
@Bind(R.id.toggle_saved) ViewGroup savedButton;
|
||||
@Bind(R.id.toggle_saved_icon) View savedButtonIcon;
|
||||
@Bind(R.id.toggle_saved_text) View savedButtonText;
|
||||
private StateToggleBinding binding;
|
||||
|
||||
public StateToggleButton(Context context, AttributeSet art) {
|
||||
super(context, art);
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View view = inflater.inflate(R.layout.state_toggle, this);
|
||||
ButterKnife.bind(this, view);
|
||||
binding = StateToggleBinding.bind(view);
|
||||
|
||||
// smooth layout transitions are enabled in our layout XML; this smooths out toggle
|
||||
// transitions on newer devices
|
||||
allButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
someButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
focusButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
savedButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
binding.toggleAll.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
binding.toggleSome.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
binding.toggleFocus.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
binding.toggleSaved.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
|
||||
|
||||
setState(state);
|
||||
|
||||
binding.toggleAll.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setState(StateFilter.ALL);
|
||||
}
|
||||
});
|
||||
binding.toggleSome.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setState(StateFilter.SOME);
|
||||
}
|
||||
});
|
||||
binding.toggleFocus.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setState(StateFilter.BEST);
|
||||
}
|
||||
});
|
||||
binding.toggleSaved.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setState(StateFilter.SAVED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setStateListener(final StateChangedListener stateChangedListener) {
|
||||
this.stateChangedListener = stateChangedListener;
|
||||
}
|
||||
|
||||
@OnClick({R.id.toggle_all, R.id.toggle_some, R.id.toggle_focus, R.id.toggle_saved})
|
||||
public void onClickToggle(View v) {
|
||||
if (v.getId() == R.id.toggle_all) {
|
||||
setState(StateFilter.ALL);
|
||||
} else if (v.getId() == R.id.toggle_some) {
|
||||
setState(StateFilter.SOME);
|
||||
} else if (v.getId() == R.id.toggle_focus) {
|
||||
setState(StateFilter.BEST);
|
||||
} else if (v.getId() == R.id.toggle_saved) {
|
||||
setState(StateFilter.SAVED);
|
||||
}
|
||||
}
|
||||
|
||||
public void setState(StateFilter state) {
|
||||
this.state = state;
|
||||
updateButtonStates();
|
||||
|
@ -93,21 +90,21 @@ public class StateToggleButton extends LinearLayout {
|
|||
if (widthDP > COLLAPSE_WIDTH_DP) compactMode = false;
|
||||
}
|
||||
|
||||
allButtonText.setVisibility((!compactMode || state == StateFilter.ALL) ? View.VISIBLE : View.GONE);
|
||||
allButton.setEnabled(state != StateFilter.ALL);
|
||||
allButtonIcon.setAlpha(state == StateFilter.ALL ? 1.0f : 0.6f);
|
||||
binding.toggleAllText.setVisibility((!compactMode || state == StateFilter.ALL) ? View.VISIBLE : View.GONE);
|
||||
binding.toggleAll.setEnabled(state != StateFilter.ALL);
|
||||
binding.toggleAllIcon.setAlpha(state == StateFilter.ALL ? 1.0f : 0.6f);
|
||||
|
||||
someButtonText.setVisibility((!compactMode || state == StateFilter.SOME) ? View.VISIBLE : View.GONE);
|
||||
someButton.setEnabled(state != StateFilter.SOME);
|
||||
someButtonIcon.setAlpha(state == StateFilter.SOME ? 1.0f : 0.6f);
|
||||
binding.toggleSomeText.setVisibility((!compactMode || state == StateFilter.SOME) ? View.VISIBLE : View.GONE);
|
||||
binding.toggleSome.setEnabled(state != StateFilter.SOME);
|
||||
binding.toggleSomeIcon.setAlpha(state == StateFilter.SOME ? 1.0f : 0.6f);
|
||||
|
||||
focusButtonText.setVisibility((!compactMode || state == StateFilter.BEST) ? View.VISIBLE : View.GONE);
|
||||
focusButton.setEnabled(state != StateFilter.BEST);
|
||||
focusButtonIcon.setAlpha(state == StateFilter.BEST ? 1.0f : 0.6f);
|
||||
binding.toggleFocusText.setVisibility((!compactMode || state == StateFilter.BEST) ? View.VISIBLE : View.GONE);
|
||||
binding.toggleFocus.setEnabled(state != StateFilter.BEST);
|
||||
binding.toggleFocusIcon.setAlpha(state == StateFilter.BEST ? 1.0f : 0.6f);
|
||||
|
||||
savedButtonText.setVisibility((!compactMode || state == StateFilter.SAVED) ? View.VISIBLE : View.GONE);
|
||||
savedButton.setEnabled(state != StateFilter.SAVED);
|
||||
savedButtonIcon.setAlpha(state == StateFilter.SAVED ? 1.0f : 0.6f);
|
||||
binding.toggleSavedText.setVisibility((!compactMode || state == StateFilter.SAVED) ? View.VISIBLE : View.GONE);
|
||||
binding.toggleSaved.setEnabled(state != StateFilter.SAVED);
|
||||
binding.toggleSavedIcon.setAlpha(state == StateFilter.SAVED ? 1.0f : 0.6f);
|
||||
}
|
||||
|
||||
public interface StateChangedListener {
|
||||
|
|
|
@ -150,4 +150,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||
if (!ThemeManager.themeManager.isDarkTheme) {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return UIStatusBarStyleDarkContent;
|
||||
}
|
||||
}
|
||||
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -734,8 +734,9 @@ static const CGFloat kFolderTitleHeight = 36.0;
|
|||
if (self.operation == FeedChooserOperationMuteSites) {
|
||||
UIImage *image = [UIImage imageNamed:@"mute_feed_on.png"];
|
||||
UIImage *highlightedImage = [UIImage imageNamed:@"mute_feed_off.png"];
|
||||
|
||||
cell.accessoryView = [[UIImageView alloc] initWithImage:image highlightedImage:highlightedImage];
|
||||
UIImageView *imageView = [[UIImageView alloc] initWithImage:image highlightedImage:highlightedImage];
|
||||
imageView.highlighted = [tableView.indexPathsForSelectedRows containsObject:indexPath];
|
||||
cell.accessoryView = imageView;
|
||||
} else {
|
||||
cell.accessoryView = nil;
|
||||
}
|
||||
|
@ -778,6 +779,10 @@ static const CGFloat kFolderTitleHeight = 36.0;
|
|||
[self setWidgetIncludes:YES itemForIndexPath:indexPath];
|
||||
}
|
||||
|
||||
UIImageView *imageView = (UIImageView *)[tableView cellForRowAtIndexPath:indexPath].accessoryView;
|
||||
|
||||
imageView.highlighted = YES;
|
||||
|
||||
[self updateControls];
|
||||
}
|
||||
|
||||
|
@ -786,6 +791,10 @@ static const CGFloat kFolderTitleHeight = 36.0;
|
|||
[self setWidgetIncludes:NO itemForIndexPath:indexPath];
|
||||
}
|
||||
|
||||
UIImageView *imageView = (UIImageView *)[tableView cellForRowAtIndexPath:indexPath].accessoryView;
|
||||
|
||||
imageView.highlighted = NO;
|
||||
|
||||
[self updateControls];
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
@property (nonatomic) UISearchBar *searchBar;
|
||||
@property (nonatomic) IBOutlet UIView *messageView;
|
||||
@property (nonatomic) IBOutlet UILabel *messageLabel;
|
||||
@property (nonatomic, strong) id standardInteractivePopGestureDelegate;
|
||||
|
||||
@property (nonatomic, readwrite) BOOL pageFetching;
|
||||
@property (nonatomic, readwrite) BOOL pageFinished;
|
||||
|
|
|
@ -332,10 +332,6 @@
|
|||
|
||||
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
|
||||
|
||||
if (self.standardInteractivePopGestureDelegate == nil) {
|
||||
self.standardInteractivePopGestureDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
|
||||
}
|
||||
|
||||
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
|
||||
[self setUserAvatarLayout:orientation];
|
||||
self.finishedAnimatingIn = NO;
|
||||
|
@ -453,10 +449,6 @@
|
|||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if (self.navigationController.interactivePopGestureRecognizer.delegate != self.standardInteractivePopGestureDelegate) {
|
||||
self.navigationController.interactivePopGestureRecognizer.delegate = self.standardInteractivePopGestureDelegate;
|
||||
}
|
||||
|
||||
if (appDelegate.inStoryDetail && self.isPhoneOrCompact) {
|
||||
appDelegate.inStoryDetail = NO;
|
||||
// [appDelegate.storyPageControl resetPages];
|
||||
|
@ -663,7 +655,7 @@
|
|||
}
|
||||
|
||||
[self.storyTitlesTable reloadData];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, CGRectGetHeight(self.searchBar.frame), 1, 1) animated:YES];
|
||||
}
|
||||
|
||||
- (void)beginOfflineTimer {
|
||||
|
@ -774,7 +766,7 @@
|
|||
NSInteger storyCount = storiesCollection.storyCount;
|
||||
if (storyCount == 0) {
|
||||
[self.storyTitlesTable reloadData];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
|
||||
}
|
||||
if (storiesCollection.feedPage == 1) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
|
||||
|
@ -970,7 +962,7 @@
|
|||
NSInteger storyCount = storiesCollection.storyCount;
|
||||
if (storyCount == 0) {
|
||||
[self.storyTitlesTable reloadData];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, CGRectGetHeight(self.searchBar.frame), 1) animated:YES];
|
||||
// [self.notifier initWithTitle:@"Loading more..." inView:self.view];
|
||||
|
||||
}
|
||||
|
@ -2193,34 +2185,15 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
|
|||
}
|
||||
}
|
||||
|
||||
if (!everything && !infrequent && !read && !saved) {
|
||||
NSString *deleteText = [NSString stringWithFormat:@"Delete %@",
|
||||
appDelegate.storiesCollection.isRiverView ?
|
||||
@"this entire folder" :
|
||||
@"this site"];
|
||||
|
||||
[viewController addTitle:deleteText iconName:@"menu_icn_delete.png" selectionShouldDismiss:NO handler:^{
|
||||
[self confirmDeleteSite:weakViewController.navigationController];
|
||||
}];
|
||||
|
||||
[viewController addTitle:@"Move to another folder" iconName:@"menu_icn_move.png" selectionShouldDismiss:NO handler:^{
|
||||
[self openMoveView:weakViewController.navigationController];
|
||||
}];
|
||||
}
|
||||
|
||||
if (!infrequent && !saved && !read) {
|
||||
NSString *renameText = [NSString stringWithFormat:@"Rename this %@", appDelegate.storiesCollection.isRiverView ? @"folder" : @"site"];
|
||||
NSString *manageText = [NSString stringWithFormat:@"Manage this %@", appDelegate.storiesCollection.isRiverView ? @"folder" : @"site"];
|
||||
|
||||
[viewController addTitle:renameText iconName:@"menu_icn_rename.png" selectionShouldDismiss:YES handler:^{
|
||||
[self openRenameSite];
|
||||
[viewController addTitle:manageText iconName:@"menu_icn_move.png" selectionShouldDismiss:NO handler:^{
|
||||
[self manageSite:weakViewController.navigationController manageText:manageText everything:everything];
|
||||
}];
|
||||
}
|
||||
|
||||
if (!appDelegate.storiesCollection.isRiverView && !infrequent && !saved && !read) {
|
||||
[viewController addTitle:@"Mute this site" iconName:@"menu_icn_mute.png" selectionShouldDismiss:NO handler:^{
|
||||
[self confirmMuteSite:weakViewController.navigationController];
|
||||
}];
|
||||
|
||||
[viewController addTitle:@"Train this site" iconName:@"menu_icn_train.png" selectionShouldDismiss:YES handler:^{
|
||||
[self openTrainSite];
|
||||
}];
|
||||
|
@ -2366,6 +2339,41 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)manageSite:(UINavigationController *)menuNavigationController manageText:(NSString *)manageText everything:(BOOL)everything {
|
||||
MenuViewController *viewController = [MenuViewController new];
|
||||
__weak MenuViewController *weakViewController = viewController;
|
||||
viewController.title = manageText;
|
||||
|
||||
if (!everything) {
|
||||
NSString *deleteText = [NSString stringWithFormat:@"Delete %@",
|
||||
appDelegate.storiesCollection.isRiverView ?
|
||||
@"this entire folder" :
|
||||
@"this site"];
|
||||
|
||||
[viewController addTitle:deleteText iconName:@"menu_icn_delete.png" selectionShouldDismiss:NO handler:^{
|
||||
[self confirmDeleteSite:weakViewController.navigationController];
|
||||
}];
|
||||
|
||||
[viewController addTitle:@"Move to another folder" iconName:@"menu_icn_move.png" selectionShouldDismiss:NO handler:^{
|
||||
[self openMoveView:weakViewController.navigationController];
|
||||
}];
|
||||
}
|
||||
|
||||
NSString *renameText = [NSString stringWithFormat:@"Rename this %@", appDelegate.storiesCollection.isRiverView ? @"folder" : @"site"];
|
||||
|
||||
[viewController addTitle:renameText iconName:@"menu_icn_rename.png" selectionShouldDismiss:YES handler:^{
|
||||
[self openRenameSite];
|
||||
}];
|
||||
|
||||
if (!appDelegate.storiesCollection.isRiverView) {
|
||||
[viewController addTitle:@"Mute this site" iconName:@"menu_icn_mute.png" selectionShouldDismiss:NO handler:^{
|
||||
[self confirmMuteSite:weakViewController.navigationController];
|
||||
}];
|
||||
}
|
||||
|
||||
[menuNavigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
|
||||
- (void)confirmDeleteSite:(UINavigationController *)menuNavigationController {
|
||||
MenuViewController *viewController = [MenuViewController new];
|
||||
viewController.title = @"Positive?";
|
||||
|
@ -2491,6 +2499,10 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
|
|||
NSString *thisIdentifier = [NSString stringWithFormat:@"%@", storiesCollection.activeFeed[@"id"]];
|
||||
[activeIdentifiers removeObject:thisIdentifier];
|
||||
|
||||
for (NSString *feedId in self.appDelegate.dictInactiveFeeds.allKeys) {
|
||||
[activeIdentifiers removeObject:feedId];
|
||||
}
|
||||
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
NSString *urlString = [NSString stringWithFormat:@"%@/reader/save_feed_chooser", self.appDelegate.url];
|
||||
|
||||
|
@ -2739,8 +2751,10 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
|
|||
}
|
||||
|
||||
if ([ThemeManager themeManager].isDarkTheme) {
|
||||
self.storyTitlesTable.indicatorStyle = UIScrollViewIndicatorStyleWhite;
|
||||
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
|
||||
} else {
|
||||
self.storyTitlesTable.indicatorStyle = UIScrollViewIndicatorStyleBlack;
|
||||
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDefault;
|
||||
}
|
||||
|
||||
|
@ -2802,7 +2816,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
|
|||
storiesCollection.feedPage = 1;
|
||||
self.pageFetching = YES;
|
||||
[self.storyTitlesTable reloadData];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
|
||||
[storyTitlesTable scrollRectToVisible:CGRectMake(0, CGRectGetHeight(self.searchBar.frame), 1, 1) animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
@property (assign, nonatomic) int savedStoriesCount;
|
||||
@property (assign, nonatomic) BOOL isSocial;
|
||||
@property (assign, nonatomic) BOOL isSaved;
|
||||
@property (assign, nonatomic) BOOL isInactive;
|
||||
@property (nonatomic) NSString *searchQuery;
|
||||
@property (nonatomic) NSString *negativeCountStr;
|
||||
@property (nonatomic) UnreadCountView *unreadCount;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue