Using flow to show items counter

This commit is contained in:
Maxime Naturel 2022-02-07 16:10:26 +01:00
parent c7dae341c0
commit 0aaa650ac3
8 changed files with 56 additions and 47 deletions

View File

@ -58,6 +58,7 @@ ext.libs = [
'lifecycleCommon' : "androidx.lifecycle:lifecycle-common:$lifecycle", 'lifecycleCommon' : "androidx.lifecycle:lifecycle-common:$lifecycle",
'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle", 'lifecycleLivedata' : "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle",
'lifecycleProcess' : "androidx.lifecycle:lifecycle-process:$lifecycle", 'lifecycleProcess' : "androidx.lifecycle:lifecycle-process:$lifecycle",
'lifecycleRuntimeKtx' : "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle",
'datastore' : "androidx.datastore:datastore:1.0.0", 'datastore' : "androidx.datastore:datastore:1.0.0",
'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0", 'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0",
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2", 'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
@ -141,4 +142,4 @@ ext.libs = [
'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1", 'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1",
'junit' : "junit:junit:4.13.2" 'junit' : "junit:junit:4.13.2"
] ]
] ]

View File

@ -73,9 +73,6 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
freeCompilerArgs += [
"-Xopt-in=kotlin.RequiresOptIn"
]
} }
sourceSets { sourceSets {

View File

@ -29,8 +29,10 @@ import io.realm.kotlin.toFlow
import io.realm.kotlin.where import io.realm.kotlin.where
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.ActiveSpaceFilter
import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.isNormalized import org.matrix.android.sdk.api.query.isNormalized
@ -63,7 +65,8 @@ internal class RoomSummaryDataSource @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider, private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryMapper: RoomSummaryMapper, private val roomSummaryMapper: RoomSummaryMapper,
private val queryStringValueProcessor: QueryStringValueProcessor private val queryStringValueProcessor: QueryStringValueProcessor,
private val coroutineDispatchers: MatrixCoroutineDispatchers
) { ) {
fun getRoomSummary(roomIdOrAlias: String): RoomSummary? { fun getRoomSummary(roomIdOrAlias: String): RoomSummary? {
@ -237,28 +240,16 @@ internal class RoomSummaryDataSource @Inject constructor(
} }
} }
// @OptIn(ExperimentalCoroutinesApi::class)
// fun getCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> = callbackFlow {
// val realmResult = realmSessionProvider.withRealm { realm ->
// roomSummariesQuery(realm, queryParams).findAllAsync()
// }
// val changeListener = RealmChangeListener<RealmResults<RoomSummaryEntity>> {
// trySendBlocking(it.size)
// .onFailure { throwable -> Timber.e(throwable) }
// }
// realmResult.addChangeListener(changeListener)
// awaitClose { realmResult.removeChangeListener(changeListener) }
// }
fun getCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> = fun getCountFlow(queryParams: RoomSummaryQueryParams): Flow<Int> =
// TODO handle properly threads and dispatchers otherwise use livedata of monarchy
realmSessionProvider realmSessionProvider
.withRealm { realm -> roomSummariesQuery(realm, queryParams).findAllAsync() } .withRealm { realm -> roomSummariesQuery(realm, queryParams).findAllAsync() }
.toFlow() .toFlow()
// need to create the flow on a context dispatcher with a thread with attached Looper
.flowOn(coroutineDispatchers.main)
.map { it.size } .map { it.size }
.flowOn(Dispatchers.IO) .flowOn(coroutineDispatchers.io)
.distinctUntilChanged()
// TODO should we improve how we update notification count with flow ??
fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {
var notificationCount: RoomAggregateNotificationCount? = null var notificationCount: RoomAggregateNotificationCount? = null
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->

View File

@ -355,6 +355,7 @@ dependencies {
// Lifecycle // Lifecycle
implementation libs.androidx.lifecycleLivedata implementation libs.androidx.lifecycleLivedata
implementation libs.androidx.lifecycleProcess implementation libs.androidx.lifecycleProcess
implementation libs.androidx.lifecycleRuntimeKtx
implementation libs.androidx.datastore implementation libs.androidx.datastore
implementation libs.androidx.datastorepreferences implementation libs.androidx.datastorepreferences

View File

@ -23,6 +23,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -50,8 +52,10 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -287,11 +291,14 @@ class RoomListFragment @Inject constructor(
)) ))
checkEmptyState() checkEmptyState()
} }
// TODO use flow if possible ? lifecycleScope.launch {
section.itemCount.observe(viewLifecycleOwner) { count -> section.itemCount
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
itemCount = count .collect { count ->
)) sectionAdapter.updateSection(
sectionAdapter.roomsSectionData.copy(itemCount = count)
)
}
} }
section.notificationCount.observe(viewLifecycleOwner) { counts -> section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(
@ -332,11 +339,14 @@ class RoomListFragment @Inject constructor(
isLoading = false)) isLoading = false))
checkEmptyState() checkEmptyState()
} }
// TODO use flow instead ? lifecycleScope.launch {
section.itemCount.observe(viewLifecycleOwner) { count -> section.itemCount
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
itemCount = count .collect { count ->
)) sectionAdapter.updateSection(
sectionAdapter.roomsSectionData.copy(itemCount = count)
)
}
} }
section.notificationCount.observe(viewLifecycleOwner) { counts -> section.notificationCount.observe(viewLifecycleOwner) { counts ->
sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy( sectionAdapter.updateSection(sectionAdapter.roomsSectionData.copy(

View File

@ -68,11 +68,18 @@ class RoomListSectionBuilderGroup(
it.memberships = Membership.activeMemberships() it.memberships = Membership.activeMemberships()
}, },
{ qpm -> { qpm ->
// TODO find a way to show the filtered rooms count ?
val name = stringProvider.getString(R.string.bottom_action_rooms) val name = stringProvider.getString(R.string.bottom_action_rooms)
session.getFilteredPagedRoomSummariesLive(qpm) session.getFilteredPagedRoomSummariesLive(qpm)
.let { updatableFilterLivePageResult -> .let { updatableFilterLivePageResult ->
onUpdatable(updatableFilterLivePageResult) onUpdatable(updatableFilterLivePageResult)
sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList)) sections.add(
RoomsSection(
sectionName = name,
livePages = updatableFilterLivePageResult.livePagedList,
itemCount = session.getRoomCountFlow(qpm)
)
)
} }
} }
) )
@ -242,7 +249,6 @@ class RoomListSectionBuilderGroup(
@StringRes nameRes: Int, @StringRes nameRes: Int,
notifyOfLocalEcho: Boolean = false, notifyOfLocalEcho: Boolean = false,
query: (RoomSummaryQueryParams.Builder) -> Unit) { query: (RoomSummaryQueryParams.Builder) -> Unit) {
// TODO check when this class is used: difference with RoomListSectionBuilderSpace ?
withQueryParams( withQueryParams(
{ query.invoke(it) }, { query.invoke(it) },
{ roomQueryParams -> { roomQueryParams ->
@ -252,7 +258,6 @@ class RoomListSectionBuilderGroup(
activeSpaceUpdaters.add(it) activeSpaceUpdaters.add(it)
}.livePagedList }.livePagedList
.let { livePagedList -> .let { livePagedList ->
// TODO should we improve this ?
// use it also as a source to update count // use it also as a source to update count
livePagedList.asFlow() livePagedList.asFlow()
.onEach { .onEach {
@ -267,7 +272,8 @@ class RoomListSectionBuilderGroup(
RoomsSection( RoomsSection(
sectionName = name, sectionName = name,
livePages = livePagedList, livePages = livePagedList,
notifyOfLocalEcho = notifyOfLocalEcho notifyOfLocalEcho = notifyOfLocalEcho,
itemCount = session.getRoomCountFlow(roomQueryParams)
) )
) )
} }

View File

@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.ActiveSpaceFilter
@ -87,11 +88,18 @@ class RoomListSectionBuilderSpace(
it.memberships = Membership.activeMemberships() it.memberships = Membership.activeMemberships()
}, },
{ qpm -> { qpm ->
// TODO find a way to show the filtered rooms count ?
val name = stringProvider.getString(R.string.bottom_action_rooms) val name = stringProvider.getString(R.string.bottom_action_rooms)
session.getFilteredPagedRoomSummariesLive(qpm) session.getFilteredPagedRoomSummariesLive(qpm)
.let { updatableFilterLivePageResult -> .let { updatableFilterLivePageResult ->
onUpdatable(updatableFilterLivePageResult) onUpdatable(updatableFilterLivePageResult)
sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList)) sections.add(
RoomsSection(
sectionName = name,
livePages = updatableFilterLivePageResult.livePagedList,
itemCount = session.getRoomCountFlow(qpm)
)
)
} }
} }
) )
@ -261,7 +269,8 @@ class RoomListSectionBuilderSpace(
RoomsSection( RoomsSection(
sectionName = stringProvider.getString(R.string.suggested_header), sectionName = stringProvider.getString(R.string.suggested_header),
liveSuggested = liveSuggestedRooms, liveSuggested = liveSuggestedRooms,
notifyOfLocalEcho = false notifyOfLocalEcho = false,
itemCount = suggestedRoomsFlow.map { suggestions -> suggestions.size }
) )
) )
} }
@ -374,7 +383,6 @@ class RoomListSectionBuilderSpace(
// use it also as a source to update count // use it also as a source to update count
livePagedList.asFlow() livePagedList.asFlow()
.onEach { .onEach {
// TODO should we improve this ?
Timber.v("Thread space list: ${Thread.currentThread()}") Timber.v("Thread space list: ${Thread.currentThread()}")
sections.find { it.sectionName == name } sections.find { it.sectionName == name }
?.notificationCount ?.notificationCount
@ -395,16 +403,11 @@ class RoomListSectionBuilderSpace(
RoomsSection( RoomsSection(
sectionName = name, sectionName = name,
livePages = livePagedList, livePages = livePagedList,
notifyOfLocalEcho = notifyOfLocalEcho notifyOfLocalEcho = notifyOfLocalEcho,
itemCount = session.getRoomCountFlow(roomQueryParams)
) )
) )
} }
// TODO extract into a dedicated private method
session.getRoomCountFlow(roomQueryParams)
.onEach { count -> sections.find { section -> section.sectionName == name }?.itemCount?.postValue(count) }
.flowOn(Dispatchers.Default)
.launchIn(viewModelScope)
} }
) )

View File

@ -19,6 +19,7 @@ package im.vector.app.features.home.room.list
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.paging.PagedList import androidx.paging.PagedList
import kotlinx.coroutines.flow.Flow
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
@ -30,8 +31,7 @@ data class RoomsSection(
val liveList: LiveData<List<RoomSummary>>? = null, val liveList: LiveData<List<RoomSummary>>? = null,
val liveSuggested: LiveData<SuggestedRoomInfo>? = null, val liveSuggested: LiveData<SuggestedRoomInfo>? = null,
val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true), val isExpanded: MutableLiveData<Boolean> = MutableLiveData(true),
// TODO expose a Flow<Int> instead ? val itemCount: Flow<Int>,
val itemCount: MutableLiveData<Int> = MutableLiveData(0),
val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)), val notificationCount: MutableLiveData<RoomAggregateNotificationCount> = MutableLiveData(RoomAggregateNotificationCount(0, 0)),
val notifyOfLocalEcho: Boolean = false val notifyOfLocalEcho: Boolean = false
) )