BayernMessenger/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt

211 lines
9.5 KiB
Kotlin
Raw Normal View History

2019-01-18 10:12:08 +00:00
/*
* Copyright 2019 New Vector Ltd
2019-01-18 10:12:08 +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
*
* 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-17 16:21:09 +00:00
package im.vector.matrix.android.internal.session.sync
2018-10-18 09:16:02 +00:00
import com.zhuinden.monarchy.Monarchy
2018-10-17 16:21:09 +00:00
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.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
2019-05-16 08:23:57 +00:00
import im.vector.matrix.android.internal.crypto.CryptoManager
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.lastStateIndex
2018-10-12 17:26:22 +00:00
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
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
2019-05-16 08:23:57 +00:00
import im.vector.matrix.android.internal.session.sync.model.*
2018-10-15 17:42:13 +00:00
import io.realm.Realm
import io.realm.kotlin.createObject
import timber.log.Timber
internal class RoomSyncHandler(private val monarchy: Monarchy,
private val readReceiptHandler: ReadReceiptHandler,
private val roomSummaryUpdater: RoomSummaryUpdater,
2019-05-16 08:23:57 +00:00
private val roomTagHandler: RoomTagHandler,
private val cryptoManager: CryptoManager) {
2018-10-15 17:42:13 +00:00
sealed class HandlingStrategy {
data class JOINED(val data: Map<String, RoomSync>) : HandlingStrategy()
data class INVITED(val data: Map<String, InvitedRoomSync>) : HandlingStrategy()
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
}
2018-10-30 17:22:29 +00:00
fun handle(roomsSyncResponse: RoomsSyncResponse) {
monarchy.runTransactionSync { realm ->
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join))
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite))
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave))
2018-10-30 17:22:29 +00:00
}
}
// PRIVATE METHODS *****************************************************************************
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
val rooms = when (handlingStrategy) {
2019-05-29 16:43:33 +00:00
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
2019-05-29 16:43:33 +00:00
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(realm, it.key, it.value) }
2018-10-12 17:26:22 +00:00
}
realm.insertOrUpdate(rooms)
}
2018-10-15 17:42:13 +00:00
private fun handleJoinedRoom(realm: Realm,
roomId: String,
roomSync: RoomSync): RoomEntity {
Timber.v("Handle join sync for room $roomId")
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
2019-05-16 08:23:57 +00:00
?: realm.createObject(roomId)
2018-10-15 17:42:13 +00:00
if (roomEntity.membership == Membership.INVITE) {
2018-10-15 17:42:13 +00:00
roomEntity.chunks.deleteAllFromRealm()
2018-10-12 17:26:22 +00:00
}
roomEntity.membership = Membership.JOIN
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
val isInitialSync = lastChunk == null
val lastStateIndex = lastChunk?.lastStateIndex(PaginationDirection.FORWARDS) ?: 0
val numberOfStateEvents = roomSync.state?.events?.size ?: 0
val stateIndexOffset = lastStateIndex + numberOfStateEvents
2019-05-21 13:42:09 +00:00
// State event
2018-10-15 17:42:13 +00:00
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val untimelinedStateIndex = if (isInitialSync) Int.MIN_VALUE else stateIndexOffset
roomEntity.addStateEvents(roomSync.state.events, filterDuplicates = true, stateIndex = untimelinedStateIndex)
2019-05-21 13:42:09 +00:00
// Give info to crypto module
roomSync.state.events.forEach {
cryptoManager.onStateEvent(roomId, it)
2019-05-21 13:42:09 +00:00
}
2018-10-15 17:42:13 +00:00
}
2018-10-15 17:42:13 +00:00
if (roomSync.timeline != null && roomSync.timeline.events.isNotEmpty()) {
val timelineStateOffset = if (isInitialSync || roomSync.timeline.limited.not()) 0 else stateIndexOffset
val chunkEntity = handleTimelineEvents(
realm,
roomId,
roomSync.timeline.events,
roomSync.timeline.prevToken,
roomSync.timeline.limited,
timelineStateOffset
)
roomEntity.addOrUpdate(chunkEntity)
2019-05-16 08:23:57 +00:00
// Give info to crypto module
roomSync.timeline.events.forEach {
cryptoManager.onLiveEvent(roomId, it)
2019-05-16 08:23:57 +00:00
}
// Try to remove local echo
val transactionIds = roomSync.timeline.events.mapNotNull { it.unsignedData?.transactionId }
transactionIds.forEach {
val sendingEventEntity = roomEntity.sendingTimelineEvents.find(it)
if (sendingEventEntity != null) {
2019-05-29 16:43:33 +00:00
Timber.v("Remove local echo for tx:$it")
roomEntity.sendingTimelineEvents.remove(sendingEventEntity)
2019-05-29 16:43:33 +00:00
} else {
Timber.v("Can't find corresponding local echo for tx:$it")
}
}
2018-10-12 17:26:22 +00:00
}
roomSummaryUpdater.update(realm, roomId, Membership.JOIN, roomSync.summary, roomSync.unreadNotifications)
if (roomSync.ephemeral != null && roomSync.ephemeral.events.isNotEmpty()) {
handleEphemeral(realm, roomId, roomSync.ephemeral)
}
if (roomSync.accountData != null && roomSync.accountData.events.isNullOrEmpty().not()) {
handleRoomAccountDataEvents(realm, roomId, roomSync.accountData)
}
2018-10-15 17:42:13 +00:00
return roomEntity
}
2018-10-12 17:26:22 +00:00
2018-10-15 17:42:13 +00:00
private fun handleInvitedRoom(realm: Realm,
roomId: String,
roomSync:
InvitedRoomSync): RoomEntity {
Timber.v("Handle invited sync for room $roomId")
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
2019-05-29 16:43:33 +00:00
?: realm.createObject(roomId)
roomEntity.membership = Membership.INVITE
2018-10-15 17:42:13 +00:00
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
val chunkEntity = handleTimelineEvents(realm, roomId, roomSync.inviteState.events)
roomEntity.addOrUpdate(chunkEntity)
2018-10-12 17:26:22 +00:00
}
roomSummaryUpdater.update(realm, roomId, Membership.INVITE)
2018-10-12 17:26:22 +00:00
return roomEntity
}
private fun handleLeftRoom(realm: Realm,
roomId: String,
2018-10-15 17:42:13 +00:00
roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
2019-05-29 16:43:33 +00:00
?: realm.createObject(roomId)
roomEntity.membership = Membership.LEAVE
roomEntity.chunks.deleteAllFromRealm()
roomSummaryUpdater.update(realm, roomId, Membership.LEAVE, roomSync.summary, roomSync.unreadNotifications)
return roomEntity
}
private fun handleTimelineEvents(realm: Realm,
roomId: String,
eventList: List<Event>,
prevToken: String? = null,
isLimited: Boolean = true,
stateIndexOffset: Int = 0): ChunkEntity {
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
val chunkEntity = if (!isLimited && lastChunk != null) {
lastChunk
2018-10-15 17:42:13 +00:00
} else {
realm.createObject<ChunkEntity>().apply { this.prevToken = prevToken }
}
lastChunk?.isLastForward = false
chunkEntity.isLastForward = true
chunkEntity.addAll(roomId, eventList, PaginationDirection.FORWARDS, stateIndexOffset)
//update eventAnnotationSummary here?
2018-10-13 09:18:49 +00:00
return chunkEntity
}
private fun handleEphemeral(realm: Realm,
roomId: String,
ephemeral: RoomSyncEphemeral) {
ephemeral.events
.filter { it.getClearType() == EventType.RECEIPT }
.map { it.content.toModel<ReadReceiptContent>() }
.forEach { readReceiptHandler.handle(realm, roomId, it) }
}
private fun handleRoomAccountDataEvents(realm: Realm, roomId: String, accountData: RoomSyncAccountData) {
accountData.events
.filter { it.getClearType() == EventType.TAG }
.map { it.content.toModel<RoomTagContent>() }
.forEach { roomTagHandler.handle(realm, roomId, it) }
}
}