Merge branch 'sictiru'

* sictiru:
  Android v12.1.0.
  Update dependencies
  #1731 Add option to add feed by url
  Notification settings (#14)
  #1626 Add share as a notification action
This commit is contained in:
Samuel Clay 2022-10-26 09:53:25 -04:00
commit 9f96972181
34 changed files with 665 additions and 107 deletions

View file

@ -75,6 +75,7 @@
android:label="@string/settings"/>
<activity android:name=".activity.ImportExportActivity" />
<activity android:name=".activity.NotificationsActivity" />
<activity
android:name=".activity.WidgetConfig"
android:launchMode="singleTask"
@ -167,6 +168,7 @@
<receiver android:name=".util.NotifyDismissReceiver" android:exported="false" />
<receiver android:name=".util.NotifySaveReceiver" android:exported="false" />
<receiver android:name=".util.NotifyMarkreadReceiver" android:exported="false" />
<receiver android:name=".util.NotifyShareReceiver" android:exported="false" />
<receiver android:name=".widget.WidgetProvider"
android:exported="false">
<intent-filter>

View file

@ -1,17 +1,16 @@
buildscript {
ext.kotlin_version = '1.6.21'
ext.kotlin_version = '1.7.10'
repositories {
mavenCentral()
maven {
url 'https://maven.google.com'
}
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.40.5'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.43.2'
}
}
@ -20,7 +19,6 @@ repositories {
maven {
url 'https://maven.google.com'
}
jcenter()
google()
}
@ -44,8 +42,8 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "com.google.dagger:hilt-android:2.40.5"
kapt "com.google.dagger:hilt-compiler:2.40.5"
implementation "com.google.dagger:hilt-android:2.43.2"
kapt "com.google.dagger:hilt-compiler:2.43.2"
}
android {
@ -54,14 +52,17 @@ android {
applicationId "com.newsblur"
minSdkVersion 21
targetSdkVersion 31
versionCode 205
versionName "12.0.1"
versionCode 206
versionName "12.1.0"
}
compileOptions.with {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
android.buildFeatures.viewBinding = true
buildFeatures {
viewBinding = true
}
sourceSets {
main {

View file

@ -1,6 +1,6 @@
#Wed Aug 11 15:59:29 EDT 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View file

@ -0,0 +1,4 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray55" android:state_checked="true" />
<item android:color="@color/gray30" android:state_checked="false" />
</selector>

View file

@ -0,0 +1,4 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray75" android:state_checked="true" />
<item android:color="@color/gray90" android:state_checked="false" />
</selector>

View file

@ -0,0 +1,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray96" android:state_checkable="true" android:state_checked="true" android:state_enabled="true" />
<item android:color="@color/gray65" android:state_checkable="true" android:state_checked="false" android:state_enabled="true" />
<item android:color="@color/gray65" android:state_enabled="true" />
<item android:color="@color/gray65" />
</selector>

View file

@ -0,0 +1,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray10" android:state_checkable="true" android:state_checked="true" android:state_enabled="true" />
<item android:color="@color/gray30" android:state_checkable="true" android:state_checked="false" android:state_enabled="true" />
<item android:color="@color/gray30" android:state_enabled="true" />
<item android:color="@color/gray30" />
</selector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/gray55" />
<corners android:radius="4dp" />
</shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/gray30" />
<corners android:radius="4dp" />
</shape>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<include layout="@layout/toolbar_newsblur" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_feeds"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_no_notifications"
style="?defaultText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:gravity="center"
android:text="@string/no_feed_notifications"
android:textSize="15sp"
android:visibility="gone" />
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/img_icon"
android:layout_width="19dp"
android:layout_height="19dp"
app:shapeAppearanceOverlay="@style/smallRoundImageShapeAppearance" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:ellipsize="end"
android:singleLine="true"
android:textSize="15sp"
android:textStyle="bold" />
</FrameLayout>
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/group_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:orientation="horizontal"
app:selectionRequired="true"
app:singleSelection="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_unread"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="@string/state_unread"
app:icon="@drawable/ic_indicator_unread"
app:iconGravity="textStart"
app:iconSize="19dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_focus"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_weight="1"
android:text="@string/state_focus"
app:icon="@drawable/ic_indicator_focus"
app:iconGravity="textStart"
app:iconSize="16dp"
app:iconTint="#FF6BBF7A" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/group_platform"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_email"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:checkable="true"
android:gravity="center"
android:text="@string/notification_email" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_web"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/notification_web" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_ios"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/notification_ios" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_android"
style="?toggleButton"
android:layout_width="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/notification_android" />
</com.google.android.material.button.MaterialButtonToggleGroup>
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -22,6 +22,10 @@
android:title="@string/import_export"
app:showAsAction="never" />
<item android:id="@+id/menu_notifications"
android:title="@string/menu_notifications"
app:showAsAction="never" />
<item android:id="@+id/menu_text_size"
android:title="@string/menu_text_size" >
<menu>

View file

@ -41,6 +41,7 @@
<attr name="muteicon" format="string" />
<attr name="circleProgressIndicator" format="string" />
<attr name="delimiter" format="string" />
<attr name="toggleButton" format="string" />
<attr name="flow" format="string" />
<attr name="imageViewSize" format="integer" />

View file

@ -281,12 +281,14 @@
<string name="menu_mute_sites">Mute Sites…</string>
<string name="mute_sites">Mute Sites</string>
<string name="menu_widget">Widget…</string>
<string name="menu_notifications">Notifications…</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>
<string name="import_export_title">Import/Export OPML</string>
<string name="notifications_title">Notifications</string>
<string name="premium_subscribers_folder">Reading by folder is only available to</string>
<string name="premium_subscribers_search">Search is only available to</string>
@ -711,4 +713,10 @@
<string name="add_feed">Add feed</string>
<string name="story_author">• %s</string>
<string name="notification_email">Email</string>
<string name="notification_web">Web</string>
<string name="notification_ios">iOS</string>
<string name="notification_android">Android</string>
<string name="no_feed_notifications">No feed notifications</string>
</resources>

View file

@ -21,11 +21,13 @@
<item name="colorControlNormal">@color/gray55</item>
<item name="fontFamily">@font/whitney</item>
</style>
<style name="actionbar.dark" parent="ThemeOverlay.MaterialComponents">
<item name="android:background">@color/dark_bar_background</item>
<item name="colorControlNormal">@color/gray55</item>
<item name="fontFamily">@font/whitney</item>
</style>
<style name="actionbar.black" parent="ThemeOverlay.MaterialComponents">
<item name="android:background">@color/black</item>
<item name="colorControlNormal">@color/gray55</item>
@ -35,6 +37,7 @@
<style name="delimiter">
<item name="android:background">@color/gray85</item>
</style>
<style name="delimiter.dark">
<item name="android:background">@color/gray30</item>
</style>
@ -43,12 +46,15 @@
<item name="android:paddingTop">2dp</item>
<item name="android:paddingBottom">2dp</item>
</style>
<style name="selectorFolderBackground" parent="selectorFolderParent">
<item name="android:background">@drawable/selector_folder_background</item>
</style>
<style name="selectorFolderBackground.dark" parent="selectorFolderParent">
<item name="android:background">@drawable/dark_selector_folder_background</item>
</style>
<style name="selectorFolderBackground.black" parent="selectorFolderParent">
<item name="android:background">@drawable/black_selector_folder_background</item>
</style>
@ -56,19 +62,25 @@
<style name="selectorFeedAlmostBlueBackground" parent="selectorFeedBackground">
<item name="android:background">@drawable/selector_feed_almost_blue_background</item>
</style>
<style name="selectorFeedAlmostRedBackground" parent="selectorFeedBackground">
<item name="android:background">@drawable/selector_feed_almost_red_background</item>
</style>
<style name="selectorFeedAlmostGreenBackground" parent="selectorFeedBackground">
<item name="android:background">@drawable/selector_feed_almost_green_background</item>
</style>
<style name="selectorFeedAlmostBlueBackground.dark" parent="selectorFeedBackground.dark" />
<style name="selectorFeedAlmostRedBackground.dark" parent="selectorFeedBackground.dark" />
<style name="selectorFeedAlmostGreenBackground.dark" parent="selectorFeedBackground.dark" />
<style name="selectorFeedBackground">
<item name="android:background">@drawable/selector_feed_background</item>
</style>
<style name="selectorFeedBackground.dark">
<item name="android:background">@drawable/dark_selector_feed_background</item>
</style>
@ -76,6 +88,7 @@
<style name="feedRowNeutCountText">
<item name="android:textColor">@color/white</item>
</style>
<style name="feedRowNeutCountText.dark">
<item name="android:textColor">@color/black</item>
</style>
@ -83,9 +96,11 @@
<style name="actionbarBackground">
<item name="android:background">@color/bar_background</item>
</style>
<style name="actionbarBackground.dark">
<item name="android:background">@color/dark_bar_background</item>
</style>
<style name="actionbarBackground.black">
<item name="android:background">@color/black</item>
</style>
@ -93,6 +108,7 @@
<style name="listBackground">
<item name="android:background">@drawable/list_background</item>
</style>
<style name="listBackground.dark">
<item name="android:background">@drawable/dark_list_background</item>
</style>
@ -100,9 +116,11 @@
<style name="layoutBackground">
<item name="android:background">@color/item_background</item>
</style>
<style name="layoutBackground.dark">
<item name="android:background">@color/dark_item_background</item>
</style>
<style name="layoutBackground.black">
<item name="android:background">@color/black</item>
</style>
@ -110,9 +128,11 @@
<style name="layoutRoundedBackground">
<item name="android:background">@drawable/shape_rounded_corners_6dp_light</item>
</style>
<style name="layoutRoundedBackground.dark">
<item name="android:background">@drawable/shape_rounded_corners_6dp_dark</item>
</style>
<style name="layoutRoundedBackground.black">
<item name="android:background">@drawable/shape_rounded_corners_6dp_black</item>
</style>
@ -120,9 +140,11 @@
<style name="readingBackground">
<item name="android:background">@color/white</item>
</style>
<style name="readingBackground.dark">
<item name="android:background">@color/dark_item_background</item>
</style>
<style name="readingBackground.black">
<item name="android:background">@color/black</item>
</style>
@ -131,6 +153,7 @@
<item name="android:textColorLink">@color/linkblue</item>
<item name="android:textColor">@color/text</item>
</style>
<style name="defaultText.dark">
<item name="android:textColorLink">@color/dark_linkblue</item>
<item name="android:textColor">@color/white</item>
@ -140,6 +163,7 @@
<item name="android:textColorLink">@color/linkblue</item>
<item name="android:textColor">@color/linkblue</item>
</style>
<style name="linkText.dark">
<item name="android:textColorLink">@color/dark_linkblue</item>
<item name="android:textColor">@color/dark_linkblue</item>
@ -148,6 +172,7 @@
<style name="storySnippetText">
<item name="android:textColor">@color/story_content_text</item>
</style>
<style name="storySnippetText.dark">
<item name="android:textColor">@color/dark_story_content_text</item>
</style>
@ -155,6 +180,7 @@
<style name="storyFeedTitleText">
<item name="android:textColor">@color/story_feed_title_text</item>
</style>
<style name="storyFeedTitleText.dark">
<item name="android:textColor">@color/dark_story_feed_title_text</item>
</style>
@ -162,9 +188,11 @@
<style name="selectorStoryBackground">
<item name="android:background">@drawable/selector_story_background</item>
</style>
<style name="selectorStoryBackground.dark">
<item name="android:background">@drawable/dark_selector_story_background</item>
</style>
<style name="selectorStoryBackground.black">
<item name="android:background">@drawable/black_selector_story_background</item>
</style>
@ -172,9 +200,11 @@
<style name="rowItemHeaderBackground">
<item name="android:background">@drawable/row_item_header_background</item>
</style>
<style name="rowItemHeaderBackground.dark">
<item name="android:background">@drawable/dark_row_item_header_background</item>
</style>
<style name="rowItemHeaderBackground.black">
<item name="android:background">@drawable/black_row_item_header_background</item>
</style>
@ -182,6 +212,7 @@
<style name="readingItemMetadata">
<item name="android:textColor">@color/half_darkgray</item>
</style>
<style name="readingItemMetadata.dark">
<item name="android:textColor">@color/half_white</item>
</style>
@ -191,6 +222,7 @@
<item name="chipBackgroundColor">@color/tag_gray</item>
<item name="android:fontFamily">@font/whitney</item>
</style>
<style name="chip.dark">
<item name="android:textColor">@color/tag_gray</item>
<item name="chipBackgroundColor">@color/tag_bg_dark</item>
@ -203,6 +235,7 @@
<item name="android:background">@color/gray90</item>
<item name="android:letterSpacing">0</item>
</style>
<style name="actionButtons.dark">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@color/button_text_dark</item>
@ -217,6 +250,7 @@
<item name="iconTint">@color/gray75</item>
<item name="iconSize">16dp</item>
</style>
<style name="storyButtons.dark" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@color/gray55</item>
@ -229,6 +263,7 @@
<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>
@ -236,9 +271,11 @@
<style name="shareBarBackground">
<item name="android:background">@color/share_bar_background</item>
</style>
<style name="shareBarBackground.dark">
<item name="android:background">@color/dark_share_bar_background</item>
</style>
<style name="shareBarBackground.black">
<item name="android:background">@color/black</item>
</style>
@ -250,6 +287,7 @@
<item name="android:shadowDy">1</item>
<item name="android:shadowRadius">1</item>
</style>
<style name="shareBarText.dark">
<item name="android:textColor">@color/gray55</item>
<item name="android:shadowDx">0</item>
@ -261,10 +299,12 @@
<item name="android:background">@drawable/gradient_background_default</item>
<item name="android:textColor">@color/text</item>
</style>
<style name="commentsHeader.dark">
<item name="android:background">@drawable/dark_gradient_background_default</item>
<item name="android:textColor">@color/gray55</item>
</style>
<style name="commentsHeader.black">
<item name="android:background">@drawable/black_gradient_background_default</item>
<item name="android:textColor">@color/gray55</item>
@ -274,10 +314,12 @@
<item name="android:background">@drawable/gradient_background_default</item>
<item name="android:textColor">@color/text</item>
</style>
<style name="activityDetailsPager.dark">
<item name="android:background">@drawable/dark_gradient_background_default</item>
<item name="android:textColor">@color/dark_text</item>
</style>
<style name="activityDetailsPager.black">
<item name="android:background">@drawable/black_gradient_background_default</item>
<item name="android:textColor">@color/dark_text</item>
@ -286,6 +328,7 @@
<style name="rowBorder">
<item name="android:background">@color/row_border</item>
</style>
<style name="rowBorder.dark">
<item name="android:background">@color/dark_row_border</item>
</style>
@ -294,10 +337,12 @@
<item name="android:background">@color/item_background</item>
<item name="android:textColor">@color/text</item>
</style>
<style name="profileCount.dark">
<item name="android:background">@color/dark_item_background</item>
<item name="android:textColor">@color/white</item>
</style>
<style name="profileCount.black">
<item name="android:background">@color/black</item>
<item name="android:textColor">@color/white</item>
@ -307,10 +352,12 @@
<item name="android:background">@color/item_background</item>
<item name="android:divider">@drawable/divider_light</item>
</style>
<style name="profileActivityList.dark">
<item name="android:background">@color/dark_item_background</item>
<item name="android:divider">@drawable/divider_dark</item>
</style>
<style name="profileActivityList.black">
<item name="android:background">@color/black</item>
<item name="android:divider">@drawable/divider_dark</item>
@ -319,9 +366,11 @@
<style name="storyCommentDivider">
<item name="android:background">@color/story_comment_divider</item>
</style>
<style name="storyCommentDivider.dark">
<item name="android:background">@color/dark_story_comment_divider</item>
</style>
<style name="storyCommentDivider.black">
<item name="android:background">@color/gray07</item>
</style>
@ -329,6 +378,7 @@
<style name="explainerText">
<item name="android:textColor">@color/gray55</item>
</style>
<style name="explainerText.dark">
<item name="android:textColor">@color/dark_text</item>
</style>
@ -338,15 +388,17 @@
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="toggleText.dark">
<item name="android:textColor">@color/gray55</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="selectorOverlayBackgroundLeft">
<item name="android:background">@drawable/selector_overlay_bg_left</item>
</style>
<style name="selectorOverlayBackgroundLeft.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_left</item>
</style>
@ -355,6 +407,7 @@
<item name="android:background">@drawable/selector_overlay_bg_right</item>
<item name="android:textColor">@color/button_text</item>
</style>
<style name="selectorOverlayBackgroundRight.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_right</item>
<item name="android:textColor">@color/button_text_dark</item>
@ -364,6 +417,7 @@
<item name="android:background">@drawable/selector_overlay_bg_right_done</item>
<item name="android:textColor">@color/button_text</item>
</style>
<style name="selectorOverlayBackgroundRightDone.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_right_done</item>
<item name="android:textColor">@color/button_text_dark</item>
@ -372,6 +426,7 @@
<style name="selectorOverlayBackgroundSend">
<item name="android:background">@drawable/selector_overlay_bg_send</item>
</style>
<style name="selectorOverlayBackgroundSend.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_send</item>
</style>
@ -380,6 +435,7 @@
<item name="android:background">@drawable/selector_overlay_bg_story</item>
<item name="android:textColor">@color/button_text</item>
</style>
<style name="selectorOverlayBackgroundStory.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_story</item>
<item name="android:textColor">@color/button_text_dark</item>
@ -389,6 +445,7 @@
<item name="android:background">@drawable/selector_overlay_bg_text</item>
<item name="android:textColor">@color/button_text</item>
</style>
<style name="selectorOverlayBackgroundText.dark">
<item name="android:background">@drawable/selector_overlay_bg_dark_text</item>
<item name="android:textColor">@color/button_text_dark</item>
@ -397,6 +454,7 @@
<style name="muteicon">
<item name="android:src">@drawable/mute_white</item>
</style>
<style name="muteicon.dark">
<item name="android:src">@drawable/mute_black</item>
</style>
@ -429,7 +487,7 @@
<style name="dialogPreference" parent="Preference.DialogPreference.Material">
<item name="iconSpaceReserved">false</item>
</style>
<style name="circleProgressIndicator" parent="Widget.MaterialComponents.CircularProgressIndicator">
<item name="indicatorColor">@color/newsblur_blue</item>
<item name="indicatorSize">24dp</item>
@ -448,4 +506,22 @@
<item name="cornerSize">10%</item>
</style>
<style name="toggleButton" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:letterSpacing">0</item>
<item name="android:textAllCaps">false</item>
<item name="backgroundTint">@color/mtrl_btn_bg_color_selector_light</item>
<item name="android:textColor">@color/mtrl_btn_text_color_selector_light</item>
<item name="android:textSize">14sp</item>
<item name="android:layout_height">40dp</item>
</style>
<style name="toggleButton.dark" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:letterSpacing">0</item>
<item name="android:textAllCaps">false</item>
<item name="backgroundTint">@color/mtrl_btn_bg_color_selector_dark</item>
<item name="android:textColor">@color/mtrl_btn_text_color_selector_dark</item>
<item name="android:textSize">14sp</item>
<item name="android:layout_height">40dp</item>
</style>
</resources>

View file

@ -54,6 +54,7 @@
<item name="preferenceTheme">@style/preferenceTheme</item>
<item name="fontFamily">@font/whitney</item>
<item name="circleProgressIndicator">@style/circleProgressIndicator</item>
<item name="toggleButton">@style/toggleButton</item>
</style>
<style name="NewsBlurDarkTheme" parent="Theme.MaterialComponents.NoActionBar">
@ -111,6 +112,7 @@
<item name="android:navigationBarColor">@android:color/black</item>
<item name="fontFamily">@font/whitney</item>
<item name="circleProgressIndicator">@style/circleProgressIndicator</item>
<item name="toggleButton">@style/toggleButton.dark</item>
</style>
<style name="NewsBlurBlackTheme" parent="Theme.MaterialComponents.NoActionBar" >
@ -168,6 +170,7 @@
<item name="android:navigationBarColor">@android:color/black</item>
<item name="fontFamily">@font/whitney</item>
<item name="circleProgressIndicator">@style/circleProgressIndicator</item>
<item name="toggleButton">@style/toggleButton.dark</item>
</style>
<style name="NewsBlurTheme.Translucent" parent="NewsBlurTheme">

View file

@ -16,6 +16,7 @@ import com.newsblur.domain.Feed;
import com.newsblur.fragment.DeleteFeedFragment;
import com.newsblur.fragment.FeedIntelTrainerFragment;
import com.newsblur.fragment.RenameDialogFragment;
import com.newsblur.util.FeedExt;
import com.newsblur.util.FeedSet;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.UIUtils;
@ -116,11 +117,11 @@ public class FeedItemsList extends ItemsList {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (feed.isNotifyUnread()) {
if (FeedExt.isAndroidNotifyUnread(feed)) {
menu.findItem(R.id.menu_notifications_disable).setChecked(false);
menu.findItem(R.id.menu_notifications_unread).setChecked(true);
menu.findItem(R.id.menu_notifications_focus).setChecked(false);
} else if (feed.isNotifyFocus()) {
} else if (FeedExt.isAndroidNotifyFocus(feed)) {
menu.findItem(R.id.menu_notifications_disable).setChecked(false);
menu.findItem(R.id.menu_notifications_unread).setChecked(false);
menu.findItem(R.id.menu_notifications_focus).setChecked(true);

View file

@ -20,7 +20,6 @@ import com.newsblur.util.executeAsyncTask
import dagger.hilt.android.AndroidEntryPoint
import java.net.MalformedURLException
import java.net.URL
import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
@ -86,13 +85,9 @@ class FeedSearchActivity : NbActivity(), OnFeedSearchResultClickListener, AddFee
override fun afterTextChanged(s: Editable) {
searchQueryRunnable?.let { handler.removeCallbacks(it) }
searchQueryRunnable = Runnable {
if (tryAddByURL(s.toString())) {
return@Runnable
}
syncClearIconVisibility(s)
if (s.isNotEmpty()) searchQuery(s)
else syncSearchResults(arrayOf())
else syncSearchResults(emptyList())
}
handler.postDelayed(searchQueryRunnable!!, 350)
}
@ -111,7 +106,12 @@ class FeedSearchActivity : NbActivity(), OnFeedSearchResultClickListener, AddFee
onPostExecute = {
binding.loadingCircle.visibility = View.GONE
binding.clearText.visibility = View.VISIBLE
syncSearchResults(it ?: arrayOf())
syncSearchResults(buildList {
if (matchesUrl(query.toString())) {
add(FeedResult.createFeedResultForUrl(query.toString().lowercase()))
}
addAll(it ?: arrayOf())
})
}
)
}
@ -120,15 +120,20 @@ class FeedSearchActivity : NbActivity(), OnFeedSearchResultClickListener, AddFee
binding.clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE
}
private fun syncSearchResults(results: Array<FeedResult>) {
private fun syncSearchResults(results: List<FeedResult>) {
adapter.replaceAll(results)
}
private fun showAddFeedDialog(feedUrl: String, feedLabel: String) {
val addFeedFragment: DialogFragment = AddFeedFragment.newInstance(feedUrl, feedLabel)
addFeedFragment.show(supportFragmentManager, "dialog")
}
/**
* See if the text entered in the query field was actually a URL so we can skip the
* search step and just let users who know feed URLs directly subscribe.
* See if the text entered in the query field was actually a URL
* to let users who know feed URLs directly subscribe.
*/
private fun tryAddByURL(s: String): Boolean {
private fun matchesUrl(s: String): Boolean {
var u: URL? = null
try {
u = URL(s)
@ -144,12 +149,6 @@ class FeedSearchActivity : NbActivity(), OnFeedSearchResultClickListener, AddFee
if (u.host == null || u.host.trim().isEmpty()) {
return false
}
showAddFeedDialog(s, s)
return true
}
private fun showAddFeedDialog(feedUrl: String, feedLabel: String) {
val addFeedFragment: DialogFragment = AddFeedFragment.newInstance(feedUrl, feedLabel)
addFeedFragment.show(supportFragmentManager, "dialog")
}
}

View file

@ -8,8 +8,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.newsblur.R
import com.newsblur.databinding.ViewFeedSearchRowBinding
import com.newsblur.domain.FeedResult
import com.newsblur.util.FeedUtils
import com.newsblur.util.ImageLoader
import com.newsblur.util.setViewGone
import com.newsblur.util.setViewVisible
class FeedSearchAdapter(
private val onClickListener: OnFeedSearchResultClickListener,
@ -30,12 +31,11 @@ class FeedSearchAdapter(
override fun getItemCount(): Int = resultsList.size
fun replaceAll(results: Array<FeedResult>) {
val newResultsList: List<FeedResult> = results.toList()
val diffCallback = ResultDiffCallback(resultsList, newResultsList)
fun replaceAll(results: List<FeedResult>) {
val diffCallback = ResultDiffCallback(resultsList, results)
val diffResult = DiffUtil.calculateDiff(diffCallback)
resultsList.clear()
resultsList.addAll(newResultsList)
resultsList.addAll(results)
diffResult.dispatchUpdatesTo(this)
}
@ -46,19 +46,25 @@ class FeedSearchAdapter(
fun bind(result: FeedResult) {
val resultFaviconUrl = result.faviconUrl
if (resultFaviconUrl.isNotEmpty()) {
iconLoader.displayImage(resultFaviconUrl, binding.imgFeedIcon)
iconLoader.displayImage(resultFaviconUrl, binding.imgFeedIcon)
}
binding.textTitle.text = result.label
binding.textTagline.text = result.tagline
val subscribersCountText = binding.root.context.getString(R.string.feed_subscribers, result.numberOfSubscriber)
binding.textSubscriptionCount.text = subscribersCountText
if (result.numberOfSubscriber > 0) {
val subscribersCountText = binding.root.context.getString(R.string.feed_subscribers, result.numberOfSubscriber)
binding.textSubscriptionCount.text = subscribersCountText
binding.textSubscriptionCount.setViewVisible()
} else {
binding.textSubscriptionCount.setViewGone()
}
if (result.url.isNotEmpty()) {
binding.rowResultAddress.text = result.url
binding.rowResultAddress.visibility = View.VISIBLE
binding.rowResultAddress.setViewVisible()
} else {
binding.rowResultAddress.visibility = View.GONE
binding.rowResultAddress.setViewGone()
}
itemView.setOnClickListener {

View file

@ -405,6 +405,10 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
Intent intent = new Intent(this, ImportExportActivity.class);
startActivity(intent);
return true;
} else if (item.getItemId() == R.id.menu_notifications) {
Intent intent = new Intent(this, NotificationsActivity.class);
startActivity(intent);
return true;
}
return false;
}

View file

@ -0,0 +1,62 @@
package com.newsblur.activity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.newsblur.R
import com.newsblur.databinding.ActivityNotificationsBinding
import com.newsblur.di.IconLoader
import com.newsblur.domain.Feed
import com.newsblur.util.ImageLoader
import com.newsblur.util.UIUtils
import com.newsblur.util.setViewGone
import com.newsblur.util.setViewVisible
import com.newsblur.viewModel.NotificationsViewModel
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class NotificationsActivity : NbActivity(), NotificationsAdapter.Listener {
@IconLoader
@Inject
lateinit var imageLoader: ImageLoader
private lateinit var binding: ActivityNotificationsBinding
private lateinit var viewModel: NotificationsViewModel
private lateinit var adapter: NotificationsAdapter
override fun onCreate(bundle: Bundle?) {
super.onCreate(bundle)
viewModel = ViewModelProvider(this)[NotificationsViewModel::class.java]
binding = ActivityNotificationsBinding.inflate(layoutInflater)
setContentView(binding.root)
setupUI()
setupListeners()
}
private fun setupUI() {
UIUtils.setupToolbar(this, R.drawable.logo, getString(R.string.notifications_title), true)
adapter = NotificationsAdapter(imageLoader, this).also {
binding.recyclerViewFeeds.adapter = it
}
}
private fun setupListeners() {
viewModel.feeds.observe(this) {
val feeds = it.values
if (feeds.isNotEmpty()) {
binding.recyclerViewFeeds.setViewVisible()
binding.txtNoNotifications.setViewGone()
} else {
binding.recyclerViewFeeds.setViewGone()
binding.txtNoNotifications.setViewVisible()
}
adapter.refreshFeeds(feeds)
}
}
override fun onFeedUpdated(feed: Feed) {
viewModel.updateFeed(this, feed)
}
}

View file

@ -0,0 +1,95 @@
package com.newsblur.activity
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.newsblur.databinding.ViewNotificationsItemBinding
import com.newsblur.domain.Feed
import com.newsblur.util.FeedExt
import com.newsblur.util.FeedExt.disableNotificationType
import com.newsblur.util.FeedExt.enableNotificationType
import com.newsblur.util.FeedExt.isNotifyAndroid
import com.newsblur.util.FeedExt.isNotifyEmail
import com.newsblur.util.FeedExt.isNotifyFocus
import com.newsblur.util.FeedExt.isNotifyIOS
import com.newsblur.util.FeedExt.isNotifyUnread
import com.newsblur.util.FeedExt.isNotifyWeb
import com.newsblur.util.FeedExt.setNotifyFocus
import com.newsblur.util.FeedExt.setNotifyUnread
import com.newsblur.util.ImageLoader
class NotificationsAdapter(
private val imageLoader: ImageLoader,
private val listener: Listener,
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>() {
private val feeds: MutableList<Feed> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ViewNotificationsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, imageLoader)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(feeds[position], listener)
override fun getItemCount(): Int = feeds.size
fun refreshFeeds(feeds: Collection<Feed>) {
this.feeds.clear()
this.feeds.addAll(feeds)
this.notifyItemRangeInserted(0, feeds.size)
}
class ViewHolder(val binding: ViewNotificationsItemBinding, val imageLoader: ImageLoader) : RecyclerView.ViewHolder(binding.root) {
fun bind(feed: Feed, listener: Listener) {
binding.textTitle.text = feed.title
imageLoader.displayImage(feed.faviconUrl, binding.imgIcon, binding.imgIcon.height, true)
with(binding.groupFilter) {
if (feed.isNotifyUnread()) check(binding.btnUnread.id)
else if (feed.isNotifyFocus()) check(binding.btnFocus.id)
}
with(binding.groupPlatform) {
if (feed.isNotifyEmail()) check(binding.btnEmail.id)
if (feed.isNotifyWeb()) check(binding.btnWeb.id)
if (feed.isNotifyIOS()) check(binding.btnIos.id)
if (feed.isNotifyAndroid()) check(binding.btnAndroid.id)
}
binding.groupFilter.addOnButtonCheckedListener { _, checkedId, isChecked ->
updateFilter(feed, checkedId, isChecked)
listener.onFeedUpdated(feed)
}
binding.groupPlatform.addOnButtonCheckedListener { _, checkedId, isChecked ->
updatePlatform(feed, checkedId, isChecked)
listener.onFeedUpdated(feed)
}
}
private fun updateFilter(feed: Feed, checkedBtnId: Int, isChecked: Boolean) {
when (checkedBtnId) {
binding.btnUnread.id -> if (isChecked) feed.setNotifyUnread()
binding.btnFocus.id -> if (isChecked) feed.setNotifyFocus()
}
}
private fun updatePlatform(feed: Feed, checkedBtnId: Int, isChecked: Boolean) {
when (checkedBtnId) {
binding.btnEmail.id -> FeedExt.NOTIFY_EMAIL
binding.btnWeb.id -> FeedExt.NOTIFY_WEB
binding.btnIos.id -> FeedExt.NOTIFY_IOS
binding.btnIos.id -> FeedExt.NOTIFY_ANDROID
else -> null
}?.let {
if (isChecked) feed.enableNotificationType(it)
else feed.disableNotificationType(it)
}
}
}
interface Listener {
fun onFeedUpdated(feed: Feed)
}
}

View file

@ -800,6 +800,7 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
companion object {
const val EXTRA_FEEDSET = "feed_set"
const val EXTRA_STORY_HASH = "story_hash"
const val EXTRA_STORY = "story"
private const val BUNDLE_STARTING_UNREAD = "starting_unread"
/** special value for starting story hash that jumps to the first unread. */

View file

@ -8,8 +8,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.CancellationSignal;
import androidx.annotation.Nullable;
import androidx.loader.content.AsyncTaskLoader;
import androidx.loader.content.Loader;
import android.text.TextUtils;
import android.util.Log;
@ -26,7 +25,6 @@ import com.newsblur.network.domain.CommentResponse;
import com.newsblur.network.domain.StoriesResponse;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.ReadingAction;
import com.newsblur.util.ReadFilter;

View file

@ -74,7 +74,6 @@ public class Feed implements Comparable<Feed>, Serializable {
@SerializedName("notification_types")
public List<String> notificationTypes;
// NB: only stored if notificationTypes was set to include android
@SerializedName("notification_filter")
public String notificationFilter;
@ -102,9 +101,7 @@ public class Feed implements Comparable<Feed>, Serializable {
values.put(DatabaseConstants.FEED_TITLE, title);
values.put(DatabaseConstants.FEED_UPDATED_SECONDS, lastUpdated);
values.put(DatabaseConstants.FEED_NOTIFICATION_TYPES, DatabaseConstants.flattenStringList(notificationTypes));
if (isNotifyAndroid()) {
values.put(DatabaseConstants.FEED_NOTIFICATION_FILTER, notificationFilter);
}
values.put(DatabaseConstants.FEED_NOTIFICATION_FILTER, notificationFilter);
values.put(DatabaseConstants.FEED_FETCH_PENDING, fetchPending);
return values;
}
@ -168,47 +165,6 @@ public class Feed implements Comparable<Feed>, Serializable {
return title.compareToIgnoreCase(f.title);
}
private boolean isNotifyAndroid() {
if (notificationTypes == null) return false;
for (String type : notificationTypes) {
if (type.equals(NOTIFY_TYPE_ANDROID)) return true;
}
return false;
}
public void enableAndroidNotifications(boolean enable) {
if (notificationTypes == null) notificationTypes = new ArrayList<String>();
if (enable && (!notificationTypes.contains(NOTIFY_TYPE_ANDROID))) {
notificationTypes.add(NOTIFY_TYPE_ANDROID);
}
if (!enable) {
notificationTypes.remove(NOTIFY_TYPE_ANDROID);
notificationFilter = null;
}
}
public boolean isNotifyUnread() {
if (!isNotifyAndroid()) return false;
return NOTIFY_FILTER_UNREAD.equals(notificationFilter);
}
public boolean isNotifyFocus() {
if (!isNotifyAndroid()) return false;
return NOTIFY_FILTER_FOCUS.equals(notificationFilter);
}
public void setNotifyUnread() {
this.notificationFilter = NOTIFY_FILTER_UNREAD;
}
public void setNotifyFocus() {
this.notificationFilter = NOTIFY_FILTER_FOCUS;
}
private static final String NOTIFY_TYPE_ANDROID = "android";
public static final String NOTIFY_FILTER_UNREAD = "unread";
public static final String NOTIFY_FILTER_FOCUS = "focus";
public static Comparator<Feed> getFeedListOrderComparator(FeedListOrder feedListOrder) {
return (o1, o2) -> {
if (feedListOrder == FeedListOrder.MOST_USED_AT_TOP) {
@ -218,4 +174,7 @@ public class Feed implements Comparable<Feed>, Serializable {
}
};
}
public static String NOTIFY_FILTER_UNREAD = "unread";
public static String NOTIFY_FILTER_FOCUS = "focus";
}

View file

@ -18,4 +18,13 @@ data class FeedResult(
val faviconUrl: String
get() = "${APIConstants.buildUrl(APIConstants.PATH_FEED_FAVICON_URL)}$id"
companion object {
fun createFeedResultForUrl(url: String) = FeedResult(
id = -1,
tagline = "Add feed manually by URL",
label = url,
url = url
)
}
}

View file

@ -52,6 +52,7 @@ import com.newsblur.domain.Folder;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.SocialFeed;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedExt;
import com.newsblur.util.SpacingStyle;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
@ -277,11 +278,11 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
} else {
menu.removeItem(R.id.menu_mute_feed);
}
if (feed.isNotifyUnread()) {
if (FeedExt.isAndroidNotifyUnread(feed)) {
menu.findItem(R.id.menu_notifications_disable).setChecked(false);
menu.findItem(R.id.menu_notifications_unread).setChecked(true);
menu.findItem(R.id.menu_notifications_focus).setChecked(false);
} else if (feed.isNotifyFocus()) {
} else if (FeedExt.isAndroidNotifyFocus(feed)) {
menu.findItem(R.id.menu_notifications_disable).setChecked(false);
menu.findItem(R.id.menu_notifications_unread).setChecked(false);
menu.findItem(R.id.menu_notifications_focus).setChecked(true);

View file

@ -570,6 +570,7 @@ public class APIManager {
return response.getResponse(gson, AddFeedResponse.class);
}
@Nullable
public FeedResult[] searchForFeed(String searchTerm) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FEED_SEARCH_TERM, searchTerm);

View file

@ -0,0 +1,54 @@
package com.newsblur.util
import com.newsblur.domain.Feed
object FeedExt {
fun Feed.isNotifyEmail(): Boolean = isNotify(NOTIFY_EMAIL)
fun Feed.isNotifyWeb(): Boolean = isNotify(NOTIFY_WEB)
fun Feed.isNotifyIOS(): Boolean = isNotify(NOTIFY_IOS)
fun Feed.isNotifyAndroid(): Boolean = isNotify(NOTIFY_ANDROID)
fun Feed.enableNotificationType(type: String) {
if (notificationTypes == null) notificationTypes = mutableListOf()
if (!notificationTypes.contains(type)) notificationTypes.add(type)
}
fun Feed.disableNotificationType(type: String) {
notificationTypes?.remove(type)
}
fun Feed.disableNotification() {
notificationFilter = null
}
@JvmStatic
fun Feed.isAndroidNotifyUnread(): Boolean = isNotifyUnread() && isNotifyAndroid()
@JvmStatic
fun Feed.isAndroidNotifyFocus(): Boolean = isNotifyFocus() && isNotifyAndroid()
@JvmStatic
fun Feed.isNotifyUnread(): Boolean = notificationFilter == Feed.NOTIFY_FILTER_UNREAD
@JvmStatic
fun Feed.isNotifyFocus(): Boolean = notificationFilter == Feed.NOTIFY_FILTER_FOCUS
fun Feed.setNotifyFocus() {
notificationFilter = Feed.NOTIFY_FILTER_FOCUS
}
fun Feed.setNotifyUnread() {
notificationFilter = Feed.NOTIFY_FILTER_UNREAD
}
private fun Feed.isNotify(type: String): Boolean = notificationTypes?.contains(type) ?: false
const val NOTIFY_EMAIL = "email"
const val NOTIFY_WEB = "web"
const val NOTIFY_IOS = "ios"
const val NOTIFY_ANDROID = "android"
}

View file

@ -7,17 +7,20 @@ import android.text.TextUtils
import com.newsblur.R
import com.newsblur.activity.NbActivity
import com.newsblur.database.BlurDatabaseHelper
import com.newsblur.domain.*
import com.newsblur.domain.Classifier
import com.newsblur.domain.Feed
import com.newsblur.domain.Story
import com.newsblur.fragment.ReadingActionConfirmationFragment
import com.newsblur.network.APIConstants
import com.newsblur.network.APIManager
import com.newsblur.service.NBSyncService
import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_METADATA
import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_SOCIAL
import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_STORY
import com.newsblur.service.NBSyncService
import com.newsblur.util.FeedExt.disableNotification
import com.newsblur.util.FeedExt.setNotifyFocus
import com.newsblur.util.FeedExt.setNotifyUnread
import com.newsblur.util.UIUtils.syncUpdateStatus
import java.lang.IllegalStateException
import java.util.*
class FeedUtils(
private val dbHelper: BlurDatabaseHelper,
@ -273,26 +276,23 @@ class FeedUtils(
}
fun disableNotifications(context: Context, feed: Feed) {
updateFeedNotifications(context, feed, enable = false, focusOnly = false)
feed.disableNotification()
updateFeedNotifications(context, feed)
}
fun enableUnreadNotifications(context: Context, feed: Feed) {
updateFeedNotifications(context, feed, enable = true, focusOnly = false)
feed.setNotifyUnread()
updateFeedNotifications(context, feed)
}
fun enableFocusNotifications(context: Context, feed: Feed) {
updateFeedNotifications(context, feed, enable = true, focusOnly = true)
feed.setNotifyFocus()
updateFeedNotifications(context, feed)
}
private fun updateFeedNotifications(context: Context, feed: Feed, enable: Boolean, focusOnly: Boolean) {
fun updateFeedNotifications(context: Context, feed: Feed) {
NBScope.executeAsyncTask(
doInBackground = {
if (focusOnly) {
feed.setNotifyFocus()
} else {
feed.setNotifyUnread()
}
feed.enableAndroidNotifications(enable)
dbHelper.updateFeed(feed)
val ra = ReadingAction.setNotify(feed.feedId, feed.notificationTypes, feed.notificationFilter)
doAction(ra, context)

View file

@ -128,6 +128,10 @@ public class NotificationUtils {
markreadIntent.putExtra(Reading.EXTRA_STORY_HASH, story.storyHash);
PendingIntent markreadPendingIntent = PendingIntentUtils.getImmutableBroadcast(context.getApplicationContext(), story.hashCode(), markreadIntent, 0);
Intent shareIntent = new Intent(context, NotifyShareReceiver.class);
shareIntent.putExtra(Reading.EXTRA_STORY, story);
PendingIntent sharePendingIntent = PendingIntentUtils.getImmutableBroadcast(context.getApplicationContext(), story.hashCode(), shareIntent, 0);
String feedTitle = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_TITLE));
StringBuilder title = new StringBuilder();
title.append(feedTitle).append(": ").append(story.title);
@ -144,8 +148,9 @@ public class NotificationUtils {
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setWhen(story.timestamp)
.addAction(0, "Save", savePendingIntent)
.addAction(0, "Mark Read", markreadPendingIntent)
.addAction(0, "Save", savePendingIntent)
.addAction(0, "Share", sharePendingIntent)
.setColor(NOTIFY_COLOUR);
if (feedIcon != null) {
nb.setLargeIcon(feedIcon);

View file

@ -0,0 +1,33 @@
package com.newsblur.util
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.newsblur.activity.Reading
import com.newsblur.database.BlurDatabaseHelper
import com.newsblur.domain.Story
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class NotifyShareReceiver : BroadcastReceiver() {
@Inject
lateinit var feedUtils: FeedUtils
@Inject
lateinit var dbHelper: BlurDatabaseHelper
override fun onReceive(context: Context, intent: Intent) {
val story = intent.getSerializableExtra(Reading.EXTRA_STORY) as? Story?
NotificationUtils.cancel(context, story?.storyHash.hashCode())
story?.let {
NBScope.executeAsyncTask(
doInBackground = {
dbHelper.putStoryDismissed(it.storyHash)
feedUtils.shareStory(it, "", it.sourceUserId, context)
}
)
}
}
}

View file

@ -0,0 +1,67 @@
package com.newsblur.viewModel
import android.content.Context
import android.database.Cursor
import android.os.CancellationSignal
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.newsblur.database.BlurDatabaseHelper
import com.newsblur.domain.Feed
import com.newsblur.util.FeedUtils
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class NotificationsViewModel
@Inject constructor(
private val dbHelper: BlurDatabaseHelper,
private val feedUtils: FeedUtils,
) : ViewModel() {
private val cancellationSignal = CancellationSignal()
private val _feeds = MutableLiveData<Map<String, Feed>>()
val feeds: LiveData<Map<String, Feed>> = _feeds
init {
loadFeeds()
}
fun updateFeed(context: Context, feed: Feed) {
viewModelScope.launch(Dispatchers.IO) {
feedUtils.updateFeedNotifications(context, feed)
}
}
private fun loadFeeds() {
viewModelScope.launch(Dispatchers.IO) {
launch {
val cursor = dbHelper.getFeedsCursor(cancellationSignal)
val feeds = extractFeeds(cursor).filterValues(notificationFeedFilter)
_feeds.postValue(feeds)
}
}
}
private fun extractFeeds(cursor: Cursor): Map<String, Feed> = buildMap {
if (!cursor.isBeforeFirst) return@buildMap
while (cursor.moveToNext()) {
val feed = Feed.fromCursor(cursor)
this[feed.feedId] = feed
}
}
private val notificationFeedFilter: (Feed) -> Boolean = {
it.active && !it.notificationFilter.isNullOrBlank()
}
override fun onCleared() {
cancellationSignal.cancel()
super.onCleared()
}
}