Merge branch 'master' into bgfetch
* master: (38 commits) New Play Store image assets. Fix layout bug on small screens. Revert "Updating stories which give integrity errors." Fix laggy updates for saved stories unsave ops. Ability to unsave stories. Make height of saved stories row match other folders. Make rows reliably hilight when tapped. (#282) Allowing replacement of story dates on integrityerror updated stories. Swapping errors. Updating stories which give integrity errors. Fixing margin on subscribe/follow taskbar button. Upgrading Android Studio. Fixing #261. Decoding html entities in story titles (both in the share dialog and in the story detail view). Flattening gradients on feed and story selection. Adding colored background back in to selected river stories. Fixing modal resizing. Thanks to @tomtaylor for finding the broken dialog. Remove unused ABS progress indicator support for reading view. Implement #426 - preference to show/hide public comments Pressed state for overlay_done button. Pressed states for all overlay buttons. Fit overlays on 360dp screens. Move progress indicator to overlay. Add progress indicator for text mode. ...
|
|
@ -187,6 +187,7 @@ class UserAgentBanMiddleware:
|
|||
|
||||
if 'profile' in request.path: return
|
||||
if 'haproxy' in request.path: return
|
||||
if getattr(settings, 'TEST_DEBUG'): return
|
||||
|
||||
if any(ua in user_agent for ua in BANNED_USER_AGENTS):
|
||||
data = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"pk": 1,
|
||||
"model": "reader.usersubscriptionfolders",
|
||||
"fields": {
|
||||
"folders": "[1, {\"Tech\": [4, 5, {\"Deep Tech\": [6, 7]}]}, 2, 3, 8, 9, {\"Blogs\": [8, 9]}]",
|
||||
"folders": "[{\"Tech\": [1, 4, 5, {\"Deep Tech\": [6, 7]}]}, 2, 3, 8, 9, {\"Blogs\": [8, 9]}, 1]",
|
||||
"user": 1
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -917,7 +917,25 @@ class UserSubscriptionFolders(models.Model):
|
|||
user_sub_folders = add_object_to_folder(obj, parent_folder, user_sub_folders)
|
||||
self.folders = json.encode(user_sub_folders)
|
||||
self.save()
|
||||
|
||||
def arranged_folders(self):
|
||||
user_sub_folders = json.decode(self.folders)
|
||||
def _arrange_folder(folder):
|
||||
folder_feeds = []
|
||||
folder_folders = []
|
||||
for item in folder:
|
||||
if isinstance(item, int):
|
||||
folder_feeds.append(item)
|
||||
elif isinstance(item, dict):
|
||||
for f_k, f_v in item.items():
|
||||
arranged_folder = _arrange_folder(f_v)
|
||||
folder_folders.append({f_k: arranged_folder})
|
||||
|
||||
arranged_folder = folder_feeds + folder_folders
|
||||
return arranged_folder
|
||||
|
||||
return _arrange_folder(user_sub_folders)
|
||||
|
||||
def delete_feed(self, feed_id, in_folder, commit_delete=True):
|
||||
def _find_feed_in_folders(old_folders, folder_name='', multiples_found=False, deleted=False):
|
||||
new_folders = []
|
||||
|
|
@ -944,7 +962,7 @@ class UserSubscriptionFolders(models.Model):
|
|||
|
||||
return new_folders, multiples_found, deleted
|
||||
|
||||
user_sub_folders = json.decode(self.folders)
|
||||
user_sub_folders = self.arranged_folders()
|
||||
user_sub_folders, multiples_found, deleted = _find_feed_in_folders(user_sub_folders)
|
||||
self.folders = json.encode(user_sub_folders)
|
||||
self.save()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ from django.conf import settings
|
|||
from mongoengine.connection import connect, disconnect
|
||||
|
||||
class ReaderTest(TestCase):
|
||||
fixtures = ['subscriptions.json', 'stories.json', '../../rss_feeds/fixtures/gawker1.json']
|
||||
fixtures = ['../../rss_feeds/fixtures/rss_feeds.json',
|
||||
'subscriptions.json', 'stories.json',
|
||||
'../../rss_feeds/fixtures/gawker1.json']
|
||||
|
||||
|
||||
def setUp(self):
|
||||
|
|
@ -23,16 +25,16 @@ class ReaderTest(TestCase):
|
|||
response = self.client.get(reverse('load-feeds'))
|
||||
content = json.decode(response.content)
|
||||
|
||||
self.assertEquals(len(content['feeds']), 1)
|
||||
self.assertEquals(len(content['feeds']), 10)
|
||||
self.assertEquals(content['feeds']['1']['feed_title'], 'Gawker')
|
||||
self.assertEquals(content['folders'], [1, {'Tech': [4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}])
|
||||
self.assertEquals(content['folders'], [{'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}, 1])
|
||||
|
||||
def test_delete_feed(self):
|
||||
self.client.login(username='conesus', password='test')
|
||||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [1, {'Tech': [4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}])
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}, 1])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 1, 'in_folder': ''})
|
||||
|
|
@ -41,7 +43,7 @@ class ReaderTest(TestCase):
|
|||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}])
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, {'Blogs': [8, 9]}])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 9, 'in_folder': 'Blogs'})
|
||||
|
|
@ -50,7 +52,7 @@ class ReaderTest(TestCase):
|
|||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8]}])
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, {'Blogs': [8]}])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 5, 'in_folder': 'Tech'})
|
||||
|
|
@ -59,7 +61,7 @@ class ReaderTest(TestCase):
|
|||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [4, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8]}])
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, 4, {'Deep Tech': [6, 7]}]}, {'Blogs': [8]}])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 4, 'in_folder': 'Tech'})
|
||||
|
|
@ -68,7 +70,7 @@ class ReaderTest(TestCase):
|
|||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [{'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8]}])
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, {'Deep Tech': [6, 7]}]}, {'Blogs': [8]}])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 8, 'in_folder': ''})
|
||||
|
|
@ -77,7 +79,23 @@ class ReaderTest(TestCase):
|
|||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [{'Deep Tech': [6, 7]}]}, 2, 3, 9, {'Blogs': [8]}])
|
||||
self.assertEquals(feeds['folders'], [2, 3, 9, {'Tech': [1, {'Deep Tech': [6, 7]}]}, {'Blogs': [8]}])
|
||||
|
||||
def test_delete_feed__multiple_folders(self):
|
||||
self.client.login(username='conesus', password='test')
|
||||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [{'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, 2, 3, 8, 9, {'Blogs': [8, 9]}, 1])
|
||||
|
||||
# Delete feed
|
||||
response = self.client.post(reverse('delete-feed'), {'feed_id': 1})
|
||||
response = json.decode(response.content)
|
||||
self.assertEquals(response['code'], 1)
|
||||
|
||||
response = self.client.get(reverse('load-feeds'))
|
||||
feeds = json.decode(response.content)
|
||||
self.assertEquals(feeds['folders'], [2, 3, 8, 9, {'Tech': [1, 4, 5, {'Deep Tech': [6, 7]}]}, {'Blogs': [8, 9]}])
|
||||
|
||||
def test_load_single_feed(self):
|
||||
# from django.conf import settings
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ def load_feeds_flat(request):
|
|||
|
||||
feeds = {}
|
||||
flat_folders = {" ": []}
|
||||
iphone_version = "3.0"
|
||||
iphone_version = "2.1"
|
||||
|
||||
if include_favicons == 'false': include_favicons = False
|
||||
if update_counts == 'false': update_counts = False
|
||||
|
|
@ -607,8 +607,9 @@ def load_single_feed(request, feed_id):
|
|||
if not include_story_content:
|
||||
del story['story_content']
|
||||
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
if usersub:
|
||||
story['read_status'] = 1
|
||||
if (read_filter == 'all' or query) and usersub:
|
||||
|
|
@ -782,12 +783,13 @@ def load_starred_stories(request):
|
|||
comments=story.comments))
|
||||
for story in shared_stories])
|
||||
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
for story in stories:
|
||||
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
starred_date = localtime_for_timezone(story['starred_date'], user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, nowtz)
|
||||
story['read_status'] = 1
|
||||
story['starred'] = True
|
||||
story['intelligence'] = {
|
||||
|
|
@ -939,6 +941,7 @@ def load_river_stories__redis(request):
|
|||
|
||||
|
||||
# Just need to format stories
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
for story in stories:
|
||||
story['read_status'] = 0
|
||||
if read_filter == 'all':
|
||||
|
|
@ -946,8 +949,8 @@ def load_river_stories__redis(request):
|
|||
story['story_hash'] not in unread_feed_story_hashes):
|
||||
story['read_status'] = 1
|
||||
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']],
|
||||
|
|
@ -1399,7 +1402,7 @@ def add_folder(request):
|
|||
def delete_feed(request):
|
||||
feed_id = int(request.POST['feed_id'])
|
||||
in_folder = request.POST.get('in_folder', None)
|
||||
if in_folder == ' ':
|
||||
if not in_folder or in_folder == ' ':
|
||||
in_folder = ""
|
||||
|
||||
user_sub_folders = get_object_or_404(UserSubscriptionFolders, user=request.user)
|
||||
|
|
|
|||
|
|
@ -136,5 +136,302 @@
|
|||
"email": "samuel@newsblur.com",
|
||||
"date_joined": "2009-01-04 17:32:58"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"pk": 2,
|
||||
"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/gawker2.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker2.html",
|
||||
"hash_address_and_link": "2",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 3,
|
||||
"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/gawker3.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker3.html",
|
||||
"hash_address_and_link": "3",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 4,
|
||||
"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/gawker4.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker4.html",
|
||||
"hash_address_and_link": "4",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 5,
|
||||
"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/gawker5.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker5.html",
|
||||
"hash_address_and_link": "5",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 6,
|
||||
"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/gawker6.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker6.html",
|
||||
"hash_address_and_link": "6",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 7,
|
||||
"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/gawker7.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker7.html",
|
||||
"hash_address_and_link": "7",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 8,
|
||||
"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/gawker8.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker8.html",
|
||||
"hash_address_and_link": "8",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 9,
|
||||
"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/gawker9.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker9.html",
|
||||
"hash_address_and_link": "9",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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": 56,
|
||||
"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/gawker56.xml",
|
||||
"feed_link": "%(NEWSBLUR_DIR)s/apps/rss_feeds/fixtures/gawker56.html",
|
||||
"hash_address_and_link": "56",
|
||||
"feed_link_locked": true,
|
||||
"last_update": "2011-08-27 02:45:21",
|
||||
"etag": null,
|
||||
"average_stories_per_month": 0,
|
||||
"feed_title": "Gawker",
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -912,6 +912,7 @@ class Feed(models.Model):
|
|||
story_content = strip_comments(story_content)
|
||||
story_tags = self.get_tags(story)
|
||||
story_link = self.get_permalink(story)
|
||||
replace_story_date = False
|
||||
|
||||
try:
|
||||
existing_story, story_has_changed = _1(story, story_content, existing_stories)
|
||||
|
|
@ -993,7 +994,8 @@ class Feed(models.Model):
|
|||
existing_story.story_tags = story_tags
|
||||
# Do not allow publishers to change the story date once a story is published.
|
||||
# Leads to incorrect unread story counts.
|
||||
# existing_story.story_date = story.get('published') # No, don't
|
||||
if replace_story_date:
|
||||
existing_story.story_date = story.get('published') # Really shouldn't do this.
|
||||
existing_story.extract_image_urls()
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -3001,14 +3001,17 @@ class MActivity(mongo.Document):
|
|||
|
||||
@classmethod
|
||||
def remove_shared_story(cls, user_id, story_feed_id, story_id):
|
||||
params = dict(user_id=user_id,
|
||||
category='sharedstory',
|
||||
feed_id="social:%s" % user_id,
|
||||
story_feed_id=story_feed_id,
|
||||
content_id=story_id)
|
||||
try:
|
||||
a = cls.objects.get(user_id=user_id,
|
||||
category='sharedstory',
|
||||
feed_id="social:%s" % user_id,
|
||||
story_feed_id=story_feed_id,
|
||||
content_id=story_id)
|
||||
a = cls.objects.get(**params)
|
||||
except cls.DoesNotExist:
|
||||
return
|
||||
except cls.MultipleObjectsReturned:
|
||||
a = cls.objects.filter(**params)
|
||||
|
||||
a.delete()
|
||||
|
||||
|
|
|
|||
|
|
@ -119,12 +119,13 @@ def load_social_stories(request, user_id, username=None):
|
|||
comments=story.comments))
|
||||
for story in shared_stories])
|
||||
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
for story in stories:
|
||||
story['social_user_id'] = social_user_id
|
||||
# story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
|
||||
shared_date = localtime_for_timezone(story['shared_date'], user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(shared_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(shared_date)
|
||||
story['short_parsed_date'] = format_story_link_date__short(shared_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(shared_date, nowtz)
|
||||
|
||||
story['read_status'] = 1
|
||||
if (read_filter == 'all' or query) and socialsub:
|
||||
|
|
@ -270,13 +271,14 @@ def load_river_blurblog(request):
|
|||
classifier_tags = []
|
||||
|
||||
# Just need to format stories
|
||||
nowtz = localtime_for_timezone(now, user.profile.timezone)
|
||||
for story in stories:
|
||||
story['read_status'] = 0
|
||||
if story['story_hash'] not in unread_feed_story_hashes:
|
||||
story['read_status'] = 1
|
||||
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
|
||||
story['short_parsed_date'] = format_story_link_date__short(story_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']], user.profile.timezone)
|
||||
|
|
@ -521,7 +523,9 @@ def mark_story_as_shared(request):
|
|||
source_user_id = request.POST.get('source_user_id')
|
||||
relative_user_id = request.POST.get('relative_user_id') or request.user.pk
|
||||
post_to_services = request.POST.getlist('post_to_services')
|
||||
format = request.REQUEST.get('format', 'json')
|
||||
format = request.REQUEST.get('format', 'json')
|
||||
now = datetime.datetime.now()
|
||||
nowtz = localtime_for_timezone(now, request.user.profile.timezone)
|
||||
|
||||
MSocialProfile.get_user(request.user.pk)
|
||||
|
||||
|
|
@ -576,8 +580,8 @@ def mark_story_as_shared(request):
|
|||
story['shared_by_user'] = True
|
||||
story['shared'] = True
|
||||
shared_date = localtime_for_timezone(shared_story['shared_date'], request.user.profile.timezone)
|
||||
story['short_parsed_date'] = format_story_link_date__short(shared_date)
|
||||
story['long_parsed_date'] = format_story_link_date__long(shared_date)
|
||||
story['short_parsed_date'] = format_story_link_date__short(shared_date, nowtz)
|
||||
story['long_parsed_date'] = format_story_link_date__long(shared_date, nowtz)
|
||||
|
||||
if post_to_services:
|
||||
for service in post_to_services:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.newsblur"
|
||||
android:versionCode="43"
|
||||
android:versionName="2.5.0" >
|
||||
android:versionCode="50"
|
||||
android:versionName="3.0.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="8"
|
||||
|
|
|
|||
|
|
@ -10,16 +10,23 @@
|
|||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android 4.2.2" jdkType="Android SDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="android-support-v4" level="project" />
|
||||
<orderEntry type="library" name="actionbarsherlock" level="project" />
|
||||
<orderEntry type="library" name="gson-2.2.3" level="project" />
|
||||
<orderEntry type="library" name="classes" level="project" />
|
||||
<orderEntry type="module" module-name="ActionBarSherlock" />
|
||||
<orderEntry type="library" name="android-support-v4" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/libs/ActionBarSherlock/bin/actionbarsherlock.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@
|
|||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android 4.2.2" jdkType="Android SDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="android-support-v4" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@
|
|||
|
||||
android.library=true
|
||||
# Project target.
|
||||
target=android-17
|
||||
target=android-14
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@
|
|||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-17
|
||||
target=android-14
|
||||
android.library.reference.1=libs/ActionBarSherlock
|
||||
|
|
|
|||
BIN
clients/android/NewsBlur/res/drawable-hdpi/clock.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
BIN
clients/android/NewsBlur/res/drawable-hdpi/share_icon.png
Normal file
|
After Width: | Height: | Size: 820 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 605 B |
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/feed_background_selected_start"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:top="0.5dp"
|
||||
android:bottom="0.5dp">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/feed_background_selected_end"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:type="linear"
|
||||
android:startColor="@color/folder_background_start"
|
||||
android:endColor="@color/folder_background_end"/>
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:type="linear"
|
||||
android:startColor="@color/folder_background_selected_start"
|
||||
android:endColor="@color/folder_background_selected_end"/>
|
||||
</shape>
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_send_enabled.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_send_pressed.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_story_enabled.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_story_pressed.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_text_enabled.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
clients/android/NewsBlur/res/drawable/overlay_text_pressed.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/gradient_activation_highlight" />
|
||||
<item android:state_pressed="true" android:drawable="@drawable/feed_background_highlight" />
|
||||
<item android:drawable="@drawable/feed_background_default" />
|
||||
</selector>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/gradient_activation_highlight" />
|
||||
<item android:drawable="@drawable/gradient_background_default" />
|
||||
<item android:state_pressed="true" android:drawable="@drawable/folder_background_highlight" />
|
||||
<item android:drawable="@drawable/folder_background_default" />
|
||||
</selector>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/overlay_right_pressed" />
|
||||
<item android:state_enabled="false" android:drawable="@drawable/overlay_right_disabled" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/overlay_right_enabled" />
|
||||
</selector>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/overlay_right_done_pressed" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/overlay_right_done_enabled" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/overlay_send_pressed" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/overlay_send_enabled" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/overlay_story_pressed" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/overlay_story_enabled" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/overlay_text_pressed" />
|
||||
<item android:state_enabled="true" android:drawable="@drawable/overlay_text_enabled" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/story_background_highlight"/>
|
||||
<item android:drawable="@drawable/story_background_default"/>
|
||||
</selector>
|
||||
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/story_background"/>
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/story_background_start"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:top="0.5dp"
|
||||
android:bottom="0.5dp">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/story_background_end"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
|
@ -14,6 +14,42 @@
|
|||
android:layout_height="1dip"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentLeft="true" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/reading_overlay_text"
|
||||
android:layout_width="110.6dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/selector_overlay_bg_text"
|
||||
android:text="@string/overlay_text"
|
||||
android:textColor="@color/half_darkgray"
|
||||
android:textSize="14sp"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:paddingRight="6dp"
|
||||
android:onClick="overlayText" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/reading_overlay_send"
|
||||
android:layout_width="51.8dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/selector_overlay_bg_send"
|
||||
android:textSize="14sp"
|
||||
android:padding="6dp"
|
||||
android:layout_marginLeft="1dp"
|
||||
android:onClick="overlaySend" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -25,8 +61,8 @@
|
|||
|
||||
<Button
|
||||
android:id="@+id/reading_overlay_left"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="41dp"
|
||||
android:layout_width="50.6dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/selector_overlay_bg_left"
|
||||
android:textSize="14sp"
|
||||
android:padding="6dp"
|
||||
|
|
@ -35,8 +71,8 @@
|
|||
|
||||
<Button
|
||||
android:id="@+id/reading_overlay_right"
|
||||
android:layout_width="125dp"
|
||||
android:layout_height="41dp"
|
||||
android:layout_width="122.4dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/selector_overlay_bg_right"
|
||||
android:text="@string/overlay_next"
|
||||
android:textColor="@color/half_darkgray"
|
||||
|
|
@ -46,22 +82,35 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reading_overlay_count"
|
||||
android:text=""
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="100dp"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:background="@drawable/neutral_count_rect"
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
<com.newsblur.view.ProgressCircle
|
||||
android:id="@+id/reading_overlay_progress"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginRight="98dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true" />
|
||||
android:layout_alignParentRight="true"
|
||||
android:onClick="overlayCount" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/reading_overlay_progress_right"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginRight="98dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/reading_overlay_progress_left"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginLeft="90dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
|||
|
|
@ -19,4 +19,13 @@
|
|||
android:defaultValue="@string/default_read_filter_value" />
|
||||
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:title="@string/settings_social">
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="show_public_comments"
|
||||
android:title="@string/settings_show_public_comments" >
|
||||
</CheckBoxPreference>
|
||||
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
android:layout_toRightOf="@id/comment_user_image"
|
||||
android:textColor="@color/newsblur_blue"
|
||||
android:textSize="14sp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/comment_text"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -105,6 +105,18 @@
|
|||
android:textColor="@color/darkgray"
|
||||
android:textSize="14dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/comment_location"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comment_text"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_toRightOf="@id/comment_user_image"
|
||||
android:textColor="@color/lightgray"
|
||||
android:textSize="12dp"
|
||||
/>
|
||||
|
||||
<com.newsblur.view.FlowLayout
|
||||
xmlns:newsblur="http://schemas.android.com/apk/res/com.newsblur"
|
||||
android:id="@+id/comment_favourite_avatars"
|
||||
|
|
@ -121,6 +133,7 @@
|
|||
|
||||
<LinearLayout
|
||||
android:id="@+id/comment_replies_container"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
|
@ -129,6 +142,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="#A6A6A6" />
|
||||
android:background="#F0F0F0" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -5,7 +5,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="15dp" >
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:baselineAligned="false" >
|
||||
|
||||
<Button
|
||||
android:id="@+id/share_story_button"
|
||||
|
|
@ -20,7 +22,7 @@
|
|||
android:paddingBottom="6dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:drawableLeft="@drawable/share_half"
|
||||
android:drawableLeft="@drawable/share_icon"
|
||||
android:text="@string/share_this" />
|
||||
|
||||
<Button
|
||||
|
|
@ -36,7 +38,7 @@
|
|||
android:paddingBottom="6dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:drawableLeft="@drawable/clock_half"
|
||||
android:drawableLeft="@drawable/clock"
|
||||
android:text="@string/save_this" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -45,51 +47,103 @@
|
|||
android:id="@+id/reading_shared_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="50dp" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_friend_comment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/reading_friend_comment_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/darkgray"
|
||||
android:id="@+id/reading_friend_comment_total"
|
||||
android:paddingTop="3dp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="3dp" />
|
||||
|
||||
</LinearLayout>
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/comment_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:layout_below="@id/reading_friend_comment_container"
|
||||
android:background="@drawable/divider_light" />
|
||||
<View
|
||||
android:id="@+id/reading_friend_header_top_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:textColor="@color/darkgray"
|
||||
android:id="@+id/reading_friend_comment_total"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="10sp"
|
||||
android:background="@drawable/gradient_background_default"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_friend_header_bottom_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/reading_friend_comment_total"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_public_comment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comment_divider"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/reading_friend_comment_container"
|
||||
android:layout_below="@id/reading_friend_comment_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/darkgray"
|
||||
android:paddingTop="3dp"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/reading_public_comment_total"
|
||||
android:paddingBottom="3dp" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_public_comment_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comment_divider"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_public_header_top_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/darkgray"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="10sp"
|
||||
android:background="@drawable/gradient_background_default"
|
||||
android:id="@+id/reading_public_comment_total" />
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_public_header_bottom_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/reading_public_comment_total"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_public_comment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_below="@id/reading_public_comment_header"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</merge>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/selector_folder_background">
|
||||
android:background="@drawable/selector_folder_background"
|
||||
android:addStatesFromChildren="true" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/row_folder_icon"
|
||||
|
|
@ -86,7 +87,7 @@
|
|||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"/>
|
||||
android:ellipsize="end" />
|
||||
|
||||
<View
|
||||
android:layout_height="1dp"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/item_background"
|
||||
android:background="@drawable/selector_story_background"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<RelativeLayout
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/selector_feed_background"
|
||||
android:background="@drawable/selector_story_background"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<RelativeLayout
|
||||
|
|
@ -20,7 +20,6 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_toRightOf="@id/row_item_favicon_borderbar_1" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/row_item_title_container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/row_saved_icon"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="9dp"
|
||||
android:paddingTop="9dp"
|
||||
android:text="@string/saved_stories_row_title"
|
||||
android:textColor="@color/folder_text"
|
||||
android:textSize="14dp"
|
||||
android:textSize="13dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
|
|
@ -37,7 +37,10 @@
|
|||
android:layout_marginRight="12dp"
|
||||
android:background="@drawable/saved_count_rect"
|
||||
android:gravity="center"
|
||||
android:padding="3dp"
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:shadowColor="@color/saved_drop_shadow"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/selector_feed_background"
|
||||
android:background="@drawable/selector_story_background"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<RelativeLayout
|
||||
|
|
|
|||
|
|
@ -6,16 +6,21 @@
|
|||
<color name="midgray">#898989</color>
|
||||
<color name="lightgray">#ccc</color>
|
||||
|
||||
<color name="folder_background_end">#E9EBE4</color>
|
||||
<color name="folder_background_start">#DDE0D7</color>
|
||||
<color name="folder_background_selected_end">#fdfcca</color>
|
||||
<color name="folder_background_selected_start">#fbec8c</color>
|
||||
<color name="folder_background_end">#E9EBE4</color>
|
||||
<color name="folder_background_start">#DDE0D7</color>
|
||||
<color name="folder_background_selected_end">#D9DBD4</color>
|
||||
<color name="folder_background_selected_start">#CDD0C7</color>
|
||||
<color name="folder_text">#4C4C4C</color>
|
||||
<color name="folder_border_top">#FDFDFD</color>
|
||||
<color name="folder_border_bottom">#B7BBAA</color>
|
||||
<color name="feed_background">#F7F8F5</color>
|
||||
<color name="feed_background_end">#303030</color>
|
||||
<color name="feed_background_start">#505050</color>
|
||||
<color name="feed_background_selected_end">#FFFFD2</color>
|
||||
<color name="feed_background_selected_start">#E3D0AE</color>
|
||||
<color name="story_background">#F7F8F5</color>
|
||||
<color name="story_background_end">#FFFDEF</color>
|
||||
<color name="story_background_start">#DFDDCF</color>
|
||||
|
||||
<color name="story_title_unread">#333333</color>
|
||||
<color name="story_title_read">#808080</color>
|
||||
|
|
@ -70,6 +75,9 @@
|
|||
|
||||
<color name="newsblur_blue">#0b445a</color>
|
||||
|
||||
<color name="progress_circle_complete">#9a9c96</color>
|
||||
<color name="progress_circle_remaining">#dbe4e1</color>
|
||||
|
||||
<!-- To use a selector on a bg color, the options must be drawables, not colors. -->
|
||||
<drawable name="toggle_bg_selected">#fdfdfd</drawable>
|
||||
<drawable name="toggle_bg_normal">#dfe1dd</drawable>
|
||||
|
|
|
|||
|
|
@ -56,10 +56,15 @@
|
|||
<string name="share_newsblur">Share \"%s\" to your Blurblog?</string>
|
||||
|
||||
<string name="save_this">SAVE THIS STORY</string>
|
||||
<string name="unsave_this">UNSAVE THIS STORY</string>
|
||||
<string name="share_this">SHARE THIS STORY</string>
|
||||
|
||||
<string name="overlay_next">NEXT</string>
|
||||
<string name="overlay_done">DONE</string>
|
||||
<string name="overlay_count_toast_N">%d unread stories</string>
|
||||
<string name="overlay_count_toast_1">1 unread story</string>
|
||||
<string name="overlay_text">TEXT</string>
|
||||
<string name="overlay_story">STORY</string>
|
||||
|
||||
<string name="reply_to">Reply to \"%s\"</string>
|
||||
|
||||
|
|
@ -94,6 +99,7 @@
|
|||
<string name="menu_sharenewsblur">Share this story</string>
|
||||
<string name="menu_textsize">Adjust text size</string>
|
||||
<string name="menu_save_story">Save this story</string>
|
||||
<string name="menu_unsave_story">Unsave this story</string>
|
||||
<string name="menu_mark_previous_stories_as_read">Mark previous as read</string>
|
||||
<string name="menu_mark_story_as_read">Mark as read</string>
|
||||
<string name="menu_mark_unread">Mark as unread</string>
|
||||
|
|
@ -106,10 +112,14 @@
|
|||
|
||||
<string name="toast_story_saved">Story saved</string>
|
||||
<string name="toast_story_save_error">Error marking story as saved.</string>
|
||||
<string name="toast_story_unsaved">Story unsaved</string>
|
||||
<string name="toast_story_unsave_error">Error marking story as unsaved.</string>
|
||||
|
||||
<string name="toast_story_unread">Story marked as unread</string>
|
||||
<string name="toast_story_unread_error">Error marking story as unread</string>
|
||||
|
||||
<string name="toast_unread_search_error">Could not load next unread story</string>
|
||||
|
||||
<string name="toast_feed_deleted">Feed deleted</string>
|
||||
<string name="toast_feed_delete_error">There was an error deleting the feed.</string>
|
||||
|
||||
|
|
@ -184,4 +194,6 @@
|
|||
<item>Mark entire folder read</item>
|
||||
<item>Cancel</item>
|
||||
</string-array>
|
||||
<string name="settings_social">Social</string>
|
||||
<string name="settings_show_public_comments">Show Public Comments</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.newsblur.R;
|
||||
|
|
@ -8,6 +9,7 @@ import com.newsblur.database.DatabaseConstants;
|
|||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.database.MixedFeedsReadingAdapter;
|
||||
import com.newsblur.service.SyncService;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
|
|
@ -21,6 +23,13 @@ public class AllSharedStoriesReading extends Reading {
|
|||
StoryOrder storyOrder = PrefsUtils.getStoryOrderForFolder(this, PrefConstants.ALL_SHARED_STORIES_FOLDER_NAME);
|
||||
stories = contentResolver.query(FeedProvider.ALL_SHARED_STORIES_URI, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder));
|
||||
setTitle(getResources().getString(R.string.all_shared_stories));
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.SOCIALCOUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver(), stories);
|
||||
|
||||
setupPager();
|
||||
|
|
@ -30,7 +39,7 @@ public class AllSharedStoriesReading extends Reading {
|
|||
|
||||
@Override
|
||||
public void triggerRefresh(int page) {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
updateSyncStatus(true);
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.MULTISOCIALFEED_UPDATE);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.newsblur.database.DatabaseConstants;
|
|||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.database.MixedFeedsReadingAdapter;
|
||||
import com.newsblur.service.SyncService;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
|
|
@ -22,6 +23,13 @@ public class AllStoriesReading extends Reading {
|
|||
StoryOrder storyOrder = PrefsUtils.getStoryOrderForFolder(this, PrefConstants.ALL_STORIES_FOLDER_NAME);
|
||||
stories = contentResolver.query(FeedProvider.ALL_STORIES_URI, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder));
|
||||
setTitle(getResources().getString(R.string.all_stories_row_title));
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FEED_COUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver(), stories);
|
||||
|
||||
setupPager();
|
||||
|
|
@ -31,7 +39,7 @@ public class AllStoriesReading extends Reading {
|
|||
|
||||
@Override
|
||||
public void triggerRefresh(int page) {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
updateSyncStatus(true);
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.MULTIFEED_UPDATE);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ public class FeedReading extends Reading {
|
|||
feed = Feed.fromCursor(feedCursor);
|
||||
setTitle(feed.title);
|
||||
|
||||
this.unreadCount = FeedUtils.getFeedUnreadCount(this.feed, this.currentState);
|
||||
int unreadCount = FeedUtils.getFeedUnreadCount(this.feed, this.currentState);
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new FeedReadingAdapter(getSupportFragmentManager(), feed, stories, classifier);
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ public class FeedReading extends Reading {
|
|||
|
||||
@Override
|
||||
public void triggerRefresh(int page) {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
updateSyncStatus(true);
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.FEED_UPDATE);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ import com.newsblur.database.DatabaseConstants;
|
|||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.database.MixedFeedsReadingAdapter;
|
||||
import com.newsblur.service.SyncService;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
||||
public class FolderReading extends Reading {
|
||||
|
|
@ -26,6 +28,12 @@ public class FolderReading extends Reading {
|
|||
Uri storiesURI = FeedProvider.MULTIFEED_STORIES_URI;
|
||||
stories = contentResolver.query(storiesURI, null, DatabaseConstants.getStorySelectionFromState(currentState), feedIds, null);
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FOLDERS_URI.buildUpon().appendPath(folderName).build(), null, null, new String[] { DatabaseConstants.getFolderSelectionFromState(currentState) }, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver(), stories);
|
||||
|
||||
setupPager();
|
||||
|
|
@ -35,7 +43,7 @@ public class FolderReading extends Reading {
|
|||
|
||||
@Override
|
||||
public void triggerRefresh(int page) {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
updateSyncStatus(true);
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.MULTIFEED_UPDATE);
|
||||
|
|
|
|||
|
|
@ -154,7 +154,9 @@ public class Main extends NbFragmentActivity implements StateChangedListener, Sy
|
|||
public void updateAfterSync() {
|
||||
folderFeedList.hasUpdated();
|
||||
setSupportProgressBarIndeterminateVisibility(false);
|
||||
menu.findItem(R.id.menu_refresh).setEnabled(true);
|
||||
|
||||
MenuItem refreshItem = menu.findItem(R.id.menu_refresh);
|
||||
if (refreshItem != null) refreshItem.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,28 +1,36 @@
|
|||
package com.newsblur.activity;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v4.view.ViewPager.OnPageChangeListener;
|
||||
import android.util.Log;
|
||||
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 com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
import com.actionbarsherlock.view.Window;
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Main;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
|
|
@ -30,6 +38,7 @@ import com.newsblur.fragment.ShareDialogFragment;
|
|||
import com.newsblur.fragment.SyncUpdateFragment;
|
||||
import com.newsblur.fragment.TextSizeDialogFragment;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.domain.StoryTextResponse;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
|
|
@ -38,7 +47,7 @@ import com.newsblur.util.UIUtils;
|
|||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.NonfocusScrollview.ScrollChangeListener;
|
||||
|
||||
public abstract class Reading extends NbFragmentActivity implements OnPageChangeListener, SyncUpdateFragment.SyncUpdateFragmentInterface, OnSeekBarChangeListener, ScrollChangeListener {
|
||||
public abstract class Reading extends NbFragmentActivity implements OnPageChangeListener, SyncUpdateFragment.SyncUpdateFragmentInterface, OnSeekBarChangeListener, ScrollChangeListener, FeedUtils.ActionCompletionListener {
|
||||
|
||||
public static final String EXTRA_FEED = "feed_selected";
|
||||
public static final String EXTRA_POSITION = "feed_position";
|
||||
|
|
@ -48,15 +57,26 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
public static final String EXTRA_FEED_IDS = "feed_ids";
|
||||
private static final String TEXT_SIZE = "textsize";
|
||||
|
||||
private static final int OVERLAY_RANGE_TOP_DP = 50;
|
||||
private static final int OVERLAY_RANGE_TOP_DP = 45;
|
||||
private static final int OVERLAY_RANGE_BOT_DP = 60;
|
||||
|
||||
/** The minimum screen width (in DP) needed to show all the overlay controls. */
|
||||
private static final int OVERLAY_MIN_WIDTH_DP = 355;
|
||||
|
||||
/** The longest time (in seconds) the UI will wait for API pages to load while
|
||||
searching for the next unread story. */
|
||||
private static final long UNREAD_SEARCH_LOAD_WAIT_SECONDS = 30;
|
||||
|
||||
private final Object UNREAD_SEARCH_MUTEX = new Object();
|
||||
private CountDownLatch unreadSearchLatch;
|
||||
|
||||
protected int passedPosition;
|
||||
protected int currentState;
|
||||
|
||||
protected ViewPager pager;
|
||||
protected Button overlayLeft, overlayRight;
|
||||
protected TextView overlayCount;
|
||||
protected ProgressBar overlayProgress, overlayProgressRight, overlayProgressLeft;
|
||||
protected Button overlayText, overlaySend;
|
||||
protected FragmentManager fragmentManager;
|
||||
protected ReadingAdapter readingAdapter;
|
||||
protected ContentResolver contentResolver;
|
||||
|
|
@ -68,28 +88,33 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
private int currentApiPage = 0;
|
||||
private Set<Story> storiesToMarkAsRead;
|
||||
|
||||
// subclasses may set this to a nonzero value to enable the unread count overlay
|
||||
protected int unreadCount = 0;
|
||||
// unread counts for the circular progress overlay. set to nonzero to activate the progress indicator overlay
|
||||
protected int startingUnreadCount = 0;
|
||||
protected int currentUnreadCount = 0;
|
||||
|
||||
// keep a local cache of stories we have viewed within this activity cycle. We need
|
||||
// this to track unread counts since it would be too costly to query and update the DB
|
||||
// on every page change.
|
||||
// A list of stories we have marked as read during this reading session. Needed to help keep track of unread
|
||||
// counts since it would be too costly to query and update the DB on every page change.
|
||||
private Set<Story> storiesAlreadySeen;
|
||||
|
||||
|
||||
private float overlayRangeTopPx;
|
||||
private float overlayRangeBotPx;
|
||||
|
||||
private List<Story> pageHistory;
|
||||
|
||||
private Boolean textMode = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
requestWindowFeature(Window.FEATURE_PROGRESS);
|
||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
super.onCreate(savedInstanceBundle);
|
||||
|
||||
setContentView(R.layout.activity_reading);
|
||||
this.overlayLeft = (Button) findViewById(R.id.reading_overlay_left);
|
||||
this.overlayRight = (Button) findViewById(R.id.reading_overlay_right);
|
||||
this.overlayCount = (TextView) findViewById(R.id.reading_overlay_count);
|
||||
this.overlayProgress = (ProgressBar) findViewById(R.id.reading_overlay_progress);
|
||||
this.overlayProgressRight = (ProgressBar) findViewById(R.id.reading_overlay_progress_right);
|
||||
this.overlayProgressLeft = (ProgressBar) findViewById(R.id.reading_overlay_progress_left);
|
||||
this.overlayText = (Button) findViewById(R.id.reading_overlay_text);
|
||||
this.overlaySend = (Button) findViewById(R.id.reading_overlay_send);
|
||||
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
|
||||
|
|
@ -107,11 +132,9 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
this.overlayRangeTopPx = (float) UIUtils.convertDPsToPixels(this, OVERLAY_RANGE_TOP_DP);
|
||||
this.overlayRangeBotPx = (float) UIUtils.convertDPsToPixels(this, OVERLAY_RANGE_BOT_DP);
|
||||
|
||||
// the unread count overlay defaults to neutral colour. set it to positive if we are in focus mode
|
||||
if (this.currentState == AppConstants.STATE_BEST) {
|
||||
ViewUtils.setViewBackground(this.overlayCount, R.drawable.positive_count_rect);
|
||||
}
|
||||
this.pageHistory = new ArrayList<Story>();
|
||||
|
||||
enableProgressCircle(overlayProgressLeft, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,9 +152,13 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
pager.setPageMargin(UIUtils.convertDPsToPixels(getApplicationContext(), 1));
|
||||
pager.setPageMarginDrawable(R.drawable.divider_light);
|
||||
pager.setOnPageChangeListener(this);
|
||||
|
||||
pager.setAdapter(readingAdapter);
|
||||
|
||||
pager.setCurrentItem(passedPosition);
|
||||
// setCurrentItem sometimes fails to pass the first page to the callback, so call it manually
|
||||
// for the first one.
|
||||
this.onPageSelected(passedPosition);
|
||||
|
||||
this.enableOverlays();
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +170,14 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
Story story = readingAdapter.getStory(pager.getCurrentItem());
|
||||
menu.findItem(R.id.menu_reading_save).setTitle(story.starred ? R.string.menu_unsave_story : R.string.menu_save_story);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int currentItem = pager.getCurrentItem();
|
||||
|
|
@ -161,8 +196,7 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_reading_sharenewsblur) {
|
||||
if (story != null) {
|
||||
ReadingItemFragment currentFragment = (ReadingItemFragment) readingAdapter.instantiateItem(pager, currentItem);
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(currentFragment, story, currentFragment.previouslySavedShareText);
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(getReadingFragment(), story, getReadingFragment().previouslySavedShareText);
|
||||
newFragment.show(getSupportFragmentManager(), "dialog");
|
||||
}
|
||||
return true;
|
||||
|
|
@ -175,7 +209,11 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
textSize.show(getSupportFragmentManager(), TEXT_SIZE);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_reading_save) {
|
||||
FeedUtils.saveStory(story, Reading.this, apiManager);
|
||||
if (story.starred) {
|
||||
FeedUtils.unsaveStory(story, Reading.this, apiManager, this);
|
||||
} else {
|
||||
FeedUtils.saveStory(story, Reading.this, apiManager, this);
|
||||
}
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_reading_markunread) {
|
||||
this.markStoryUnread(story);
|
||||
|
|
@ -185,6 +223,14 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionCompleteCallback() {
|
||||
stories.requery();
|
||||
ReadingItemFragment fragment = getReadingFragment();
|
||||
fragment.updateStory(readingAdapter.getStory(pager.getCurrentItem()));
|
||||
fragment.updateSaveButton();
|
||||
}
|
||||
|
||||
// interface OnPageChangeListener
|
||||
|
||||
@Override
|
||||
|
|
@ -197,12 +243,18 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
this.enableOverlays();
|
||||
|
||||
if (readingAdapter.getStory(position) != null) {
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(position));
|
||||
Story story = readingAdapter.getStory(position);
|
||||
if (story != null) {
|
||||
synchronized (this.pageHistory) {
|
||||
// if the history is just starting out or the last entry in it isn't this page, add this page
|
||||
if ((this.pageHistory.size() < 1) || (!story.equals(this.pageHistory.get(this.pageHistory.size()-1)))) {
|
||||
this.pageHistory.add(story);
|
||||
}
|
||||
}
|
||||
addStoryToMarkAsRead(story);
|
||||
checkStoryCount(position);
|
||||
}
|
||||
this.enableOverlays();
|
||||
}
|
||||
|
||||
// interface ScrollChangeListener
|
||||
|
|
@ -231,36 +283,69 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
private void setOverlayAlpha(float a) {
|
||||
UIUtils.setViewAlpha(this.overlayLeft, a);
|
||||
UIUtils.setViewAlpha(this.overlayRight, a);
|
||||
|
||||
if (this.unreadCount > 0) {
|
||||
UIUtils.setViewAlpha(this.overlayCount, a);
|
||||
} else {
|
||||
UIUtils.setViewAlpha(this.overlayCount, 0.0f);
|
||||
}
|
||||
UIUtils.setViewAlpha(this.overlayProgress, a);
|
||||
UIUtils.setViewAlpha(this.overlayProgressLeft, a);
|
||||
UIUtils.setViewAlpha(this.overlayProgressRight, a);
|
||||
UIUtils.setViewAlpha(this.overlayText, a);
|
||||
UIUtils.setViewAlpha(this.overlaySend, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and correct the display status of the overlays. Call this any time
|
||||
* an event happens that might change out list position.
|
||||
* an event happens that might change our list position.
|
||||
*/
|
||||
private void enableOverlays() {
|
||||
int page = this.pager.getCurrentItem();
|
||||
this.overlayLeft.setEnabled(page > 0);
|
||||
this.overlayRight.setEnabled(page < (this.readingAdapter.getCount()-1));
|
||||
this.overlayRight.setText((page < (this.readingAdapter.getCount()-1)) ? R.string.overlay_next : R.string.overlay_done);
|
||||
// check to see if the device even has room for all the overlays, moving some to overflow if not
|
||||
int widthPX = findViewById(android.R.id.content).getMeasuredWidth();
|
||||
if (widthPX != 0) {
|
||||
float widthDP = UIUtils.px2dp(this, widthPX);
|
||||
if ( widthDP < OVERLAY_MIN_WIDTH_DP ){
|
||||
this.overlaySend.setVisibility(View.GONE);
|
||||
} else {
|
||||
this.overlaySend.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
this.overlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
|
||||
this.overlayRight.setText((this.currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
|
||||
this.overlayRight.setBackgroundResource((this.currentUnreadCount > 0) ? R.drawable.selector_overlay_bg_right : R.drawable.selector_overlay_bg_right_done);
|
||||
|
||||
if (this.startingUnreadCount == 0 ) {
|
||||
// sessions with no unreads just show a full progress bar
|
||||
this.overlayProgress.setMax(1);
|
||||
this.overlayProgress.setProgress(1);
|
||||
} else {
|
||||
int unreadProgress = this.startingUnreadCount - this.currentUnreadCount;
|
||||
this.overlayProgress.setMax(this.startingUnreadCount);
|
||||
this.overlayProgress.setProgress(unreadProgress);
|
||||
}
|
||||
this.overlayProgress.invalidate();
|
||||
|
||||
// make sure we start in story mode and the ui reflects it
|
||||
synchronized (textMode) {
|
||||
enableStoryMode();
|
||||
}
|
||||
|
||||
this.overlayCount.setText(Integer.toString(this.unreadCount));
|
||||
this.setOverlayAlpha(1.0f);
|
||||
}
|
||||
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
// this callback is a good API-level-independent way to tell when the root view size/layout changes
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
enableOverlays();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAfterSync() {
|
||||
this.requestedPage = false;
|
||||
setSupportProgressBarIndeterminateVisibility(false);
|
||||
updateSyncStatus(false);
|
||||
stories.requery();
|
||||
readingAdapter.notifyDataSetChanged();
|
||||
this.enableOverlays();
|
||||
checkStoryCount(pager.getCurrentItem());
|
||||
if (this.unreadSearchLatch != null) {
|
||||
this.unreadSearchLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -269,6 +354,9 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
readingAdapter.notifyDataSetChanged();
|
||||
this.enableOverlays();
|
||||
checkStoryCount(pager.getCurrentItem());
|
||||
if (this.unreadSearchLatch != null) {
|
||||
this.unreadSearchLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -278,11 +366,20 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
@Override
|
||||
public void setNothingMoreToUpdate() {
|
||||
this.noMoreApiPages = true;
|
||||
if (this.unreadSearchLatch !=null) {
|
||||
this.unreadSearchLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStoryCount(int position) {
|
||||
/**
|
||||
* While navigating the story list and at the specified position, see if it is possible
|
||||
* and desirable to start loading more stories in the background. Note that if a load
|
||||
* is triggered, this method will be called again by the callback to ensure another
|
||||
* load is not needed and all latches are tripped.
|
||||
*/
|
||||
private void checkStoryCount(int position) {
|
||||
// if the pager is at or near the number of stories loaded, check for more unless we know we are at the end of the list
|
||||
if (((position + 1) >= stories.getCount()) && !noMoreApiPages && !requestedPage) {
|
||||
if (((position + 2) >= stories.getCount()) && !noMoreApiPages && !requestedPage) {
|
||||
currentApiPage += 1;
|
||||
requestedPage = true;
|
||||
triggerRefresh(currentApiPage);
|
||||
|
|
@ -290,10 +387,24 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateSyncStatus(boolean syncRunning) {
|
||||
setSupportProgressBarIndeterminateVisibility(syncRunning);
|
||||
public void updateSyncStatus(final boolean syncRunning) {
|
||||
enableProgressCircle(overlayProgressRight, syncRunning);
|
||||
}
|
||||
|
||||
private void enableProgressCircle(final ProgressBar view, final boolean enabled) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (enabled) {
|
||||
view.setProgress(0);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.setProgress(100);
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public abstract void triggerRefresh(int page);
|
||||
|
||||
@Override
|
||||
|
|
@ -316,9 +427,10 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
if (this.storiesToMarkAsRead.size() >= AppConstants.MAX_MARK_READ_BATCH) {
|
||||
flushStoriesMarkedRead();
|
||||
}
|
||||
if (this.storiesAlreadySeen.add(story)) {
|
||||
if (!this.storiesAlreadySeen.contains(story)) {
|
||||
// only decrement the cached story count if the story wasn't already read
|
||||
this.unreadCount--;
|
||||
this.storiesAlreadySeen.add(story);
|
||||
this.currentUnreadCount--;
|
||||
}
|
||||
this.enableOverlays();
|
||||
}
|
||||
|
|
@ -341,12 +453,13 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
// operation, or it was read long before now.
|
||||
FeedUtils.markStoryUnread(story, Reading.this, this.apiManager);
|
||||
|
||||
this.unreadCount++;
|
||||
this.currentUnreadCount++;
|
||||
this.storiesAlreadySeen.remove(story);
|
||||
|
||||
this.enableOverlays();
|
||||
}
|
||||
|
||||
// NB: this callback is for the text size slider
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
getSharedPreferences(PrefConstants.PREFERENCES, 0).edit().putFloat(PrefConstants.PREFERENCE_TEXT_SIZE, (float) progress / AppConstants.FONT_SIZE_INCREMENT_FACTOR).commit();
|
||||
|
|
@ -363,12 +476,193 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the righthand overlay nav button.
|
||||
*/
|
||||
public void overlayRight(View v) {
|
||||
pager.setCurrentItem(pager.getCurrentItem()+1, true);
|
||||
if (this.currentUnreadCount == 0) {
|
||||
// if there are no unread stories, go back to the feed list
|
||||
Intent i = new Intent(this, Main.class);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(i);
|
||||
} else {
|
||||
// if there are unreads, go to the next one
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
nextUnread();
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
//}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search our set of stories for the next unread one. This requires some heavy
|
||||
* cooperation with the way stories are automatically loaded in the background
|
||||
* as we walk through the list.
|
||||
*/
|
||||
private void nextUnread() {
|
||||
synchronized (UNREAD_SEARCH_MUTEX) {
|
||||
int candidate = 0;
|
||||
boolean unreadFound = false;
|
||||
boolean error = false;
|
||||
unreadSearch:while (!unreadFound) {
|
||||
|
||||
Story story = readingAdapter.getStory(candidate);
|
||||
|
||||
if (story == null) {
|
||||
if (this.noMoreApiPages) {
|
||||
// this is odd. if there were no unreads, how was the button even enabled?
|
||||
Log.e(this.getClass().getName(), "Ran out of stories while looking for unreads.");
|
||||
break unreadSearch;
|
||||
}
|
||||
} else {
|
||||
if ((candidate == pager.getCurrentItem()) || (story.read) || (this.storiesAlreadySeen.contains(story))) {
|
||||
candidate++;
|
||||
continue unreadSearch;
|
||||
} else {
|
||||
unreadFound = true;
|
||||
break unreadSearch;
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a story trigger a check to see if there are any more to search before proceeding
|
||||
this.unreadSearchLatch = new CountDownLatch(1);
|
||||
this.checkStoryCount(candidate+1);
|
||||
try {
|
||||
boolean unlatched = this.unreadSearchLatch.await(UNREAD_SEARCH_LOAD_WAIT_SECONDS, TimeUnit.SECONDS);
|
||||
if (unlatched) {
|
||||
continue unreadSearch;
|
||||
} else {
|
||||
Log.e(this.getClass().getName(), "Timed out waiting for next API page while looking for unreads.");
|
||||
break unreadSearch;
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
Log.e(this.getClass().getName(), "Interrupted waiting for next API page while looking for unreads.");
|
||||
break unreadSearch;
|
||||
}
|
||||
|
||||
}
|
||||
if (error) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
Toast.makeText(Reading.this, R.string.toast_unread_search_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (unreadFound) {
|
||||
final int page = candidate;
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
pager.setCurrentItem(page, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the lefthand overlay nav button.
|
||||
*/
|
||||
public void overlayLeft(View v) {
|
||||
pager.setCurrentItem(pager.getCurrentItem()-1, true);
|
||||
int targetPosition = this.getLastReadPosition(true);
|
||||
if (targetPosition != -1) {
|
||||
pager.setCurrentItem(targetPosition, true);
|
||||
} else {
|
||||
Log.e(this.getClass().getName(), "reading history contained item not found in cursor.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pager position of the last story read during this activity or -1 if there is nothing
|
||||
* in the history.
|
||||
*
|
||||
* @param trimHistory optionally trim the history of the currently displayed page iff the
|
||||
* back button has been pressed.
|
||||
*/
|
||||
private int getLastReadPosition(boolean trimHistory) {
|
||||
synchronized (this.pageHistory) {
|
||||
// the last item is always the currently shown page, do not count it
|
||||
if (this.pageHistory.size() < 2) {
|
||||
return -1;
|
||||
}
|
||||
Story targetStory = this.pageHistory.get(this.pageHistory.size()-2);
|
||||
int targetPosition = this.readingAdapter.getPosition(targetStory);
|
||||
if (trimHistory && (targetPosition != -1)) {
|
||||
this.pageHistory.remove(this.pageHistory.size()-1);
|
||||
}
|
||||
return targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the progress indicator on the righthand overlay nav button.
|
||||
*/
|
||||
public void overlayCount(View v) {
|
||||
String unreadText = getString((this.currentUnreadCount == 1) ? R.string.overlay_count_toast_1 : R.string.overlay_count_toast_N);
|
||||
Toast.makeText(this, String.format(unreadText, this.currentUnreadCount), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public void overlaySend(View v) {
|
||||
Story story = readingAdapter.getStory(pager.getCurrentItem());
|
||||
FeedUtils.shareStory(story, this);
|
||||
}
|
||||
|
||||
public void overlayText(View v) {
|
||||
synchronized (textMode) {
|
||||
// if we were already in text mode, switch back to story mode
|
||||
if (textMode) {
|
||||
enableStoryMode();
|
||||
} else {
|
||||
enableTextMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enableTextMode() {
|
||||
final Story story = readingAdapter.getStory(pager.getCurrentItem());
|
||||
if (story != null) {
|
||||
new AsyncTask<Void, Void, StoryTextResponse>() {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
enableProgressCircle(overlayProgressLeft, true);
|
||||
}
|
||||
@Override
|
||||
protected StoryTextResponse doInBackground(Void... arg) {
|
||||
return apiManager.getStoryText(story.feedId, story.id);
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(StoryTextResponse result) {
|
||||
ReadingItemFragment item = getReadingFragment();
|
||||
if (item != null) item.setCustomWebview(result.originalText);
|
||||
enableProgressCircle(overlayProgressLeft, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
this.overlayText.setBackgroundResource(R.drawable.selector_overlay_bg_story);
|
||||
this.overlayText.setText(R.string.overlay_story);
|
||||
this.textMode = true;
|
||||
}
|
||||
|
||||
private void enableStoryMode() {
|
||||
ReadingItemFragment item = getReadingFragment();
|
||||
if (item != null) item.setDefaultWebview();
|
||||
|
||||
this.overlayText.setBackgroundResource(R.drawable.selector_overlay_bg_text);
|
||||
this.overlayText.setText(R.string.overlay_text);
|
||||
this.textMode = false;
|
||||
}
|
||||
|
||||
private ReadingItemFragment getReadingFragment() {
|
||||
Object o = readingAdapter.instantiateItem(pager, pager.getCurrentItem());
|
||||
if (o instanceof ReadingItemFragment) {
|
||||
return (ReadingItemFragment) o;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import android.view.ViewGroup;
|
|||
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.fragment.LoadingFragment;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
|
||||
public abstract class ReadingAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
|
|
@ -41,6 +40,18 @@ public abstract class ReadingAdapter extends FragmentStatePagerAdapter {
|
|||
return Story.fromCursor(stories);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPosition(Story story) {
|
||||
int pos = 0;
|
||||
while (pos < stories.getCount()) {
|
||||
stories.moveToPosition(pos);
|
||||
if (Story.fromCursor(stories).equals(story)) {
|
||||
return pos;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ public class SocialFeedReading extends Reading {
|
|||
stories = contentResolver.query(storiesURI, null, DatabaseConstants.getStorySelectionFromState(currentState), null, null);
|
||||
setTitle(getIntent().getStringExtra(EXTRA_USERNAME));
|
||||
|
||||
this.unreadCount = FeedUtils.getFeedUnreadCount(this.socialFeed, this.currentState);
|
||||
int unreadCount = FeedUtils.getFeedUnreadCount(this.socialFeed, this.currentState);
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver(), stories);
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ public class SocialFeedReading extends Reading {
|
|||
|
||||
@Override
|
||||
public void triggerRefresh(int page) {
|
||||
setSupportProgressBarIndeterminateVisibility(true);
|
||||
updateSyncStatus(true);
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_TYPE, SyncService.TaskType.SOCIALFEED_UPDATE);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
private final String TEXT = " text";
|
||||
private final String INTEGER = " integer";
|
||||
public final static String DB_NAME = "blur.db";
|
||||
private final static int VERSION = 1;
|
||||
private final static int VERSION = 2;
|
||||
|
||||
public BlurDatabase(Context context) {
|
||||
super(context, DB_NAME, null, VERSION);
|
||||
|
|
@ -44,7 +44,8 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
private final String USER_SQL = "CREATE TABLE " + DatabaseConstants.USER_TABLE + " (" +
|
||||
DatabaseConstants.USER_PHOTO_URL + TEXT + ", " +
|
||||
DatabaseConstants.USER_USERID + INTEGER + " PRIMARY KEY, " +
|
||||
DatabaseConstants.USER_USERNAME + TEXT + ")";
|
||||
DatabaseConstants.USER_USERNAME + TEXT + ", " +
|
||||
DatabaseConstants.USER_LOCATION + TEXT + ")";
|
||||
|
||||
private final String SOCIAL_FEED_SQL = "CREATE TABLE " + DatabaseConstants.SOCIALFEED_TABLE + " (" +
|
||||
DatabaseConstants.SOCIAL_FEED_ID + INTEGER + " PRIMARY KEY, " +
|
||||
|
|
@ -107,6 +108,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
|
|||
DatabaseConstants.STORY_TAGS + TEXT + ", " +
|
||||
DatabaseConstants.STORY_PERMALINK + TEXT + ", " +
|
||||
DatabaseConstants.STORY_READ + INTEGER + ", " +
|
||||
DatabaseConstants.STORY_STARRED + INTEGER + ", " +
|
||||
DatabaseConstants.STORY_TITLE + TEXT;
|
||||
|
||||
private final String STORY_SQL = "CREATE TABLE " + DatabaseConstants.STORY_TABLE + " (" + STORY_TABLES_COLS + ")";
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ public class DatabaseConstants {
|
|||
|
||||
public static final String USER_TABLE = "user_table";
|
||||
public static final String USER_USERID = BaseColumns._ID;
|
||||
public static final String USER_USERNAME = "username";
|
||||
public static final String USER_USERNAME = "username";
|
||||
public static final String USER_LOCATION = "location";
|
||||
public static final String USER_PHOTO_URL = "photo_url";
|
||||
|
||||
public static final String STORY_TABLE = "stories";
|
||||
|
|
@ -84,6 +85,7 @@ public class DatabaseConstants {
|
|||
public static final String STORY_INTELLIGENCE_TITLE = "intelligence_title";
|
||||
public static final String STORY_PERMALINK = "permalink";
|
||||
public static final String STORY_READ = "read";
|
||||
public static final String STORY_STARRED = "starred";
|
||||
public static final String STORY_SHARE_COUNT = "share_count";
|
||||
public static final String STORY_SHARED_USER_IDS = "shared_user_ids";
|
||||
public static final String STORY_FRIEND_USER_IDS = "comment_user_ids";
|
||||
|
|
@ -169,11 +171,11 @@ public class DatabaseConstants {
|
|||
|
||||
public static final String[] STORY_COLUMNS = {
|
||||
STORY_AUTHORS, STORY_COMMENT_COUNT, STORY_CONTENT, STORY_DATE, STORY_SHARED_DATE, STORY_SHORTDATE, STORY_LONGDATE, STORY_TABLE + "." + STORY_FEED_ID, STORY_TABLE + "." + STORY_ID, STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS, STORY_INTELLIGENCE_TITLE,
|
||||
STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH
|
||||
STORY_PERMALINK, STORY_READ, STORY_STARRED, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH
|
||||
};
|
||||
public static final String[] STARRED_STORY_COLUMNS = {
|
||||
STORY_AUTHORS, STORY_COMMENT_COUNT, STORY_CONTENT, STORY_DATE, STORY_SHARED_DATE, STORY_SHORTDATE, STORY_LONGDATE, STARRED_STORIES_TABLE + "." + STORY_FEED_ID, STARRED_STORIES_TABLE + "." + STORY_ID, STORY_INTELLIGENCE_AUTHORS, STORY_INTELLIGENCE_FEED, STORY_INTELLIGENCE_TAGS, STORY_INTELLIGENCE_TITLE,
|
||||
STORY_PERMALINK, STORY_READ, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH
|
||||
STORY_PERMALINK, STORY_READ, STORY_STARRED, STORY_SHARE_COUNT, STORY_TAGS, STORY_TITLE, STORY_SOCIAL_USER_ID, STORY_SOURCE_USER_ID, STORY_SHARED_USER_IDS, STORY_FRIEND_USER_IDS, STORY_PUBLIC_USER_IDS, STORY_SUM_TOTAL, STORY_HASH
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -336,7 +336,11 @@ public class FeedProvider extends ContentProvider {
|
|||
Log.d(LoggingDatabase.class.getName(), "rawQuery: " + sql);
|
||||
Log.d(LoggingDatabase.class.getName(), "selArgs : " + Arrays.toString(selectionArgs));
|
||||
}
|
||||
return mdb.rawQuery(sql, selectionArgs);
|
||||
Cursor cursor = mdb.rawQuery(sql, selectionArgs);
|
||||
if (AppConstants.VERBOSE_LOG) {
|
||||
Log.d(LoggingDatabase.class.getName(), "result rows: " + cursor.getCount());
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
|
||||
return mdb.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
|
||||
|
|
@ -599,8 +603,11 @@ public class FeedProvider extends ContentProvider {
|
|||
case SOCIALFEED_STORIES:
|
||||
return db.update(DatabaseConstants.SOCIALFEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[] { uri.getLastPathSegment() });
|
||||
case INDIVIDUAL_STORY:
|
||||
return db.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_ID + " = ?", new String[] { uri.getLastPathSegment() });
|
||||
// In order to run a raw SQL query whereby we make decrement the column we need to a dynamic reference - something the usual content provider can't easily handle. Hence this circuitous hack.
|
||||
int count = 0;
|
||||
count += db.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_ID + " = ?", new String[] { uri.getLastPathSegment() });
|
||||
count += db.update(DatabaseConstants.STARRED_STORIES_TABLE, values, DatabaseConstants.STORY_ID + " = ?", new String[] { uri.getLastPathSegment() });
|
||||
return count;
|
||||
// In order to run a raw SQL query whereby we make decrement the column we need to a dynamic reference - something the usual content provider can't easily handle. Hence this circuitous hack.
|
||||
case FEED_COUNT:
|
||||
db.execSQL("UPDATE " + DatabaseConstants.FEED_TABLE + " SET " + selectionArgs[0] + " = " + selectionArgs[0] + " - 1 WHERE " + DatabaseConstants.FEED_ID + " = " + selectionArgs[1]);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.newsblur.fragment.ReadingItemFragment;
|
|||
|
||||
public class MixedFeedsReadingAdapter extends ReadingAdapter {
|
||||
|
||||
private String TAG = "FeedReadingAdapter";
|
||||
private LoadingFragment loadingFragment;
|
||||
private final ContentResolver resolver;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public class Story implements Serializable {
|
|||
@SerializedName("read_status")
|
||||
public boolean read;
|
||||
|
||||
@SerializedName("starred")
|
||||
public boolean starred;
|
||||
|
||||
@SerializedName("story_tags")
|
||||
public String[] tags;
|
||||
|
||||
|
|
@ -110,6 +113,7 @@ public class Story implements Serializable {
|
|||
values.put(DatabaseConstants.STORY_INTELLIGENCE_TITLE, intelligence.intelligenceTitle);
|
||||
values.put(DatabaseConstants.STORY_TAGS, TextUtils.join(",", tags));
|
||||
values.put(DatabaseConstants.STORY_READ, read);
|
||||
values.put(DatabaseConstants.STORY_STARRED, starred);
|
||||
values.put(DatabaseConstants.STORY_FEED_ID, feedId);
|
||||
values.put(DatabaseConstants.STORY_HASH, storyHash);
|
||||
return values;
|
||||
|
|
@ -137,6 +141,7 @@ public class Story implements Serializable {
|
|||
story.intelligence.intelligenceTags = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_TAGS));
|
||||
story.intelligence.intelligenceTitle = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_TITLE));
|
||||
story.read = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_READ)) > 0;
|
||||
story.starred = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_STARRED)) > 0;
|
||||
story.tags = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_TAGS)), ",");
|
||||
story.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_FEED_ID));
|
||||
story.id = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_ID));
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ public class UserProfile {
|
|||
|
||||
@SerializedName("user_id")
|
||||
public String userId;
|
||||
|
||||
public String username;
|
||||
|
||||
public String username;
|
||||
public String location;
|
||||
|
||||
public static UserProfile fromCursor(final Cursor c) {
|
||||
if (c.isBeforeFirst()) {
|
||||
|
|
@ -26,7 +27,8 @@ public class UserProfile {
|
|||
UserProfile profile = new UserProfile();
|
||||
profile.userId = c.getString(c.getColumnIndex(DatabaseConstants.USER_USERID));
|
||||
profile.photoUrl = c.getString(c.getColumnIndex(DatabaseConstants.USER_PHOTO_URL));
|
||||
profile.username = c.getString(c.getColumnIndex(DatabaseConstants.USER_USERNAME));
|
||||
profile.username = c.getString(c.getColumnIndex(DatabaseConstants.USER_USERNAME));
|
||||
profile.location = c.getString(c.getColumnIndex(DatabaseConstants.USER_LOCATION));
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
|
@ -35,7 +37,8 @@ public class UserProfile {
|
|||
final ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.USER_PHOTO_URL, photoUrl);
|
||||
values.put(DatabaseConstants.USER_USERID, userId);
|
||||
values.put(DatabaseConstants.USER_USERNAME, username);
|
||||
values.put(DatabaseConstants.USER_USERNAME, username);
|
||||
values.put(DatabaseConstants.USER_LOCATION, location);
|
||||
return values;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,15 @@ import android.graphics.Color;
|
|||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ScaleDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
|
@ -67,6 +70,7 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
public String previouslySavedShareText;
|
||||
private ImageView feedIcon;
|
||||
private Reading activity;
|
||||
private Boolean customContent = false;
|
||||
|
||||
public static ReadingItemFragment newInstance(Story story, String feedTitle, String feedFaviconColor, String feedFaviconFade, String feedFaviconBorder, String faviconText, String faviconUrl, Classifier classifier, boolean displayFeedDetails) {
|
||||
ReadingItemFragment readingFragment = new ReadingItemFragment();
|
||||
|
|
@ -120,6 +124,10 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
receiver = new TextSizeReceiver();
|
||||
getActivity().registerReceiver(receiver, new IntentFilter(TEXT_SIZE_CHANGED));
|
||||
}
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putSerializable("story", story);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
|
@ -146,7 +154,12 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
view = inflater.inflate(R.layout.fragment_readingitem, null);
|
||||
|
||||
web = (NewsblurWebview) view.findViewById(R.id.reading_webview);
|
||||
setupWebview(web);
|
||||
|
||||
synchronized (customContent) {
|
||||
setupWebview(story.content);
|
||||
customContent = false;
|
||||
}
|
||||
|
||||
setupItemMetadata();
|
||||
setupShareButton();
|
||||
setupSaveButton();
|
||||
|
|
@ -164,19 +177,31 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
}
|
||||
|
||||
private void setupSaveButton() {
|
||||
final Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
|
||||
Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
|
||||
saveButton.setOnClickListener(new OnClickListener() {
|
||||
saveButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FeedUtils.saveStory(story, getActivity(), apiManager);
|
||||
if (story.starred) {
|
||||
FeedUtils.unsaveStory(story, activity, apiManager, activity);
|
||||
} else {
|
||||
FeedUtils.saveStory(story, activity, apiManager, activity);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupShareButton() {
|
||||
public void updateSaveButton() {
|
||||
Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
}
|
||||
|
||||
public void updateStory(Story story) {
|
||||
this.story = story;
|
||||
}
|
||||
|
||||
private void setupShareButton() {
|
||||
Button shareButton = (Button) view.findViewById(R.id.share_story_button);
|
||||
|
||||
for (String userId : story.sharedUserIds) {
|
||||
|
|
@ -195,7 +220,6 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
public void changeTextSize(float newTextSize) {
|
||||
if (web != null) {
|
||||
web.setTextSize(newTextSize);
|
||||
|
|
@ -247,7 +271,7 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
itemFeed.setText(feedTitle);
|
||||
}
|
||||
|
||||
itemTitle.setText(story.title);
|
||||
itemTitle.setText(Html.fromHtml(story.title));
|
||||
itemDate.setText(story.longDate);
|
||||
|
||||
if (!TextUtils.isEmpty(story.authors)) {
|
||||
|
|
@ -300,7 +324,34 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
|
||||
}
|
||||
|
||||
private void setupWebview(NewsblurWebview web) {
|
||||
/**
|
||||
* Set the webview to show the default story content.
|
||||
*/
|
||||
public void setDefaultWebview() {
|
||||
// if the default content hasn't been changed, don't reset it
|
||||
synchronized (customContent) {
|
||||
if (!customContent) return;
|
||||
setupWebview(story.content);
|
||||
customContent = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the webview to show non-default content, tracking the change.
|
||||
*/
|
||||
public void setCustomWebview(String content) {
|
||||
synchronized (customContent) {
|
||||
setupWebview(content);
|
||||
customContent = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupWebview(String storyText) {
|
||||
if (getActivity() == null) {
|
||||
// this method gets called by async UI bits that might hold stale fragment references with no assigned
|
||||
// activity. If this happens, just abort the call.
|
||||
return;
|
||||
}
|
||||
final SharedPreferences preferences = getActivity().getSharedPreferences(PrefConstants.PREFERENCES, 0);
|
||||
float currentSize = preferences.getFloat(PrefConstants.PREFERENCE_TEXT_SIZE, 0.5f);
|
||||
|
||||
|
|
@ -310,10 +361,9 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
builder.append(String.format("body { font-size: %s em; } ", Float.toString(currentSize + AppConstants.FONT_SIZE_LOWER_BOUND)));
|
||||
builder.append("</style>");
|
||||
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"reading.css\" /></head><body><div class=\"NB-story\">");
|
||||
builder.append(story.content);
|
||||
builder.append(storyText);
|
||||
builder.append("</div></body></html>");
|
||||
web.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
|
||||
|
||||
}
|
||||
|
||||
private class TextSizeReceiver extends BroadcastReceiver {
|
||||
|
|
@ -377,11 +427,18 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
commentView.setTag(SetupCommentSectionTask.COMMENT_VIEW_BY + user.id);
|
||||
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
commentText.setTag("commentBy" + user.id);
|
||||
commentText.setText(sharedText);
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
commentText.setTag("commentBy" + user.id);
|
||||
commentText.setText(sharedText);
|
||||
|
||||
if (PrefsUtils.getUserImage(getActivity()) != null) {
|
||||
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
|
||||
if (!TextUtils.isEmpty(user.location)) {
|
||||
commentLocation.setText(user.location.toUpperCase());
|
||||
} else {
|
||||
commentLocation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (PrefsUtils.getUserImage(getActivity()) != null) {
|
||||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
commentImage.setImageBitmap(UIUtils.roundCorners(PrefsUtils.getUserImage(getActivity()), 10f));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.os.AsyncTask;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -108,7 +109,7 @@ public class ShareDialogFragment extends DialogFragment {
|
|||
public void onTextChanged(CharSequence s, int start, int before, int count) { }
|
||||
});
|
||||
|
||||
message.setText(String.format(shareString, story.title));
|
||||
message.setText(String.format(shareString, Html.fromHtml(story.title)));
|
||||
|
||||
if (hasBeenShared) {
|
||||
shareButton.setText(R.string.edit);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class APIConstants {
|
|||
public static final String URL_MARK_STORIES_READ = NEWSBLUR_URL + "/reader/mark_story_hashes_as_read/";
|
||||
public static final String URL_SHARE_STORY = NEWSBLUR_URL + "/social/share_story";
|
||||
public static final String URL_MARK_STORY_AS_STARRED = NEWSBLUR_URL + "/reader/mark_story_as_starred/";
|
||||
public static final String URL_MARK_STORY_AS_UNSTARRED = NEWSBLUR_URL + "/reader/mark_story_as_unstarred/";
|
||||
public static final String URL_MARK_STORY_AS_UNREAD = NEWSBLUR_URL + "/reader/mark_story_as_unread/";
|
||||
public static final String URL_STARRED_STORIES = NEWSBLUR_URL + "/reader/starred_stories";
|
||||
public static final String URL_FEED_AUTOCOMPLETE = NEWSBLUR_URL + "/rss_feeds/feed_autocomplete";
|
||||
|
|
@ -41,6 +42,7 @@ public class APIConstants {
|
|||
public static final String URL_ADD_FEED = NEWSBLUR_URL + "/reader/add_url";
|
||||
public static final String URL_DELETE_FEED = NEWSBLUR_URL + "/reader/delete_feed";
|
||||
public static final String URL_CLASSIFIER_SAVE = NEWSBLUR_URL + "/classifier/save";
|
||||
public static final String URL_STORY_TEXT = NEWSBLUR_URL + "/rss_feeds/original_text";
|
||||
|
||||
public static final String PARAMETER_FEEDS = "f";
|
||||
public static final String PARAMETER_PASSWORD = "password";
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import com.newsblur.network.domain.ProfileResponse;
|
|||
import com.newsblur.network.domain.RegisterResponse;
|
||||
import com.newsblur.network.domain.SocialFeedResponse;
|
||||
import com.newsblur.network.domain.StoriesResponse;
|
||||
import com.newsblur.network.domain.StoryTextResponse;
|
||||
import com.newsblur.serialization.BooleanTypeAdapter;
|
||||
import com.newsblur.serialization.DateStringTypeAdapter;
|
||||
import com.newsblur.util.AppConstants;
|
||||
|
|
@ -135,6 +136,14 @@ public class APIManager {
|
|||
final APIResponse response = post(APIConstants.URL_MARK_STORY_AS_STARRED, values, false);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse markStoryAsUnstarred(final String feedId, final String storyId) {
|
||||
final ValueMultimap values = new ValueMultimap();
|
||||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
final APIResponse response = post(APIConstants.URL_MARK_STORY_AS_UNSTARRED, values, false);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse markStoryAsUnread( String feedId, String storyId ) {
|
||||
final ValueMultimap values = new ValueMultimap();
|
||||
|
|
@ -602,6 +611,19 @@ public class APIManager {
|
|||
}
|
||||
}
|
||||
|
||||
public StoryTextResponse getStoryText(String feedId, String storyId) {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
final APIResponse response = get(APIConstants.URL_STORY_TEXT, values);
|
||||
if (!response.isError()) {
|
||||
StoryTextResponse storyTextResponse = (StoryTextResponse) response.getResponse(gson, StoryTextResponse.class);
|
||||
return storyTextResponse;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshFeedCounts() {
|
||||
final APIResponse response = get(APIConstants.URL_FEED_COUNTS);
|
||||
if (!response.isError()) {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,12 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
|
||||
while (commentCursor.moveToNext()) {
|
||||
final Comment comment = Comment.fromCursor(commentCursor);
|
||||
|
||||
// skip public comments if they are disabled
|
||||
if (!comment.byFriend && !PrefsUtils.showPublicComments(context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
commentView.setTag(COMMENT_VIEW_BY + comment.userId);
|
||||
|
||||
|
|
@ -87,7 +93,7 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
|
||||
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
|
||||
commentSharedDate.setText(comment.sharedDate.toUpperCase() + " AGO");
|
||||
commentSharedDate.setText(comment.sharedDate + " ago");
|
||||
commentSharedDate.setTag(COMMENT_DATE_BY + comment.userId);
|
||||
|
||||
final FlowLayout favouriteContainer = (FlowLayout) commentView.findViewById(R.id.comment_favourite_avatars);
|
||||
|
|
@ -167,7 +173,7 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
}
|
||||
|
||||
TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate);
|
||||
replySharedDate.setText(reply.shortDate.toUpperCase() + " AGO");
|
||||
replySharedDate.setText(reply.shortDate + " ago");
|
||||
|
||||
((LinearLayout) commentView.findViewById(R.id.comment_replies_container)).addView(replyView);
|
||||
}
|
||||
|
|
@ -179,7 +185,14 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
commentUsername.setText(commentUser.username);
|
||||
String userPhoto = commentUser.photoUrl;
|
||||
|
||||
if (!TextUtils.isEmpty(comment.sourceUserId)) {
|
||||
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
|
||||
if (!TextUtils.isEmpty(commentUser.location)) {
|
||||
commentLocation.setText(commentUser.location.toUpperCase());
|
||||
} else {
|
||||
commentLocation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(comment.sourceUserId)) {
|
||||
commentImage.setVisibility(View.INVISIBLE);
|
||||
ImageView usershareImage = (ImageView) commentView.findViewById(R.id.comment_user_reshare_image);
|
||||
ImageView sourceUserImage = (ImageView) commentView.findViewById(R.id.comment_sharesource_image);
|
||||
|
|
@ -270,7 +283,8 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
publicCommentTotal.setText(String.format(commentCount, publicCommentViews.size()));
|
||||
}
|
||||
viewHolder.get().findViewById(R.id.reading_public_comment_header).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (friendCommentViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.friends_comments_count);
|
||||
|
|
@ -278,7 +292,8 @@ public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
|||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
friendCommentTotal.setText(String.format(commentCount, friendCommentViews.size()));
|
||||
}
|
||||
viewHolder.get().findViewById(R.id.reading_friend_comment_header).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < publicCommentViews.size(); i++) {
|
||||
if (i == publicCommentViews.size() - 1) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.newsblur.network.domain;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class StoryTextResponse extends NewsBlurResponse {
|
||||
|
||||
@SerializedName("original_text")
|
||||
public String originalText;
|
||||
|
||||
}
|
||||
|
|
@ -12,8 +12,10 @@ import android.content.ContentProviderOperation;
|
|||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
|
@ -36,27 +38,44 @@ public class FeedUtils {
|
|||
|
||||
private static Gson gson = new Gson();
|
||||
|
||||
public static void saveStory(final Story story, final Context context, final APIManager apiManager) {
|
||||
if (story != null) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... arg) {
|
||||
private static void setStorySaved(final Story story, final boolean saved, final Context context, final APIManager apiManager, final ActionCompletionListener receiver) {
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... arg) {
|
||||
if (saved) {
|
||||
return apiManager.markStoryAsStarred(story.feedId, story.storyHash);
|
||||
} else {
|
||||
return apiManager.markStoryAsUnstarred(story.feedId, story.storyHash);
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse result) {
|
||||
if (!result.isError()) {
|
||||
Toast.makeText(context, R.string.toast_story_saved, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(context, result.getErrorMessage(context.getString(R.string.toast_story_save_error)), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse result) {
|
||||
if (!result.isError()) {
|
||||
Toast.makeText(context, (saved ? R.string.toast_story_saved : R.string.toast_story_unsaved), Toast.LENGTH_SHORT).show();
|
||||
story.starred = saved;
|
||||
Uri storyUri = FeedProvider.STORY_URI.buildUpon().appendPath(story.id).build();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_STARRED, saved);
|
||||
context.getContentResolver().update(storyUri, values, null, null);
|
||||
} else {
|
||||
Toast.makeText(context, result.getErrorMessage(context.getString(saved ? R.string.toast_story_save_error : R.string.toast_story_unsave_error)), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}.execute();
|
||||
} else {
|
||||
Log.w(FeedUtils.class.getName(), "Couldn't save story, no selection found.");
|
||||
}
|
||||
|
||||
if (receiver != null) {
|
||||
receiver.actionCompleteCallback();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
public static void saveStory(final Story story, final Context context, final APIManager apiManager, ActionCompletionListener receiver) {
|
||||
setStorySaved(story, true, context, apiManager, receiver);
|
||||
}
|
||||
|
||||
public static void unsaveStory(final Story story, final Context context, final APIManager apiManager, ActionCompletionListener receiver) {
|
||||
setStorySaved(story, false, context, apiManager, receiver);
|
||||
}
|
||||
|
||||
public static void deleteFeed( final long feedId, final String folderName, final Context context, final APIManager apiManager) {
|
||||
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
|
|
@ -138,6 +157,11 @@ public class FeedUtils {
|
|||
}.execute();
|
||||
}
|
||||
|
||||
// update the local object to show as read even before requeried
|
||||
for (Story story : stories) {
|
||||
story.read = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void appendStoryReadOperations(Story story, List<ContentProviderOperation> operations) {
|
||||
|
|
@ -234,14 +258,39 @@ public class FeedUtils {
|
|||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public static int getCursorUnreadCount(Cursor cursor, int currentState) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
count += cursor.getInt(cursor.getColumnIndexOrThrow(DatabaseConstants.SUM_POS));
|
||||
if ((currentState == AppConstants.STATE_ALL) || (currentState == AppConstants.STATE_SOME)) {
|
||||
count += cursor.getInt(cursor.getColumnIndexOrThrow(DatabaseConstants.SUM_NEUT));
|
||||
}
|
||||
if (currentState == AppConstants.STATE_ALL ) {
|
||||
count += cursor.getInt(cursor.getColumnIndexOrThrow(DatabaseConstants.SUM_NEG));
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public static void shareStory(Story story, Context context) {
|
||||
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, story.title);
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, Html.fromHtml(story.title));
|
||||
final String shareString = context.getResources().getString(R.string.share);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.format(shareString, new Object[] { story.title, story.permalink }));
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.format(shareString, new Object[] { Html.fromHtml(story.title),
|
||||
story.permalink }));
|
||||
context.startActivity(Intent.createChooser(intent, "Share using"));
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface usable by callers of this utility class that allows them to receive
|
||||
* notification that the async methods here have finihed and may have updated the DB
|
||||
* as a result.
|
||||
*/
|
||||
public interface ActionCompletionListener {
|
||||
public abstract void actionCompleteCallback();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,6 @@ public class PrefConstants {
|
|||
|
||||
public static final String DEFAULT_STORY_ORDER = "default_story_order";
|
||||
public static final String DEFAULT_READ_FILTER = "default_read_filter";
|
||||
|
||||
public static final String SHOW_PUBLIC_COMMENTS = "show_public_comments";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,4 +234,9 @@ public class PrefsUtils {
|
|||
private static ReadFilter getDefaultReadFilter(SharedPreferences prefs) {
|
||||
return ReadFilter.valueOf(prefs.getString(PrefConstants.DEFAULT_READ_FILTER, ReadFilter.ALL.toString()));
|
||||
}
|
||||
|
||||
public static boolean showPublicComments(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
|
||||
return prefs.getBoolean(PrefConstants.SHOW_PUBLIC_COMMENTS, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,13 +78,16 @@ public class UIUtils {
|
|||
return (int) (dps * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static float px2dp(Context context, int px) {
|
||||
return ((float) px) / context.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha of a view in a manner that is safe to use before API version 11.
|
||||
* If alpha isn't supported, just make the view invisible if the alpha is so low
|
||||
* that it may as well be.
|
||||
*/
|
||||
public static void setViewAlpha(View v, float alpha) {
|
||||
v.setVisibility((alpha > 0.001) ? View.VISIBLE : View.INVISIBLE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
v.setAlpha(alpha);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package com.newsblur.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.newsblur.R;
|
||||
|
||||
/**
|
||||
* A determinate, circular progress indicator.
|
||||
*
|
||||
* NB: You *must* set the style attribute of the indicator to "@android:style/Widget.ProgressBar.Horizontal",
|
||||
* even though this is circular. The parent class disables determinate behaviour otherwise.
|
||||
*/
|
||||
public class ProgressCircle extends ProgressBar {
|
||||
|
||||
/** The thickness of the circular ring, in DP. */
|
||||
public static final int STROKE_THICKNESS = 5;
|
||||
|
||||
public ProgressCircle(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ProgressCircle(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.setIndeterminate(false);
|
||||
}
|
||||
|
||||
protected void onDraw(Canvas canvas) {
|
||||
|
||||
// the outline of the view w.r.t the screen
|
||||
Rect r = new Rect();
|
||||
this.getDrawingRect(r);
|
||||
|
||||
// a bitmap on which we will render so that clearing can be done
|
||||
Bitmap bm = Bitmap.createBitmap(r.width(), r.height(), Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bm);
|
||||
|
||||
// the outline of the view w.r.t the bitmap
|
||||
Rect cr = new Rect();
|
||||
cr.top = 0;
|
||||
cr.left = 0;
|
||||
cr.bottom = r.width();
|
||||
cr.right = r.height();
|
||||
|
||||
float angle = (360f * this.getProgress()) / this.getMax();
|
||||
|
||||
Paint p = new Paint();
|
||||
p.setStyle( Paint.Style.FILL );
|
||||
p.setAntiAlias(true);
|
||||
// draw the "remaining" part of the arc as a background
|
||||
p.setColor( getResources().getColor(R.color.progress_circle_remaining) );
|
||||
c.drawArc(new RectF(cr), -90f, 360f, true, p);
|
||||
// draw the "completed" part of the arc over that
|
||||
p.setColor( getResources().getColor(R.color.progress_circle_complete) );
|
||||
c.drawArc(new RectF(cr), -90f, angle, true, p);
|
||||
// clear the centre to form a ring
|
||||
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
p.setAlpha(0xFF);
|
||||
RectF innerR = new RectF(cr);
|
||||
innerR.top += STROKE_THICKNESS;
|
||||
innerR.left += STROKE_THICKNESS;
|
||||
innerR.bottom -= STROKE_THICKNESS;
|
||||
innerR.right -= STROKE_THICKNESS;
|
||||
c.drawArc(innerR, -90f, 360f, true, p);
|
||||
|
||||
// apply the bitmap onto this view
|
||||
canvas.drawBitmap(bm, r.left, r.top, null);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ gunicorn==0.17.2
|
|||
httplib2==0.8
|
||||
iconv==1.0
|
||||
kombu==2.5.7
|
||||
lxml==3.1.0
|
||||
# lxml==3.1.0
|
||||
mongoengine==0.8.2
|
||||
nltk==2.0.4
|
||||
oauth2==1.5.211
|
||||
|
|
|
|||
|
|
@ -3809,7 +3809,7 @@ background: transparent;
|
|||
#story_taskbar .NB-tryfeed-add,
|
||||
#story_taskbar .NB-tryfeed-follow,
|
||||
#story_taskbar .NB-tryout-signup {
|
||||
margin: 6px auto 0px;
|
||||
margin: 2px auto 0px;
|
||||
width: 80px;
|
||||
height: 14px;
|
||||
text-align: center;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 301 KiB |
|
|
@ -35,6 +35,7 @@ NEWSBLUR.Modal.prototype = {
|
|||
});
|
||||
setTimeout(function() {
|
||||
// $(window).resize();
|
||||
self.resize();
|
||||
self.flags.modal_loaded = true;
|
||||
});
|
||||
});
|
||||
|
|
@ -60,6 +61,7 @@ NEWSBLUR.Modal.prototype = {
|
|||
|
||||
resize: function() {
|
||||
// $(window).trigger('resize.simplemodal');
|
||||
$.modal.resize();
|
||||
},
|
||||
|
||||
close: function(callback) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ NEWSBLUR.ReaderAccount.prototype.constructor = NEWSBLUR.ReaderAccount;
|
|||
_.extend(NEWSBLUR.ReaderAccount.prototype, {
|
||||
|
||||
runner: function() {
|
||||
this.options.onOpen = _.bind(function() {
|
||||
// $(window).resize();
|
||||
}, this);
|
||||
this.make_modal();
|
||||
this.open_modal();
|
||||
|
||||
|
|
|
|||
33
media/js/vendor/jquery.simplemodal-1.3.js
vendored
|
|
@ -82,6 +82,9 @@
|
|||
return $.modal.impl.init(data, options);
|
||||
};
|
||||
|
||||
$.modal.resize = function (callback) {
|
||||
$.modal.impl.resize(callback);
|
||||
};
|
||||
/*
|
||||
* Close the modal dialog.
|
||||
*/
|
||||
|
|
@ -391,22 +394,24 @@
|
|||
});
|
||||
|
||||
// update window size
|
||||
$(window).bind('resize.simplemodal', function () {
|
||||
// redetermine the window width/height
|
||||
w = s.getDimensions();
|
||||
$(window).bind('resize.simplemodal', _.bind(this.resize_modal, this));
|
||||
},
|
||||
|
||||
resize_modal: function () {
|
||||
// redetermine the window width/height
|
||||
w = this.getDimensions();
|
||||
|
||||
// reposition the dialog
|
||||
s.o.autoResize ? s.setContainerDimensions() : s.o.autoPosition && s.setPosition();
|
||||
// reposition the dialog
|
||||
this.o.autoResize ? this.setContainerDimensions() : this.o.autoPosition && this.setPosition();
|
||||
|
||||
if (ie6 || ieQuirks) {
|
||||
s.fixIE();
|
||||
}
|
||||
else if (s.o.modal) {
|
||||
// update the iframe & overlay
|
||||
s.d.iframe && s.d.iframe.css({height: w[0], width: w[1]});
|
||||
s.d.overlay.css({height: w[0], width: w[1]});
|
||||
}
|
||||
});
|
||||
if (ie6 || ieQuirks) {
|
||||
this.fixIE();
|
||||
}
|
||||
else if (this.o.modal) {
|
||||
// update the iframe & overlay
|
||||
this.d.iframe && this.d.iframe.css({height: w[0], width: w[1]});
|
||||
this.d.overlay.css({height: w[0], width: w[1]});
|
||||
}
|
||||
},
|
||||
/*
|
||||
* Unbind events
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ from vendor import reseekfile
|
|||
# COMMENTS_RE = re.compile('\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>')
|
||||
COMMENTS_RE = re.compile('\<!--.*?--\>')
|
||||
|
||||
def midnight_today():
|
||||
return datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
def midnight_today(now=None):
|
||||
if not now:
|
||||
now = datetime.datetime.now()
|
||||
return now.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
|
||||
|
||||
def midnight_yesterday(midnight=None):
|
||||
if not midnight:
|
||||
|
|
@ -28,9 +30,11 @@ def midnight_yesterday(midnight=None):
|
|||
def beginning_of_this_month():
|
||||
return datetime.datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
def format_story_link_date__short(date):
|
||||
def format_story_link_date__short(date, now=None):
|
||||
if not now:
|
||||
now = datetime.datetime.now()
|
||||
date = date.replace(tzinfo=None)
|
||||
midnight = midnight_today()
|
||||
midnight = midnight_today(now)
|
||||
if date > midnight:
|
||||
return date.strftime('%I:%M%p').lstrip('0').lower()
|
||||
elif date > midnight_yesterday(midnight):
|
||||
|
|
|
|||