Commit fae4f7e9 authored by Christophe Henry's avatar Christophe Henry

Merge branch 'fix-viewmodel-memory-retain' into 'develop'

Multiple performance fixes

Closes #67 and #80

See merge request !65
parents 693a3371 cae406aa
Pipeline #3505 failed with stage
in 0 seconds
......@@ -12,6 +12,8 @@
* Fix a bug preventing to refresh when subscription lists are empty ([!62](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/62/))
* Fix [#82](https://git.feneas.org/christophehenry/freshrss-android/issues/82): home and back buttons are sometimes not displayed correctly or not displayed at all ([!71](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/71))
* Fix [#80](https://git.feneas.org/christophehenry/freshrss-android/issues/80): multiple performance bugs in the subscriptions and subscription's articles pages display ([!65](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/65))
* Fix [#67](https://git.feneas.org/christophehenry/freshrss-android/issues/67): swipe right to flag article as 'read' sometime doesn't work ([#65](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/65))
## Refactoring
......@@ -41,7 +43,7 @@
* Implements [#74](https://git.feneas.org/christophehenry/freshrss-android/issues/74): Ability to add a new feed ([!54](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/54))
## Bug fixes
* [#50](https://git.feneas.org/christophehenry/freshrss-android/issues/50): Categories are not alphabetically sorted
* [#51](https://git.feneas.org/christophehenry/freshrss-android/issues/51): Unread subscription time groups are not time-sorted
* [#55](https://git.feneas.org/christophehenry/freshrss-android/issues/55): Links click open page in the webview ([!18](https://git.feneas.org/christophehenry/freshrss-android/merge_requests/18))
......@@ -105,4 +107,4 @@ Basic implementation using FreshRSS' GReader HTTP API implmentation. This lets y
* set an article as read/unread
* share an article to other android application
* open the original page of the article
* refresh your feed
\ No newline at end of file
* refresh your feed
......@@ -106,7 +106,8 @@ spotless {
}
dependencies {
def lifecycle_version = '2.2.0'
def appcompat_version = "1.1.0"
def lifecycle_version = "2.2.0"
def room_version = '2.2.3'
def roomigrant_version = "0.1.7"
def fuel_version = "2.0.1"
......@@ -114,7 +115,6 @@ dependencies {
def espresso_version = "3.2.0"
def test_runnner_version = "1.2.0"
def promise_version = "3.3.0"
def android_support_version = "28.0.0"
def android_navigation = "1.0.0"
def jsoup_version = "1.12.1"
def acraVersion = "5.1.3"
......@@ -130,36 +130,33 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// Compat libraries
implementation "com.android.support:appcompat-v7:$android_support_version"
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:$android_support_version"
implementation "com.android.support:support-core-ui:$android_support_version"
// AndroidX
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
// AndroidX layout
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.core:core-ktx:1.1.0"
implementation 'com.google.android.material:material:1.2.0-alpha04'
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.preference:preference-ktx:1.1.0"
implementation "com.google.android.material:material:1.2.0-alpha04"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
// Room
// Navigation
implementation "android.arch.navigation:navigation-fragment-ktx:$android_navigation"
implementation "android.arch.navigation:navigation-ui-ktx:$android_navigation"
// Room & roomigrant
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"
implementation "androidx.room:room-guava:$room_version"
androidTestImplementation "androidx.room:room-testing:$room_version"
// roomigrant
implementation "com.github.MatrixDev.Roomigrant:RoomigrantLib:$roomigrant_version"
kapt "com.github.MatrixDev.Roomigrant:RoomigrantCompiler:$roomigrant_version"
......@@ -172,13 +169,9 @@ 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.8"
implementation 'joda-time:joda-time:2.10.5'
implementation "joda-time:joda-time:2.10.5"
implementation "com.squareup.picasso:picasso:2.71828"
implementation "com.x5dev:chunk-templates:3.5.0"
implementation "eu.davidea:flexible-adapter:5.1.0"
......
......@@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.ResultReceiver
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
......@@ -17,17 +16,16 @@ import fr.chenry.android.freshrss.R
import fr.chenry.android.freshrss.store.Store
import fr.chenry.android.freshrss.utils.*
import kotlinx.android.synthetic.main.activity_login.*
import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.resolve
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import java.util.Properties
/**
* A login screen that offers login via email/password.
*/
class LoginActivity : AppCompatActivity() {
private lateinit var instanceUrl: InstanceUrl
private val inputManager by lazy {
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -133,15 +131,8 @@ class LoginActivity : AppCompatActivity() {
}
private fun hideKeyboard() {
val deferred = deferred<Unit, Exception>()
val view = this.currentFocus
view?.let {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(it.windowToken, 0, object : ResultReceiver(null) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) = deferred.resolve()
})
}.whenNull { deferred.resolve() }
deferred.promise.get()
val token = (this.currentFocus ?: findViewById<View>(android.R.id.content).rootView).windowToken
token?.let {inputManager.hideSoftInputFromWindow(it, 0)}
}
private fun showProgress(show: Boolean) {
......
......@@ -23,7 +23,7 @@ class MainActivity: AppCompatActivity() {
Navigation.findNavController(this, R.id.main_activity_host_fragment)
}
val drawerLayout: DrawerLayout by lazy {
private val drawerLayout: DrawerLayout by lazy {
findViewById<DrawerLayout>(R.id.activity_main_navigation_drawer)
}
......@@ -39,7 +39,6 @@ class MainActivity: AppCompatActivity() {
restoreState()
setContentView(R.layout.activity_main)
F.app.refresherService.value?.apply {refresh()}
NavigationUI.setupActionBarWithNavController(this, navigation, appBarConfiguration)
setupActionBarWithNavController(navigation, appBarConfiguration)
drawerLayout.addDrawerListener(BurgerMenuDrawerListener(this))
}
......
......@@ -6,8 +6,8 @@ import fr.chenry.android.freshrss.F
import fr.chenry.android.freshrss.R
class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener {
private val refreshFrequencyPreference: ListPreference by lazy {
findPreference(F.preferences.refreshFrequencyKey) as ListPreference
private val refreshFrequencyPreference by lazy {
findPreference(F.preferences.refreshFrequencyKey) ?: ListPreference(context)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
......
......@@ -14,8 +14,12 @@ import fr.chenry.android.freshrss.databinding.FragmentSubscriptionArticleBinding
import fr.chenry.android.freshrss.store.database.models.Article
import fr.chenry.android.freshrss.store.database.models.Subscription
class SubscriptionArticleViewItem(val article: Article, private val subscription: Subscription) :
AbstractFlexibleItem<ArticleViewHolder>() {
class SubscriptionArticleViewItem(
private val subscription: Subscription,
val article: Article
): AbstractFlexibleItem<ArticleViewHolder>() {
init {
isDraggable = false
isSelectable = false
......@@ -30,7 +34,7 @@ class SubscriptionArticleViewItem(val article: Article, private val subscription
) = holder.bind(article)
override fun equals(other: Any?): Boolean {
if (other !is SubscriptionArticleViewItem) return false
if(other !is SubscriptionArticleViewItem) return false
return article == other.article
}
......@@ -46,7 +50,9 @@ class SubscriptionArticleViewItem(val article: Article, private val subscription
inner class ArticleViewHolder(
private val binding: FragmentSubscriptionArticleBinding,
flexibleAdapter: FlexibleAdapter<*>
) : FlexibleViewHolder(binding.root, flexibleAdapter) {
): FlexibleViewHolder(binding.root, flexibleAdapter) {
private val frontView = binding.root.findViewById<View>(R.id.fragment_subscription_article_front_view)
private val rearLeftView =
binding.root.findViewById<View>(R.id.fragment_subscription_article_rear_left_view)
......
......@@ -27,8 +27,8 @@ import nl.komponents.kovenant.ui.failUi
class SubscriptionArticlesDetailFragment : Fragment() {
private lateinit var articleId: String
private val model: SubscriptionArticleVM by lazy {
ViewModelProvider(this, SubscriptionArticleVMF(articleId))
.get(SubscriptionArticleVM::class.java)
ViewModelProvider(activity!!.viewModelStore, SubscriptionArticleVMF(articleId))
.get("articleId=$articleId", SubscriptionArticleVM::class.java)
}
private var isFetching = MutableLiveData<Boolean>().apply { value = false }
private val article: Article get() = model.liveData.value!!
......@@ -108,7 +108,7 @@ class SubscriptionArticlesDetailFragment : Fragment() {
}
mutateUi(article)
model.liveData.observe(this, Observer(::mutateUi))
model.liveData.observe(viewLifecycleOwner, Observer(::mutateUi))
it.setOnMenuItemClickListener { setReadStatus(article.readStatus.toggle()) }
}
}
......
package fr.chenry.android.freshrss.components.subscriptionarticles
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.*
import androidx.navigation.findNavController
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener
import fr.chenry.android.freshrss.R
import fr.chenry.android.freshrss.store.Store
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 fr.chenry.android.freshrss.store.database.models.Subscription
import fr.chenry.android.freshrss.store.viewmodels.SubscriptionArticlesVM
import fr.chenry.android.freshrss.store.viewmodels.SubscriptionArticlesVMF
import kotlinx.android.synthetic.main.fragment_subscription_articles.*
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.ui.alwaysUi
class SubscriptionArticlesFragment: Fragment(), Observer<List<SubscriptionArticleViewItem>> {
class SubscriptionArticlesFragment: Fragment() {
private val args: SubscriptionArticlesFragmentArgs by navArgs()
private val streamId: String by lazy {args.id}
private val subscription: Subscription by lazy {args.subscription}
private val readStatus: ReadStatus by lazy {args.readStatus}
private val model: SubscriptionArticlesVM by lazy {
ViewModelProvider(this, SubscriptionArticlesVMF(streamId, readStatus))
.get(SubscriptionArticlesVM::class.java).apply {
liveData.observe(this@SubscriptionArticlesFragment, this@SubscriptionArticlesFragment)
}
}
private val adapter by lazy {
ArticleAdapter(model.liveData.value ?: listOf())
ViewModelProvider(activity!!.viewModelStore, SubscriptionArticlesVMF(subscription, readStatus))
.get("stream=${subscription.id}:readStatus=$readStatus", SubscriptionArticlesVM::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
......@@ -43,30 +38,13 @@ class SubscriptionArticlesFragment: Fragment(), Observer<List<SubscriptionArticl
view.findViewById<RecyclerView>(R.id.fragment_subscription_article_recycler).apply {
layoutManager = LinearLayoutManager(context)
adapter = this@SubscriptionArticlesFragment.adapter
adapter = ArticleAdapter(model.liveData.value ?: listOf())
}
return view
}
override fun onChanged(articlesViewItemSubscriptions: List<SubscriptionArticleViewItem>?) {
(articlesViewItemSubscriptions ?: listOf()).let {
toggleProgressCircle(it)
adapter.updateDataSet(it, true)
}
}
fun onItemClick(position: Int): Boolean =
adapter.getItem(position)?.article?.id?.let {articleId ->
val direction = SubscriptionArticlesFragmentDirections.articlesToArticleDetail(articleId)
findNavController().navigate(direction)
true
} ?: false
fun toggleArticleReadStatus(position: Int): Promise<Unit, Exception> = adapter.getItem(position)?.let {
Store.postReadStatus(it.article, it.article.readStatus.toggle())
} ?: Promise.ofSuccess(Unit)
@SuppressLint("StringFormatInvalid")
private fun toggleProgressCircle(articles: List<*>) {
fragment_subscription_article_empty_list.text = when(readStatus) {
READ -> context?.getString(R.string.empty_subscription_list, model.subscription.title)
......@@ -97,29 +75,56 @@ class SubscriptionArticlesFragment: Fragment(), Observer<List<SubscriptionArticl
}
private inner class ArticleAdapter(items: List<SubscriptionArticleViewItem>):
FlexibleAdapter<SubscriptionArticleViewItem>(items, null, true) {
FlexibleAdapter<SubscriptionArticleViewItem>(items, null, true),
Observer<List<SubscriptionArticleViewItem>>,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemSwipeListener {
init {
model.liveData.observe(viewLifecycleOwner, this)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
isSwipeEnabled = true
itemTouchHelperCallback.setSwipeFlags(ItemTouchHelper.RIGHT)
addListener(OnItemClickListener {_, position -> this@SubscriptionArticlesFragment.onItemClick(position)})
addListener(object: OnItemSwipeListener {
override fun onActionStateChanged(viewHolder: ViewHolder?, actionState: Int) {}
override fun onItemSwipe(position: Int, direction: Int) {
if(direction == ItemTouchHelper.RIGHT) toggleArticleReadStatus(position).alwaysUi {
if(readStatus == READ) this@ArticleAdapter.notifyItemChanged(position)
}
}
})
addListener(this as OnItemClickListener)
addListener(this as OnItemSwipeListener)
}
override fun getItemId(position: Int) =
getItem(position)?.article?.id?.hashCode()?.toLong() ?: RecyclerView.NO_ID
override fun isEmpty(): Boolean = (model.liveData.value ?: listOf()).isEmpty()
override fun onChanged(articlesViewItemSubscriptions: List<SubscriptionArticleViewItem>?) {
(articlesViewItemSubscriptions ?: listOf()).let {
toggleProgressCircle(it)
updateDataSet(it, true)
}
}
override fun onItemClick(view: View?, position: Int): Boolean = getArticleId(position)?.let {
val direction = SubscriptionArticlesFragmentDirections.articlesToArticleDetail(it)
findNavController().navigate(direction)
true
} ?: false
override fun onActionStateChanged(viewHolder: ViewHolder?, actionState: Int) {}
override fun onItemSwipe(position: Int, direction: Int) {
if(direction == ItemTouchHelper.RIGHT) toggleArticleReadStatus(position).alwaysUi {
if(readStatus == READ) this@ArticleAdapter.notifyItemChanged(position)
}
}
private fun getArticleId(position: Int) = getItem(position)?.article?.id
private fun toggleArticleReadStatus(position: Int): Promise<Unit, Exception> = getItem(position)?.let {
Store.postReadStatus(it.article, it.article.readStatus.toggle())
} ?: Promise.ofSuccess(Unit)
}
}
......@@ -11,8 +11,7 @@ import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener
import com.google.android.material.bottomnavigation.BottomNavigationItemView
import com.google.android.material.bottomnavigation.BottomNavigationView
import fr.chenry.android.freshrss.MainNavDirections
import fr.chenry.android.freshrss.R
import fr.chenry.android.freshrss.*
import fr.chenry.android.freshrss.store.Store
import fr.chenry.android.freshrss.store.database.models.VoidCategory
import kotlinx.android.synthetic.main.fragment_main_subscription.*
......@@ -54,6 +53,7 @@ class MainSubscriptionFragment: Fragment(), BottomNavigationView.OnNavigationIte
}
fun onPageSelected(position: Int) = SubscriptionSection.byPosition(position).let {
F.app.preferences.subscriptionSection = it
Store.subscriptionsSection.value = it
subcription_bottom_navigation.selectedItemId = it.navigationButtonId
}
......
......@@ -14,29 +14,27 @@ import eu.davidea.fastscroller.FastScroller
import fr.chenry.android.freshrss.F
import fr.chenry.android.freshrss.R
import fr.chenry.android.freshrss.components.subscriptions.SubscriptionSection.*
import fr.chenry.android.freshrss.components.subscriptions.adapters.SubscriptionViewItems
import fr.chenry.android.freshrss.components.subscriptions.adapters.SubscriptionsFlexibleAdapter
import fr.chenry.android.freshrss.store.Store
import fr.chenry.android.freshrss.store.database.models.*
import fr.chenry.android.freshrss.store.database.models.ReadStatus.READ
import fr.chenry.android.freshrss.store.viewmodels.*
import fr.chenry.android.freshrss.store.viewmodels.SubscriptionsFragmentCategoryVMF
import fr.chenry.android.freshrss.store.viewmodels.SubscriptionsVM
import fr.chenry.android.freshrss.utils.EmotionnalImageSubtext
import kotlinx.android.synthetic.main.fragment_subscriptions.*
import nl.komponents.kovenant.Promise
class SubscriptionsFragment: Fragment(), Observer<Subscriptions> {
class SubscriptionsFragment: Fragment(), Observer<SubscriptionViewItems> {
private val args: SubscriptionsFragmentArgs by navArgs()
private val section by lazy {args.section}
private val category by lazy {args.category}
val model by lazy {
if(category != VoidCategory)
ViewModelProvider(this, SubscriptionsFragmentCategoryVMF(category, section))
.get(AllSubscriptionsVM::class.java)
else when(section) {
ALL -> ViewModelProvider(this).get(AllSubscriptionsVM::class.java)
UNREAD -> ViewModelProvider(this).get(UnreadSubscriptionsVM::class.java)
FAVORITES -> ViewModelProvider(this).get(FavoritesSubscriptionsVM::class.java)
}
val key = "section=${section.name}${if(category != VoidCategory) ":category=${category.id}" else ""}"
ViewModelProvider(activity!!.viewModelStore, SubscriptionsFragmentCategoryVMF(category, section))
.get(key, SubscriptionsVM::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
......@@ -44,9 +42,7 @@ class SubscriptionsFragment: Fragment(), Observer<Subscriptions> {
initRecyclerView(view)
Store.refreshingPromise.observe(
viewLifecycleOwner,
Observer {toggleProgressCircle(model.subscriptions.value ?: listOf())})
Store.refreshingPromise.observe(viewLifecycleOwner, Observer {toggleProgressCircle(model.isEmpty())})
model.subscriptions.observe(viewLifecycleOwner, this)
val contentDescription = if(category != VoidCategory) {
......@@ -86,12 +82,13 @@ class SubscriptionsFragment: Fragment(), Observer<Subscriptions> {
return view
}
override fun onChanged(subscriptions: Subscriptions?) = toggleProgressCircle((subscriptions ?: listOf()))
override fun onChanged(subscriptions: SubscriptionViewItems?)
= toggleProgressCircle((subscriptions.isNullOrEmpty()))
fun onClick(subscription: Subscription) = when(section) {
FAVORITES -> subscription.id to READ
ALL -> subscription.id to READ
UNREAD -> subscription.id to ReadStatus.UNREAD
FAVORITES -> subscription to READ
ALL -> subscription to READ
UNREAD -> subscription to ReadStatus.UNREAD
}.let {
val direction = if(category == VoidCategory)
MainSubscriptionFragmentDirections.mainSubscriptionsToArticles(it.first, it.second) else
......@@ -117,8 +114,8 @@ class SubscriptionsFragment: Fragment(), Observer<Subscriptions> {
}
}
private fun toggleProgressCircle(subscriptions: Subscriptions) {
if(subscriptions.isNotEmpty()) {
private fun toggleProgressCircle(isEmpty: Boolean) {
if(!isEmpty) {
fragment_subscriptions_list.visibility = View.VISIBLE
fast_scroller.visibility = View.VISIBLE
......
......@@ -14,6 +14,7 @@ import fr.chenry.android.freshrss.databinding.FragmentSubscriptionBinding
import fr.chenry.android.freshrss.store.database.models.Subscription
import fr.chenry.android.freshrss.store.database.models.SubscriptionCategory
typealias SubscriptionViewItems = List<AbstractSubscriptionViewItem>
sealed class AbstractSubscriptionViewItem(header: SubscriptionViewHeaderItem) :
AbstractSectionableItem<SubscriptionViewHolder, SubscriptionViewHeaderItem>(header) {
abstract val virtualSubscription: Subscription
......
package fr.chenry.android.freshrss.components.subscriptions.adapters
import android.view.View
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener
import eu.davidea.flexibleadapter.items.IFlexible
import fr.chenry.android.freshrss.components.subscriptions.SubscriptionsFragment
import fr.chenry.android.freshrss.store.database.models.SubscriptionCategories
import fr.chenry.android.freshrss.store.database.models.Subscriptions
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.task
class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment) :
FlexibleAdapter<AbstractSubscriptionViewItem>(listOf(), null, true),
OnItemClickListener {
private val mediator = MediatorLiveData<List<AbstractSubscriptionViewItem>>()
private val subscriptionCategories
get() = fragment.model.subscriptionCategories.value ?: listOf()
private val subscriptions
get() = fragment.model.subscriptions.value ?: listOf()
private val grouper get() = fragment.model.grouper
init {
setStickyHeaders(true)
setDisplayHeadersAtStartUp(true)
mediator.observe(fragment.viewLifecycleOwner, Observer { updateDataSet(it, true) })
mediator.addSource(fragment.model.subscriptions) {
computeNewDataSet(it, subscriptionCategories) success { res -> mediator.postValue(res) }
}
mediator.addSource(fragment.model.subscriptionCategories) {
computeNewDataSet(subscriptions, it) success { res -> mediator.postValue(res) }
}
fragment.model.subscriptions.observe(fragment.viewLifecycleOwner, Observer { updateDataSet(it, true) })
addListener(this)
}
override fun isEmpty(): Boolean = fragment.model.subscriptions.value.isNullOrEmpty()
override fun isEmpty(): Boolean = fragment.model.isEmpty()
override fun onCreateBubbleText(position: Int): String? {
return when (val item = getGenericItem(position)) {
......@@ -55,26 +39,6 @@ class SubscriptionsFlexibleAdapter(private val fragment: SubscriptionsFragment)
private fun getGenericItem(position: Int): IFlexible<*>? = (this as FlexibleAdapter<*>).getItem(position)
private fun computeNewDataSet(
subscriptions: Subscriptions,
subscriptionCategories: SubscriptionCategories
): Promise<ArrayList<AbstractSubscriptionViewItem>, Exception> = task {
val targetSize = subscriptionCategories.size + subscriptions.size
val result = ArrayList<AbstractSubscriptionViewItem>(targetSize)
(0 until targetSize).forEach {
val newElt = if (it < subscriptionCategories.size)
SubscriptionCategoryViewItem(subscriptionCategories[it]) else
SubscriptionViewItem(
subscriptions[it - subscriptionCategories.size],
grouper(subscriptions[it - subscriptionCategories.size])
)
result.add(newElt)
}
result
}
override fun getItemId(position: Int): Long = getGenericItem(position)?.let {
when (it) {
is SubscriptionViewItem -> it.subscription.id.hashCode().toLong()
......
package fr.chenry.android.freshrss.store
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import fr.chenry.android.freshrss.FreshRSSApplication
import fr.chenry.android.freshrss.R
......@@ -30,7 +31,7 @@ class FreshRSSPreferences(private val application: FreshRSSApplication) {
return Try{SubscriptionSection.valueOf(pref)}.getOrDefault(SubscriptionSection.ALL)
}
set(value) {
sharedPreferences.edit().putString(subscriptionSectionKey, value.name).apply()
sharedPreferences.edit {putString(subscriptionSectionKey, value.name)}
}
private val refreshFrequencies by lazy {application.resources.getStringArray(R.array.refresh_frequencies)}
......