diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt index 18f191de..f09b0376 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/session/room/timeline/TimelineTest.kt @@ -18,25 +18,6 @@ package im.vector.matrix.android.session.room.timeline import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.InstrumentedTest -import im.vector.matrix.android.api.session.crypto.CryptoService -import im.vector.matrix.android.api.session.room.timeline.Timeline -import im.vector.matrix.android.api.session.room.timeline.TimelineEvent -import im.vector.matrix.android.internal.crypto.CryptoManager -import im.vector.matrix.android.internal.database.model.SessionRealmModule -import im.vector.matrix.android.internal.session.room.EventRelationExtractor -import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor -import im.vector.matrix.android.internal.session.room.timeline.DefaultTimeline -import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory -import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor -import im.vector.matrix.android.internal.task.TaskExecutor -import im.vector.matrix.android.testCoroutineDispatchers -import io.realm.Realm -import io.realm.RealmConfiguration -import org.amshove.kluent.shouldEqual -import org.junit.Before -import org.junit.Test -import timber.log.Timber -import java.util.concurrent.CountDownLatch internal class TimelineTest : InstrumentedTest { 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 1741796b..f8ccc5f9 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 @@ -18,13 +18,14 @@ 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.api.session.room.send.SendState import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.toEntity 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.model.EventAnnotationsSummaryEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields +import im.vector.matrix.android.internal.database.query.find +import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import io.realm.Sort @@ -32,12 +33,12 @@ import io.realm.Sort // By default if a chunk is empty we consider it unlinked internal fun ChunkEntity.isUnlinked(): Boolean { assertIsManaged() - return events.where().equalTo(EventEntityFields.IS_UNLINKED, false).findAll().isEmpty() + return timelineEvents.where().equalTo(TimelineEventEntityFields.ROOT.IS_UNLINKED, false).findAll().isEmpty() } internal fun ChunkEntity.deleteOnCascade() { assertIsManaged() - this.events.deleteAllFromRealm() + this.timelineEvents.deleteAllFromRealm() this.deleteFromRealm() } @@ -50,20 +51,22 @@ internal fun ChunkEntity.merge(roomId: String, val isUnlinked = isCurrentChunkUnlinked && isChunkToMergeUnlinked if (isCurrentChunkUnlinked && !isChunkToMergeUnlinked) { - this.events.forEach { it.isUnlinked = false } + this.timelineEvents.forEach { it.root?.isUnlinked = false } } - val eventsToMerge: List + val eventsToMerge: List if (direction == PaginationDirection.FORWARDS) { this.nextToken = chunkToMerge.nextToken this.isLastForward = chunkToMerge.isLastForward - eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) + eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.ASCENDING) } else { this.prevToken = chunkToMerge.prevToken this.isLastBackward = chunkToMerge.isLastBackward - eventsToMerge = chunkToMerge.events.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) + eventsToMerge = chunkToMerge.timelineEvents.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING) } eventsToMerge.forEach { - add(roomId, it.asDomain(), direction, isUnlinked = isUnlinked) + it.root?.let { root -> + add(roomId, root.asDomain(), direction, isUnlinked = isUnlinked) + } } } @@ -86,7 +89,7 @@ internal fun ChunkEntity.add(roomId: String, isUnlinked: Boolean = false) { assertIsManaged() - if (event.eventId != null && events.fastContains(event.eventId)) { + if (event.eventId != null && timelineEvents.find(event.eventId) != null) { return } var currentDisplayIndex = lastDisplayIndex(direction, 0) @@ -101,21 +104,22 @@ internal fun ChunkEntity.add(roomId: String, if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(event.getClearType())) { currentStateIndex += 1 forwardsStateIndex = currentStateIndex - } else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) { - val lastEventType = events.last()?.type ?: "" + } else if (direction == PaginationDirection.BACKWARDS && timelineEvents.isNotEmpty()) { + val lastEventType = timelineEvents.last()?.root?.type ?: "" if (EventType.isStateEvent(lastEventType)) { currentStateIndex -= 1 backwardsStateIndex = currentStateIndex } } - val eventEntity = event.toEntity(roomId).apply { - this.stateIndex = currentStateIndex - this.isUnlinked = isUnlinked - this.displayIndex = currentDisplayIndex - this.sendState = SendState.SYNCED + + val eventEntity = TimelineEventEntity().apply { + this.root = event.toEntity(roomId) + this.eventId = event.eventId ?: "" + this.roomId = roomId + this.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() } - val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size - events.add(position, eventEntity) + val position = if (direction == PaginationDirection.FORWARDS) 0 else this.timelineEvents.size + timelineEvents.add(position, eventEntity) } internal fun ChunkEntity.lastDisplayIndex(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 04921a9a..885b25c7 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 @@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.database.mapper.toEntity 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.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.fastContains import im.vector.matrix.android.internal.extensions.assertIsManaged @@ -60,5 +61,10 @@ internal fun RoomEntity.addSendingEvent(event: Event) { val eventEntity = event.toEntity(roomId).apply { this.sendState = SendState.UNSENT } - sendingTimelineEvents.add(0, eventEntity) + val timelineEventEntity = TimelineEventEntity().also { + it.root = eventEntity + it.eventId = event.eventId ?: "" + it.roomId = roomId + } + sendingTimelineEvents.add(0, timelineEventEntity) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventAnnotationsSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventAnnotationsSummaryMapper.kt index e6b6197c..1b7b7f64 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventAnnotationsSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventAnnotationsSummaryMapper.kt @@ -19,7 +19,10 @@ package im.vector.matrix.android.internal.database.mapper import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary +import im.vector.matrix.android.internal.database.model.EditAggregatedSummaryEntity import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity +import im.vector.matrix.android.internal.database.model.ReactionAggregatedSummaryEntity +import io.realm.RealmList internal object EventAnnotationsSummaryMapper { fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary { @@ -45,6 +48,35 @@ internal object EventAnnotationsSummaryMapper { } ) } + + fun map(annotationsSummary: EventAnnotationsSummary, roomId: String): EventAnnotationsSummaryEntity { + val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity() + eventAnnotationsSummaryEntity.eventId = annotationsSummary.eventId + eventAnnotationsSummaryEntity.roomId = roomId + eventAnnotationsSummaryEntity.editSummary = annotationsSummary.editSummary?.let { + EditAggregatedSummaryEntity( + ContentMapper.map(it.aggregatedContent), + RealmList().apply { addAll(it.sourceEvents) }, + RealmList().apply { addAll(it.localEchos) }, + it.lastEditTs + ) + } + eventAnnotationsSummaryEntity.reactionsSummary = annotationsSummary.reactionsSummary?.let { + RealmList().apply { + addAll(it.map { + ReactionAggregatedSummaryEntity( + it.key, + it.count, + it.addedByMe, + it.firstTimestamp, + RealmList().apply { addAll(it.sourceEvents) }, + RealmList().apply { addAll(it.localEchoEvents) } + ) + }) + } + } + return eventAnnotationsSummaryEntity + } } internal fun EventAnnotationsSummaryEntity.asDomain(): EventAnnotationsSummary { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index b5fdabc7..9991049e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -19,29 +19,21 @@ package im.vector.matrix.android.internal.database.mapper import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.android.internal.database.model.RoomSummaryEntity -import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory import javax.inject.Inject -internal class RoomSummaryMapper @Inject constructor(private val timelineEventFactory: TimelineEventFactory) { +internal class RoomSummaryMapper @Inject constructor() { fun map(roomSummaryEntity: RoomSummaryEntity, getLatestEvent: Boolean = false): RoomSummary { val tags = roomSummaryEntity.tags.map { RoomTag(it.tagName, it.tagOrder) } - val latestEvent = if (getLatestEvent) { - roomSummaryEntity.latestEvent?.let { - timelineEventFactory.create(it, it.realm) - } - } else { - null - } return RoomSummary( roomId = roomSummaryEntity.roomId, displayName = roomSummaryEntity.displayName ?: "", topic = roomSummaryEntity.topic ?: "", avatarUrl = roomSummaryEntity.avatarUrl ?: "", isDirect = roomSummaryEntity.isDirect, - latestEvent = latestEvent, + latestEvent = roomSummaryEntity.latestEvent?.asDomain(), otherMemberIds = roomSummaryEntity.otherMemberIds.toList(), highlightCount = roomSummaryEntity.highlightCount, notificationCount = roomSummaryEntity.notificationCount, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt new file mode 100644 index 00000000..27d4a11b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/TimelineEventMapper.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019 New Vector Ltd + * + * 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 + * + * 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. + */ + +package im.vector.matrix.android.internal.database.mapper + +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.send.SendState +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.matrix.android.internal.database.model.TimelineEventEntity + +internal object TimelineEventMapper { + + fun map(timelineEvent: TimelineEvent, roomId: String): TimelineEventEntity { + val timelineEventEntity = TimelineEventEntity() + timelineEventEntity.root = timelineEvent.root.toEntity(roomId) + timelineEventEntity.eventId = timelineEvent.root.eventId ?: "" + timelineEventEntity.roomId = roomId + timelineEventEntity.annotations = timelineEvent.annotations?.let { EventAnnotationsSummaryMapper.map(it, roomId) } + return timelineEventEntity + } + + fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent { + return TimelineEvent( + root = timelineEventEntity.root?.asDomain() + ?: Event("", timelineEventEntity.eventId), + annotations = timelineEventEntity.annotations?.asDomain(), + localId = timelineEventEntity.localId, + displayIndex = timelineEventEntity.root?.displayIndex ?: 0, + senderName = timelineEventEntity.senderName, + isUniqueDisplayName = timelineEventEntity.isUniqueDisplayName, + senderAvatar = timelineEventEntity.senderAvatar, + sendState = timelineEventEntity.root?.sendState ?: SendState.UNKNOWN, + hasClearEventFlag = false + ) + } + +} + +internal fun TimelineEventEntity.asDomain(): TimelineEvent { + return TimelineEventMapper.map(this) +} + +internal fun TimelineEvent.toEntity(roomId: String): TimelineEventEntity { + return TimelineEventMapper.map(this, roomId) +} + + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt index 6f34fb03..77a97f72 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt @@ -24,7 +24,7 @@ import io.realm.annotations.LinkingObjects internal open class ChunkEntity(@Index var prevToken: String? = null, @Index var nextToken: String? = null, - var events: RealmList = RealmList(), + var timelineEvents: RealmList = RealmList(), @Index var isLastForward: Boolean = false, @Index var isLastBackward: Boolean = false, var backwardsDisplayIndex: Int? = null, 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 0721f684..91375a96 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 @@ -27,7 +27,7 @@ import java.util.* internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(), @Index var eventId: String = "", - var roomId: String = "", + @Index var roomId: String = "", @Index var type: String = "", var content: String? = null, var prevContent: String? = null, @@ -61,9 +61,6 @@ internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUI companion object - @LinkingObjects("events") - val chunk: RealmResults? = null - @LinkingObjects("untimelinedStateEvents") val room: RealmResults? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt index 2341e492..b9c110b5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt @@ -26,7 +26,7 @@ import kotlin.properties.Delegates internal open class RoomEntity(@PrimaryKey var roomId: String = "", var chunks: RealmList = RealmList(), var untimelinedStateEvents: RealmList = RealmList(), - var sendingTimelineEvents: RealmList = RealmList(), + var sendingTimelineEvents: RealmList = RealmList(), var areAllMembersLoaded: Boolean = false ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index 5e3a9752..c178711c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -27,7 +27,7 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", var displayName: String? = "", var avatarUrl: String? = "", var topic: String? = "", - var latestEvent: EventEntity? = null, + var latestEvent: TimelineEventEntity? = null, var heroes: RealmList = RealmList(), var joinedMembersCount: Int? = 0, var invitedMembersCount: Int? = 0, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/TimelineEventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/TimelineEventEntity.kt index c1c0b547..bb910aec 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/TimelineEventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/TimelineEventEntity.kt @@ -17,13 +17,16 @@ package im.vector.matrix.android.internal.database.model import io.realm.RealmObject +import io.realm.RealmResults import io.realm.annotations.Index +import io.realm.annotations.LinkingObjects import io.realm.annotations.PrimaryKey import java.util.* internal open class TimelineEventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(), @Index var eventId: String = "", + @Index var roomId: String = "", var root: EventEntity? = null, var annotations: EventAnnotationsSummaryEntity? = null, var senderName: String? = null, @@ -31,6 +34,9 @@ internal open class TimelineEventEntity(@PrimaryKey var localId: String = UUID.r var senderAvatar: String? = null ) : RealmObject() { + @LinkingObjects("timelineEvents") + val chunk: RealmResults? = null + companion object } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt index bfaac609..2f714562 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt @@ -49,7 +49,7 @@ internal fun ChunkEntity.Companion.findLastLiveChunkFromRoom(realm: Realm, roomI internal fun ChunkEntity.Companion.findAllIncludingEvents(realm: Realm, eventIds: List): RealmResults { return realm.where() - .`in`(ChunkEntityFields.EVENTS.EVENT_ID, eventIds.toTypedArray()) + .`in`(ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID, eventIds.toTypedArray()) .findAll() } 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 f7a71c47..cf0f845b 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 @@ -16,12 +16,13 @@ package im.vector.matrix.android.internal.database.query -import im.vector.matrix.android.internal.database.helper.addSendingEvent 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.EventEntity.LinkFilterMode.* import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery @@ -62,30 +63,6 @@ internal fun EventEntity.Companion.types(realm: Realm, return query } - -internal fun EventEntity.Companion.latestEvent(realm: Realm, - roomId: String, - includedTypes: List = emptyList(), - excludedTypes: List = emptyList()): EventEntity? { - - val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null - val eventList = if (roomEntity.sendingTimelineEvents.isNotEmpty()) { - roomEntity.sendingTimelineEvents - } else { - ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.events - } - val query = eventList?.where() - if (includedTypes.isNotEmpty()) { - query?.`in`(EventEntityFields.TYPE, includedTypes.toTypedArray()) - } else if (excludedTypes.isNotEmpty()) { - query?.not()?.`in`(EventEntityFields.TYPE, excludedTypes.toTypedArray()) - } - return query - ?.sort(EventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) - ?.findFirst() -} - - internal fun RealmQuery.next(from: Int? = null, strict: Boolean = true): EventEntity? { if (from != null) { if (strict) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt new file mode 100644 index 00000000..28b2bc01 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/TimelineEventEntityQueries.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2019 New Vector Ltd + * + * 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 + * + * 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. + */ + +package im.vector.matrix.android.internal.database.query + +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.EventEntity.LinkFilterMode.* +import im.vector.matrix.android.internal.database.model.EventEntityFields +import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields +import io.realm.Realm +import io.realm.RealmList +import io.realm.RealmQuery +import io.realm.Sort +import io.realm.kotlin.where + +internal fun TimelineEventEntity.Companion.where(realm: Realm, eventId: String): RealmQuery { + return realm.where().equalTo(TimelineEventEntityFields.EVENT_ID, eventId) +} + +internal fun TimelineEventEntity.Companion.where(realm: Realm, eventIds: List): RealmQuery { + return realm.where().`in`(TimelineEventEntityFields.EVENT_ID, eventIds.toTypedArray()) +} + + +internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm, + roomId: String, + includedTypes: List = emptyList(), + excludedTypes: List = emptyList()): TimelineEventEntity? { + + val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null + val eventList = if (roomEntity.sendingTimelineEvents.isNotEmpty()) { + roomEntity.sendingTimelineEvents + } else { + ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents + } + val query = eventList?.where() + if (includedTypes.isNotEmpty()) { + query?.`in`(EventEntityFields.TYPE, includedTypes.toTypedArray()) + } else if (excludedTypes.isNotEmpty()) { + query?.not()?.`in`(EventEntityFields.TYPE, excludedTypes.toTypedArray()) + } + return query + ?.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING) + ?.findFirst() +} + +internal fun RealmList.find(eventId: String): TimelineEventEntity? { + return this.where().equalTo(TimelineEventEntityFields.ROOT.EVENT_ID, eventId).findFirst() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt deleted file mode 100644 index 147726b1..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/EventRelationExtractor.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * 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 - * - * 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. - */ -package im.vector.matrix.android.internal.session.room - -import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity -import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.SessionScope -import io.realm.Realm -import javax.inject.Inject - -/** - * Fetches annotations (reactions, edits...) associated to a given eventEntity from the data layer. - */ - -internal class EventRelationExtractor @Inject constructor() { - - fun extractFrom(event: EventEntity, realm: Realm = event.realm): EventAnnotationsSummary? { - return EventAnnotationsSummaryEntity.where(realm, event.eventId).findFirst()?.asDomain() - } -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index d49471fb..13ca4e67 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -39,7 +39,6 @@ import im.vector.matrix.android.internal.session.room.state.DefaultStateService import im.vector.matrix.android.internal.session.room.state.SendStateTask 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.InMemoryTimelineEventFactory import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import im.vector.matrix.android.internal.task.TaskExecutor import javax.inject.Inject @@ -63,8 +62,7 @@ internal class RoomFactory @Inject constructor(private val context: Context, private val leaveRoomTask: LeaveRoomTask) { fun create(roomId: String): Room { - val timelineEventFactory = InMemoryTimelineEventFactory(SenderRoomMemberExtractor(), EventRelationExtractor(), cryptoService) - val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, timelineEventFactory, contextOfEventTask, cryptoService, paginationTask) + val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, cryptoService, paginationTask) val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy) val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask) val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask) 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 1e0794a4..9cf6ac69 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 @@ -138,10 +138,4 @@ internal abstract class RoomModule { @Binds abstract fun bindTimelineService(timelineService: DefaultTimelineService): TimelineService - @Binds - abstract fun bindSimpleTimelineEventFactory(timelineEventFactory: SimpleTimelineEventFactory): TimelineEventFactory - - @Binds - abstract fun bindCacheableTimelineEventFactory(inMemoryTimelineEventFactory: InMemoryTimelineEventFactory): CacheableTimelineEventFactory - } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 76f2d151..8fdb5fe9 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.RoomTopicContent import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.prev import im.vector.matrix.android.internal.database.query.where @@ -77,19 +78,19 @@ internal class RoomSummaryUpdater @Inject constructor(private val credentials: C } } roomSummaryEntity.highlightCount = unreadNotifications?.highlightCount ?: 0 - roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?:0 + roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 if (membership != null) { roomSummaryEntity.membership = membership } - val lastEvent = EventEntity.latestEvent(realm, roomId, includedTypes = PREVIEWABLE_TYPES) + val latestEvent = TimelineEventEntity.latestEvent(realm, roomId, includedTypes = PREVIEWABLE_TYPES) val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain() val otherRoomMembers = RoomMembers(realm, roomId).getLoaded().filterKeys { it != credentials.userId } roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString() roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId) roomSummaryEntity.topic = lastTopicEvent?.content.toModel()?.topic - roomSummaryEntity.latestEvent = lastEvent + roomSummaryEntity.latestEvent = latestEvent roomSummaryEntity.otherMemberIds.clear() roomSummaryEntity.otherMemberIds.addAll(otherRoomMembers.keys) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt index 890ecc8e..f2629f4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/SenderRoomMemberExtractor.kt @@ -16,29 +16,12 @@ package im.vector.matrix.android.internal.session.room.membership -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.RoomMember -import im.vector.matrix.android.api.session.room.send.SendState -import im.vector.matrix.android.internal.database.mapper.ContentMapper -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.model.RoomEntity -import im.vector.matrix.android.internal.database.query.findIncludingEvent -import im.vector.matrix.android.internal.database.query.next -import im.vector.matrix.android.internal.database.query.prev -import im.vector.matrix.android.internal.database.query.where -import io.realm.Realm -import io.realm.RealmList -import io.realm.RealmQuery -import javax.inject.Inject +internal object SenderRoomMemberExtractor { -internal class SenderRoomMemberExtractor @Inject constructor() { - - fun extractFrom(event: EventEntity, realm: Realm = event.realm): RoomMember? { + /* + fun extractFrom(event: Event, realm: Realm): RoomMember? { val roomId = event.roomId - val sender = event.sender ?: return null + val sender = event.senderId ?: return null // If the event is unlinked we want to fetch unlinked state events val unlinked = event.isUnlinked val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return null @@ -69,4 +52,5 @@ internal class SenderRoomMemberExtractor @Inject constructor() { .equalTo(EventEntityFields.IS_UNLINKED, isUnlinked) } +*/ } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt index 7b90a8ac..ff899968 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt @@ -21,8 +21,8 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.session.room.read.ReadService 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.ReadReceiptEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity 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.latestEvent @@ -55,21 +55,21 @@ internal class DefaultReadService @Inject constructor(private val roomId: String setReadMarkersTask.configureWith(params).dispatchTo(callback).executeBy(taskExecutor) } - private fun getLatestEvent(): EventEntity? { - return monarchy.fetchCopied { EventEntity.latestEvent(it, roomId) } + private fun getLatestEvent(): TimelineEventEntity? { + return monarchy.fetchCopied { TimelineEventEntity.latestEvent(it, roomId) } } override fun isEventRead(eventId: String): Boolean { var isEventRead = false monarchy.doWithRealm { val readReceipt = ReadReceiptEntity.where(it, roomId, credentials.userId).findFirst() - ?: return@doWithRealm + ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) - ?: return@doWithRealm - val readReceiptIndex = liveChunk.events.find(readReceipt.eventId)?.displayIndex - ?: Int.MIN_VALUE - val eventToCheckIndex = liveChunk.events.find(eventId)?.displayIndex - ?: Int.MAX_VALUE + ?: return@doWithRealm + val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex + ?: Int.MIN_VALUE + val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex + ?: Int.MAX_VALUE isEventRead = eventToCheckIndex <= readReceiptIndex } return isEventRead diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt index 7ff2d009..2106ab55 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/SetReadMarkersTask.kt @@ -23,6 +23,7 @@ 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.ReadReceiptEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity +import im.vector.matrix.android.internal.database.model.TimelineEventEntity 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.latestEvent @@ -82,7 +83,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI private fun updateNotificationCountIfNecessary(roomId: String, eventId: String) { monarchy.tryTransactionAsync { realm -> - val isLatestReceived = EventEntity.latestEvent(realm, roomId)?.eventId == eventId + val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId)?.eventId == eventId if (isLatestReceived) { val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst() ?: return@tryTransactionAsync @@ -99,9 +100,9 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI ?: return@doWithRealm val liveChunk = ChunkEntity.findLastLiveChunkFromRoom(it, roomId) ?: return@doWithRealm - val readReceiptIndex = liveChunk.events.find(readReceipt.eventId)?.displayIndex + val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.root?.displayIndex ?: Int.MIN_VALUE - val eventToCheckIndex = liveChunk.events.find(eventId)?.displayIndex + val eventToCheckIndex = liveChunk.timelineEvents.find(eventId)?.root?.displayIndex ?: Int.MAX_VALUE isEventRead = eventToCheckIndex <= readReceiptIndex } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 8f5c62cd..44d1808e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -67,7 +67,6 @@ internal class DefaultTimeline( private val realmConfiguration: RealmConfiguration, private val taskExecutor: TaskExecutor, private val contextOfEventTask: GetContextOfEventTask, - private val timelineEventFactory: CacheableTimelineEventFactory, private val paginationTask: PaginationTask, private val cryptoService: CryptoService, private val allowedTypes: List? @@ -116,7 +115,6 @@ internal class DefaultTimeline( nextDisplayIndex = DISPLAY_INDEX_UNKNOWN builtEvents.clear() builtEventsIdMap.clear() - timelineEventFactory.clear() } changeSet.insertionRanges.forEach { range -> val (startDisplayIndex, direction) = if (range.startIndex == 0) { @@ -145,7 +143,7 @@ internal class DefaultTimeline( builtEventsIdMap[eventId]?.let { builtIndex -> //Update the relation of existing event builtEvents[builtIndex]?.let { te -> - builtEvents[builtIndex] = timelineEventFactory.create(eventEntity, eventEntity.realm) + //builtEvents[builtIndex] = timelineEventFactory.create(eventEntity, eventEntity.realm) hasChanged = true } } @@ -189,7 +187,7 @@ internal class DefaultTimeline( && (timelineEvent.root.mClearEvent == null || timelineEvent.root.mCryptoError != null)) { //we need to rebuild this event EventEntity.where(realm, eventId = timelineEvent.root.eventId!!).findFirst()?.let { - builtEvents[index] = timelineEventFactory.create(it, realm) + //builtEvents[index] = timelineEventFactory.create(it, realm) hasChange = true } } @@ -326,10 +324,10 @@ internal class DefaultTimeline( val sendingEvents = ArrayList() if (hasReachedEnd(Timeline.Direction.FORWARDS)) { roomEntity?.sendingTimelineEvents - ?.filter { allowedTypes?.contains(it.type) ?: false } + ?.filter { allowedTypes?.contains(it.root?.type) ?: false } ?.forEach { - val timelineEvent = timelineEventFactory.create(it, it.realm) - sendingEvents.add(timelineEvent) + //val timelineEvent = timelineEventFactory.create(it, it.realm) + //sendingEvents.add(timelineEvent) } } return sendingEvents @@ -432,7 +430,7 @@ internal class DefaultTimeline( * This has to be called on TimelineThread as it access realm live results */ private fun getLiveChunk(): ChunkEntity? { - return liveEvents.firstOrNull()?.chunk?.firstOrNull() + return null //return liveEvents.firstOrNull()?.chunk?.firstOrNull() } /** diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt index 1d3462e2..d70f1b92 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineService.kt @@ -17,15 +17,15 @@ package im.vector.matrix.android.internal.session.room.timeline import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.Transformations import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineService import im.vector.matrix.android.internal.database.RealmLiveData -import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity -import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.mapper.asDomain +import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.util.fetchCopyMap @@ -34,7 +34,6 @@ import javax.inject.Inject internal class DefaultTimelineService @Inject constructor(private val roomId: String, private val monarchy: Monarchy, private val taskExecutor: TaskExecutor, - private val timelineEventFactory: CacheableTimelineEventFactory, private val contextOfEventTask: GetContextOfEventTask, private val cryptoService: CryptoService, private val paginationTask: PaginationTask @@ -42,46 +41,31 @@ internal class DefaultTimelineService @Inject constructor(private val roomId: St override fun createTimeline(eventId: String?, allowedTypes: List?): Timeline { return DefaultTimeline(roomId, - eventId, - monarchy.realmConfiguration, - taskExecutor, - contextOfEventTask, - timelineEventFactory, - paginationTask, - cryptoService, - allowedTypes) + eventId, + monarchy.realmConfiguration, + taskExecutor, + contextOfEventTask, + paginationTask, + cryptoService, + allowedTypes) } override fun getTimeLineEvent(eventId: String): TimelineEvent? { return monarchy .fetchCopyMap({ - EventEntity.where(it, eventId = eventId).findFirst() - }, { entity, realm -> - timelineEventFactory.create(entity, realm) - }) + TimelineEventEntity.where(it, eventId = eventId).findFirst() + }, { entity, realm -> + entity.asDomain() + }) } override fun liveTimeLineEvent(eventId: String): LiveData { - val liveEventEntity = RealmLiveData(monarchy.realmConfiguration) { realm -> - EventEntity.where(realm, eventId = eventId) + val liveData = RealmLiveData(monarchy.realmConfiguration) { + TimelineEventEntity.where(it, eventId = eventId) } - val liveAnnotationsEntity = RealmLiveData(monarchy.realmConfiguration) { realm -> - EventAnnotationsSummaryEntity.where(realm, eventId = eventId) + return Transformations.map(liveData) { + it.firstOrNull()?.asDomain() } - val result = MediatorLiveData() - result.addSource(liveEventEntity) { realmResults -> - result.value = realmResults.firstOrNull()?.let { timelineEventFactory.create(it, it.realm) } - } - - result.addSource(liveAnnotationsEntity) { - liveEventEntity.value?.let { - result.value = liveEventEntity.value?.let { realmResults -> - //recreate the timeline event - realmResults.firstOrNull()?.let { timelineEventFactory.create(it, it.realm) } - } - } - } - return result } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt deleted file mode 100644 index aeb35e10..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * 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 - * - * 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. - */ - -package im.vector.matrix.android.internal.session.room.timeline - -import im.vector.matrix.android.api.session.crypto.CryptoService -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.room.timeline.TimelineEvent -import im.vector.matrix.android.api.session.room.timeline.TimelineService -import im.vector.matrix.android.internal.crypto.MXDecryptionException -import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult -import im.vector.matrix.android.internal.database.mapper.asDomain -import im.vector.matrix.android.internal.database.model.EventEntity -import im.vector.matrix.android.internal.session.room.EventRelationExtractor -import im.vector.matrix.android.internal.session.room.membership.RoomMembers -import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor -import io.realm.Realm -import timber.log.Timber -import java.util.* -import javax.inject.Inject - -internal interface TimelineEventFactory { - fun create(eventEntity: EventEntity, realm: Realm): TimelineEvent -} - -internal interface CacheableTimelineEventFactory : TimelineEventFactory { - fun clear() -} - -/** - * This class is responsible for building [TimelineEvent] returned by a [Timeline] through [TimelineService] - * It handles decryption, extracting additional data around an event as sender data and relation. - */ -internal class SimpleTimelineEventFactory @Inject constructor(private val roomMemberExtractor: SenderRoomMemberExtractor, - private val relationExtractor: EventRelationExtractor, - private val cryptoService: CryptoService -) : TimelineEventFactory { - - override fun create(eventEntity: EventEntity, realm: Realm): TimelineEvent { - val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) - val relations = relationExtractor.extractFrom(eventEntity, realm) - - val event = eventEntity.asDomain() - if (event.getClearType() == EventType.ENCRYPTED) { - handleEncryptedEvent(event) - } - - val isUniqueDisplayName = RoomMembers(realm, eventEntity.roomId).isUniqueDisplayName(senderRoomMember?.displayName) - - return TimelineEvent( - event, - eventEntity.localId, - eventEntity.displayIndex, - senderRoomMember?.displayName, - isUniqueDisplayName, - senderRoomMember?.avatarUrl, - eventEntity.sendState, - event.mClearEvent != null, - relations - ) - } - - private fun handleEncryptedEvent(event: Event) { - Timber.v("Encrypted event: try to decrypt ${event.eventId}") - try { - val result = cryptoService.decryptEvent(event, UUID.randomUUID().toString()) - event.setClearData(result) - } catch (failure: Throwable) { - Timber.e("Encrypted event: decryption failed") - if (failure is MXDecryptionException) { - event.setCryptoError(failure.cryptoError) - } - } - } -} - -internal class InMemoryTimelineEventFactory @Inject constructor(private val roomMemberExtractor: SenderRoomMemberExtractor, - private val relationExtractor: EventRelationExtractor, - private val cryptoService: CryptoService) : CacheableTimelineEventFactory { - - private val timelineId = UUID.randomUUID().toString() - private val senderCache = mutableMapOf() - private val decryptionCache = mutableMapOf() - - override fun create(eventEntity: EventEntity, realm: Realm): TimelineEvent { - val sender = eventEntity.sender - val cacheKey = sender + eventEntity.localId - val senderData = senderCache.getOrPut(cacheKey) { - val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) - val isUniqueDisplayName = RoomMembers(realm, eventEntity.roomId).isUniqueDisplayName(senderRoomMember?.displayName) - - SenderData(senderRoomMember?.displayName, - isUniqueDisplayName, - senderRoomMember?.avatarUrl) - } - val event = eventEntity.asDomain() - if (event.getClearType() == EventType.ENCRYPTED && !event.isRedacted()) { - handleEncryptedEvent(event, eventEntity.localId) - } - - val relations = relationExtractor.extractFrom(eventEntity, realm) - return TimelineEvent( - event, - eventEntity.localId, - eventEntity.displayIndex, - senderData.senderName, - senderData.isUniqueDisplayName, - senderData.senderAvatar, - eventEntity.sendState, - event.mClearEvent != null, - relations - ) - } - - private fun handleEncryptedEvent(event: Event, cacheKey: String) { - Timber.v("Encrypted event: try to decrypt ${event.eventId}") - val cachedDecryption = decryptionCache[cacheKey] - if (cachedDecryption != null) { - Timber.v("Encrypted event ${event.eventId} cached") - event.setClearData(cachedDecryption) - } else { - try { - val result = cryptoService.decryptEvent(event, timelineId) - if (result != null) { - decryptionCache[cacheKey] = result - } - event.setClearData(result) - } catch (failure: Throwable) { - Timber.e("Encrypted event: decryption failed ${failure.localizedMessage}") - if (failure is MXDecryptionException) { - event.setCryptoError(failure.cryptoError) - } else { - // Other error - Timber.e("Other error, should be handled") - } - } - } - } - - override fun clear() { - senderCache.clear() - decryptionCache.clear() - } - - private data class SenderData( - val senderName: String?, - val isUniqueDisplayName: Boolean, - val senderAvatar: String? - ) -} \ No newline at end of file