Skip to content

Commit

Permalink
Merge pull request #6486 from vector-im/feature/mna/delete-lls
Browse files Browse the repository at this point in the history
[Location sharing] - Delete action on a live message (PSG-523)
  • Loading branch information
mnaturel authored Jul 19, 2022
2 parents b08337e + b2d7ef9 commit c3105c8
Show file tree
Hide file tree
Showing 41 changed files with 1,059 additions and 41 deletions.
1 change: 1 addition & 0 deletions changelog.d/6437.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Location sharing] - Delete action on a live message
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClear

fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER

fun Event.isLiveLocation(): Boolean = getClearType() in EventType.STATE_ROOM_BEACON_INFO

fun Event.getRelationContent(): RelationDefaultContent? {
return if (isEncrypted()) {
content.toModel<EncryptedEventContent>()?.relatesTo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.matrix.android.sdk.api.session.room.location

import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
import org.matrix.android.sdk.api.util.Cancelable
Expand Down Expand Up @@ -59,16 +58,21 @@ interface LocationSharingService {
*/
suspend fun stopLiveLocationShare(): UpdateLiveLocationShareResult

/**
* Redact (delete) the live associated to the given beacon info event id.
* @param beaconInfoEventId event id of the initial beacon info state event
* @param reason Optional reason string
*/
suspend fun redactLiveLocationShare(beaconInfoEventId: String, reason: String?)

/**
* Returns a LiveData on the list of current running live location shares.
*/
@MainThread
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>

/**
* Returns a LiveData on the live location share summary with the given eventId.
* @param beaconInfoEventId event id of the initial beacon info state event
*/
@MainThread
fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.getRelationContent
import org.matrix.android.sdk.api.session.events.model.isEdition
import org.matrix.android.sdk.api.session.events.model.isLiveLocation
import org.matrix.android.sdk.api.session.events.model.isPoll
import org.matrix.android.sdk.api.session.events.model.isReply
import org.matrix.android.sdk.api.session.events.model.isSticker
Expand Down Expand Up @@ -165,6 +166,10 @@ fun TimelineEvent.isSticker(): Boolean {
return root.isSticker()
}

fun TimelineEvent.isLiveLocation(): Boolean {
return root.isLiveLocation()
}

/**
* Returns whether or not the event is a root thread event.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo029
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo030
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo031
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo032
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo033
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
Expand All @@ -57,7 +58,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
schemaVersion = 32L,
schemaVersion = 33L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
Expand Down Expand Up @@ -99,5 +100,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 30) MigrateSessionTo030(realm).perform()
if (oldVersion < 31) MigrateSessionTo031(realm).perform()
if (oldVersion < 32) MigrateSessionTo032(realm).perform()
if (oldVersion < 33) MigrateSessionTo033(realm).perform()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* 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.migration

import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator

/**
* Migrating to:
* Live location sharing aggregated summary: adding new field relatedEventIds.
*/
internal class MigrateSessionTo033(realm: DynamicRealm) : RealmMigrator(realm, 33) {

override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("LiveLocationShareAggregatedSummaryEntity")
?.addRealmListField(LiveLocationShareAggregatedSummaryEntityFields.RELATED_EVENT_IDS.`$`, String::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.matrix.android.sdk.internal.database.model.livelocation

import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey

Expand All @@ -29,6 +30,11 @@ internal open class LiveLocationShareAggregatedSummaryEntity(
@PrimaryKey
var eventId: String = "",

/**
* List of event ids used to compute the aggregated summary data.
*/
var relatedEventIds: RealmList<String> = RealmList(),

var roomId: String = "",

var userId: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEnt
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity

internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
return realm.where<EventAnnotationsSummaryEntity>()
.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
}

internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
return realm.where<EventAnnotationsSummaryEntity>()
.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
Expand All @@ -44,3 +49,7 @@ internal fun EventAnnotationsSummaryEntity.Companion.getOrCreate(realm: Realm, r
return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
}

internal fun EventAnnotationsSummaryEntity.Companion.get(realm: Realm, eventId: String): EventAnnotationsSummaryEntity? {
return EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEnt
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields

internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
realm: Realm,
eventId: String,
): RealmQuery<LiveLocationShareAggregatedSummaryEntity> {
return realm.where<LiveLocationShareAggregatedSummaryEntity>()
.equalTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId)
}

internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
realm: Realm,
roomId: String,
Expand Down Expand Up @@ -72,6 +80,13 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
}

internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
realm: Realm,
eventId: String,
): LiveLocationShareAggregatedSummaryEntity? {
return LiveLocationShareAggregatedSummaryEntity.where(realm, eventId).findFirst()
}

internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveInRoomForUser(
realm: Realm,
roomId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationPro
import org.matrix.android.sdk.internal.session.room.aggregation.poll.DefaultPollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor
import org.matrix.android.sdk.internal.session.room.location.LiveLocationShareRedactionEventProcessor
import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessorCoroutine
Expand Down Expand Up @@ -321,6 +322,10 @@ internal abstract class SessionModule {
@IntoSet
abstract fun bindEventRedactionProcessor(processor: RedactionEventProcessor): EventInsertLiveProcessor

@Binds
@IntoSet
abstract fun bindLiveLocationShareRedactionEventProcessor(processor: LiveLocationShareRedactionEventProcessor): EventInsertLiveProcessor

@Binds
@IntoSet
abstract fun bindEventRelationsAggregationProcessor(processor: EventRelationsAggregationProcessor): EventInsertLiveProcessor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVi
import org.matrix.android.sdk.internal.session.room.location.CheckIfExistingActiveLiveTask
import org.matrix.android.sdk.internal.session.room.location.DefaultCheckIfExistingActiveLiveTask
import org.matrix.android.sdk.internal.session.room.location.DefaultGetActiveBeaconInfoForUserTask
import org.matrix.android.sdk.internal.session.room.location.DefaultRedactLiveLocationShareTask
import org.matrix.android.sdk.internal.session.room.location.DefaultSendLiveLocationTask
import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask
import org.matrix.android.sdk.internal.session.room.location.DefaultStartLiveLocationShareTask
import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask
import org.matrix.android.sdk.internal.session.room.location.GetActiveBeaconInfoForUserTask
import org.matrix.android.sdk.internal.session.room.location.RedactLiveLocationShareTask
import org.matrix.android.sdk.internal.session.room.location.SendLiveLocationTask
import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask
import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask
Expand Down Expand Up @@ -339,4 +341,7 @@ internal abstract class RoomModule {

@Binds
abstract fun bindCheckIfExistingActiveLiveTask(task: DefaultCheckIfExistingActiveLiveTask): CheckIfExistingActiveLiveTask

@Binds
abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation

import androidx.work.ExistingWorkPolicy
import io.realm.Realm
import io.realm.RealmList
import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.toContent
Expand Down Expand Up @@ -73,6 +74,11 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
eventId = targetEventId
)

if (!isLive && !event.eventId.isNullOrEmpty()) {
// in this case, the received event is a new state event related to the previous one
addRelatedEventId(event.eventId, aggregatedSummary)
}

// remote event can stay with isLive == true while the local summary is no more active
val isActive = aggregatedSummary.isActive.orTrue() && isLive
val endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
Expand Down Expand Up @@ -144,6 +150,11 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
roomId = roomId,
eventId = relatedEventId
)

if (!event.eventId.isNullOrEmpty()) {
addRelatedEventId(event.eventId, aggregatedSummary)
}

val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
val currentLocationTimestamp = ContentMapper
.map(aggregatedSummary.lastLocationContent)
Expand All @@ -160,6 +171,17 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
}
}

private fun addRelatedEventId(
eventId: String,
aggregatedSummary: LiveLocationShareAggregatedSummaryEntity
) {
Timber.d("adding related event id $eventId to summary of id ${aggregatedSummary.eventId}")
val updatedEventIds = aggregatedSummary.relatedEventIds.toMutableList().also {
it.add(eventId)
}
aggregatedSummary.relatedEventIds = RealmList(*updatedEventIds.toTypedArray())
}

private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) {
LiveLocationShareAggregatedSummaryEntity
.findActiveLiveInRoomForUser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
private val startLiveLocationShareTask: StartLiveLocationShareTask,
private val stopLiveLocationShareTask: StopLiveLocationShareTask,
private val checkIfExistingActiveLiveTask: CheckIfExistingActiveLiveTask,
private val redactLiveLocationShareTask: RedactLiveLocationShareTask,
private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
) : LocationSharingService {

Expand Down Expand Up @@ -102,6 +103,15 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
return stopLiveLocationShareTask.execute(params)
}

