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 5f5810bba7..c4197eeffc 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 @@ -17,6 +17,10 @@ class TimelineEventController(private val roomId: String, EpoxyAsyncUtil.getAsyncBackgroundHandler() ) { + init { + setFilterDuplicates(true) + } + private val pagedListCallback = object : PagedList.Callback() { override fun onChanged(position: Int, count: Int) { buildSnapshotList() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt index 15ba170e9f..4c3abab0fd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt @@ -3,35 +3,51 @@ package im.vector.matrix.android.internal.database.helper import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.mapper.asEntity +import im.vector.matrix.android.internal.database.mapper.fillWith import im.vector.matrix.android.internal.database.model.ChunkEntity +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.fastContains import im.vector.matrix.android.internal.database.query.find import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import io.realm.Sort +import io.realm.kotlin.createObject +internal fun ChunkEntity.isUnlinked(): Boolean { + return events.where().equalTo(EventEntityFields.IS_UNLINKED, true).findAll().isNotEmpty() +} -internal fun ChunkEntity.merge(chunkEntity: ChunkEntity, +internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity, direction: PaginationDirection) { + val isChunkToMergeUnlinked = chunkToMerge.isUnlinked() + val isCurrentChunkUnlinked = this.isUnlinked() + val isUnlinked = isCurrentChunkUnlinked && isChunkToMergeUnlinked - chunkEntity.events.forEach { - addOrUpdate(it.asDomain(), direction) + if (isCurrentChunkUnlinked && !isChunkToMergeUnlinked) { + this.events.forEach { it.isUnlinked = false } } + val eventsToMerge: List if (direction == PaginationDirection.FORWARDS) { - nextToken = chunkEntity.nextToken + this.nextToken = chunkToMerge.nextToken + this.isLast = chunkToMerge.isLast + eventsToMerge = chunkToMerge.events.reversed() } else { - prevToken = chunkEntity.prevToken + this.prevToken = chunkToMerge.prevToken + eventsToMerge = chunkToMerge.events + } + eventsToMerge.forEach { + addOrUpdate(it.asDomain(), direction, isUnlinked = isUnlinked) } } internal fun ChunkEntity.addAll(events: List, direction: PaginationDirection, - stateIndexOffset: Int = 0) { + stateIndexOffset: Int = 0, + isUnlinked: Boolean = false) { events.forEach { event -> - addOrUpdate(event, direction, stateIndexOffset) + addOrUpdate(event, direction, stateIndexOffset, isUnlinked) } } @@ -41,7 +57,8 @@ internal fun ChunkEntity.updateDisplayIndexes() { internal fun ChunkEntity.addOrUpdate(event: Event, direction: PaginationDirection, - stateIndexOffset: Int = 0) { + stateIndexOffset: Int = 0, + isUnlinked: Boolean = false) { if (!isManaged) { throw IllegalStateException("Chunk entity should be managed to use fast contains") } @@ -60,15 +77,17 @@ internal fun ChunkEntity.addOrUpdate(event: Event, } } + val eventEntity: EventEntity? if (!events.fastContains(event.eventId)) { - val eventEntity = event.asEntity() - eventEntity.stateIndex = currentStateIndex + eventEntity = realm.createObject() + eventEntity.fillWith(event) val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size events.add(position, eventEntity) } else { - val eventEntity = events.find(event.eventId) - eventEntity?.stateIndex = currentStateIndex + eventEntity = events.find(event.eventId) } + eventEntity?.stateIndex = currentStateIndex + eventEntity?.isUnlinked = isUnlinked } internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt index 07018c3523..7c9c223af8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt @@ -19,7 +19,9 @@ internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) { } } -internal fun RoomEntity.addStateEvents(stateEvents: List, stateIndex: Int = Int.MIN_VALUE) { +internal fun RoomEntity.addStateEvents(stateEvents: List, + stateIndex: Int = Int.MIN_VALUE, + isUnlinked: Boolean = false) { if (!isManaged) { throw IllegalStateException("Chunk entity should be managed to use fast contains") } @@ -29,6 +31,7 @@ internal fun RoomEntity.addStateEvents(stateEvents: List, stateIndex: Int } val eventEntity = event.asEntity() eventEntity.stateIndex = stateIndex + eventEntity.isUnlinked = isUnlinked untimelinedStateEvents.add(eventEntity) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index 96ba986afb..9aa040f151 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -14,7 +14,8 @@ internal open class EventEntity(var eventId: String = "", var age: Long? = 0, var redacts: String? = null, var stateIndex: Int = 0, - var displayIndex: Int = 0 + var displayIndex: Int = 0, + var isUnlinked: Boolean = false ) : RealmObject() { companion object { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 91a828c7ed..7f2d69c3f8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -27,6 +27,7 @@ internal fun EventEntity.Companion.where(realm: Realm, roomId: String? = null, t if (type != null) { query.equalTo(EventEntityFields.TYPE, type) } + query.notEqualTo(EventEntityFields.IS_UNLINKED, true) return query } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 4b9ddb6623..8c88122c55 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -14,6 +14,7 @@ import im.vector.matrix.android.internal.session.room.RoomAvatarResolver import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater import im.vector.matrix.android.internal.session.room.members.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.members.RoomMemberDisplayNameResolver +import im.vector.matrix.android.internal.util.md5 import io.realm.RealmConfiguration import org.koin.dsl.context.ModuleDefinition import org.koin.dsl.module.Module @@ -31,7 +32,8 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module scope(DefaultSession.SCOPE) { val context = get() - val directory = File(context.filesDir, sessionParams.credentials.userId) + val childPath = sessionParams.credentials.userId.md5() + val directory = File(context.filesDir, childPath) RealmConfiguration.Builder() .directory(directory) @@ -47,7 +49,7 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module } scope(DefaultSession.SCOPE) { - val retrofitBuilder = get() as Retrofit.Builder + val retrofitBuilder = get() retrofitBuilder .baseUrl(sessionParams.homeServerConnectionConfig.homeServerUri.toString()) .build() 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 e2604ded43..7bed72864c 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 @@ -8,6 +8,7 @@ import im.vector.matrix.android.internal.session.DefaultSession import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineHolder +import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventRequest import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback import im.vector.matrix.android.internal.util.PagingRequestHelper @@ -35,6 +36,10 @@ class RoomModule : Module { PaginationRequest(get(), get(), get()) } + scope(DefaultSession.SCOPE) { + GetContextOfEventRequest(get(), get(), get()) + } + scope(DefaultSession.SCOPE) { val sessionParams = get() EventFactory(sessionParams.credentials) @@ -43,10 +48,9 @@ class RoomModule : Module { factory { (roomId: String) -> val helper = PagingRequestHelper(Executors.newSingleThreadExecutor()) val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), helper) - DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback) as TimelineHolder + DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback, get()) as TimelineHolder } - factory { (roomId: String) -> DefaultSendService(roomId, get(), get()) as SendService } 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/DefaultTimelineHolder.kt index 5f24e15517..65c3edd534 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/DefaultTimelineHolder.kt @@ -4,6 +4,7 @@ import android.arch.lifecycle.LiveData import android.arch.paging.LivePagedListBuilder import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.MatrixCallback 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 @@ -20,7 +21,8 @@ private const val PAGE_SIZE = 30 internal class DefaultTimelineHolder(private val roomId: String, private val monarchy: Monarchy, - private val boundaryCallback: TimelineBoundaryCallback + private val boundaryCallback: TimelineBoundaryCallback, + private val contextOfEventRequest: GetContextOfEventRequest ) : TimelineHolder { private val eventInterceptors = ArrayList() @@ -32,7 +34,7 @@ internal class DefaultTimelineHolder(private val roomId: String, override fun timeline(eventId: String?): LiveData> { if (eventId != null) { - fetchEventIfNeeded() + fetchEventIfNeeded(eventId) } val realmDataSourceFactory = monarchy.createDataSourceFactory { buildDataSourceFactoryQuery(it, eventId) @@ -60,8 +62,18 @@ internal class DefaultTimelineHolder(private val roomId: String, return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) } - private fun fetchEventIfNeeded() { + private fun fetchEventIfNeeded(eventId: String) { + if (!isEventPersisted(eventId)) { + contextOfEventRequest.execute(roomId, eventId, object : MatrixCallback {}) + } + } + private fun isEventPersisted(eventId: String): Boolean { + var isEventPersisted = false + monarchy.doWithRealm { + isEventPersisted = EventEntity.where(it, eventId = eventId).findFirst() != null + } + return isEventPersisted } private fun buildDataSourceFactoryQuery(realm: Realm, eventId: String?): RealmQuery { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/EventContextResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/EventContextResponse.kt index 79cfac72c9..76ea2a8ae8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/EventContextResponse.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/EventContextResponse.kt @@ -12,11 +12,4 @@ data class EventContextResponse( @Json(name = "events_after") val eventsAfter: List = emptyList(), @Json(name = "end") val nextToken: String? = null, @Json(name = "state") val stateEvents: List = emptyList() -) { - - val timelineEvents: List by lazy { - eventsBefore + event + eventsAfter - } - - -} +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt index 248c616a47..5ea69ba9bd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt @@ -4,7 +4,6 @@ import arrow.core.Try import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.database.helper.addAll import im.vector.matrix.android.internal.database.helper.addOrUpdate import im.vector.matrix.android.internal.database.helper.addStateEvents import im.vector.matrix.android.internal.database.helper.deleteOnCascade @@ -46,7 +45,7 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, filter: String?) = withContext(coroutineDispatchers.io) { executeRequest { - apiCall = roomAPI.getContextOfEvent(roomId, eventId, 1, filter) + apiCall = roomAPI.getContextOfEvent(roomId, eventId, 0, filter) }.flatMap { response -> insertInDb(response, roomId) } @@ -56,17 +55,14 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, return monarchy .tryTransactionSync { realm -> val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: throw IllegalStateException("You shouldn't use this method without a room") + ?: throw IllegalStateException("You shouldn't use this method without a room") val currentChunk = realm.createObject().apply { prevToken = response.prevToken nextToken = response.nextToken } - currentChunk.addOrUpdate(response.event, PaginationDirection.FORWARDS) - currentChunk.addAll(response.eventsAfter, PaginationDirection.FORWARDS) - currentChunk.addAll(response.eventsBefore, PaginationDirection.BACKWARDS) - + currentChunk.addOrUpdate(response.event, PaginationDirection.FORWARDS, isUnlinked = true) // Now, handles chunk merge val prevChunk = ChunkEntity.find(realm, roomId, nextToken = response.prevToken) val nextChunk = ChunkEntity.find(realm, roomId, prevToken = response.nextToken) @@ -79,18 +75,8 @@ internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, currentChunk.merge(nextChunk, PaginationDirection.FORWARDS) roomEntity.deleteOnCascade(nextChunk) } - /* - val eventIds = response.timelineEvents.mapNotNull { it.eventId } - ChunkEntity - .findAllIncludingEvents(realm, eventIds) - .filter { it != currentChunk } - .forEach { overlapped -> - currentChunk.merge(overlapped, direction) - roomEntity.deleteOnCascade(overlapped) - } - */ roomEntity.addOrUpdate(currentChunk) - roomEntity.addStateEvents(response.stateEvents) + roomEntity.addStateEvents(response.stateEvents, stateIndex = Int.MIN_VALUE, isUnlinked = true) } .map { response } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt index 29a3e6c936..02f4e8231c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt @@ -5,11 +5,7 @@ import arrow.core.failure import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.database.helper.addAll -import im.vector.matrix.android.internal.database.helper.addOrUpdate -import im.vector.matrix.android.internal.database.helper.addStateEvents -import im.vector.matrix.android.internal.database.helper.deleteOnCascade -import im.vector.matrix.android.internal.database.helper.merge +import im.vector.matrix.android.internal.database.helper.* import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.find @@ -34,7 +30,7 @@ internal class PaginationRequest(private val roomAPI: RoomAPI, fun execute(roomId: String, from: String?, direction: PaginationDirection, - limit: Int = 10, + limit: Int, callback: MatrixCallback ): Cancelable { val job = GlobalScope.launch(coroutineDispatchers.main) { @@ -48,7 +44,7 @@ internal class PaginationRequest(private val roomAPI: RoomAPI, private suspend fun execute(roomId: String, from: String?, direction: PaginationDirection, - limit: Int = 10, + limit: Int, filter: String?) = withContext(coroutineDispatchers.io) { if (from == null) { @@ -61,38 +57,45 @@ internal class PaginationRequest(private val roomAPI: RoomAPI, } } - private fun insertInDb(receivedChunk: TokenChunkEvent, roomId: String, direction: PaginationDirection): Try { + private fun insertInDb(receivedChunk: TokenChunkEvent, + roomId: String, + direction: PaginationDirection): Try { return monarchy .tryTransactionSync { realm -> val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: throw IllegalStateException("You shouldn't use this method without a room") + ?: throw IllegalStateException("You shouldn't use this method without a room") - val currentChunk = ChunkEntity.find(realm, roomId, prevToken = receivedChunk.nextToken) - ?: realm.createObject() - - currentChunk.prevToken = receivedChunk.prevToken - currentChunk.addAll(receivedChunk.events, direction) + val currentChunk = realm.createObject().apply { + prevToken = receivedChunk.prevToken + nextToken = receivedChunk.nextToken + } + currentChunk.addAll(receivedChunk.events, direction, isUnlinked = true) // Now, handles chunk merge - val prevChunk = ChunkEntity.find(realm, roomId, nextToken = receivedChunk.prevToken) + val nextChunk = ChunkEntity.find(realm, roomId, prevToken = receivedChunk.nextToken) + if (prevChunk != null) { - currentChunk.merge(prevChunk, direction) + currentChunk.merge(prevChunk, PaginationDirection.BACKWARDS) roomEntity.deleteOnCascade(prevChunk) - } else { - val eventIds = receivedChunk.events.mapNotNull { it.eventId } - ChunkEntity - .findAllIncludingEvents(realm, eventIds) - .filter { it != currentChunk } - .forEach { overlapped -> - currentChunk.merge(overlapped, direction) - roomEntity.deleteOnCascade(overlapped) - } } + if (nextChunk != null) { + currentChunk.merge(nextChunk, PaginationDirection.FORWARDS) + roomEntity.deleteOnCascade(nextChunk) + } + val eventIds = receivedChunk.events.mapNotNull { it.eventId } + ChunkEntity + .findAllIncludingEvents(realm, eventIds) + .filter { it != currentChunk } + .forEach { overlapped -> + currentChunk.merge(overlapped, direction) + roomEntity.deleteOnCascade(overlapped) + } roomEntity.addOrUpdate(currentChunk) // TODO : there is an issue with the pagination sending unwanted room member events - roomEntity.addStateEvents(receivedChunk.stateEvents) + val isUnlinked = currentChunk.isUnlinked() + roomEntity.addStateEvents(receivedChunk.stateEvents, isUnlinked = isUnlinked) } .map { receivedChunk } } 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 ff06dbb57f..bab802e24d 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 @@ -8,7 +8,6 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.util.PagingRequestHelper import java.util.* -import java.util.concurrent.Executor internal class TimelineBoundaryCallback(private val roomId: String, private val paginationRequest: PaginationRequest, @@ -24,28 +23,37 @@ internal class TimelineBoundaryCallback(private val roomId: String, override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) { helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) { - monarchy.doWithRealm { realm -> - if (itemAtEnd.root.eventId == null) { - return@doWithRealm - } - val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.root.eventId)).firstOrNull() - paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, limit, callback = createCallback(it)) - } + runPaginationRequest(it, itemAtEnd, PaginationDirection.BACKWARDS) } } override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) { helper.runIfNotRunning(PagingRequestHelper.RequestType.BEFORE) { - monarchy.doWithRealm { realm -> - if (itemAtFront.root.eventId == null) { - return@doWithRealm - } - val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.root.eventId)).firstOrNull() - paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, limit, callback = createCallback(it)) - } + runPaginationRequest(it, itemAtFront, PaginationDirection.FORWARDS) } } + private fun runPaginationRequest(requestCallback: PagingRequestHelper.Request.Callback, + item: EnrichedEvent, + direction: PaginationDirection) { + 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() + token = if (direction == PaginationDirection.FORWARDS) chunkEntity?.nextToken else chunkEntity?.prevToken + } + paginationRequest.execute( + roomId = roomId, + from = token, + direction = direction, + limit = limit, + callback = createCallback(requestCallback) + ) + } + + private fun createCallback(pagingRequestCallback: PagingRequestHelper.Request.Callback) = object : MatrixCallback { override fun onSuccess(data: TokenChunkEvent) { pagingRequestCallback.recordSuccess() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt new file mode 100644 index 0000000000..fbfad9c2ad --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/Hash.kt @@ -0,0 +1,17 @@ +package im.vector.matrix.android.internal.util + +import java.security.MessageDigest + +fun String.md5() = try { + val digest = MessageDigest.getInstance("md5") + digest.update(toByteArray()) + val bytes = digest.digest() + val sb = StringBuilder() + for (i in bytes.indices) { + sb.append(String.format("%02X", bytes[i])) + } + sb.toString().toLowerCase() +} catch (exc: Exception) { + // Should not happen, but just in case + hashCode().toString() +}