From 1269715b5cdc3e4d50467511c03f6fb044c7c06d Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 7 Jan 2019 19:38:36 +0100 Subject: [PATCH] Timeline : introduce timeline data class to allow listening for isLoadingForward and isLoadingBackward --- .../home/room/detail/RoomDetailFragment.kt | 29 +++++----- .../home/room/detail/RoomDetailViewModel.kt | 5 +- .../home/room/detail/RoomDetailViewState.kt | 7 +-- .../timeline/TimelineEventController.kt | 58 +++++++++++-------- .../main/java/im/vector/matrix/rx/RxRoom.kt | 5 +- .../matrix/android/api/session/room/Room.kt | 3 +- .../api/session/room/TimelineHolder.kt | 11 ---- .../api/session/room/timeline/TimelineData.kt | 10 ++++ .../session/room/timeline/TimelineService.kt | 9 +++ .../internal/session/room/DefaultRoom.kt | 9 +-- .../internal/session/room/RoomModule.kt | 12 +++- .../room/timeline/DefaultPaginationTask.kt | 9 +-- ...ineHolder.kt => DefaultTimelineService.kt} | 30 ++++++---- .../room/timeline/TimelineBoundaryCallback.kt | 46 +++++++++++---- .../android/internal/util/LiveDataUtils.kt | 38 ++++++++++++ .../internal/util/PagingRequestHelper.java | 5 ++ 16 files changed, 192 insertions(+), 94 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/TimelineHolder.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/{DefaultTimelineHolder.kt => DefaultTimelineService.kt} (73%) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/LiveDataUtils.kt diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 812b64b356..fe1f379879 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -6,9 +6,9 @@ import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.airbnb.mvrx.Success import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel -import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.ToolbarConfigurable @@ -19,7 +19,6 @@ import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_detail.* import org.koin.android.ext.android.inject import org.koin.core.parameter.parametersOf -import timber.log.Timber @Parcelize data class RoomDetailArgs( @@ -72,7 +71,7 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) - //scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager) + scrollOnNewMessageCallback = ScrollOnNewMessageCallback(layoutManager) recyclerView.layoutManager = layoutManager //timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) } recyclerView.setHasFixedSize(true) @@ -82,16 +81,19 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { } private fun renderState(state: RoomDetailViewState) { - Timber.v("Render state") - val timeline = state.asyncTimeline() - if (timeline != null) { - renderTimeline(timeline) - } - renderRoomSummary(state.asyncRoomSummary()) + renderRoomSummary(state) + renderTimeline(state) } - private fun renderRoomSummary(roomSummary: RoomSummary?) { - roomSummary?.let { + private fun renderTimeline(state: RoomDetailViewState) { + when (state.asyncTimelineData) { + is Success -> timelineEventController.update(state.asyncTimelineData()) + + } + } + + private fun renderRoomSummary(state: RoomDetailViewState) { + state.asyncRoomSummary()?.let { toolbarTitleView.text = it.displayName AvatarRenderer.render(it, toolbarAvatarImageView) if (it.topic.isNotEmpty()) { @@ -103,11 +105,6 @@ class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback { } } - private fun renderTimeline(timeline: Timeline?) { - //scrollOnNewMessageCallback.hasBeenUpdated.set(true) - timelineEventController.timeline = timeline - } - // TimelineEventController.Callback ************************************************************ override fun onUrlClicked(url: String) { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index de89ed16f2..05a6881300 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -53,9 +53,10 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, private fun observeTimeline() { room.rx().timeline(eventId) - .execute { asyncTimeline -> - copy(asyncTimeline = asyncTimeline) + .execute { timelineData -> + copy(asyncTimelineData= timelineData) } } + } \ No newline at end of file diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt index 83f048513c..1bd9e3fc8e 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewState.kt @@ -1,19 +1,16 @@ package im.vector.riotredesign.features.home.room.detail -import android.arch.paging.PagedList import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.room.model.RoomSummary - -typealias Timeline = PagedList +import im.vector.matrix.android.api.session.room.timeline.TimelineData data class RoomDetailViewState( val roomId: String, val eventId: String?, val asyncRoomSummary: Async = Uninitialized, - val asyncTimeline: Async = Uninitialized + val asyncTimelineData: Async = Uninitialized ) : MvRxState { constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId) diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt index ecda3c0255..88649561d1 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt @@ -5,9 +5,9 @@ import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyController import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.features.home.LoadingItemModel_ -import im.vector.riotredesign.features.home.room.detail.Timeline class TimelineEventController(private val roomId: String, private val messageItemFactory: MessageItemFactory, @@ -36,28 +36,38 @@ class TimelineEventController(private val roomId: String, } } - private var snapshotList: List? = emptyList() - var timeline: Timeline? = null - set(value) { - field?.removeWeakCallback(pagedListCallback) - field = value - field?.addWeakCallback(null, pagedListCallback) - buildSnapshotList() - } - + private var snapshotList: List = emptyList() + private var timelineData: TimelineData? = null var callback: Callback? = null - override fun buildModels() { - buildModels(snapshotList) + fun update(timelineData: TimelineData?) { + timelineData?.events?.removeWeakCallback(pagedListCallback) + this.timelineData = timelineData + timelineData?.events?.addWeakCallback(null, pagedListCallback) + buildSnapshotList() } - private fun buildModels(data: List?) { - if (data.isNullOrEmpty()) { + override fun buildModels() { + buildModelsWith( + snapshotList, + timelineData?.isLoadingForward ?: false, + timelineData?.isLoadingBackward ?: false + ) + } + + private fun buildModelsWith(events: List, + isLoadingForward: Boolean, + isLoadingBackward: Boolean) { + if (events.isEmpty()) { return } - for (index in 0 until data.size) { - val event = data[index] ?: continue - val nextEvent = if (index + 1 < data.size) data[index + 1] else null + LoadingItemModel_() + .id(roomId + "forward_loading_item") + .addIf(isLoadingForward, this) + + for (index in 0 until events.size) { + val event = events[index] ?: continue + val nextEvent = if (index + 1 < events.size) events[index + 1] else null val date = event.root.localDateTime() val nextDate = nextEvent?.root?.localDateTime() @@ -65,10 +75,13 @@ class TimelineEventController(private val roomId: String, val item = when (event.root.type) { EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, addDaySeparator, date, callback) - else -> textItemFactory.create(event) + else -> textItemFactory.create(event) } + item - ?.onBind { timeline?.loadAround(index) } + ?.onBind { + timelineData?.events?.loadAround(index) + } ?.id(event.localId) ?.addTo(this) @@ -78,15 +91,14 @@ class TimelineEventController(private val roomId: String, } } - //It's a hack at the moment - val isLastEvent = data.last()?.root?.type == EventType.STATE_ROOM_CREATE LoadingItemModel_() .id(roomId + "backward_loading_item") - .addIf(!isLastEvent, this) + .addIf(isLoadingBackward, this) + } private fun buildSnapshotList() { - snapshotList = timeline?.snapshot() ?: emptyList() + snapshotList = timelineData?.events?.snapshot() ?: emptyList() requestModelBuild() } diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt index 8699235690..510d7e018e 100644 --- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt @@ -1,9 +1,8 @@ package im.vector.matrix.rx -import android.arch.paging.PagedList -import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.timeline.TimelineData import io.reactivex.Observable class RxRoom(private val room: Room) { @@ -12,7 +11,7 @@ class RxRoom(private val room: Room) { return room.roomSummary.asObservable() } - fun timeline(eventId: String? = null): Observable> { + fun timeline(eventId: String? = null): Observable { return room.timeline(eventId).asObservable() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt index 412131f171..8e17a0a181 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt @@ -3,9 +3,10 @@ package im.vector.matrix.android.api.session.room import android.arch.lifecycle.LiveData import im.vector.matrix.android.api.session.room.model.MyMembership import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.api.util.Cancelable -interface Room : TimelineHolder, SendService { +interface Room : TimelineService, SendService { val roomId: String diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/TimelineHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/TimelineHolder.kt deleted file mode 100644 index fc01a47b59..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/TimelineHolder.kt +++ /dev/null @@ -1,11 +0,0 @@ -package im.vector.matrix.android.api.session.room - -import android.arch.lifecycle.LiveData -import android.arch.paging.PagedList -import im.vector.matrix.android.api.session.events.model.EnrichedEvent - -interface TimelineHolder { - - fun timeline(eventId: String? = null): LiveData> - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt new file mode 100644 index 0000000000..556c95f980 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineData.kt @@ -0,0 +1,10 @@ +package im.vector.matrix.android.api.session.room.timeline + +import android.arch.paging.PagedList +import im.vector.matrix.android.api.session.events.model.EnrichedEvent + +data class TimelineData( + val events: PagedList, + val isLoadingForward: Boolean = false, + val isLoadingBackward: Boolean = false +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt new file mode 100644 index 0000000000..d1069dad08 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineService.kt @@ -0,0 +1,9 @@ +package im.vector.matrix.android.api.session.room.timeline + +import android.arch.lifecycle.LiveData + +interface TimelineService { + + fun timeline(eventId: String? = null): LiveData + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index 6b373842f5..4cf645a50d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -9,10 +9,11 @@ import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.SendService -import im.vector.matrix.android.api.session.room.TimelineHolder +import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.MyMembership import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.RoomSummaryEntity @@ -32,7 +33,7 @@ internal data class DefaultRoom( private val loadRoomMembersTask by inject() private val monarchy by inject() - private val timelineHolder by inject { parametersOf(roomId) } + private val timelineService by inject { parametersOf(roomId) } private val sendService by inject { parametersOf(roomId) } private val taskExecutor by inject() @@ -47,8 +48,8 @@ internal data class DefaultRoom( } } - override fun timeline(eventId: String?): LiveData> { - return timelineHolder.timeline(eventId) + override fun timeline(eventId: String?): LiveData { + return timelineService.timeline(eventId) } override fun loadRoomMembersIfNeeded(): Cancelable { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 6ecc63eeba..eaee079849 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -2,14 +2,20 @@ package im.vector.matrix.android.internal.session.room import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.room.SendService -import im.vector.matrix.android.api.session.room.TimelineHolder import im.vector.matrix.android.api.session.room.send.EventFactory +import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.internal.session.DefaultSession import im.vector.matrix.android.internal.session.room.members.DefaultLoadRoomMembersTask import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor import im.vector.matrix.android.internal.session.room.send.DefaultSendService -import im.vector.matrix.android.internal.session.room.timeline.* +import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask +import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask +import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService +import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask +import im.vector.matrix.android.internal.session.room.timeline.PaginationTask +import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback +import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import im.vector.matrix.android.internal.util.PagingRequestHelper import org.koin.dsl.module.module import retrofit2.Retrofit @@ -50,7 +56,7 @@ class RoomModule { val helper = PagingRequestHelper(Executors.newSingleThreadExecutor()) val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), get(), helper) val roomMemberExtractor = RoomMemberExtractor(get(), roomId) - DefaultTimelineHolder(roomId, get(), get(), timelineBoundaryCallback, get(), roomMemberExtractor) as TimelineHolder + DefaultTimelineService(roomId, get(), get(), timelineBoundaryCallback, get(), roomMemberExtractor) as TimelineService } factory { (roomId: String) -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt index 5dfb3c3f48..a9a549a323 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt @@ -1,10 +1,9 @@ package im.vector.matrix.android.internal.session.room.timeline import arrow.core.Try -import arrow.core.failure -import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.util.FilterUtil @@ -12,7 +11,7 @@ internal interface PaginationTask : Task data class Params( val roomId: String, - val from: String?, + val from: String, val direction: PaginationDirection, val limit: Int ) @@ -24,9 +23,6 @@ internal class DefaultPaginationTask(private val roomAPI: RoomAPI, ) : PaginationTask { override fun execute(params: PaginationTask.Params): Try { - if (params.from == null) { - return RuntimeException("From token shouldn't be null").failure() - } val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() return executeRequest { apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter) @@ -36,4 +32,5 @@ internal class DefaultPaginationTask(private val roomAPI: RoomAPI, .map { chunk } } } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt similarity index 73% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index 8db811ef36..401da51dac 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -6,7 +6,8 @@ import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor import im.vector.matrix.android.api.session.events.model.EnrichedEvent -import im.vector.matrix.android.api.session.room.TimelineHolder +import im.vector.matrix.android.api.session.room.timeline.TimelineData +import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.ChunkEntityFields import im.vector.matrix.android.internal.database.model.EventEntity @@ -15,23 +16,25 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith +import im.vector.matrix.android.internal.util.LiveDataUtils +import im.vector.matrix.android.internal.util.PagingRequestHelper import im.vector.matrix.android.internal.util.tryTransactionAsync import io.realm.Realm import io.realm.RealmQuery private const val PAGE_SIZE = 30 -internal class DefaultTimelineHolder(private val roomId: String, - private val monarchy: Monarchy, - private val taskExecutor: TaskExecutor, - private val boundaryCallback: TimelineBoundaryCallback, - private val contextOfEventTask: GetContextOfEventTask, - private val roomMemberExtractor: RoomMemberExtractor -) : TimelineHolder { +internal class DefaultTimelineService(private val roomId: String, + private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, + private val boundaryCallback: TimelineBoundaryCallback, + private val contextOfEventTask: GetContextOfEventTask, + private val roomMemberExtractor: RoomMemberExtractor +) : TimelineService { private val eventInterceptors = ArrayList() - override fun timeline(eventId: String?): LiveData> { + override fun timeline(eventId: String?): LiveData { clearUnlinkedEvents() if (eventId != null) { fetchEventIfNeeded(eventId) @@ -51,9 +54,16 @@ internal class DefaultTimelineHolder(private val roomId: String, .build() val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback) - return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) + val eventsLiveData = monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) + + return LiveDataUtils.combine(eventsLiveData, boundaryCallback.status) { events, status -> + val isLoadingForward = status.before == PagingRequestHelper.Status.RUNNING + val isLoadingBackward = status.after == PagingRequestHelper.Status.RUNNING + TimelineData(events, isLoadingForward, isLoadingBackward) + } } + private fun clearUnlinkedEvents() { monarchy.tryTransactionAsync { realm -> val unlinkedEvents = EventEntity diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt index ba57438646..a9ff32d0dc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt @@ -1,5 +1,6 @@ package im.vector.matrix.android.internal.session.room.timeline +import android.arch.lifecycle.LiveData import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback @@ -20,35 +21,60 @@ internal class TimelineBoundaryCallback(private val roomId: String, var limit = 30 + val status = object : LiveData() { + + init { + value = PagingRequestHelper.StatusReport.createDefault() + } + + val listener = PagingRequestHelper.Listener { postValue(it) } + + override fun onActive() { + helper.addListener(listener) + } + + override fun onInactive() { + helper.removeListener(listener) + } + } + override fun onZeroItemsLoaded() { // actually, it's not possible } override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) { + val token = itemAtEnd.root.eventId?.let { getToken(it, PaginationDirection.BACKWARDS) } + ?: return + helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) { - runPaginationRequest(it, itemAtEnd, PaginationDirection.BACKWARDS) + runPaginationRequest(it, token, PaginationDirection.BACKWARDS) } } override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) { + val token = itemAtFront.root.eventId?.let { getToken(it, PaginationDirection.FORWARDS) } + ?: return + helper.runIfNotRunning(PagingRequestHelper.RequestType.BEFORE) { - runPaginationRequest(it, itemAtFront, PaginationDirection.FORWARDS) + runPaginationRequest(it, token, PaginationDirection.FORWARDS) } } - private fun runPaginationRequest(requestCallback: PagingRequestHelper.Request.Callback, - item: EnrichedEvent, - direction: PaginationDirection) { + private fun getToken(eventId: String, direction: PaginationDirection): String? { var token: String? = null monarchy.doWithRealm { realm -> - if (item.root.eventId == null) { - return@doWithRealm - } - val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(item.root.eventId)).firstOrNull() + val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(eventId)).firstOrNull() token = if (direction == PaginationDirection.FORWARDS) chunkEntity?.nextToken else chunkEntity?.prevToken } + return token + } + + private fun runPaginationRequest(requestCallback: PagingRequestHelper.Request.Callback, + from: String, + direction: PaginationDirection) { + val params = PaginationTask.Params(roomId = roomId, - from = token, + from = from, direction = direction, limit = limit) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/LiveDataUtils.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/LiveDataUtils.kt new file mode 100644 index 0000000000..55de4d34f5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/LiveDataUtils.kt @@ -0,0 +1,38 @@ +package im.vector.matrix.android.internal.util + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MediatorLiveData + +object LiveDataUtils { + + fun combine(firstSource: LiveData, + secondSource: LiveData, + mapper: (FIRST, SECOND) -> OUT): LiveData { + + return MediatorLiveData().apply { + var firstValue: FIRST? = null + var secondValue: SECOND? = null + + val valueDispatcher = { + firstValue?.let { safeFirst -> + secondValue?.let { safeSecond -> + val mappedValue = mapper(safeFirst, safeSecond) + postValue(mappedValue) + } + } + } + + + addSource(firstSource) { + firstValue = it + valueDispatcher() + } + + addSource(secondSource) { + secondValue = it + valueDispatcher() + } + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/PagingRequestHelper.java b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/PagingRequestHelper.java index 7e77fead88..fa1ad08692 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/PagingRequestHelper.java +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/PagingRequestHelper.java @@ -379,6 +379,11 @@ public class PagingRequestHelper { @NonNull private final Throwable[] mErrors; + public static StatusReport createDefault() { + final Throwable[] errors = {}; + return new StatusReport(Status.SUCCESS, Status.SUCCESS, Status.SUCCESS, errors); + } + StatusReport(@NonNull Status initial, @NonNull Status before, @NonNull Status after, @NonNull Throwable[] errors) { this.initial = initial;