Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
FreshRSS-Android
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
30
Issues
30
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
Requirements
Requirements
List
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Operations
Operations
Environments
Packages & Registries
Packages & Registries
Package Registry
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issue
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Christophe Henry
FreshRSS-Android
Commits
11af8196
Commit
11af8196
authored
Mar 04, 2019
by
Christophe Henry
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Solves
#15
: Implement Jetpack's NavigationUI and get rid of the router
Incidentaly solves
#20
as expected
parent
fcd87dcd
Changes
25
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
249 additions
and
236 deletions
+249
-236
app/build.gradle
app/build.gradle
+16
-7
app/src/main/java/fr/chenry/android/freshrss/RefresherService.kt
.../main/java/fr/chenry/android/freshrss/RefresherService.kt
+2
-0
app/src/main/java/fr/chenry/android/freshrss/activities/MainActivity.kt
...ava/fr/chenry/android/freshrss/activities/MainActivity.kt
+11
-47
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/RecyclerViewAdapter.kt
...ss/components/subscriptionarticles/RecyclerViewAdapter.kt
+2
-7
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesDetailFragment.kt
...ubscriptionarticles/SubscriptionArticlesDetailFragment.kt
+8
-12
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesFragment.kt
...ents/subscriptionarticles/SubscriptionArticlesFragment.kt
+48
-31
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/AllSubscriptionsFragment.kt
...hrss/components/subscriptions/AllSubscriptionsFragment.kt
+6
-1
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/FavoritesSubscriptionsFragment.kt
...omponents/subscriptions/FavoritesSubscriptionsFragment.kt
+6
-4
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/MainSubscriptionFragment.kt
...hrss/components/subscriptions/MainSubscriptionFragment.kt
+16
-7
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/UnreadSubscriptionsFragment.kt
...s/components/subscriptions/UnreadSubscriptionsFragment.kt
+5
-3
app/src/main/java/fr/chenry/android/freshrss/store/Router.kt
app/src/main/java/fr/chenry/android/freshrss/store/Router.kt
+0
-69
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
+9
-21
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Account.kt
.../chenry/android/freshrss/store/database/models/Account.kt
+4
-3
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Article.kt
.../chenry/android/freshrss/store/database/models/Article.kt
+4
-1
app/src/main/java/fr/chenry/android/freshrss/store/databindingsupport/viewmodels/SubscriptionArticlesVM.kt
...e/databindingsupport/viewmodels/SubscriptionArticlesVM.kt
+1
-0
app/src/main/res/layout/activity_main.xml
app/src/main/res/layout/activity_main.xml
+8
-4
app/src/main/res/layout/activity_test.xml
app/src/main/res/layout/activity_test.xml
+3
-1
app/src/main/res/layout/fragment_main_subscription.xml
app/src/main/res/layout/fragment_main_subscription.xml
+3
-1
app/src/main/res/layout/fragment_subscription.xml
app/src/main/res/layout/fragment_subscription.xml
+1
-1
app/src/main/res/layout/fragment_subscription_article.xml
app/src/main/res/layout/fragment_subscription_article.xml
+2
-2
app/src/main/res/layout/fragment_subscription_articles.xml
app/src/main/res/layout/fragment_subscription_articles.xml
+29
-7
app/src/main/res/layout/fragment_subscriptions.xml
app/src/main/res/layout/fragment_subscriptions.xml
+19
-6
app/src/main/res/layout/fragment_waiting.xml
app/src/main/res/layout/fragment_waiting.xml
+0
-1
app/src/main/res/navigation/nav_graph.xml
app/src/main/res/navigation/nav_graph.xml
+44
-0
build.gradle
build.gradle
+2
-0
No files found.
app/build.gradle
View file @
11af8196
...
...
@@ -3,6 +3,7 @@ apply plugin: "kotlin-android"
apply
plugin:
"kotlin-android-extensions"
apply
plugin:
"kotlinx-serialization"
apply
plugin:
"kotlin-kapt"
apply
plugin:
"androidx.navigation.safeargs.kotlin"
android
{
compileSdkVersion
28
...
...
@@ -24,8 +25,6 @@ android {
enabled
=
true
}
com
.
android
.
build
.
gradle
.
internal
.
api
.
ApplicationVariantImpl
applicationVariants
.
all
{
variant
->
if
(
variant
.
name
.
contains
(
"elease"
))
{
variant
.
mergeAssetsProvider
.
get
().
doLast
{
...
...
@@ -33,6 +32,10 @@ android {
}
}
}
androidExtensions
{
experimental
=
true
}
}
dependencies
{
...
...
@@ -43,14 +46,14 @@ dependencies {
def
test_runnner_version
=
"1.1.1"
def
promise_version
=
"3.3.0"
def
android_support_version
=
"28.0.0"
def
android_navigation
=
"1.0.0-rc02"
implementation
'androidx.legacy:legacy-support-v4:1.0.0-beta01'
implementation
'androidx.lifecycle:lifecycle-extensions:2.0.0-beta01'
implementation
'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
configurations
{
all
*.
exclude
group:
'com.google.guava'
,
module:
'listenablefuture'
}
implementation
"androidx.legacy:legacy-support-v4:1.0.0"
implementation
fileTree
(
dir:
"libs"
,
include:
[
"*.jar"
])
// Kotlin stuff
...
...
@@ -62,7 +65,7 @@ dependencies {
implementation
"com.android.support:preference-v7:$android_support_version"
implementation
"com.android.support:support-core-utils:$android_support_version"
implementation
"com.android.support:support-fragment:$android_support_version"
implementation
"com.android.support:support-compat:
28.0.0
"
implementation
"com.android.support:support-compat:
$android_support_version
"
// AndroidX layout
implementation
"androidx.appcompat:appcompat:1.0.0-beta01"
...
...
@@ -72,9 +75,10 @@ dependencies {
implementation
"androidx.recyclerview:recyclerview:1.0.0"
// ViewModel and LiveData
implementation
'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
implementation
"androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
annotationProcessor
"androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation
"androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
annotationProcessor
"androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
testImplementation
"androidx.arch.core:core-testing:$lifecycle_version"
// Room
...
...
@@ -92,8 +96,13 @@ dependencies {
implementation
"nl.komponents.kovenant:kovenant:$promise_version"
implementation
"nl.komponents.kovenant:kovenant-android:$promise_version"
// Navigation
implementation
"android.arch.navigation:navigation-fragment-ktx:$android_navigation"
implementation
"android.arch.navigation:navigation-ui-ktx:$android_navigation"
// Utils
implementation
"org.apache.commons:commons-text:1.4"
implementation
"joda-time:joda-time:2.10.1"
// Tests
testImplementation
"junit:junit:4.12"
...
...
app/src/main/java/fr/chenry/android/freshrss/RefresherService.kt
View file @
11af8196
...
...
@@ -34,6 +34,8 @@ class RefresherService: Service() {
this
.
startForeground
(
NotificationHelper
.
ONGOING_REFRESH_NOTIFICATION
,
refreshNotification
)
Store
.
ensureToken
()
val
promise
=
Store
.
getSubscriptions
()
.
bind
{
Store
.
getUnreadCount
()}
.
bind
{
all
(
Store
.
subscriptions
.
keys
.
map
{
Store
.
getStreamContents
(
it
)},
cancelOthersOnError
=
false
)}
...
...
app/src/main/java/fr/chenry/android/freshrss/activities/MainActivity.kt
View file @
11af8196
package
fr.chenry.android.freshrss.activities
import
android.os.Bundle
import
android.view.MenuItem
import
androidx.appcompat.app.AppCompatActivity
import
androidx.fragment.app.Fragment
import
androidx.navigation.NavController
import
androidx.navigation.Navigation
import
androidx.navigation.ui.AppBarConfiguration
import
androidx.navigation.ui.setupActionBarWithNavController
import
fr.chenry.android.freshrss.*
import
fr.chenry.android.freshrss.components.waiting.WaitingFragment
import
fr.chenry.android.freshrss.store.*
import
fr.chenry.android.freshrss.utils.*
import
nl.komponents.kovenant.deferred
import
nl.komponents.kovenant.resolve
import
nl.komponents.kovenant.ui.failUi
import
nl.komponents.kovenant.ui.successUi
class
MainActivity
:
AppCompatActivity
()
{
private
val
deferred
=
deferred
<
Unit
,
Exception
>()
private
val
navigation
:
NavController
by
lazy
{
Navigation
.
findNavController
(
this
,
R
.
id
.
main_activity_host_fragment
)
}
private
val
appBarConfiguration
by
lazy
{
AppBarConfiguration
(
navigation
.
graph
)}
init
{
FreshRSSApplication
.
application
.
refresherService
.
whenNotNull
{
...
...
@@ -27,54 +32,13 @@ class MainActivity: AppCompatActivity() {
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
Router
.
observe
(
::
onRouteChanged
)
setupActionBar
()
setupActionBarWithNavController
(
navigation
,
appBarConfiguration
)
}
override
fun
onStart
()
{
super
.
onStart
()
deferred
.
promise
failUi
{
this
.
e
(
it
)}
Router
.
navigate
(
RouteName
.
SUBSCRIPTIONS
,
pushHistory
=
false
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
?):
Boolean
{
if
(
item
==
null
)
return
super
.
onOptionsItemSelected
(
item
)
return
when
(
item
.
itemId
)
{
android
.
R
.
id
.
home
->
onBackPressed
().
let
{
true
}
else
->
super
.
onOptionsItemSelected
(
item
)
}
}
override
fun
onBackPressed
()
{
if
(
supportFragmentManager
.
backStackEntryCount
>
0
)
supportFragmentManager
.
popBackStackImmediate
()
}
private
fun
onRouteChanged
(
to
:
Router
.
Route
)
{
val
bundle
=
if
(
to
.
parameters
.
isEmpty
())
Bundle
.
EMPTY
else
Bundle
(
to
.
parameters
.
entries
.
size
).
apply
{
to
.
parameters
.
entries
.
forEach
{(
k
,
v
)
->
this
.
putString
(
k
,
v
)}
}
val
fragmentTransaction
=
supportFragmentManager
?.
beginTransaction
()
?.
replace
(
R
.
id
.
fragment_container
,
Fragment
.
instantiate
(
this
,
to
.
fragment
.
qualifiedName
,
bundle
)
)
if
(
to
.
pushHistory
)
fragmentTransaction
?.
addToBackStack
(
null
)
fragmentTransaction
?.
commit
()
}
private
fun
setupActionBar
()
{
supportFragmentManager
.
addOnBackStackChangedListener
{
if
(
supportFragmentManager
.
backStackEntryCount
>
0
)
supportActionBar
?.
setDisplayHomeAsUpEnabled
(
true
)
else
supportActionBar
?.
setDisplayHomeAsUpEnabled
(
false
)
}
supportFragmentManager
?.
beginTransaction
()
?.
add
(
R
.
id
.
fragment_container
,
WaitingFragment
())
?.
commit
()
}
override
fun
onSupportNavigateUp
()
=
navigation
.
navigateUp
()
||
super
.
onSupportNavigateUp
()
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/RecyclerViewAdapter.kt
View file @
11af8196
...
...
@@ -27,17 +27,12 @@ class RecyclerViewAdapter(private val fragment: SubscriptionArticlesFragment):
override
fun
onBindViewHolder
(
holder
:
ViewHolder
,
position
:
Int
)
{
val
article
=
articles
[
position
]
holder
.
bind
(
article
)
holder
.
binding
.
root
.
setOnClickListener
{
Router
.
navigate
(
RouteName
.
SUBSCRIPTION_CONTENT_DETAIL
,
mapOf
(
"streamId"
to
fragment
.
streamId
,
"articleId"
to
article
.
id
))
}
holder
.
binding
.
root
.
setOnClickListener
{
fragment
.
navigateToArticle
(
article
.
id
)}
}
override
fun
getItemCount
():
Int
=
articles
.
size
inner
class
ViewHolder
(
val
binding
:
FragmentSubscriptionArticleBinding
):
RecyclerView
.
ViewHolder
(
binding
.
root
)
{
inner
class
ViewHolder
(
val
binding
:
FragmentSubscriptionArticleBinding
):
RecyclerView
.
ViewHolder
(
binding
.
root
)
{
fun
bind
(
article
:
Article
)
{
binding
.
setVariable
(
BR
.
article
,
article
)
binding
.
executePendingBindings
()
...
...
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesDetailFragment.kt
View file @
11af8196
...
...
@@ -15,6 +15,7 @@ import fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.store.database.FreshRSSDabatabase
import
fr.chenry.android.freshrss.store.Store
import
fr.chenry.android.freshrss.store.database.models.*
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.UNREAD
import
fr.chenry.android.freshrss.store.databindingsupport.viewmodels.SubscriptionArticlesVM
import
fr.chenry.android.freshrss.store.databindingsupport.viewmodels.SubscriptionArticlesVMF
import
fr.chenry.android.freshrss.utils.capitalizeFull
...
...
@@ -72,7 +73,7 @@ class SubscriptionArticlesDetailFragment: Fragment() {
override
fun
onResume
()
{
super
.
onResume
()
setReadStatus
(
ReadStatus
.
READ
)
if
(
article
.
readStatus
==
UNREAD
)
setReadStatus
(
ReadStatus
.
READ
)
}
override
fun
onDestroyView
()
{
...
...
@@ -137,17 +138,6 @@ class SubscriptionArticlesDetailFragment: Fragment() {
return
false
}
val
readText
=
when
(
readStatus
)
{
ReadStatus
.
READ
->
this
.
getString
(
R
.
string
.
read
)
ReadStatus
.
UNREAD
->
this
.
getString
(
R
.
string
.
unread
)
}.
let
{
this
.
getString
(
R
.
string
.
mark_read_status_authorization
,
it
)}
if
(!
FreshRSSDabatabase
.
instance
.
account
.
canPostRequests
)
{
val
toastText
=
this
.
getString
(
R
.
string
.
cannot_make_post_requests
,
readText
)
Toast
.
makeText
(
context
,
toastText
,
LENGTH_LONG
).
show
()
return
false
}
isFetching
.
value
=
true
Store
.
postReadStatus
(
article
,
readStatus
)
...
...
@@ -162,6 +152,12 @@ class SubscriptionArticlesDetailFragment: Fragment() {
}
.
failUi
{
e
->
this
.
e
(
e
)
val
readText
=
when
(
readStatus
)
{
ReadStatus
.
READ
->
this
.
getString
(
R
.
string
.
read
)
ReadStatus
.
UNREAD
->
this
.
getString
(
R
.
string
.
unread
)
}.
let
{
this
.
getString
(
R
.
string
.
mark_read_status_authorization
,
it
)}
val
toastText
=
context
?.
getString
(
R
.
string
.
unable_to
,
readText
)
Toast
.
makeText
(
context
,
toastText
,
LENGTH_SHORT
).
show
()
}
alwaysUi
{
isFetching
.
value
=
false
}
...
...
app/src/main/java/fr/chenry/android/freshrss/components/subscriptionarticles/SubscriptionArticlesFragment.kt
View file @
11af8196
...
...
@@ -3,46 +3,63 @@ package fr.chenry.android.freshrss.components.subscriptionarticles
import
android.os.Bundle
import
android.view.*
import
androidx.fragment.app.Fragment
import
androidx.lifecycle.*
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.Observer
import
androidx.navigation.findNavController
import
androidx.navigation.fragment.navArgs
import
androidx.recyclerview.widget.LinearLayoutManager
import
androidx.recyclerview.widget.RecyclerView
import
fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.store.api.models.StreamId
import
fr.chenry.android.freshrss.store.database.FreshRSSDabatabase
import
fr.chenry.android.freshrss.store.database.models.Articles
import
fr.chenry.android.freshrss.store.databindingsupport.viewmodels.*
import
fr.chenry.android.freshrss.store.database.models.ReadStatus
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.READ
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.UNREAD
import
kotlinx.android.synthetic.main.fragment_subscription_articles.*
sealed
class
SubscriptionArticlesFragment
:
Fragment
()
{
lateinit
var
streamId
:
String
private
set
private
lateinit
var
model
:
LiveData
<
Articles
>
open
class
SubscriptionArticlesFragment
:
Fragment
(),
Observer
<
Articles
>
{
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
{
streamId
=
arguments
?.
getString
(
"id"
)
!!
model
=
getModel
(
streamId
)
return
inflater
.
inflate
(
R
.
layout
.
fragment_subscription_articles
,
container
,
false
)
.
let
{
it
as
RecyclerView
it
.
layoutManager
=
LinearLayoutManager
(
context
)
it
.
adapter
=
RecyclerViewAdapter
(
this
).
let
{
adapter
->
model
.
observe
(
this
,
Observer
{
articles
->
adapter
.
articles
=
articles
adapter
.
notifyDataSetChanged
()
})
adapter
}
it
}
private
val
streamId
:
String
by
lazy
{
args
.
id
}
private
val
readStatus
:
ReadStatus
by
lazy
{
args
.
readStatus
}
private
val
args
:
SubscriptionArticlesFragmentArgs
by
navArgs
()
private
val
model
:
LiveData
<
Articles
>
by
lazy
{
when
(
readStatus
)
{
READ
->
FreshRSSDabatabase
.
instance
.
getArticleByStreamId
(
streamId
)
UNREAD
->
FreshRSSDabatabase
.
instance
.
getArticleByStreamIdAndUnread
(
streamId
)
}
}
private
val
adapter
by
lazy
{
RecyclerViewAdapter
(
this
).
apply
{
model
.
observe
(
this
@SubscriptionArticlesFragment
,
this
@SubscriptionArticlesFragment
)
}
}
abstract
fun
getModel
(
streamId
:
StreamId
):
LiveData
<
Articles
>
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
{
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_subscription_articles
,
container
,
false
)
class
SubscriptionAllArticlesFragment
:
SubscriptionArticlesFragment
()
{
override
fun
getModel
(
streamId
:
StreamId
)
=
FreshRSSDabatabase
.
instance
.
getArticleByStreamId
(
streamId
)
}
model
.
observe
(
this
,
this
)
view
.
findViewById
<
RecyclerView
>(
R
.
id
.
fragment_subscription_article_recycler
).
apply
{
layoutManager
=
LinearLayoutManager
(
context
)
adapter
=
this
@SubscriptionArticlesFragment
.
adapter
}
class
SubscriptionUnreadArticlesFragment
:
SubscriptionArticlesFragment
()
{
override
fun
getModel
(
streamId
:
StreamId
)
=
FreshRSSDabatabase
.
instance
.
getArticleByStreamIdAndUnread
(
streamId
)
return
view
}
override
fun
onChanged
(
articles
:
Articles
?)
{
(
articles
?:
listOf
()).
let
{
fragment_subscription_article_waiting
.
visibility
=
if
(
it
.
isNotEmpty
())
View
.
GONE
else
View
.
VISIBLE
fragment_subscription_article_recycler
.
visibility
=
if
(
it
.
isEmpty
())
View
.
GONE
else
View
.
VISIBLE
adapter
.
articles
=
it
adapter
.
notifyDataSetChanged
()
}
}
fun
navigateToArticle
(
articleId
:
String
)
{
val
direction
=
SubscriptionArticlesFragmentDirections
.
actionSubscriptionArticlesFragmentToSubscriptionArticlesDetailFragment
(
articleId
)
view
?.
findNavController
()
?.
navigate
(
direction
)
}
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/AllSubscriptionsFragment.kt
View file @
11af8196
...
...
@@ -2,9 +2,13 @@ package fr.chenry.android.freshrss.components.subscriptions
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.MutableLiveData
import
androidx.navigation.findNavController
import
fr.chenry.android.freshrss.MainNavDirections
import
fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.store.*
import
fr.chenry.android.freshrss.store.api.models.Subscription
import
fr.chenry.android.freshrss.store.api.models.Subscriptions
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.READ
class
AllSubscriptionsFragment
:
SubscriptionsFragment
()
{
override
val
liveData
:
LiveData
<
Subscriptions
>
=
MutableLiveData
<
Subscriptions
>().
apply
{
...
...
@@ -13,6 +17,7 @@ class AllSubscriptionsFragment: SubscriptionsFragment() {
}
override
fun
onClick
(
subscription
:
Subscription
)
{
Router
.
navigate
(
RouteName
.
SUBSCRIPTION_ARTICLES_ALL
,
mapOf
(
"id"
to
subscription
.
id
))
val
direction
=
MainNavDirections
.
actionGlobalSubscriptionArticlesFragment
(
subscription
.
id
,
READ
)
view
?.
findNavController
()
?.
navigate
(
direction
)
}
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/FavoritesSubscriptionsFragment.kt
View file @
11af8196
package
fr.chenry.android.freshrss.components.subscriptions
import
androidx.lifecycle.
*
import
fr.chenry.android.freshrss.store.RouteName
import
fr.chenry.android.freshrss.
store.Router
import
androidx.lifecycle.
MutableLiveData
import
androidx.navigation.findNavController
import
fr.chenry.android.freshrss.
MainNavDirections
import
fr.chenry.android.freshrss.store.api.models.Subscription
import
fr.chenry.android.freshrss.store.api.models.Subscriptions
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.UNREAD
class
FavoritesSubscriptionsFragment
:
SubscriptionsFragment
()
{
override
val
liveData
=
MutableLiveData
<
Subscriptions
>().
apply
{
this
.
value
=
listOf
()}
override
fun
onClick
(
subscription
:
Subscription
)
{
Router
.
navigate
(
RouteName
.
SUBSCRIPTION_ARTICLES_UNREAD
,
mapOf
(
"id"
to
subscription
.
id
))
val
direction
=
MainNavDirections
.
actionGlobalSubscriptionArticlesFragment
(
subscription
.
id
,
UNREAD
)
view
?.
findNavController
()
?.
navigate
(
direction
)
}
}
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/MainSubscriptionFragment.kt
View file @
11af8196
...
...
@@ -4,6 +4,7 @@ import android.os.Bundle
import
android.view.*
import
androidx.fragment.app.Fragment
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.Observer
import
androidx.recyclerview.widget.LinearLayoutManager
import
androidx.recyclerview.widget.RecyclerView
import
fr.chenry.android.freshrss.FreshRSSApplication
...
...
@@ -11,9 +12,9 @@ import fr.chenry.android.freshrss.R
import
fr.chenry.android.freshrss.store.Store
import
fr.chenry.android.freshrss.store.api.models.Subscription
import
fr.chenry.android.freshrss.store.api.models.Subscriptions
import
fr.chenry.android.freshrss.utils.getOrDefault
import
fr.chenry.android.freshrss.utils.whenNotNull
import
kotlinx.android.synthetic.main.fragment_main_subscription.*
import
kotlinx.android.synthetic.main.fragment_subscriptions.*
import
kotlin.reflect.KClass
class
MainSubscriptionFragment
:
Fragment
()
{
...
...
@@ -75,23 +76,31 @@ class MainSubscriptionFragment: Fragment() {
}
}
abstract
class
SubscriptionsFragment
:
Fragment
()
{
abstract
class
SubscriptionsFragment
:
Fragment
()
,
Observer
<
Subscriptions
>
{
abstract
val
liveData
:
LiveData
<
Subscriptions
>
val
subscriptions
:
Subscriptions
get
()
=
liveData
.
value
?:
listOf
()
val
subscriptions
:
Subscriptions
get
()
=
liveData
.
value
?:
listOf
()
private
val
adapter
by
lazy
{
RecyclerViewAdapter
(
this
)}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
{
val
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_subscriptions
,
container
,
false
)
liveData
.
observe
(
this
,
this
)
view
.
findViewById
<
RecyclerView
>(
R
.
id
.
subscriptions_list
).
let
{
it
.
layoutManager
=
LinearLayoutManager
(
context
,
RecyclerView
.
VERTICAL
,
false
)
it
.
adapter
=
RecyclerViewAdapter
(
this
).
apply
{
liveData
.
observeForever
{
this
.
notifyDataSetChanged
()}
}
it
.
adapter
=
this
.
adapter
}
return
view
}
override
fun
onChanged
(
subscriptions
:
Subscriptions
?)
{
(
subscriptions
?:
listOf
()).
let
{
subscriptions_list
.
visibility
=
if
(
it
.
isEmpty
())
View
.
GONE
else
View
.
VISIBLE
fragment_subscriptions_waiting
.
visibility
=
if
(
it
.
isNotEmpty
())
View
.
GONE
else
View
.
VISIBLE
adapter
.
notifyDataSetChanged
()
}
}
abstract
fun
onClick
(
subscription
:
Subscription
)
}
\ No newline at end of file
app/src/main/java/fr/chenry/android/freshrss/components/subscriptions/UnreadSubscriptionsFragment.kt
View file @
11af8196
...
...
@@ -2,9 +2,10 @@ package fr.chenry.android.freshrss.components.subscriptions
import
android.os.Bundle
import
androidx.lifecycle.ViewModelProviders
import
fr.chenry.android.freshrss.store.RouteName
import
fr.chenry.android.freshrss.
store.Router
import
androidx.navigation.findNavController
import
fr.chenry.android.freshrss.
MainNavDirections
import
fr.chenry.android.freshrss.store.api.models.Subscription
import
fr.chenry.android.freshrss.store.database.models.ReadStatus.UNREAD
import
fr.chenry.android.freshrss.store.databindingsupport.viewmodels.UnreadSubscriptionsViewModel
class
UnreadSubscriptionsFragment
:
SubscriptionsFragment
()
{
...
...
@@ -19,6 +20,7 @@ class UnreadSubscriptionsFragment: SubscriptionsFragment() {
}
override
fun
onClick
(
subscription
:
Subscription
)
{
Router
.
navigate
(
RouteName
.
SUBSCRIPTION_ARTICLES_UNREAD
,
mapOf
(
"id"
to
subscription
.
id
))
val
direction
=
MainNavDirections
.
actionGlobalSubscriptionArticlesFragment
(
subscription
.
id
,
UNREAD
)
view
?.
findNavController
()
?.
navigate
(
direction
)
}
}
app/src/main/java/fr/chenry/android/freshrss/store/Router.kt
deleted
100644 → 0
View file @
fcd87dcd
package
fr.chenry.android.freshrss.store
import
androidx.annotation.MainThread
import
androidx.fragment.app.Fragment
import
androidx.lifecycle.MutableLiveData
import
fr.chenry.android.freshrss.components.subscriptionarticles.*
import
fr.chenry.android.freshrss.components.subscriptions.MainSubscriptionFragment
import
fr.chenry.android.freshrss.store.Router.Route
import
kotlin.reflect.KClass
enum
class
RouteName
{
SUBSCRIPTIONS
,
SUBSCRIPTIONS_ALL
,
SUBSCRIPTIONS_UNREAD
,
SUBSCRIPTIONS_FAVORITES
,
SUBSCRIPTION_ARTICLES_ALL
,
SUBSCRIPTION_ARTICLES_UNREAD
,
SUBSCRIPTION_CONTENT_DETAIL
}
@MainThread
object
Router
{
private
val
currentRoute
=
MutableLiveData
<
Route
>()
private
fun
navigate
(
to
:
Route
?,
params
:
Map
<
String
,
Any
>,
pushHistory
:
Boolean
)
{
if
(
to
==
null
)
return
val
newTo
=
to
.
withPushHistory
(
pushHistory
)
val
groups
=
to
.
route
.
split
(
"/"
)
.
filter
{
it
.
isNotEmpty
()
&&
it
.
startsWith
(
":"
)
}
.
map
{
it
.
substring
(
1
..
it
.
length
.
minus
(
1
))
}
if
(
groups
.
isEmpty
())
return
currentRoute
.
setValue
(
newTo
)
val
parameters
=
groups
.
map
{
if
(!
params
.
containsKey
(
it
))
throw
StringIndexOutOfBoundsException
(
"Required parameter $it not found in parameters"
)
it
to
params
.
getValue
(
it
)
}
currentRoute
.
value
=
newTo
.
withParameters
(
parameters
.
toMap
())
}
fun
navigate
(
to
:
RouteName
,
params
:
Map
<
String
,
Any
>
=
mapOf
(),
pushHistory
:
Boolean
=
true
)
=
this
.
navigate
(
routes
.
find
{
it
.
name
.
name
==
to
.
name
},
params
,
pushHistory
)
fun
observe
(
onRouteChange
:
(
Route
)
->
Unit
)
=
currentRoute
.
observeForever
{
onRouteChange
(
it
)
}
data class
Route
(
val
route
:
String
,
val
name
:
RouteName
,
val
fragment
:
KClass
<
out
Fragment
>,
val
parameters
:
Map
<
String
,
String
>
=
mapOf
(),
val
pushHistory
:
Boolean
=
true
)
{
fun
withParameters
(
params
:
Map
<
String
,
Any
>)
=
this
.
copy
(
parameters
=
params
.
map
{
it
.
key
to
it
.
value
.
toString
()
}.
toMap
())
fun
withPushHistory
(
newPushHistory
:
Boolean
)
=
this
.
copy
(
pushHistory
=
newPushHistory
)
}
}
private
val
routes
=
listOf
(
Route
(
"/subscriptions"
,
RouteName
.
SUBSCRIPTIONS
,
MainSubscriptionFragment
::
class
),
Route
(
"/subscription/:id/all"
,
RouteName
.
SUBSCRIPTION_ARTICLES_ALL
,
SubscriptionAllArticlesFragment
::
class
),
Route
(
"/subscription/:id/unread"
,
RouteName
.
SUBSCRIPTION_ARTICLES_UNREAD
,
SubscriptionUnreadArticlesFragment
::
class
),
Route
(
"/subscription/:streamId/article/:articleId"
,
RouteName
.
SUBSCRIPTION_CONTENT_DETAIL
,
SubscriptionArticlesDetailFragment
::
class
)
)
app/src/main/java/fr/chenry/android/freshrss/store/Store.kt
View file @
11af8196
...
...
@@ -32,6 +32,14 @@ object Store {
fun
login
(
instance
:
String
,
user
:
String
,
password
:
String
):
Promise
<
Unit
,
Exception
>
=
Api
.
login
(
instance
,
user
,
password
)
then
{
init
(
it
)}
fun
ensureToken
():
Promise
<
Unit
,
Exception
>
{
if
(!
FreshRSSDabatabase
.
instance
.
account
.
isWriteTokenExpired
)
return
Promise
.
ofSuccess
(
Unit
)
return
api
.
getWriteToken
()
.
success
{
FreshRSSDabatabase
.
instance
.
account
.
writeToken
=
it
}
.
toSuccessVoid
()
}
fun
getSubscriptions
():
Promise
<
Unit
,
Exception
>
=
api
.
getSubscriptions
()
then
{
subscriptions
.
addAll
(
it
.
map
{
self
->
self
.
id
to
self
})}
...
...
@@ -67,29 +75,9 @@ object Store {
all
(
promises
,
cancelOthersOnError
=
false
).
toSuccessVoid
()
}
fun
getArticle
(
articleId
:
String
):
Promise
<
Article
,
ArticleNotFoundException
>
{
val
deferred
=
deferred
<
Article
,
ArticleNotFoundException
>()
task
{
val
articles
=
FreshRSSDabatabase
.
instance
.
getArticleById
(
articleId
).
blockingFirst
()
if
(
articles
.
isEmpty
())
deferred
.
reject
(
ArticleNotFoundException
(
articleId
))
else
deferred
.
resolve
(
articles
.
first
())
}
return
deferred
.
promise
}
fun
postReadStatus
(
article
:
Article
,
readStatus
:
ReadStatus
):
Promise
<
Unit
,
Exception
>
=
ensureToken
()
bind
{
api
.
postReadStatus
(
article
.
id
,
readStatus
)
.
success
{
FreshRSSDabatabase
.
instance
.
upsertArticle
(
article
.
copy
(
readStatus
=
readStatus
))}
}
private
fun
ensureToken
():
Promise
<
Unit
,
Exception
>
{
if
(!
FreshRSSDabatabase
.
instance
.
account
.
isWriteTokenExpired
)
return
Promise
.
ofSuccess
(
Unit
)
return
api
.
getWriteToken
()
.
success
{
FreshRSSDabatabase
.
instance
.
account
.
writeToken
=
it
}
.
toSuccessVoid
()
}
}
class
ArticleNotFoundException
(
val
id
:
String
):
Exception
(
"Article with id $id cound not be found in store"
)
\ No newline at end of file
}
\ No newline at end of file
app/src/main/java/fr/chenry/android/freshrss/store/database/models/Account.kt
View file @
11af8196
...
...
@@ -2,6 +2,7 @@ package fr.chenry.android.freshrss.store.database.models
import
androidx.room.*
import
com.github.kittinunf.fuel.core.ResponseDeserializable
import
org.joda.time.LocalDateTime
import
java.util.*
@Entity
(
tableName
=
"accounts"
)
...
...
@@ -19,11 +20,11 @@ data class Account(