2019-01-18 10:12:08 +00:00
|
|
|
/*
|
2019-01-25 13:04:59 +00:00
|
|
|
* Copyright 2019 New Vector Ltd
|
2019-01-18 10:12:08 +00:00
|
|
|
*
|
2019-01-25 13:04:59 +00:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2019-01-18 10:12:08 +00:00
|
|
|
*
|
2019-01-25 13:04:59 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2019-01-18 10:12:08 +00:00
|
|
|
*/
|
|
|
|
|
2018-10-25 15:25:58 +00:00
|
|
|
package im.vector.matrix.android.internal.session.room.timeline
|
|
|
|
|
2019-01-16 18:25:43 +00:00
|
|
|
import androidx.lifecycle.LiveData
|
|
|
|
import androidx.paging.LivePagedListBuilder
|
|
|
|
import androidx.paging.PagedList
|
2018-10-25 15:25:58 +00:00
|
|
|
import com.zhuinden.monarchy.Monarchy
|
2019-03-13 21:30:05 +00:00
|
|
|
import im.vector.matrix.android.api.session.room.timeline.*
|
2018-10-25 15:25:58 +00:00
|
|
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
2018-11-15 18:00:15 +00:00
|
|
|
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
|
|
|
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
|
|
|
import im.vector.matrix.android.internal.database.model.EventEntityFields
|
|
|
|
import im.vector.matrix.android.internal.database.query.where
|
2018-12-18 11:13:46 +00:00
|
|
|
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
|
2019-01-07 18:38:36 +00:00
|
|
|
import im.vector.matrix.android.internal.util.LiveDataUtils
|
|
|
|
import im.vector.matrix.android.internal.util.PagingRequestHelper
|
2019-01-07 10:39:26 +00:00
|
|
|
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
2018-11-19 14:47:54 +00:00
|
|
|
import io.realm.Realm
|
|
|
|
import io.realm.RealmQuery
|
2019-03-13 21:30:05 +00:00
|
|
|
import io.realm.Sort
|
2018-10-25 15:25:58 +00:00
|
|
|
|
2019-01-12 09:33:39 +00:00
|
|
|
private const val PAGE_SIZE = 100
|
|
|
|
private const val PREFETCH_DISTANCE = 30
|
2019-01-11 15:35:49 +00:00
|
|
|
private const val EVENT_NOT_FOUND_INDEX = -1
|
2018-10-31 15:23:02 +00:00
|
|
|
|
2019-01-07 18:38:36 +00:00
|
|
|
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 {
|
2018-10-25 15:25:58 +00:00
|
|
|
|
2019-01-14 15:18:39 +00:00
|
|
|
private val eventInterceptors = ArrayList<TimelineEventInterceptor>()
|
2018-10-25 15:25:58 +00:00
|
|
|
|
2019-01-07 18:38:36 +00:00
|
|
|
override fun timeline(eventId: String?): LiveData<TimelineData> {
|
2018-11-29 17:35:24 +00:00
|
|
|
clearUnlinkedEvents()
|
2019-01-14 15:18:39 +00:00
|
|
|
val initialLoadKey = getInitialLoadKey(eventId)
|
2018-11-19 14:47:54 +00:00
|
|
|
val realmDataSourceFactory = monarchy.createDataSourceFactory {
|
|
|
|
buildDataSourceFactoryQuery(it, eventId)
|
2018-10-25 15:25:58 +00:00
|
|
|
}
|
|
|
|
val domainSourceFactory = realmDataSourceFactory
|
2018-12-18 11:13:46 +00:00
|
|
|
.map { eventEntity ->
|
|
|
|
val roomMember = roomMemberExtractor.extractFrom(eventEntity)
|
2019-01-14 15:18:39 +00:00
|
|
|
TimelineEvent(eventEntity.asDomain(), eventEntity.localId, roomMember)
|
2018-10-25 15:25:58 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 15:18:39 +00:00
|
|
|
val pagedListConfig = buildPagedListConfig()
|
2018-10-25 15:25:58 +00:00
|
|
|
|
2019-01-11 15:35:49 +00:00
|
|
|
val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig)
|
|
|
|
.setBoundaryCallback(boundaryCallback)
|
|
|
|
.setInitialLoadKey(initialLoadKey)
|
|
|
|
|
2019-01-07 18:38:36 +00:00
|
|
|
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)
|
|
|
|
}
|
2018-10-25 15:25:58 +00:00
|
|
|
}
|
2018-11-19 14:47:54 +00:00
|
|
|
|
2019-03-13 21:30:05 +00:00
|
|
|
override fun createTimeline(eventId: String?): Timeline {
|
|
|
|
return DefaultTimeline(roomId, eventId, monarchy, taskExecutor, boundaryCallback, contextOfEventTask, roomMemberExtractor)
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:18:39 +00:00
|
|
|
// PRIVATE FUNCTIONS ***************************************************************************
|
|
|
|
|
|
|
|
private fun getInitialLoadKey(eventId: String?): Int {
|
|
|
|
var initialLoadKey = 0
|
|
|
|
if (eventId != null) {
|
|
|
|
val indexOfEvent = indexOfEvent(eventId)
|
|
|
|
if (indexOfEvent == EVENT_NOT_FOUND_INDEX) {
|
|
|
|
fetchEvent(eventId)
|
|
|
|
} else {
|
|
|
|
initialLoadKey = indexOfEvent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return initialLoadKey
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun fetchEvent(eventId: String) {
|
|
|
|
val params = GetContextOfEventTask.Params(roomId, eventId)
|
|
|
|
contextOfEventTask.configureWith(params).executeBy(taskExecutor)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun buildPagedListConfig(): PagedList.Config {
|
|
|
|
return PagedList.Config.Builder()
|
|
|
|
.setEnablePlaceholders(false)
|
|
|
|
.setPageSize(PAGE_SIZE)
|
|
|
|
.setInitialLoadSizeHint(2 * PAGE_SIZE)
|
|
|
|
.setPrefetchDistance(PREFETCH_DISTANCE)
|
|
|
|
.build()
|
|
|
|
}
|
2019-01-07 18:38:36 +00:00
|
|
|
|
2018-11-29 17:35:24 +00:00
|
|
|
private fun clearUnlinkedEvents() {
|
2019-01-07 10:39:26 +00:00
|
|
|
monarchy.tryTransactionAsync { realm ->
|
2018-11-29 17:35:24 +00:00
|
|
|
val unlinkedEvents = EventEntity
|
|
|
|
.where(realm, roomId = roomId)
|
|
|
|
.equalTo(EventEntityFields.IS_UNLINKED, true)
|
|
|
|
.findAll()
|
|
|
|
unlinkedEvents.deleteAllFromRealm()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-11 15:35:49 +00:00
|
|
|
private fun indexOfEvent(eventId: String): Int {
|
|
|
|
var displayIndex = EVENT_NOT_FOUND_INDEX
|
2018-11-27 21:42:46 +00:00
|
|
|
monarchy.doWithRealm {
|
2019-01-11 15:35:49 +00:00
|
|
|
displayIndex = EventEntity.where(it, eventId = eventId).findFirst()?.displayIndex ?: EVENT_NOT_FOUND_INDEX
|
2018-11-27 21:42:46 +00:00
|
|
|
}
|
2019-01-11 15:35:49 +00:00
|
|
|
return displayIndex
|
2018-11-27 11:06:40 +00:00
|
|
|
}
|
|
|
|
|
2018-11-19 14:47:54 +00:00
|
|
|
private fun buildDataSourceFactoryQuery(realm: Realm, eventId: String?): RealmQuery<EventEntity> {
|
|
|
|
val query = if (eventId == null) {
|
|
|
|
EventEntity
|
2018-11-29 17:35:24 +00:00
|
|
|
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.LINKED_ONLY)
|
2018-11-19 14:47:54 +00:00
|
|
|
.equalTo("${EventEntityFields.CHUNK}.${ChunkEntityFields.IS_LAST}", true)
|
|
|
|
} else {
|
|
|
|
EventEntity
|
2018-11-29 17:35:24 +00:00
|
|
|
.where(realm, roomId = roomId, linkFilterMode = EventEntity.LinkFilterMode.BOTH)
|
2018-11-19 14:47:54 +00:00
|
|
|
.`in`("${EventEntityFields.CHUNK}.${ChunkEntityFields.EVENTS.EVENT_ID}", arrayOf(eventId))
|
|
|
|
}
|
2019-03-13 21:30:05 +00:00
|
|
|
return query.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
|
2018-11-19 14:47:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-25 15:25:58 +00:00
|
|
|
}
|