mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
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:
commit
9f96972181
34 changed files with 665 additions and 107 deletions
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
107
clients/android/NewsBlur/res/layout/view_notifications_item.xml
Normal file
107
clients/android/NewsBlur/res/layout/view_notifications_item.xml
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
54
clients/android/NewsBlur/src/com/newsblur/util/FeedExt.kt
Normal file
54
clients/android/NewsBlur/src/com/newsblur/util/FeedExt.kt
Normal 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"
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue