Adding live summary entity into annotation entity

This commit is contained in:
Maxime NATUREL 2022-04-28 12:10:39 +02:00
parent 25ca50c7bd
commit b788a82d0d
9 changed files with 122 additions and 90 deletions

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.session.room.model.livelocation
import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
/**
* Aggregation info concerning a live location share.
@ -26,7 +26,8 @@ data class LiveLocationAggregatedSummary(
* Event id of the event that started the live.
*/
val eventId: String,
val roomId: String,
val isLive: Boolean,
val endOfLiveTimestampAsMilliseconds: Long,
val lastLocation: LocationInfo? = null
val lastLocationContent: MessageLocationContent? = null,
)

View File

@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
@ -61,16 +60,6 @@ data class LiveLocationBeaconContent(
*/
@Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset = LocationAsset(LocationAssetType.SELF),
@Json(name = "m.asset") val locationAsset: LocationAsset? = null,
/**
* Client side tracking of the last location
*/
var lastLocationContent: MessageLiveLocationContent? = null,
/**
* Client side tracking of whether the beacon has timed out.
*/
var hasTimedOut: Boolean = false
) : MessageContent {
fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds

View File

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationAggregatedSummaryEntity
import timber.log.Timber
internal open class EventAnnotationsSummaryEntity(
@ -27,7 +28,8 @@ internal open class EventAnnotationsSummaryEntity(
var reactionsSummary: RealmList<ReactionAggregatedSummaryEntity> = RealmList(),
var editSummary: EditAggregatedSummaryEntity? = null,
var referencesSummaryEntity: ReferencesAggregatedSummaryEntity? = null,
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null,
var liveLocationAggregatedSummary: LiveLocationAggregatedSummaryEntity? = null,
) : RealmObject() {
/**

View File

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.database.model
import io.realm.annotations.RealmModule
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntity
import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntity
@ -47,6 +48,7 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
EditAggregatedSummaryEntity::class,
EditionOfEvent::class,
PollResponseAggregatedSummaryEntity::class,
LiveLocationAggregatedSummaryEntity::class,
ReferencesAggregatedSummaryEntity::class,
PushRulesEntity::class,
PushRuleEntity::class,

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2022 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 org.matrix.android.sdk.internal.database.query
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationAggregatedSummaryEntityFields
internal fun LiveLocationAggregatedSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<LiveLocationAggregatedSummaryEntity> {
return realm.where<LiveLocationAggregatedSummaryEntity>()
.equalTo(LiveLocationAggregatedSummaryEntityFields.ROOM_ID, roomId)
.equalTo(LiveLocationAggregatedSummaryEntityFields.EVENT_ID, eventId)
}
internal fun LiveLocationAggregatedSummaryEntity.Companion.create(realm: Realm, roomId: String, eventId: String): LiveLocationAggregatedSummaryEntity {
val obj = realm.createObject(LiveLocationAggregatedSummaryEntity::class.java).apply {
this.eventId = eventId
this.roomId = roomId
}
val annotationSummary = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId = roomId, eventId = eventId)
annotationSummary.liveLocationAggregatedSummary = obj
return obj
}
internal fun LiveLocationAggregatedSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String, eventId: String): LiveLocationAggregatedSummaryEntity {
return LiveLocationAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
?: LiveLocationAggregatedSummaryEntity.create(realm, roomId, eventId)
}

View File

@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
import org.matrix.android.sdk.api.session.room.model.VoteInfo
import org.matrix.android.sdk.api.session.room.model.VoteSummary
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
@ -91,7 +92,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
// EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_KEY,
EventType.ENCRYPTED
) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + EventType.BEACON_LOCATION_DATA
) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + EventType.STATE_ROOM_BEACON_INFO + EventType.BEACON_LOCATION_DATA
override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean {
return allowedTypes.contains(eventType)
@ -106,12 +107,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
when (event.type) {
EventType.REACTION -> {
EventType.REACTION -> {
// we got a reaction!!
Timber.v("###REACTION in room $roomId , reaction eventID ${event.eventId}")
handleReaction(realm, event, roomId, isLocalEcho)
}
EventType.MESSAGE -> {
EventType.MESSAGE -> {
if (event.unsignedData?.relations?.annotations != null) {
Timber.v("###REACTION Aggregation in room $roomId for event ${event.eventId}")
handleInitialAggregatedRelations(realm, event, roomId, event.unsignedData.relations.annotations)
@ -137,7 +138,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
EventType.KEY_VERIFICATION_START,
EventType.KEY_VERIFICATION_MAC,
EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_KEY -> {
EventType.KEY_VERIFICATION_KEY -> {
Timber.v("## SAS REF in room $roomId for event ${event.eventId}")
event.content.toModel<MessageRelationContent>()?.relatesTo?.let {
if (it.type == RelationType.REFERENCE && it.eventId != null) {
@ -146,7 +147,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
EventType.ENCRYPTED -> {
EventType.ENCRYPTED -> {
// Relation type is in clear
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE ||
@ -172,23 +173,28 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
EventType.KEY_VERIFICATION_START,
EventType.KEY_VERIFICATION_MAC,
EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_KEY -> {
EventType.KEY_VERIFICATION_KEY -> {
Timber.v("## SAS REF in room $roomId for event ${event.eventId}")
encryptedEventContent.relatesTo.eventId?.let {
handleVerification(realm, event, roomId, isLocalEcho, it)
}
}
in EventType.POLL_RESPONSE -> {
in EventType.POLL_RESPONSE -> {
event.getClearContent().toModel<MessagePollResponseContent>(catchError = true)?.let {
handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId)
}
}
in EventType.POLL_END -> {
in EventType.POLL_END -> {
event.content.toModel<MessageEndPollContent>(catchError = true)?.let {
handleEndPoll(realm, event, it, roomId, isLocalEcho)
}
}
in EventType.BEACON_LOCATION_DATA -> {
in EventType.STATE_ROOM_BEACON_INFO -> {
event.content.toModel<LiveLocationBeaconContent>(catchError = true)?.let {
liveLocationAggregationProcessor.handleBeaconInfo(realm, event, it, roomId, isLocalEcho)
}
}
in EventType.BEACON_LOCATION_DATA -> {
event.content.toModel<MessageLiveLocationContent>(catchError = true)?.let {
liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho)
}
@ -213,7 +219,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
// }
// }
}
EventType.REDACTION -> {
EventType.REDACTION -> {
val eventToPrune = event.redacts?.let { EventEntity.where(realm, eventId = it).findFirst() }
?: return
when (eventToPrune.type) {
@ -233,7 +239,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
}
in EventType.POLL_START -> {
in EventType.POLL_START -> {
val content: MessagePollContent? = event.content.toModel()
if (content?.relatesTo?.type == RelationType.REPLACE) {
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
@ -241,22 +247,27 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
handleReplace(realm, event, content, roomId, isLocalEcho)
}
}
in EventType.POLL_RESPONSE -> {
in EventType.POLL_RESPONSE -> {
event.content.toModel<MessagePollResponseContent>(catchError = true)?.let {
handleResponse(realm, event, it, roomId, isLocalEcho)
}
}
in EventType.POLL_END -> {
in EventType.POLL_END -> {
event.content.toModel<MessageEndPollContent>(catchError = true)?.let {
handleEndPoll(realm, event, it, roomId, isLocalEcho)
}
}
in EventType.BEACON_LOCATION_DATA -> {
in EventType.STATE_ROOM_BEACON_INFO -> {
event.content.toModel<LiveLocationBeaconContent>(catchError = true)?.let {
liveLocationAggregationProcessor.handleBeaconInfo(realm, event, it, roomId, isLocalEcho)
}
}
in EventType.BEACON_LOCATION_DATA -> {
event.content.toModel<MessageLiveLocationContent>(catchError = true)?.let {
liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho)
}
}
else -> Timber.v("UnHandled event ${event.eventId}")
else -> Timber.v("UnHandled event ${event.eventId}")
}
} catch (t: Throwable) {
Timber.e(t, "## Should not happen ")
@ -325,7 +336,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
totalVotes = 0,
winnerVoteCount = 0,
)
.toContent())
.toContent()
)
}
val txId = event.unsignedData?.transactionId
@ -729,11 +741,13 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
EventType.KEY_VERIFICATION_READY,
EventType.KEY_VERIFICATION_KEY,
EventType.KEY_VERIFICATION_MAC -> currentState.toState(VerificationState.WAITING)
EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(if (event.senderId == userId) {
VerificationState.CANCELED_BY_ME
} else {
VerificationState.CANCELED_BY_OTHER
})
EventType.KEY_VERIFICATION_CANCEL -> currentState.toState(
if (event.senderId == userId) {
VerificationState.CANCELED_BY_ME
} else {
VerificationState.CANCELED_BY_OTHER
}
)
EventType.KEY_VERIFICATION_DONE -> currentState.toState(VerificationState.DONE)
else -> VerificationState.REQUEST
}

View File

@ -17,69 +17,35 @@
package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
import io.realm.Realm
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.query.getOrNull
import timber.log.Timber
import javax.inject.Inject
internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : LiveLocationAggregationProcessor {
override fun handleLiveLocation(realm: Realm, event: Event, content: MessageLiveLocationContent, roomId: String, isLocalEcho: Boolean) {
val locationSenderId = event.senderId ?: return
override fun handleBeaconInfo(realm: Realm, event: Event, content: LiveLocationBeaconContent, roomId: String, isLocalEcho: Boolean) {
//val locationSenderId = event.senderId ?: return
// We shouldn't process local echos
if (isLocalEcho) {
return
}
// A beacon info state event has to be sent before sending location
// TODO handle missing check of m_relatesTo field
var beaconInfoEntity: CurrentStateEventEntity? = null
val eventTypesIterator = EventType.STATE_ROOM_BEACON_INFO.iterator()
while (beaconInfoEntity == null && eventTypesIterator.hasNext()) {
beaconInfoEntity = CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, eventTypesIterator.next())
}
if (beaconInfoEntity == null) {
Timber.v("## LIVE LOCATION. There is not any beacon info which should be emitted before sending location updates")
return
}
val beaconInfoContent = ContentMapper.map(beaconInfoEntity.root?.content)?.toModel<LiveLocationBeaconContent>(catchError = true)
if (beaconInfoContent == null) {
Timber.v("## LIVE LOCATION. Beacon info content is invalid")
return
}
// Check if live location is ended
if (!beaconInfoContent.isLive.orFalse()) {
Timber.v("## LIVE LOCATION. Beacon info is not live anymore")
return
}
// Check if beacon info is outdated
if (isBeaconInfoOutdated(beaconInfoContent, content)) {
Timber.v("## LIVE LOCATION. Beacon info has timeout")
beaconInfoContent.hasTimedOut = true
} else {
beaconInfoContent.lastLocationContent = content
}
beaconInfoEntity.root?.content = ContentMapper.map(beaconInfoContent.toContent())
// TODO if live field is true, get eventId else get get replace eventId
// TODO getOrCreate existing aggregated summary
// TODO update the endOfLiveTimestamp and live fields
}
private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent,
liveLocationContent: MessageLiveLocationContent): Boolean {
val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0
val liveLocationEventTime = liveLocationContent.getBestTimestampAsMilliseconds() ?: 0
val timeout = beaconInfoContent.timeout ?: 0
return liveLocationEventTime - beaconInfoStartTime > timeout
override fun handleLiveLocation(realm: Realm, event: Event, content: MessageLiveLocationContent, roomId: String, isLocalEcho: Boolean) {
//val locationSenderId = event.senderId ?: return
// We shouldn't process local echos
if (isLocalEcho) {
return
}
// TODO getOrCreate existing aggregated summary
// TODO add location content only if more recent than the current one if any
}
}

View File

@ -18,12 +18,23 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
import io.realm.Realm
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent
import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent
internal interface LiveLocationAggregationProcessor {
fun handleLiveLocation(realm: Realm,
event: Event,
content: MessageLiveLocationContent,
roomId: String,
isLocalEcho: Boolean)
fun handleBeaconInfo(
realm: Realm,
event: Event,
content: LiveLocationBeaconContent,
roomId: String,
isLocalEcho: Boolean,
)
fun handleLiveLocation(
realm: Realm,
event: Event,
content: MessageLiveLocationContent,
roomId: String,
isLocalEcho: Boolean,
)
}

View File

@ -46,7 +46,8 @@ class LiveLocationMessageItemFactory @Inject constructor(
}
private fun isLiveRunning(liveLocationContent: LiveLocationBeaconContent): Boolean {
return liveLocationContent.isLive.orFalse() && liveLocationContent.hasTimedOut.not()
// TODO when we will use aggregatedSummary, check if the live has timed out as well
return liveLocationContent.isLive.orFalse()
}
private fun buildStartLiveItem(