Merge branch 'master' into pro

* master: (28 commits)
  Bumping up node images size.
  Android v11.3.0.
  #1600 Mark story as read button width
  Cleaning up preferences on android
  Merge master into sictiru
  #1600 Mark story read timer WIP
  #1600 Mark story read immediately or manually.
  #1634 Update infrequent logo
  #1606 Adjust story title length based on story content preview style.
  #1628 Handle feed missing metadata.
  Merge master into sictiru
  #1618 Handle multi window mode lifecycle.
  #1624 Offer context before potentially logging out.
  Android v11.2.2.
  #1598 Add immutable flag for PendingIntents
  Android v11.2.1.
  Fix reading pager fragment transaction.
  Open billing connection for the subscription sync service.
  Android v11.2
  Finishing up PostgreSQL migration. Needs to test backups.
  ...
This commit is contained in:
Samuel Clay 2022-03-09 17:32:07 -05:00
commit d231499672
32 changed files with 364 additions and 165 deletions

View file

@ -15,6 +15,7 @@ groups:
node: inventory_hostname.startswith('node')
node_socket: inventory_hostname.startswith('node-socket')
node_images: inventory_hostname.startswith('node-images')
# debugs: inventory_hostname.startswith('debug')

View file

@ -27,7 +27,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
dependencies {
implementation 'androidx.fragment:fragment-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
@ -40,7 +40,7 @@ dependencies {
implementation "androidx.browser:browser:1.4.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation 'androidx.lifecycle:lifecycle-process:2.4.0'
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
implementation 'androidx.core:core-splashscreen:1.0.0-beta01'
}
android {
@ -49,8 +49,8 @@ android {
applicationId "com.newsblur"
minSdkVersion 21
targetSdkVersion 31
versionCode 199
versionName "11.2"
versionCode 202
versionName "11.3.0"
}
compileOptions.with {
sourceCompatibility = JavaVersion.VERSION_1_8

View file

@ -211,7 +211,7 @@
android:layout_marginTop="12dp"
android:scrollbars="none" />
<include layout="@layout/include_reading_item_comment" />
<include layout="@layout/reading_item_actions" />
</LinearLayout>

View file

@ -2,26 +2,39 @@
<merge xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="wrap_content"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:baselineAligned="false" >
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/mark_read_story_button"
style="?storyButtons"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_alignStart="@+id/train_story_button"
android:layout_alignEnd="@+id/save_story_button"
android:text="@string/story_mark_read_state"
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/train_story_button"
style="?storyButtons"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_below="@+id/mark_read_story_button"
android:text="@string/train_this"
app:icon="@drawable/ic_train"/>
app:icon="@drawable/ic_train" />
<com.google.android.material.button.MaterialButton
android:id="@+id/share_story_button"
style="?storyButtons"
android:layout_marginHorizontal="12dp"
android:layout_toEndOf="@+id/train_story_button"
android:layout_below="@+id/mark_read_story_button"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:text="@string/share_this"
@ -30,12 +43,14 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/save_story_button"
style="?storyButtons"
android:layout_toEndOf="@+id/share_story_button"
android:layout_below="@+id/mark_read_story_button"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:text="@string/save_this"
app:icon="@drawable/ic_clock" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"

View file

@ -109,7 +109,7 @@
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingRight="4dp"
android:maxLines="2"
android:maxLines="3"
android:ellipsize="end" />
<TextView

View file

@ -95,7 +95,7 @@
android:layout_marginLeft="26dp"
android:layout_marginRight="2dp"
android:layout_marginBottom="2dp"
android:maxLines="2"
android:maxLines="3"
android:ellipsize="end"
/>

View file

@ -18,6 +18,7 @@
<attr name="chip" format="string" />
<attr name="actionButtons" format="string" />
<attr name="storyButtons" format="string" />
<attr name="storyButtonsDimmed" format="string" />
<attr name="shareBarBackground" format="string" />
<attr name="shareBarText" format="string" />
<attr name="commentsHeader" format="string" />

View file

@ -5,26 +5,26 @@
<string name="login_username_hint">username</string>
<string name="login_password_hint">password</string>
<string name="login_registration_email_hint">email address</string>
<string name="login_button_login">Log In</string>
<string name="login_forgot_password"><u>I forgot my password</u></string>
<string name="login_custom_server"><u>I have a custom server</u></string>
<string name="login_button_login">Login</string>
<string name="login_forgot_password"><u>Forgot password</u></string>
<string name="login_custom_server"><u>Self-hosted custom server</u></string>
<string name="login_custom_server_hint">https://www.newsblur.com</string>
<string name="login_message_error">There was problem logging in to NewsBlur.</string>
<string name="login_logging_in">Logging in…</string>
<string name="login_logged_in">Logged in!</string>
<string name="login_logged_in">Reticulating splines…</string>
<string name="login_retrieving_feeds">Retrieving feeds…</string>
<string name="login_next">Next</string>
<string name="login_custom_server_scheme_error">Expected \'https://\' custom server URL</string>
<string name="login_custom_server_scheme_error">Expected \'https://\' in custom server URL</string>
<string name="search_for_feed">Search for feeds</string>
<string name="loading">Loading…</string>
<string name="orig_text_loading">Fetching story text…</string>
<string name="follow_error">There was a problem following the user. Check your internet connection and try again.</string>
<string name="unfollow_error">There was a problem unfollowing the user. Check your internet connection and try again.</string>
<string name="follow_error">There was a problem following the user.</string>
<string name="unfollow_error">There was a problem unfollowing the user.</string>
<string name="description_login_button">Press to log in.</string>
<string name="description_login_button">Press to log in</string>
<string name="description_login_logo">NewsBlur Logo</string>
<string name="description_profile_picture">The user\'s profile picture</string>
<string name="description_row_folder_icon">folder icon</string>
@ -36,9 +36,9 @@
<string name="description_empty_list_image">Empty list placeholder</string>
<string name="description_menu">Menu</string>
<string name="title_choose_folders">Choose Folders for Feed %s</string>
<string name="title_rename_feed">Rename Feed %s</string>
<string name="title_rename_folder">Rename Folder %s</string>
<string name="title_choose_folders">Choose folders for feed %s</string>
<string name="title_rename_feed">Rename feed %s</string>
<string name="title_rename_folder">Rename folder %s</string>
<string name="all_stories_title">All Stories</string>
<string name="infrequent_title">Infrequent Site Stories</string>
@ -50,7 +50,7 @@
<string name="top_level">TOP LEVEL</string>
<string name="send_full">%1$s \n\%2$s \n\n%3$s \n\n—\nShared with NewsBlur.com</string>
<string name="send_full">%1$s \n\%2$s \n\n%3$s \n\n—\nShared with NewsBlur</string>
<string name="share_comment_hint">Comment (Optional)</string>
<string name="share_newsblur">Share \"%s\" to your Blurblog?</string>
<string name="share_this">Share</string>
@ -64,6 +64,8 @@
<string name="save_this">Save</string>
<string name="unsave_this">Unsave</string>
<string name="train_this">Train</string>
<string name="story_mark_unread_state">Read</string>
<string name="story_mark_read_state">Mark story read</string>
<string name="overlay_next">Next</string>
<string name="overlay_done">Done</string>
@ -223,8 +225,8 @@
<string name="menu_feedback">Send app feedback</string>
<string name="menu_feedback_post">Create a feedback post</string>
<string name="menu_feedback_email">Email a bug report</string>
<string name="menu_theme_choose">Theme</string>
<string name="menu_premium_account">Premium Account</string>
<string name="menu_theme_choose">Theme</string>
<string name="menu_premium_account">Premium subscription…</string>
<string name="description_add_new_folder_icon">Add new folder icon</string>
@ -237,7 +239,7 @@
<string name="empty_list_view_no_stories_unread">No unread stories</string>
<string name="empty_list_view_no_unread_stories">You have no unread stories.</string>
<string name="empty_list_view_no_focus_stories">You have no unread stories in Focus mode.\n\nSwitch to All or Unread.</string>
<string name="empty_list_view_no_focus_stories">You have no unread stories in Focus.\n\nSwitch to All or Unread.</string>
<string name="empty_list_view_no_saved_stories">You have no saved stories.\n\nSwitch to All or Unread.</string>
<string name="login_registration_custom_server">Custom server</string>
@ -267,9 +269,9 @@
<string name="feed_opens">%d opens</string>
<string name="feed_stories_per_month">%d stories/month</string>
<string name="settings">Preferences</string>
<string name="mute_sites">Mute Sites</string>
<string name="widget">Widget</string>
<string name="settings">Preferences</string>
<string name="mute_sites">Mute Sites</string>
<string name="widget">Widget</string>
<string name="title_widget_setup">Tap to setup in NewsBlur</string>
<string name="title_no_subscriptions">No active subscriptions detected</string>
<string name="title_widget_loading">Loading…</string>
@ -297,14 +299,14 @@
<string name="premium_text_view">Text view conveniently extracts the story</string>
<string name="premium_shiloh">You feed Shiloh, my poor, hungry dog, for a month</string>
<string name="settings_cat_offline">Offline</string>
<string name="settings_enable_offline">Download Stories</string>
<string name="settings_cat_offline">Offline reading</string>
<string name="settings_enable_offline">Download stories</string>
<string name="settings_enable_offline_sum">Periodically fetch stories in background</string>
<string name="settings_enable_image_prefetch">Download Images</string>
<string name="settings_enable_image_prefetch">Download images</string>
<string name="settings_enable_image_prefetch_sum">Pre-fetch images for offline reading</string>
<string name="settings_enable_text_prefetch">Download Text</string>
<string name="settings_enable_text_prefetch_sum">Pre-fetch text for offline reading</string>
<string name="settings_keep_old_stories">Keep Stories after Reading</string>
<string name="settings_enable_text_prefetch">Download full text</string>
<string name="settings_enable_text_prefetch_sum">Pre-fetch full text for offline reading</string>
<string name="settings_keep_old_stories">Keep stories after reading</string>
<string name="settings_keep_old_stories_sum">Disable to reduce storage usage</string>
<string name="mute_config_title">You can follow up to 64 sites with a free standard account</string>
@ -313,7 +315,7 @@
<string name="mute_config_upgrade">UPGRADE</string>
<string name="mute_config_sites">%1$s/%2$s</string>
<string name="menu_network_select">Download Using</string>
<string name="menu_network_select">Download using…</string>
<string name="menu_network_select_sum">Restrict background data to chosen networks</string>
<string name="menu_network_select_opt_any">Any Network</string>
<string name="menu_network_select_opt_nomo">Any Wifi or Ethernet</string>
@ -330,13 +332,13 @@
</string-array>
<string name="default_network_select_value">NOMONONME</string>
<string name="menu_delete_offline_stories">Delete offline stories </string>
<string name="menu_delete_offline_stories">Delete offline stories…</string>
<string name="menu_delete_offline_stories_key">delete_offline_stories</string>
<string name="menu_delete_offline_stories_sum">Clear all offline stories and images</string>
<string name="menu_delete_offline_stories_sum">Free up space by clearing stored stories and images</string>
<string name="menu_delete_offline_stories_confirmation">Cleared all stories and images!</string>
<string name="menu_cache_age_select">Maximum Cache Age</string>
<string name="menu_cache_age_select_sum">Clean up cached story content after…</string>
<string name="menu_cache_age_select">How long to keep stories…</string>
<string name="menu_cache_age_select_sum">Clear stored stories for offline reading</string>
<string name="menu_cache_age_select_opt_2d">2 days (reduce storage use)</string>
<string name="menu_cache_age_select_opt_7d">7 days</string>
<string name="menu_cache_age_select_opt_14d">14 days</string>
@ -355,9 +357,9 @@
</string-array>
<string name="default_cache_age_select_value">CACHE_AGE_30D</string>
<string name="settings_cat_feed_list">Feed List</string>
<string name="settings_cat_feed_list">Feed list</string>
<string name="setting_feed_list_order">Feed list order</string>
<string name="setting_feed_list_order">Feed list order</string>
<string name="settings_enable_row_global_shared">Show Global Shared Stories</string>
<string name="settings_enable_row_global_shared_sum">Show the Global Shared Stories folder</string>
<string name="settings_enable_row_infrequent_stories">Show Infrequent Stories</string>
@ -366,8 +368,8 @@
<string name="settings_cat_story_list">Story List</string>
<string name="oldest">Oldest</string>
<string name="newest">Newest First</string>
<string name="menu_story_order">Story Order</string>
<string name="newest">Newest first</string>
<string name="menu_story_order">Story order…</string>
<string-array name="default_story_order_entries">
<item>@string/newest</item>
<item>@string/oldest</item>
@ -390,12 +392,12 @@
</string-array>
<string name="default_feed_list_order_value">ALPHABETICAL</string>
<string name="all_stories">All Stories</string>
<string name="unread_only">Unread Only</string>
<string name="menu_story_content_preview">Content Preview</string>
<string name="menu_story_thumbnail_preview">Thumbnail Preview</string>
<string name="menu_mark_read_on_scroll">Mark Read On Scroll</string>
<string name="menu_read_filter">Read Filter</string>
<string name="all_stories">All stories</string>
<string name="unread_only">Unread only</string>
<string name="menu_story_content_preview">Story content preview</string>
<string name="menu_story_thumbnail_preview">Thumbnail preview</string>
<string name="menu_mark_read_on_scroll">Mark read on scroll</string>
<string name="menu_read_filter">Read story filter…</string>
<string-array name="default_read_filter_entries">
<item>@string/all_stories</item>
<item>@string/unread_only</item>
@ -419,19 +421,52 @@
<item>Cancel</item>
</string-array>
<string name="settings_auto_open_first_unread">Auto-Open First Story</string>
<string name="settings_auto_open_first_unread_sum">Automatically open first unread story from story list</string>
<string name="settings_auto_open_first_unread">Auto-open first story</string>
<string name="settings_auto_open_first_unread_sum">Automatically open first unread story in the story list</string>
<string name="settings_mark_read_on_scroll">Mark Stories Read On Scroll</string>
<string name="settings_mark_read_on_scroll_sum">Automatically mark stories as read when scrolled past</string>
<string name="settings_mark_read_on_feed_scroll">Mark story titles read on scroll</string>
<string name="settings_mark_read_on_feed_scroll_sum">Automatically mark stories as read when scrolled past</string>
<string name="settings_social">Social</string>
<string name="settings_show_public_comments">Show Public Comments</string>
<string name="settings_mark_story_read">Mark a story as read…</string>
<string name="mark_story_read_immediately">Immediately</string>
<!-- <string name="mark_story_read_5_seconds">After 5 seconds</string>-->
<!-- <string name="mark_story_read_10_seconds">After 10 seconds</string>-->
<!-- <string name="mark_story_read_20_seconds">After 20 seconds</string>-->
<!-- <string name="mark_story_read_30_seconds">After 30 seconds</string>-->
<!-- <string name="mark_story_read_45_seconds">After 45 seconds</string>-->
<!-- <string name="mark_story_read_60_seconds">After 60 seconds</string>-->
<string name="mark_story_read_manually">Manually</string>
<string-array name="mark_story_read_entries">
<item>@string/mark_story_read_immediately</item>
<!-- <item>@string/mark_story_read_5_seconds</item>-->
<!-- <item>@string/mark_story_read_10_seconds</item>-->
<!-- <item>@string/mark_story_read_20_seconds</item>-->
<!-- <item>@string/mark_story_read_30_seconds</item>-->
<!-- <item>@string/mark_story_read_45_seconds</item>-->
<!-- <item>@string/mark_story_read_60_seconds</item>-->
<item>@string/mark_story_read_manually</item>
</string-array>
<string-array name="mark_story_read_values">
<item>IMMEDIATELY</item>
<!-- <item>SECONDS_5</item>-->
<!-- <item>SECONDS_10</item>-->
<!-- <item>SECONDS_20</item>-->
<!-- <item>SECONDS_30</item>-->
<!-- <item>SECONDS_45</item>-->
<!-- <item>SECONDS_60</item>-->
<item>MANUALLY</item>
</string-array>
<string name="mark_story_read_default_value">IMMEDIATELY</string>
<string name="settings_social">Sharing stories</string>
<string name="settings_show_public_comments">Show public comments</string>
<string name="settings_reading">Reading</string>
<string name="settings_content_preview">Content Preview Text</string>
<string name="settings_thumbnails_style">Image Preview Thumbnails</string>
<string name="settings_content_preview">Content preview text…</string>
<string name="settings_thumbnails_style">Image preview thumbnails…</string>
<string name="settings_notifications">Notifications</string>
<string name="settings_enable_notifications">Enable Notifications</string>
<string name="settings_enable_notifications">Enable notifications</string>
<string-array name="show_content_preview_entries">
<item>@string/story_content_preview_none</item>
@ -509,10 +544,10 @@
<string name="sync_status_offline">Offline</string>
<string name="sync_status_feed_add">Adding feed …</string>
<string name="volume_key_navigation">Volume Key Navigation</string>
<string name="volume_key_navigation">Volume key navigation…</string>
<string name="off">Off</string>
<string name="volume_up_next">Up = Next Story</string>
<string name="volume_down_next">Down = Next Story</string>
<string name="volume_up_next">Up = next story</string>
<string name="volume_down_next">Down = next story</string>
<string-array name="volume_key_navigation_entries">
<item>@string/off</item>
<item>@string/volume_up_next</item>
@ -525,10 +560,10 @@
</string-array>
<string name="default_volume_key_navigation_value">OFF</string>
<string name="settings_confirm_mark_all_read">Confirm Mark All Read</string>
<string name="none">None</string>
<string name="feed_and_folder">Feeds and Folders</string>
<string name="folder_only">Folders Only</string>
<string name="settings_confirm_mark_all_read">Confirm mark all read on…</string>
<string name="none">Neither</string>
<string name="feed_and_folder">Feeds and folders</string>
<string name="folder_only">Folders only</string>
<string-array name="confirm_mark_all_read_entries">
<item>@string/feed_and_folder</item>
<item>@string/folder_only</item>
@ -541,15 +576,15 @@
</string-array>
<string name="confirm_mark_all_read_value">FOLDER_ONLY</string>
<string name="settings_confirm_mark_range_read">Confirm Mark Older/Newer Read</string>
<string name="settings_confirm_mark_range_read">Confirm mark older/newer read</string>
<string name="settings_ltr_gesture_action">Rightward Swipe on Story Title</string>
<string name="gest_action_none">No Action</string>
<string name="gest_action_markread">Mark Story Read</string>
<string name="gest_action_markunread">Mark Story Unread</string>
<string name="gest_action_save">Save Story</string>
<string name="gest_action_unsave">Unsave Story</string>
<string name="gest_action_statistics">Statistics</string>
<string name="settings_ltr_gesture_action">Rightward swipe on story title…</string>
<string name="gest_action_none">No action</string>
<string name="gest_action_markread">Mark story as read</string>
<string name="gest_action_markunread">Mark story as unread</string>
<string name="gest_action_save">Save story</string>
<string name="gest_action_unsave">Unsave story</string>
<string name="gest_action_statistics">Site statistics</string>
<string-array name="ltr_gesture_action_entries">
<item>@string/gest_action_none</item>
<item>@string/gest_action_markread</item>
@ -568,7 +603,7 @@
</string-array>
<string name="ltr_gesture_action_value">GEST_ACTION_MARKREAD</string>
<string name="settings_rtl_gesture_action">Leftward Swipe on Story Title</string>
<string name="settings_rtl_gesture_action">Leftward swipe on story title…</string>
<string-array name="rtl_gesture_action_entries">
<item>@string/gest_action_none</item>
<item>@string/gest_action_markread</item>
@ -587,8 +622,8 @@
</string-array>
<string name="rtl_gesture_action_value">GEST_ACTION_MARKUNREAD</string>
<string name="default_browser">Default browser</string>
<string name="font">Font</string>
<string name="default_browser">Default browser</string>
<string name="font">Font</string>
<string name="default_font">Default</string>
<string name="gotham_narrow_font">Gotham Narrow</string>
<string name="chronicle_font">Chronicle</string>
@ -649,7 +684,7 @@
<string name="default_default_browser">@string/system_default_prefvalue</string>
<string name="story_notification_channel_id">story_notification_channel</string>
<string name="story_notification_channel_name">New Stories</string>
<string name="story_notification_channel_name">New stories</string>
<string name="go_to_feed">Go to feed</string>
<string name="js_get_selection">(function(){return window.getSelection().toString()})()</string>

View file

@ -184,8 +184,6 @@
<style name="storyButtons" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="android:textSize">14sp</item>
<item name="android:layout_marginLeft">8dp</item>
<item name="android:layout_marginRight">8dp</item>
<item name="android:textColor">@color/gray75</item>
<item name="android:textAllCaps">false</item>
<item name="android:letterSpacing">0</item>
@ -195,8 +193,6 @@
</style>
<style name="storyButtons.dark" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="android:textSize">14sp</item>
<item name="android:layout_marginLeft">8dp</item>
<item name="android:layout_marginRight">8dp</item>
<item name="android:textColor">@color/gray46</item>
<item name="android:textAllCaps">false</item>
<item name="android:letterSpacing">0</item>
@ -205,6 +201,13 @@
<item name="iconSize">16dp</item>
</style>
<style name="storyButtonsDimmed" parent="storyButtons">
<item name="android:textColor">@color/gray85</item>
</style>
<style name="storyButtonsDimmed.dark" parent="storyButtons.dark">
<item name="android:textColor">@color/gray30</item>
</style>
<style name="shareBarBackground">
<item name="android:background">@color/share_bar_background</item>
</style>

View file

@ -28,6 +28,7 @@
<item name="chip">@style/chip</item>
<item name="actionButtons">@style/actionButtons</item>
<item name="storyButtons">@style/storyButtons</item>
<item name="storyButtonsDimmed">@style/storyButtonsDimmed</item>
<item name="shareBarBackground">@style/shareBarBackground</item>
<item name="shareBarText">@style/shareBarText</item>
<item name="commentsHeader">@style/commentsHeader</item>
@ -81,6 +82,7 @@
<item name="chip">@style/chip.dark</item>
<item name="actionButtons">@style/actionButtons.dark</item>
<item name="storyButtons">@style/storyButtons.dark</item>
<item name="storyButtonsDimmed">@style/storyButtonsDimmed.dark</item>
<item name="shareBarBackground">@style/shareBarBackground.dark</item>
<item name="shareBarText">@style/shareBarText.dark</item>
<item name="commentsHeader">@style/commentsHeader.dark</item>
@ -135,6 +137,7 @@
<item name="chip">@style/chip.dark</item>
<item name="actionButtons">@style/actionButtons.dark</item>
<item name="storyButtons">@style/storyButtons.dark</item>
<item name="storyButtonsDimmed">@style/storyButtonsDimmed.dark</item>
<item name="shareBarBackground">@style/shareBarBackground.black</item>
<item name="shareBarText">@style/shareBarText.dark</item>
<item name="commentsHeader">@style/commentsHeader.black</item>

View file

@ -57,15 +57,11 @@
<CheckBoxPreference
android:defaultValue="true"
android:key="enable_row_global_shared"
android:title="@string/settings_enable_row_global_shared"
android:summary="@string/settings_enable_row_global_shared_sum"
/>
android:title="@string/settings_enable_row_global_shared" />
<CheckBoxPreference
android:defaultValue="true"
android:key="enable_row_infrequent_stories"
android:title="@string/settings_enable_row_infrequent_stories"
android:summary="@string/settings_enable_row_infrequent_stories_sum"
/>
android:title="@string/settings_enable_row_infrequent_stories" />
</PreferenceCategory>
<PreferenceCategory
@ -100,6 +96,11 @@
android:key="pref_auto_open_first_unread"
android:title="@string/settings_auto_open_first_unread"
android:summary="@string/settings_auto_open_first_unread_sum" />
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_mark_read_on_scroll"
android:title="@string/settings_mark_read_on_feed_scroll"
android:summary="@string/settings_mark_read_on_feed_scroll_sum" />
<ListPreference
android:key="pref_show_content_preview_style"
android:title="@string/settings_content_preview"
@ -114,11 +115,13 @@
android:entries="@array/thumbnail_style_entries"
android:entryValues="@array/thumbnail_style_values"
android:defaultValue="@string/thumbnail_style_default_value" />
<CheckBoxPreference
android:defaultValue="false"
android:key="pref_mark_read_on_scroll"
android:title="@string/settings_mark_read_on_scroll"
android:summary="@string/settings_mark_read_on_scroll_sum" />
<ListPreference
android:key="pref_story_mark_read_behavior"
android:title="@string/settings_mark_story_read"
android:dialogTitle="@string/settings_mark_story_read"
android:entries="@array/mark_story_read_entries"
android:entryValues="@array/mark_story_read_values"
android:defaultValue="@string/mark_story_read_default_value" />
</PreferenceCategory>
<PreferenceCategory

View file

@ -196,21 +196,32 @@ public class FeedChooserAdapter extends BaseExpandableListAdapter {
private Comparator<Feed> getListComparator() {
return (o1, o2) -> {
// some feeds have missing data
if (o1.title == null) o1.title = "";
if (o2.title == null) o2.title = "";
if (feedOrderFilter == FeedOrderFilter.NAME && listOrderFilter == ListOrderFilter.ASCENDING) {
return o1.title.compareTo(o2.title);
} else if (feedOrderFilter == FeedOrderFilter.NAME && listOrderFilter == ListOrderFilter.DESCENDING) {
return o2.title.compareTo(o1.title);
} else if (feedOrderFilter == FeedOrderFilter.SUBSCRIBERS && listOrderFilter == ListOrderFilter.ASCENDING) {
} else if (o1.subscribers != null && o2.subscribers != null &&
feedOrderFilter == FeedOrderFilter.SUBSCRIBERS &&
listOrderFilter == ListOrderFilter.ASCENDING) {
return Integer.valueOf(o1.subscribers).compareTo(Integer.valueOf(o2.subscribers));
} else if (feedOrderFilter == FeedOrderFilter.SUBSCRIBERS && listOrderFilter == ListOrderFilter.DESCENDING) {
} else if (o1.subscribers != null && o2.subscribers != null &&
feedOrderFilter == FeedOrderFilter.SUBSCRIBERS &&
listOrderFilter == ListOrderFilter.DESCENDING) {
return Integer.valueOf(o2.subscribers).compareTo(Integer.valueOf(o1.subscribers));
} else if (feedOrderFilter == FeedOrderFilter.OPENS && listOrderFilter == ListOrderFilter.ASCENDING) {
return Integer.compare(o1.feedOpens, o2.feedOpens);
} else if (feedOrderFilter == FeedOrderFilter.OPENS && listOrderFilter == ListOrderFilter.DESCENDING) {
return Integer.compare(o2.feedOpens, o1.feedOpens);
} else if (feedOrderFilter == FeedOrderFilter.RECENT_STORY && listOrderFilter == ListOrderFilter.ASCENDING) {
} else if (o1.lastStoryDate != null && o2.lastStoryDate != null &&
feedOrderFilter == FeedOrderFilter.RECENT_STORY &&
listOrderFilter == ListOrderFilter.ASCENDING) {
return compareLastStoryDateTimes(o1.lastStoryDate, o2.lastStoryDate, listOrderFilter);
} else if (feedOrderFilter == FeedOrderFilter.RECENT_STORY && listOrderFilter == ListOrderFilter.DESCENDING) {
} else if (o1.lastStoryDate != null && o2.lastStoryDate != null &&
feedOrderFilter == FeedOrderFilter.RECENT_STORY &&
listOrderFilter == ListOrderFilter.DESCENDING) {
return compareLastStoryDateTimes(o1.lastStoryDate, o2.lastStoryDate, listOrderFilter);
} else if (feedOrderFilter == FeedOrderFilter.STORIES_MONTH && listOrderFilter == ListOrderFilter.ASCENDING) {
return Integer.compare(o1.storiesPerMonth, o2.storiesPerMonth);

View file

@ -16,7 +16,7 @@ public class InfrequentItemsList extends ItemsList implements InfrequentCutoffCh
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
UIUtils.setupToolbar(this, R.drawable.ak_icon_allstories, getResources().getString(R.string.infrequent_title), false);
UIUtils.setupToolbar(this, R.drawable.ak_icon_infrequent, getResources().getString(R.string.infrequent_title), false);
}
@Override

View file

@ -11,7 +11,7 @@ public class InfrequentReading extends Reading {
protected void onCreate(Bundle savedInstanceBundle) {
super.onCreate(savedInstanceBundle);
UIUtils.setupToolbar(this, R.drawable.ak_icon_allstories, getResources().getString(R.string.infrequent_title), false);
UIUtils.setupToolbar(this, R.drawable.ak_icon_infrequent, getResources().getString(R.string.infrequent_title), false);
}
}

View file

@ -18,7 +18,7 @@ class InitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().also {
it.setKeepVisibleCondition {
it.setKeepOnScreenCondition() {
// keep showing the splash screen until FeedUtils.offerInitContext(...)
// finishes and UI ready to display
FeedUtils.dbHelper != null || FeedUtils.thumbnailLoader != null

View file

@ -266,7 +266,7 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
menu.findItem(R.id.menu_story_thumbnail_no_preview).setChecked(true);
}
boolean isMarkReadOnScroll = PrefsUtils.isMarkReadOnScroll(this);
boolean isMarkReadOnScroll = PrefsUtils.isMarkReadOnFeedScroll(this);
if (isMarkReadOnScroll) {
menu.findItem(R.id.menu_mark_read_on_scroll_enabled).setChecked(true);
} else {

View file

@ -36,6 +36,9 @@ open class NbActivity : AppCompatActivity() {
PrefsUtils.applyThemePreference(this)
lastTheme = PrefsUtils.getSelectedTheme(this)
super.onCreate(bundle)
offerInitContext(this)
// in rare cases of process interruption or DB corruption, an activity can launch without valid
// login creds. redirect the user back to the loging workflow.
if (PrefsUtils.getUserId(this) == null) {
@ -44,9 +47,6 @@ open class NbActivity : AppCompatActivity() {
finish()
}
super.onCreate(bundle)
offerInitContext(this)
bundle?.let {
uniqueLoginKey = it.getString(UNIQUE_LOGIN_KEY)
}

View file

@ -33,7 +33,6 @@ import com.newsblur.util.PrefConstants.ThemeValue
import com.newsblur.view.ReadingScrollView.ScrollChangeListener
import com.newsblur.viewModel.StoriesViewModel
import java.lang.Runnable
import java.util.*
import kotlin.math.abs
abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeListener,
@ -52,6 +51,8 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
private var readingAdapter: ReadingAdapter? = null
private var stopLoading = false
private var unreadSearchActive = false
// marking a story as read immediately on reading page scroll
private var isMarkStoryReadImmediately = false
// unread count for the circular progress overlay. set to nonzero to activate the progress indicator overlay
private var startingUnreadCount = 0
@ -59,6 +60,14 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
private var overlayRangeBotPx = 0f
private var lastVScrollPos = 0
// enabling multi window mode from recent apps on the device
// creates a different activity lifecycle compared to a device rotation
// resulting in onPause being called when the app is actually on the screen.
// calling onPause sets stopLoading as true and content wouldn't be loaded.
// track the multi window mode config change and skip stopLoading in first onPause call.
// refactor stopLoading mechanism as a cancellation signal tied to the view lifecycle.
private var isMultiWindowModeHack = false
private val pageHistory = mutableListOf<Story>()
private lateinit var volumeKeyNavigation: VolumeKeyNavigation
@ -107,6 +116,7 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
intelState = PrefsUtils.getStateFilter(this)
volumeKeyNavigation = PrefsUtils.getVolumeKeyNavigation(this)
isMarkStoryReadImmediately = PrefsUtils.isMarkStoryReadImmediately(this)
setupViews()
setupListeners()
@ -143,8 +153,17 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
}
override fun onPause() {
stopLoading = true
super.onPause()
if (isMultiWindowModeHack) {
isMultiWindowModeHack = false
} else {
stopLoading = true
}
}
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration?) {
super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
isMultiWindowModeHack = isInMultiWindowMode
}
private fun setupViews() {
@ -164,12 +183,10 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
enableProgressCircle(binding.readingOverlayProgressLeft, false)
enableProgressCircle(binding.readingOverlayProgressRight, false)
supportFragmentManager.commit {
val fragment =
supportFragmentManager.findFragmentByTag(ReadingPagerFragment::class.java.name) as ReadingPagerFragment?
?: ReadingPagerFragment.newInstance()
add(R.id.activity_reading_container, fragment, ReadingPagerFragment::class.java.name)
}
supportFragmentManager.findFragmentByTag(ReadingPagerFragment::class.java.name)
?: supportFragmentManager.commit {
add(R.id.activity_reading_container, ReadingPagerFragment.newInstance(), ReadingPagerFragment::class.java.name)
}
}
private fun setupListeners() {
@ -371,13 +388,15 @@ abstract class Reading : NbActivity(), OnPageChangeListener, OnSeekBarChangeList
readingAdapter?.let { readingAdapter ->
val story = readingAdapter.getStory(position)
if (story != null) {
FeedUtils.markStoryAsRead(story, this@Reading)
synchronized(pageHistory) {
// if the history is just starting out or the last entry in it isn't this page, add this page
if (pageHistory.size < 1 || story != pageHistory[pageHistory.size - 1]) {
pageHistory.add(story)
}
}
if (isMarkStoryReadImmediately) {
FeedUtils.markStoryAsRead(story, this@Reading)
}
}
checkStoryCount(position)
updateOverlayText()

View file

@ -570,13 +570,6 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
vh.intelDot.setImageResource(android.R.color.transparent);
}
// M text size equals to 1.0
if (textSize > 1.0) {
vh.storyTitleView.setMaxLines(3);
} else {
vh.storyTitleView.setMaxLines(2);
}
vh.storyTitleView.setText(UIUtils.fromHtml(story.title));
vh.storyDate.setText(StoryUtils.formatShortDate(context, story.timestamp));
@ -659,6 +652,7 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private void bindRow(StoryRowViewHolder vh, int position, Story story) {
StoryContentPreviewStyle storyContentPreviewStyle = PrefsUtils.getStoryContentPreviewStyle(context);
if (storyContentPreviewStyle != StoryContentPreviewStyle.NONE) {
vh.storyTitleView.setMaxLines(3);
if (storyContentPreviewStyle == StoryContentPreviewStyle.LARGE) {
vh.storySnippet.setMaxLines(6);
} else if (storyContentPreviewStyle == StoryContentPreviewStyle.MEDIUM) {
@ -669,6 +663,7 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
vh.storySnippet.setVisibility(View.VISIBLE);
vh.storySnippet.setText(story.shortContent);
} else {
vh.storyTitleView.setMaxLines(6);
vh.storySnippet.setVisibility(View.GONE);
}

View file

@ -441,7 +441,7 @@ public class ItemSetFragment extends NbFragment {
indexOfLastUnread = -1;
}
if (PrefsUtils.isMarkReadOnScroll(getActivity())) {
if (PrefsUtils.isMarkReadOnFeedScroll(requireContext())) {
// we want the top row of stories that is partially obscured. go back one from the first fully visible
int markEnd = layoutManager.findFirstCompletelyVisibleItemPosition() - 1;
if (markEnd > lastAutoMarkIndex) {
@ -451,7 +451,7 @@ public class ItemSetFragment extends NbFragment {
int index = markEnd - i;
Story story = adapter.getStory(index);
if (story != null) {
FeedUtils.markStoryAsRead(story, getActivity());
FeedUtils.markStoryAsRead(story, requireContext());
}
}
}

View file

@ -5,6 +5,7 @@ import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
@ -16,12 +17,13 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.newsblur.R
import com.newsblur.activity.FeedItemsList
import com.newsblur.activity.Reading
import com.newsblur.databinding.FragmentReadingitemBinding
import com.newsblur.databinding.IncludeReadingItemCommentBinding
import com.newsblur.databinding.ReadingItemActionsBinding
import com.newsblur.domain.Classifier
import com.newsblur.domain.Story
import com.newsblur.domain.UserDetails
@ -33,7 +35,6 @@ import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_TEXT
import com.newsblur.service.OriginalTextService
import com.newsblur.util.*
import com.newsblur.util.PrefConstants.ThemeValue
import java.util.*
import java.util.regex.Pattern
import kotlin.math.roundToInt
@ -81,7 +82,9 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
private val webViewContentMutex = Any()
private lateinit var binding: FragmentReadingitemBinding
private lateinit var itemCommentBinding: IncludeReadingItemCommentBinding
private lateinit var readingItemActionsBinding: ReadingItemActionsBinding
private lateinit var markStoryReadBehavior: MarkStoryReadBehavior
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -97,7 +100,8 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
classifier = requireArguments().getSerializable("classifier") as Classifier?
sourceUserId = requireArguments().getString("sourceUserId")
user = PrefsUtils.getUserDetails(requireActivity())
user = PrefsUtils.getUserDetails(requireContext())
markStoryReadBehavior = PrefsUtils.getMarkStoryReadBehavior(requireContext())
textSizeReceiver = TextSizeReceiver()
requireActivity().registerReceiver(textSizeReceiver, IntentFilter(TEXT_SIZE_CHANGED))
@ -142,7 +146,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_readingitem, container, false)
binding = FragmentReadingitemBinding.bind(view)
itemCommentBinding = IncludeReadingItemCommentBinding.bind(binding.root)
readingItemActionsBinding = ReadingItemActionsBinding.bind(binding.root)
val readingActivity = requireActivity() as Reading
fs = readingActivity.fs
@ -160,6 +164,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
updateTrainButton()
updateShareButton()
updateSaveButton()
updateMarkReadButton()
setupItemCommentsAndShares()
binding.readingScrollview.registerScrollChangeListener(readingActivity)
@ -170,9 +175,10 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.storyContextMenuButton.setOnClickListener { onClickMenuButton() }
itemCommentBinding.trainStoryButton.setOnClickListener { clickTrain() }
itemCommentBinding.saveStoryButton.setOnClickListener { clickSave() }
itemCommentBinding.shareStoryButton.setOnClickListener { clickShare() }
readingItemActionsBinding.markReadStoryButton.setOnClickListener { clickMarkStoryRead() }
readingItemActionsBinding.trainStoryButton.setOnClickListener { clickTrain() }
readingItemActionsBinding.saveStoryButton.setOnClickListener { clickSave() }
readingItemActionsBinding.shareStoryButton.setOnClickListener { clickShare() }
}
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenuInfo?) {
@ -330,13 +336,27 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
}
}
private fun clickMarkStoryRead() {
if (story!!.read) FeedUtils.markStoryUnread(story!!, requireContext())
else FeedUtils.markStoryAsRead(story!!, requireContext())
}
private fun updateMarkReadButton() {
if (markStoryReadBehavior == MarkStoryReadBehavior.MANUALLY) {
readingItemActionsBinding.markReadStoryButton.visibility = View.VISIBLE
readingItemActionsBinding.markReadStoryButton.setStoryReadState(requireContext(), story!!.read)
} else {
readingItemActionsBinding.markReadStoryButton.visibility = View.GONE
}
}
private fun clickTrain() {
val intelFrag = StoryIntelTrainerFragment.newInstance(story, fs)
intelFrag.show(requireActivity().supportFragmentManager, StoryIntelTrainerFragment::class.java.name)
}
private fun updateTrainButton() {
itemCommentBinding.trainStoryButton.visibility = if (story!!.feedId == "0") View.GONE else View.VISIBLE
readingItemActionsBinding.trainStoryButton.visibility = if (story!!.feedId == "0") View.GONE else View.VISIBLE
}
private fun clickSave() {
@ -348,7 +368,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
}
private fun updateSaveButton() {
itemCommentBinding.saveStoryButton.setText(if (story!!.starred) R.string.unsave_this else R.string.save_this)
readingItemActionsBinding.saveStoryButton.setText(if (story!!.starred) R.string.unsave_this else R.string.save_this)
}
private fun clickShare() {
@ -359,11 +379,11 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
private fun updateShareButton() {
for (userId in story!!.sharedUserIds) {
if (TextUtils.equals(userId, user!!.id)) {
itemCommentBinding.shareStoryButton.setText(R.string.already_shared)
readingItemActionsBinding.shareStoryButton.setText(R.string.already_shared)
return
}
}
itemCommentBinding.shareStoryButton.setText(R.string.share_this)
readingItemActionsBinding.shareStoryButton.setText(R.string.share_this)
}
private fun setupItemCommentsAndShares() {
@ -603,6 +623,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
if (updateType and UPDATE_STORY != 0) {
updateSaveButton()
updateShareButton()
updateMarkReadButton()
setupItemCommentsAndShares()
}
if (updateType and UPDATE_TEXT != 0) {
@ -908,4 +929,27 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
private val altSniff4 = Pattern.compile("<img[^>]*title=(['\"])((?:(?!\\1).)*)\\1[^>]*src=(['\"])((?:(?!\\3).)*)\\3[^>]*>", Pattern.CASE_INSENSITIVE)
private val imgSniff = Pattern.compile("<img[^>]*(src\\s*=\\s*)\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE)
}
}
private fun MaterialButton.setStoryReadState(context: Context, isRead: Boolean) {
var selectedTheme = PrefsUtils.getSelectedTheme(context)
if (selectedTheme == ThemeValue.AUTO) {
selectedTheme = when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> ThemeValue.DARK
else -> ThemeValue.LIGHT
}
}
val styleResId: Int = when (selectedTheme) {
ThemeValue.LIGHT -> if (isRead) R.style.storyButtonsDimmed else R.style.storyButtons
else -> if (isRead) R.style.storyButtonsDimmed_dark else R.style.storyButtons_dark
}
val stringResId: Int = if (isRead) R.string.story_mark_unread_state else R.string.story_mark_read_state
this.text = context.getString(stringResId)
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.setTextAppearance(styleResId)
} else {
this.setTextAppearance(context, styleResId)
}
}

View file

@ -7,11 +7,11 @@ import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import com.newsblur.subscription.SubscriptionManagerImpl
import com.newsblur.subscription.SubscriptionsListener
import com.newsblur.util.AppConstants
import com.newsblur.util.Log
import com.newsblur.util.NBScope
import com.newsblur.util.PrefsUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
@ -24,6 +24,8 @@ import kotlinx.coroutines.launch
*/
class SubscriptionSyncService : JobService() {
private val scope = NBScope
override fun onStartJob(params: JobParameters?): Boolean {
Log.d(this, "onStartJob")
if (!PrefsUtils.hasCookie(this)) {
@ -31,15 +33,22 @@ class SubscriptionSyncService : JobService() {
return false
}
NBScope.launch(Dispatchers.Default) {
val subscriptionManager = SubscriptionManagerImpl(this@SubscriptionSyncService, this)
val job = subscriptionManager.syncActiveSubscription()
job.invokeOnCompletion {
Log.d(this, "sync active subscription completed.")
// manually trigger jobFinished after work is done
val subscriptionManager = SubscriptionManagerImpl(this@SubscriptionSyncService, scope)
subscriptionManager.startBillingConnection(object : SubscriptionsListener {
override fun onBillingConnectionReady() {
scope.launch {
subscriptionManager.syncActiveSubscription()
Log.d(this, "sync active subscription completed.")
// manually call jobFinished after work is done
jobFinished(params, false)
}
}
override fun onBillingConnectionError(message: String?) {
// manually call jobFinished on error
jobFinished(params, false)
}
}
})
return true // returning true due to background thread work
}

View file

@ -58,7 +58,7 @@ interface SubscriptionManager {
/**
* Sync subscription state between NewsBlur and Play Store.
*/
fun syncActiveSubscription(): Job
suspend fun syncActiveSubscription(): Job
/**
* Notify backend of active Play Store subscription.
@ -70,13 +70,13 @@ interface SubscriptionManager {
interface SubscriptionsListener {
fun onActiveSubscription(renewalMessage: String?)
fun onActiveSubscription(renewalMessage: String?) {}
fun onAvailableSubscription(skuDetails: SkuDetails)
fun onAvailableSubscription(skuDetails: SkuDetails) {}
fun onBillingConnectionReady()
fun onBillingConnectionReady() {}
fun onBillingConnectionError(message: String? = null)
fun onBillingConnectionError(message: String? = null) {}
}
class SubscriptionManagerImpl(
@ -90,7 +90,9 @@ class SubscriptionManagerImpl(
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
Log.d(this, "acknowledgePurchaseResponseListener OK")
syncActiveSubscription()
scope.launch(Dispatchers.Default) {
syncActiveSubscription()
}
}
BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
// Billing API version is not supported for the type requested.
@ -174,7 +176,7 @@ class SubscriptionManagerImpl(
billingClient.launchBillingFlow(activity, billingFlowParams)
}
override fun syncActiveSubscription() = scope.launch(Dispatchers.Default) {
override suspend fun syncActiveSubscription() = scope.launch(Dispatchers.Default) {
val hasNewsBlurSubscription = PrefsUtils.getIsPremium(context)
val activePlayStoreSubscription = getActiveSubscriptionAsync().await()
@ -258,7 +260,9 @@ class SubscriptionManagerImpl(
private fun handlePurchase(purchase: Purchase) {
Log.d(this, "handlePurchase: ${purchase.orderId}")
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED && purchase.isAcknowledged) {
syncActiveSubscription()
scope.launch(Dispatchers.Default) {
syncActiveSubscription()
}
} else if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged) {
// need to acknowledge first time sub otherwise it will void
Log.d(this, "acknowledge purchase: ${purchase.orderId}")

View file

@ -0,0 +1,15 @@
package com.newsblur.util
/**
* Same values as R.string.mark_story_read_values
*/
enum class MarkStoryReadBehavior {
IMMEDIATELY,
SECONDS_5,
SECONDS_10,
SECONDS_20,
SECONDS_30,
SECONDS_45,
SECONDS_60,
MANUALLY,
}

View file

@ -114,19 +114,19 @@ public class NotificationUtils {
// UI on some devices.
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// set the requestCode to the story hashcode to prevent the PI re-using the wrong Intent
PendingIntent pendingIntent = PendingIntent.getActivity(context, story.hashCode(), i, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntentUtils.getImmutableActivity(context, story.hashCode(), i, PendingIntent.FLAG_UPDATE_CURRENT);
Intent dismissIntent = new Intent(context, NotifyDismissReceiver.class);
dismissIntent.putExtra(Reading.EXTRA_STORY_HASH, story.storyHash);
PendingIntent dismissPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), story.hashCode(), dismissIntent, 0);
PendingIntent dismissPendingIntent = PendingIntentUtils.getImmutableBroadcast(context.getApplicationContext(), story.hashCode(), dismissIntent, 0);
Intent saveIntent = new Intent(context, NotifySaveReceiver.class);
saveIntent.putExtra(Reading.EXTRA_STORY_HASH, story.storyHash);
PendingIntent savePendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), story.hashCode(), saveIntent, 0);
PendingIntent savePendingIntent = PendingIntentUtils.getImmutableBroadcast(context.getApplicationContext(), story.hashCode(), saveIntent, 0);
Intent markreadIntent = new Intent(context, NotifyMarkreadReceiver.class);
markreadIntent.putExtra(Reading.EXTRA_STORY_HASH, story.storyHash);
PendingIntent markreadPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), story.hashCode(), markreadIntent, 0);
PendingIntent markreadPendingIntent = PendingIntentUtils.getImmutableBroadcast(context.getApplicationContext(), story.hashCode(), markreadIntent, 0);
String feedTitle = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_TITLE));
StringBuilder title = new StringBuilder();

View file

@ -0,0 +1,29 @@
package com.newsblur.util
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
object PendingIntentUtils {
@JvmStatic
fun getImmutableActivity(
context: Context, requestCode: Int,
intent: Intent, flags: Int): PendingIntent? =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getActivity(context, requestCode, intent, flags or PendingIntent.FLAG_IMMUTABLE, null)
} else {
PendingIntent.getActivity(context, requestCode, intent, flags, null)
}
@JvmStatic
fun getImmutableBroadcast(
context: Context, requestCode: Int,
intent: Intent, flags: Int): PendingIntent? =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(context, requestCode, intent, flags or PendingIntent.FLAG_IMMUTABLE)
} else {
PendingIntent.getBroadcast(context, requestCode, intent, flags)
}
}

View file

@ -63,6 +63,7 @@ public class PrefConstants {
public static final String STORIES_MARK_READ_ON_SCROLL = "pref_mark_read_on_scroll";
public static final String STORIES_SHOW_PREVIEWS_STYLE = "pref_show_content_preview_style";
public static final String STORIES_THUMBNAIL_STYLE = "pref_thumbnail_style";
public static final String STORY_MARK_READ_BEHAVIOR = "pref_story_mark_read_behavior";
public static final String ENABLE_OFFLINE = "enable_offline";
public static final String ENABLE_IMAGE_PREFETCH = "enable_image_prefetch";

View file

@ -734,7 +734,7 @@ public class PrefsUtils {
return prefs.getBoolean(PrefConstants.STORIES_AUTO_OPEN_FIRST, false);
}
public static boolean isMarkReadOnScroll(Context context) {
public static boolean isMarkReadOnFeedScroll(Context context) {
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
return prefs.getBoolean(PrefConstants.STORIES_MARK_READ_ON_SCROLL, false);
}
@ -1040,4 +1040,13 @@ public class PrefsUtils {
SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, Context.MODE_PRIVATE);
return preferences.getString(PrefConstants.PREF_COOKIE, null) != null;
}
public static MarkStoryReadBehavior getMarkStoryReadBehavior(Context context) {
SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
return MarkStoryReadBehavior.valueOf(preferences.getString(PrefConstants.STORY_MARK_READ_BEHAVIOR, MarkStoryReadBehavior.IMMEDIATELY.name()));
}
public static boolean isMarkStoryReadImmediately(Context context) {
return getMarkStoryReadBehavior(context).equals(MarkStoryReadBehavior.IMMEDIATELY);
}
}

View file

@ -15,6 +15,7 @@ import com.newsblur.activity.AllStoriesItemsList;
import com.newsblur.activity.ItemsList;
import com.newsblur.activity.WidgetConfig;
import com.newsblur.util.FeedSet;
import com.newsblur.util.PendingIntentUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.WidgetBackground;
@ -88,8 +89,7 @@ public class WidgetProvider extends AppWidgetProvider {
Intent configIntent = new Intent(context, WidgetProvider.class);
configIntent.setAction(WidgetUtils.ACTION_OPEN_CONFIG);
PendingIntent configIntentTemplate = PendingIntent.getBroadcast(context, WidgetUtils.RC_WIDGET_CONFIG, configIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent configIntentTemplate = PendingIntentUtils.getImmutableBroadcast(context, WidgetUtils.RC_WIDGET_CONFIG, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.widget_empty_view, configIntentTemplate);
// This section makes it possible for items to have individualized behavior.
@ -104,7 +104,7 @@ public class WidgetProvider extends AppWidgetProvider {
touchIntent.setAction(WidgetUtils.ACTION_OPEN_STORY);
touchIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent touchIntentTemplate = PendingIntent.getBroadcast(context, WidgetUtils.RC_WIDGET_STORY, touchIntent,
PendingIntent touchIntentTemplate = PendingIntentUtils.getImmutableBroadcast(context, WidgetUtils.RC_WIDGET_STORY, touchIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
rv.setPendingIntentTemplate(R.id.widget_list, touchIntentTemplate);

View file

@ -8,6 +8,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import com.newsblur.util.PendingIntentUtils;
import com.newsblur.util.Log;
import com.newsblur.util.PrefsUtils;
@ -28,7 +29,7 @@ public class WidgetUtils {
Log.d(TAG, "enableWidgetUpdate");
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = getUpdateIntent(context);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, RC_WIDGET_UPDATE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntentUtils.getImmutableBroadcast(context, RC_WIDGET_UPDATE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
int widgetUpdateInterval = 1000 * 60 * 5;
long startAlarmAt = SystemClock.currentThreadTimeMillis() + widgetUpdateInterval;
@ -38,7 +39,7 @@ public class WidgetUtils {
public static void disableWidgetUpdate(Context context) {
Log.d(TAG, "disableWidgetUpdate");
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, RC_WIDGET_UPDATE, getUpdateIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntentUtils.getImmutableBroadcast(context, RC_WIDGET_UPDATE, getUpdateIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
}
@ -68,7 +69,7 @@ public class WidgetUtils {
}
public static void checkWidgetUpdateAlarm(Context context) {
boolean hasActiveUpdates = PendingIntent.getBroadcast(context, RC_WIDGET_UPDATE, getUpdateIntent(context), PendingIntent.FLAG_NO_CREATE) != null;
boolean hasActiveUpdates = PendingIntentUtils.getImmutableBroadcast(context, RC_WIDGET_UPDATE, getUpdateIntent(context), PendingIntent.FLAG_NO_CREATE) != null;
if (!hasActiveUpdates) {
enableWidgetUpdate(context);
}

View file

@ -26,6 +26,7 @@ DEBUG = True
# DEBUG_ASSETS controls JS/CSS asset packaging. Turning this off requires you to run
# `./manage.py collectstatic` first. Turn this on for development so you can see
# changes in your JS/CSS.
DEBUG_ASSETS = False # Make sure to run `./manage.py collectstatic` first
DEBUG_ASSETS = False # Make sure to run `./manage.py collectstatic` first
DEBUG_ASSETS = True

View file

@ -243,7 +243,7 @@ resource "digitalocean_droplet" "node-images" {
image = var.droplet_os
name = "node-images"
region = var.droplet_region
size = var.droplet_size
size = var.droplet_size_15
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
provisioner "local-exec" {
command = "/srv/newsblur/ansible/utils/generate_inventory.py; sleep 120"