override suspend fun redactLiveLocationShare(beaconInfoEventId: String, reason: String?) {
val params = RedactLiveLocationShareTask.Params(
roomId = roomId,
beaconInfoEventId = beaconInfoEventId,
reason = reason
)
return redactLiveLocationShareTask.execute(params)
}

override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> {
return monarchy.findAllMappedWithChanges(
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* 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.session.room.location

import io.realm.Realm
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.LocalEcho
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertType
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
import org.matrix.android.sdk.internal.database.query.get
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import timber.log.Timber
import javax.inject.Inject

/**
* Listens to the database for the insertion of any redaction event.
* Delete specifically the aggregated summary related to a redacted live location share event.
*/
internal class LiveLocationShareRedactionEventProcessor @Inject constructor() : EventInsertLiveProcessor {

override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean {
return eventType == EventType.REDACTION && insertType != EventInsertType.LOCAL_ECHO
}

override suspend fun process(realm: Realm, event: Event) {
if (event.redacts.isNullOrBlank() || LocalEcho.isLocalEchoId(event.eventId.orEmpty())) {
return
}

val redactedEvent = EventEntity.where(realm, eventId = event.redacts).findFirst()
?: return

if (redactedEvent.type in EventType.STATE_ROOM_BEACON_INFO) {
val liveSummary = LiveLocationShareAggregatedSummaryEntity.get(realm, eventId = redactedEvent.eventId)

if (liveSummary != null) {
Timber.d("deleting live summary with id: ${liveSummary.eventId}")
liveSummary.deleteFromRealm()
val annotationsSummary = EventAnnotationsSummaryEntity.get(realm, eventId = redactedEvent.eventId)
if (annotationsSummary != null) {
Timber.d("deleting annotation summary with id: ${annotationsSummary.eventId}")
annotationsSummary.deleteFromRealm()
}
}
}
}
}
Loading

0 comments on commit c3105c8

Please sign in to comment.