From c095cc73cec34516673e275aad86d7d3d5c999e9 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 1 Jul 2022 17:07:40 +0200
Subject: [PATCH 01/19] Introducing use case to check if an event can be
 redacted

---
 .../action/CheckIfCanRedactEventUseCase.kt    |  38 ++++++
 .../action/MessageActionsViewModel.kt         |   8 +-
 .../CheckIfCanRedactEventUseCaseTest.kt       | 115 ++++++++++++++++++
 3 files changed, 155 insertions(+), 6 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
 create mode 100644 vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
new file mode 100644
index 0000000000..222df8f914
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
@@ -0,0 +1,38 @@
+/*
+ * 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 im.vector.app.features.home.room.detail.timeline.action
+
+import im.vector.app.core.di.ActiveSessionHolder
+import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import javax.inject.Inject
+
+class CheckIfCanRedactEventUseCase @Inject constructor(
+        private val activeSessionHolder: ActiveSessionHolder
+) {
+
+    fun execute(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
+        // Only some event types are supported for the moment
+        val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER) +
+                EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
+        if (event.root.getClearType() !in canRedactEventTypes) return false
+        // Message sent by the current user can always be redacted
+        if (event.root.senderId == activeSessionHolder.getActiveSession().myUserId) return true
+        // Check permission for messages sent by other users
+        return actionPermissions.canRedact
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index 30786dc77a..3dfb6744e0 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -82,6 +82,7 @@ class MessageActionsViewModel @AssistedInject constructor(
         private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
         private val vectorPreferences: VectorPreferences,
         private val checkIfCanReplyEventUseCase: CheckIfCanReplyEventUseCase,
+        private val checkIfCanRedactEventUseCase: CheckIfCanRedactEventUseCase,
 ) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
 
     private val informationData = initialState.informationData
@@ -518,12 +519,7 @@ class MessageActionsViewModel @AssistedInject constructor(
     }
 
     private fun canRedact(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
-        // Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
-        if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START) return false
-        // Message sent by the current user can always be redacted
-        if (event.root.senderId == session.myUserId) return true
-        // Check permission for messages sent by other users
-        return actionPermissions.canRedact
+        return checkIfCanRedactEventUseCase.execute(event, actionPermissions)
     }
 
     private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt
new file mode 100644
index 0000000000..08dd5dac5b
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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 im.vector.app.features.home.room.detail.timeline.action
+
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import io.mockk.mockk
+import org.amshove.kluent.shouldBe
+import org.junit.Test
+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.room.timeline.TimelineEvent
+
+class CheckIfCanRedactEventUseCaseTest {
+
+    private val fakeActiveSessionHolder = FakeActiveSessionHolder()
+
+    private val checkIfCanRedactEventUseCase = CheckIfCanRedactEventUseCase(
+            activeSessionHolder = fakeActiveSessionHolder.instance
+    )
+
+    @Test
+    fun `given an event which can be redacted and owned by user when use case executes then the result is true`() {
+        val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER) +
+                EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
+
+        canRedactEventTypes.forEach { eventType ->
+            val event = givenAnEvent(
+                    eventType = eventType,
+                    senderId = fakeActiveSessionHolder.fakeSession.myUserId
+            )
+
+            val actionPermissions = givenActionPermissions(canRedact = false)
+
+            val result = checkIfCanRedactEventUseCase.execute(event, actionPermissions)
+
+            result shouldBe true
+        }
+    }
+
+    @Test
+    fun `given redact permission and an event which can be redacted and sent by another user when use case executes then the result is true`() {
+        val event = givenAnEvent(
+                eventType = EventType.MESSAGE,
+                senderId = "user-id"
+        )
+
+        val actionPermissions = givenActionPermissions(canRedact = true)
+
+        val result = checkIfCanRedactEventUseCase.execute(event, actionPermissions)
+
+        result shouldBe true
+    }
+
+    @Test
+    fun `given an event which cannot be redacted when use case executes then the result is false`() {
+        val event = givenAnEvent(
+                eventType = EventType.CALL_ANSWER,
+                senderId = fakeActiveSessionHolder.fakeSession.myUserId
+        )
+
+        val actionPermissions = givenActionPermissions(canRedact = false)
+
+        val result = checkIfCanRedactEventUseCase.execute(event, actionPermissions)
+
+        result shouldBe false
+    }
+
+    @Test
+    fun `given missing redact permission and an event which can be redacted and sent by another user when use case executes then the result is false`() {
+        val event = givenAnEvent(
+                eventType = EventType.MESSAGE,
+                senderId = "user-id"
+        )
+
+        val actionPermissions = givenActionPermissions(canRedact = false)
+
+        val result = checkIfCanRedactEventUseCase.execute(event, actionPermissions)
+
+        result shouldBe false
+    }
+
+    private fun givenAnEvent(eventType: String, senderId: String): TimelineEvent {
+        val eventId = "event-id"
+        return TimelineEvent(
+                root = Event(
+                        eventId = eventId,
+                        type = eventType,
+                        senderId = senderId
+                ),
+                localId = 123L,
+                eventId = eventId,
+                displayIndex = 1,
+                ownedByThreadChunk = false,
+                senderInfo = mockk()
+        )
+    }
+
+    private fun givenActionPermissions(canRedact: Boolean): ActionPermissions {
+        return ActionPermissions(canRedact = canRedact)
+    }
+}

From e26759fa89bcef8c9e0b213954f25c1521aab6b6 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 1 Jul 2022 17:23:31 +0200
Subject: [PATCH 02/19] Adding changelog entry

---
 changelog.d/6437.feature | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/6437.feature

diff --git a/changelog.d/6437.feature b/changelog.d/6437.feature
new file mode 100644
index 0000000000..fb24819daf
--- /dev/null
+++ b/changelog.d/6437.feature
@@ -0,0 +1 @@
+[Location sharing] - Delete action on a live message

From bad4eba153eb5b917492cdbf1e54158fae70cd99 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 4 Jul 2022 15:33:52 +0200
Subject: [PATCH 03/19] Remove non necessary @MainThread annotations

---
 .../sdk/api/session/room/location/LocationSharingService.kt     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
index 14095b67c0..a92542fcf5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
@@ -62,13 +62,11 @@ interface LocationSharingService {
     /**
      * 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>>
 }

From c404454cd7bc2667b1cbd21d08da3566a1521b8f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Jul 2022 14:26:02 +0200
Subject: [PATCH 04/19] Adding related event ids of live in entity

---
 ...iveLocationShareAggregatedSummaryEntity.kt |  6 +++++
 .../LiveLocationAggregationProcessor.kt       | 22 +++++++++++++++++++
 .../LiveLocationAggregationProcessorTest.kt   | 13 +++++++----
 3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/livelocation/LiveLocationShareAggregatedSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/livelocation/LiveLocationShareAggregatedSummaryEntity.kt
index c5df8e9338..08ea06bb1e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/livelocation/LiveLocationShareAggregatedSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/livelocation/LiveLocationShareAggregatedSummaryEntity.kt
@@ -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
 
@@ -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 = "",
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
index 921749122b..3f5b1e1360 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
@@ -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
@@ -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) }
@@ -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)
@@ -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(
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt
index 933087af2b..a5e91714b7 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessorTest.kt
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
 
 import androidx.work.ExistingWorkPolicy
 import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldContain
 import org.junit.Test
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.UnsignedData
@@ -199,9 +200,10 @@ internal class LiveLocationAggregationProcessorTest {
                 age = 123,
                 replacesState = AN_EVENT_ID
         )
+        val stateEventId = "state-event-id"
         val event = Event(
                 senderId = A_SENDER_ID,
-                eventId = "",
+                eventId = stateEventId,
                 unsignedData = unsignedData
         )
         val beaconInfo = MessageBeaconInfoContent(
@@ -237,6 +239,7 @@ internal class LiveLocationAggregationProcessorTest {
         aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID
         aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID
         aggregatedEntity.isActive shouldBeEqualTo false
+        aggregatedEntity.relatedEventIds shouldContain stateEventId
         aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS
         aggregatedEntity.lastLocationContent shouldBeEqualTo null
         previousEntities.forEach { entity ->
@@ -324,7 +327,7 @@ internal class LiveLocationAggregationProcessorTest {
         val lastBeaconLocationContent = MessageBeaconLocationDataContent(
                 unstableTimestampMillis = A_TIMESTAMP
         )
-        givenLastSummaryQueryReturns(
+        val aggregatedEntity = givenLastSummaryQueryReturns(
                 eventId = AN_EVENT_ID,
                 roomId = A_ROOM_ID,
                 beaconLocationContent = lastBeaconLocationContent
@@ -340,6 +343,7 @@ internal class LiveLocationAggregationProcessorTest {
         )
 
         result shouldBeEqualTo false
+        aggregatedEntity.relatedEventIds shouldContain AN_EVENT_ID
     }
 
     @Test
@@ -353,7 +357,7 @@ internal class LiveLocationAggregationProcessorTest {
         val lastBeaconLocationContent = MessageBeaconLocationDataContent(
                 unstableTimestampMillis = A_TIMESTAMP - 60_000
         )
-        val entity = givenLastSummaryQueryReturns(
+        val aggregatedEntity = givenLastSummaryQueryReturns(
                 eventId = AN_EVENT_ID,
                 roomId = A_ROOM_ID,
                 beaconLocationContent = lastBeaconLocationContent
@@ -369,7 +373,8 @@ internal class LiveLocationAggregationProcessorTest {
         )
 
         result shouldBeEqualTo true
-        val savedLocationData = ContentMapper.map(entity.lastLocationContent).toModel<MessageBeaconLocationDataContent>()
+        aggregatedEntity.relatedEventIds shouldContain AN_EVENT_ID
+        val savedLocationData = ContentMapper.map(aggregatedEntity.lastLocationContent).toModel<MessageBeaconLocationDataContent>()
         savedLocationData?.getBestTimestampMillis() shouldBeEqualTo A_TIMESTAMP
         savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI
     }

From c9273dd067d6c8d23163a19e2d82f2d21b31d56b Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Jul 2022 15:26:42 +0200
Subject: [PATCH 05/19] Adding task to redact live location share related
 events

---
 ...cationShareAggregatedSummaryEntityQuery.kt |  15 +++
 .../sdk/internal/session/room/RoomModule.kt   |   5 +
 .../location/RedactLiveLocationShareTask.kt   |  79 ++++++++++++
 .../DefaultRedactLiveLocationShareTaskTest.kt | 116 ++++++++++++++++++
 .../test/fakes/FakeEventSenderProcessor.kt    |   4 +
 .../test/fakes/FakeLocalEchoEventFactory.kt   |  66 +++++++---
 .../sdk/test/fakes/FakeRealmConfiguration.kt  |  41 +++++++
 7 files changed, 308 insertions(+), 18 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
 create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
 create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealmConfiguration.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt
index d69f251f6f..fbf7e963a7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/LiveLocationShareAggregatedSummaryEntityQuery.kt
@@ -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,
@@ -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,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
index e1dd22a211..d01324a35f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt
@@ -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
@@ -339,4 +341,7 @@ internal abstract class RoomModule {
 
     @Binds
     abstract fun bindCheckIfExistingActiveLiveTask(task: DefaultCheckIfExistingActiveLiveTask): CheckIfExistingActiveLiveTask
+
+    @Binds
+    abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
new file mode 100644
index 0000000000..9883d1204b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.session.room.location
+
+import io.realm.RealmConfiguration
+import org.matrix.android.sdk.internal.database.awaitTransaction
+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.di.SessionDatabase
+import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
+import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
+import org.matrix.android.sdk.internal.task.Task
+import timber.log.Timber
+import javax.inject.Inject
+
+internal interface RedactLiveLocationShareTask : Task<RedactLiveLocationShareTask.Params, Unit> {
+    data class Params(
+            val roomId: String,
+            val beaconInfoEventId: String,
+            val reason: String?
+    )
+}
+
+internal class DefaultRedactLiveLocationShareTask @Inject constructor(
+        @SessionDatabase private val realmConfiguration: RealmConfiguration,
+        private val localEchoEventFactory: LocalEchoEventFactory,
+        private val eventSenderProcessor: EventSenderProcessor,
+) : RedactLiveLocationShareTask {
+
+    override suspend fun execute(params: RedactLiveLocationShareTask.Params) {
+        val relatedEventIds = getRelatedEventIdsOfLive(params.beaconInfoEventId)
+        Timber.d("beacon with id ${params.beaconInfoEventId} has related event ids: ${relatedEventIds.joinToString(", ")}")
+
+        relatedEventIds.forEach { eventId ->
+            redactEvent(
+                    eventId = eventId,
+                    roomId = params.roomId,
+                    reason = params.reason
+            )
+        }
+
+        redactEvent(
+                eventId = params.beaconInfoEventId,
+                roomId = params.roomId,
+                reason = params.reason
+        )
+    }
+
+    private suspend fun getRelatedEventIdsOfLive(beaconInfoEventId: String): List<String> {
+        return awaitTransaction(realmConfiguration) { realm ->
+            val aggregatedSummaryEntity = LiveLocationShareAggregatedSummaryEntity.get(
+                    realm = realm,
+                    eventId = beaconInfoEventId
+            )
+            aggregatedSummaryEntity?.relatedEventIds?.toList() ?: emptyList()
+        }
+    }
+
+    private fun redactEvent(eventId: String, roomId: String, reason: String?) {
+        Timber.d("redacting event of id $eventId")
+        val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, eventId, reason)
+        localEchoEventFactory.createLocalEcho(redactionEcho)
+        eventSenderProcessor.postRedaction(redactionEcho, reason)
+    }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
new file mode 100644
index 0000000000..66428ac064
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.session.room.location
+
+import io.mockk.unmockkAll
+import io.realm.RealmList
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Test
+import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
+import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
+import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor
+import org.matrix.android.sdk.test.fakes.FakeLocalEchoEventFactory
+import org.matrix.android.sdk.test.fakes.FakeRealm
+import org.matrix.android.sdk.test.fakes.FakeRealmConfiguration
+import org.matrix.android.sdk.test.fakes.givenEqualTo
+import org.matrix.android.sdk.test.fakes.givenFindFirst
+
+private const val A_ROOM_ID = "room-id"
+private const val AN_EVENT_ID = "event-id"
+private const val AN_EVENT_ID_1 = "event-id-1"
+private const val AN_EVENT_ID_2 = "event-id-2"
+private const val AN_EVENT_ID_3 = "event-id-3"
+private const val A_REASON = "reason"
+
+@ExperimentalCoroutinesApi
+class DefaultRedactLiveLocationShareTaskTest {
+
+    private val fakeRealmConfiguration = FakeRealmConfiguration()
+    private val fakeLocalEchoEventFactory = FakeLocalEchoEventFactory()
+    private val fakeEventSenderProcessor = FakeEventSenderProcessor()
+    private val fakeRealm = FakeRealm()
+
+    private val defaultRedactLiveLocationShareTask = DefaultRedactLiveLocationShareTask(
+            realmConfiguration = fakeRealmConfiguration.instance,
+            localEchoEventFactory = fakeLocalEchoEventFactory.instance,
+            eventSenderProcessor = fakeEventSenderProcessor
+    )
+
+    @After
+    fun tearDown() {
+        unmockkAll()
+    }
+
+    @Test
+    fun `given parameters when calling the task then it is correctly executed`() = runTest {
+        val params = RedactLiveLocationShareTask.Params(
+                roomId = A_ROOM_ID,
+                beaconInfoEventId = AN_EVENT_ID,
+                reason = A_REASON
+        )
+        fakeRealmConfiguration.givenAwaitTransaction<List<String>>(fakeRealm.instance)
+        val relatedEventIds = listOf(AN_EVENT_ID_1, AN_EVENT_ID_2, AN_EVENT_ID_3)
+        val aggregatedSummaryEntity = LiveLocationShareAggregatedSummaryEntity(
+                eventId = AN_EVENT_ID,
+                relatedEventIds = RealmList(*relatedEventIds.toTypedArray()),
+        )
+        fakeRealm.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
+                .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID)
+                .givenFindFirst(aggregatedSummaryEntity)
+        val redactedEvent = fakeLocalEchoEventFactory.givenCreateRedactEvent(
+                eventId = AN_EVENT_ID,
+                withLocalEcho = true
+        )
+        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent, reason = A_REASON)
+        val redactedEvent1 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
+                eventId = AN_EVENT_ID_1,
+                withLocalEcho = true
+        )
+        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent1, reason = A_REASON)
+        val redactedEvent2 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
+                eventId = AN_EVENT_ID_2,
+                withLocalEcho = true
+        )
+        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent2, reason = A_REASON)
+        val redactedEvent3 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
+                eventId = AN_EVENT_ID_3,
+                withLocalEcho = true
+        )
+        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent3, reason = A_REASON)
+
+        defaultRedactLiveLocationShareTask.execute(params)
+
+        fakeLocalEchoEventFactory.verifyCreateRedactEvent(
+                roomId = A_ROOM_ID,
+                eventId = AN_EVENT_ID,
+                reason = A_REASON
+        )
+        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent)
+        relatedEventIds.forEach { eventId ->
+            fakeLocalEchoEventFactory.verifyCreateRedactEvent(
+                    roomId = A_ROOM_ID,
+                    eventId = eventId,
+                    reason = A_REASON
+            )
+        }
+        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent1)
+        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent2)
+        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent3)
+    }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt
index fbdcf5bfd7..db04b8b8cb 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeEventSenderProcessor.kt
@@ -27,4 +27,8 @@ internal class FakeEventSenderProcessor : EventSenderProcessor by mockk() {
     fun givenPostEventReturns(event: Event, cancelable: Cancelable) {
         every { postEvent(event) } returns cancelable
     }
+
+    fun givenPostRedaction(event: Event, reason: String?) {
+        every { postRedaction(event, reason) } returns mockk()
+    }
 }
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt
index 50ec85f14a..f484e32149 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeLocalEchoEventFactory.kt
@@ -46,24 +46,6 @@ internal class FakeLocalEchoEventFactory {
         return event
     }
 
-    fun givenCreateLiveLocationEvent(withLocalEcho: Boolean): Event {
-        val event = Event()
-        every {
-            instance.createLiveLocationEvent(
-                    beaconInfoEventId = any(),
-                    roomId = any(),
-                    latitude = any(),
-                    longitude = any(),
-                    uncertainty = any()
-            )
-        } returns event
-
-        if (withLocalEcho) {
-            every { instance.createLocalEcho(event) } just runs
-        }
-        return event
-    }
-
     fun verifyCreateStaticLocationEvent(
             roomId: String,
             latitude: Double,
@@ -82,6 +64,24 @@ internal class FakeLocalEchoEventFactory {
         }
     }
 
+    fun givenCreateLiveLocationEvent(withLocalEcho: Boolean): Event {
+        val event = Event()
+        every {
+            instance.createLiveLocationEvent(
+                    beaconInfoEventId = any(),
+                    roomId = any(),
+                    latitude = any(),
+                    longitude = any(),
+                    uncertainty = any()
+            )
+        } returns event
+
+        if (withLocalEcho) {
+            every { instance.createLocalEcho(event) } just runs
+        }
+        return event
+    }
+
     fun verifyCreateLiveLocationEvent(
             roomId: String,
             beaconInfoEventId: String,
@@ -100,6 +100,36 @@ internal class FakeLocalEchoEventFactory {
         }
     }
 
+    fun givenCreateRedactEvent(eventId: String, withLocalEcho: Boolean): Event {
+        val event = Event()
+        every {
+            instance.createRedactEvent(
+                    roomId = any(),
+                    eventId = eventId,
+                    reason = any()
+            )
+        } returns event
+
+        if (withLocalEcho) {
+            every { instance.createLocalEcho(event) } just runs
+        }
+        return event
+    }
+
+    fun verifyCreateRedactEvent(
+            roomId: String,
+            eventId: String,
+            reason: String?
+    ) {
+        verify {
+            instance.createRedactEvent(
+                    roomId = roomId,
+                    eventId = eventId,
+                    reason = reason
+            )
+        }
+    }
+
     fun verifyCreateLocalEcho(event: Event) {
         verify { instance.createLocalEcho(event) }
     }
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealmConfiguration.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealmConfiguration.kt
new file mode 100644
index 0000000000..15a9823c79
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealmConfiguration.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.test.fakes
+
+import io.mockk.coEvery
+import io.mockk.mockk
+import io.mockk.mockkStatic
+import io.mockk.slot
+import io.realm.Realm
+import io.realm.RealmConfiguration
+import org.matrix.android.sdk.internal.database.awaitTransaction
+
+internal class FakeRealmConfiguration {
+
+    init {
+        mockkStatic("org.matrix.android.sdk.internal.database.AsyncTransactionKt")
+    }
+
+    val instance = mockk<RealmConfiguration>()
+
+    fun <T> givenAwaitTransaction(realm: Realm) {
+        val transaction = slot<suspend (Realm) -> T>()
+        coEvery { awaitTransaction(instance, capture(transaction)) } coAnswers {
+            secondArg<suspend (Realm) -> T>().invoke(realm)
+        }
+    }
+}

From 237a5a18f3c149853cd6083596097b90bad22d3a Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Jul 2022 16:03:18 +0200
Subject: [PATCH 06/19] Adding new method in location sharing service to redact
 a live location share

---
 .../room/location/LocationSharingService.kt   |  7 +++++++
 .../location/DefaultLocationSharingService.kt | 10 ++++++++++
 .../DefaultLocationSharingServiceTest.kt      | 19 +++++++++++++++++++
 3 files changed, 36 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
index a92542fcf5..50a87fbdc2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
@@ -59,6 +59,13 @@ 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.
      */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
index a8a9691ce9..60312071d7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingService.kt
@@ -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 {
 
@@ -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) },
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt
index ef9bde2c49..a01f51604c 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultLocationSharingServiceTest.kt
@@ -22,8 +22,10 @@ import androidx.lifecycle.Transformations
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.every
+import io.mockk.just
 import io.mockk.mockk
 import io.mockk.mockkStatic
+import io.mockk.runs
 import io.mockk.slot
 import io.mockk.unmockkAll
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -52,6 +54,7 @@ private const val A_LONGITUDE = 40.0
 private const val AN_UNCERTAINTY = 5.0
 private const val A_TIMEOUT = 15_000L
 private const val A_DESCRIPTION = "description"
+private const val A_REASON = "reason"
 
 @ExperimentalCoroutinesApi
 internal class DefaultLocationSharingServiceTest {
@@ -62,6 +65,7 @@ internal class DefaultLocationSharingServiceTest {
     private val startLiveLocationShareTask = mockk<StartLiveLocationShareTask>()
     private val stopLiveLocationShareTask = mockk<StopLiveLocationShareTask>()
     private val checkIfExistingActiveLiveTask = mockk<CheckIfExistingActiveLiveTask>()
+    private val redactLiveLocationShareTask = mockk<RedactLiveLocationShareTask>()
     private val fakeLiveLocationShareAggregatedSummaryMapper = mockk<LiveLocationShareAggregatedSummaryMapper>()
 
     private val defaultLocationSharingService = DefaultLocationSharingService(
@@ -72,6 +76,7 @@ internal class DefaultLocationSharingServiceTest {
             startLiveLocationShareTask = startLiveLocationShareTask,
             stopLiveLocationShareTask = stopLiveLocationShareTask,
             checkIfExistingActiveLiveTask = checkIfExistingActiveLiveTask,
+            redactLiveLocationShareTask = redactLiveLocationShareTask,
             liveLocationShareAggregatedSummaryMapper = fakeLiveLocationShareAggregatedSummaryMapper
     )
 
@@ -209,6 +214,20 @@ internal class DefaultLocationSharingServiceTest {
         coVerify { stopLiveLocationShareTask.execute(expectedParams) }
     }
 
+    @Test
+    fun `live location share can be redacted`() = runTest {
+        coEvery { redactLiveLocationShareTask.execute(any()) } just runs
+
+        defaultLocationSharingService.redactLiveLocationShare(beaconInfoEventId = AN_EVENT_ID, reason = A_REASON)
+
+        val expectedParams = RedactLiveLocationShareTask.Params(
+                roomId = A_ROOM_ID,
+                beaconInfoEventId = AN_EVENT_ID,
+                reason = A_REASON
+        )
+        coVerify { redactLiveLocationShareTask.execute(expectedParams) }
+    }
+
     @Test
     fun `livedata of live summaries is correctly computed`() {
         val entity = LiveLocationShareAggregatedSummaryEntity()

From 391bf842b4b266f3fabd9fd4ea840e3957d1563e Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Jul 2022 16:28:44 +0200
Subject: [PATCH 07/19] Calling usecase to redact live location share event

---
 .../sdk/api/session/events/model/Event.kt     |  2 +
 .../room/location/LocationSharingService.kt   |  1 -
 .../session/room/timeline/TimelineEvent.kt    |  5 ++
 .../home/room/detail/TimelineViewModel.kt     | 11 ++++-
 .../RedactLiveLocationShareEventUseCase.kt    | 30 ++++++++++++
 ...RedactLiveLocationShareEventUseCaseTest.kt | 49 +++++++++++++++++++
 .../test/fakes/FakeLocationSharingService.kt  | 11 +++++
 7 files changed, 107 insertions(+), 2 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCase.kt
 create mode 100644 vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
index 554dc2ec9d..59dc6c434d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
@@ -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
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
index 50a87fbdc2..cd8acbcccc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt
@@ -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
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
index 9d8c8a13bd..d391abf1e6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
@@ -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
@@ -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.
  */
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
index e305ccbec1..1a68371222 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
@@ -48,6 +48,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
 import im.vector.app.features.createdirect.DirectRoomHelper
 import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
 import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
+import im.vector.app.features.home.room.detail.location.RedactLiveLocationShareEventUseCase
 import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
 import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
 import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
@@ -105,6 +106,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
 import org.matrix.android.sdk.api.session.room.read.ReadService
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.isLiveLocation
 import org.matrix.android.sdk.api.session.sync.SyncRequestState
 import org.matrix.android.sdk.api.session.threads.ThreadNotificationBadgeState
 import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
@@ -135,6 +137,7 @@ class TimelineViewModel @AssistedInject constructor(
         private val notificationDrawerManager: NotificationDrawerManager,
         private val locationSharingServiceConnection: LocationSharingServiceConnection,
         private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
+        private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase,
         timelineFactory: TimelineFactory,
         appStateHandler: AppStateHandler,
 ) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
@@ -770,7 +773,13 @@ class TimelineViewModel @AssistedInject constructor(
 
     private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
         val event = room.getTimelineEvent(action.targetEventId) ?: return
-        room.sendService().redactEvent(event.root, action.reason)
+        if (event.isLiveLocation()) {
+            viewModelScope.launch {
+                redactLiveLocationShareEventUseCase.execute(event.root, room, action.reason)
+            }
+        } else {
+            room.sendService().redactEvent(event.root, action.reason)
+        }
     }
 
     private fun handleUndoReact(action: RoomDetailAction.UndoReaction) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCase.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCase.kt
new file mode 100644
index 0000000000..ba91000b40
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCase.kt
@@ -0,0 +1,30 @@
+/*
+ * 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 im.vector.app.features.home.room.detail.location
+
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.room.Room
+import javax.inject.Inject
+
+class RedactLiveLocationShareEventUseCase @Inject constructor() {
+
+    suspend fun execute(event: Event, room: Room, reason: String?) {
+        event.eventId
+                ?.takeUnless { it.isEmpty() }
+                ?.let { room.locationSharingService().redactLiveLocationShare(it, reason) }
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt
new file mode 100644
index 0000000000..89b619fe3c
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt
@@ -0,0 +1,49 @@
+/*
+ * 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 im.vector.app.features.home.room.detail.location
+
+import im.vector.app.test.fakes.FakeRoom
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.matrix.android.sdk.api.session.events.model.Event
+
+private const val AN_EVENT_ID = "event-id"
+private const val A_REASON = "reason"
+
+class RedactLiveLocationShareEventUseCaseTest {
+
+    private val fakeRoom = FakeRoom()
+
+    private val redactLiveLocationShareEventUseCase = RedactLiveLocationShareEventUseCase()
+
+    @Test
+    fun `given an event with valid id when calling use case then event is redacted in the room`() = runTest {
+        val event = Event(eventId = AN_EVENT_ID)
+        fakeRoom.locationSharingService().givenRedactLiveLocationShare(beaconInfoEventId = AN_EVENT_ID, reason = A_REASON)
+
+        redactLiveLocationShareEventUseCase.execute(event = event, room = fakeRoom, reason = A_REASON)
+
+        fakeRoom.locationSharingService().verifyRedactLiveLocationShare(beaconInfoEventId = AN_EVENT_ID, reason = A_REASON)
+    }
+
+    @Test
+    fun `given an event with empty id when calling use case then nothing is done`() = runTest {
+        val event = Event(eventId = "")
+
+        redactLiveLocationShareEventUseCase.execute(event = event, room = fakeRoom, reason = A_REASON)
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
index cebd45b2bb..accb3be877 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
@@ -19,8 +19,11 @@ package im.vector.app.test.fakes
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import io.mockk.coEvery
+import io.mockk.coVerify
 import io.mockk.every
+import io.mockk.just
 import io.mockk.mockk
+import io.mockk.runs
 import org.matrix.android.sdk.api.session.room.location.LocationSharingService
 import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
 import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
@@ -48,4 +51,12 @@ class FakeLocationSharingService : LocationSharingService by mockk() {
     fun givenStopLiveLocationShareReturns(result: UpdateLiveLocationShareResult) {
         coEvery { stopLiveLocationShare() } returns result
     }
+
+    fun givenRedactLiveLocationShare(beaconInfoEventId: String, reason: String?) {
+        coEvery { redactLiveLocationShare(beaconInfoEventId, reason) } just runs
+    }
+
+    fun verifyRedactLiveLocationShare(beaconInfoEventId: String, reason: String?) {
+        coVerify { redactLiveLocationShare(beaconInfoEventId, reason) }
+    }
 }

From d3ad8d8deb7cf97dccb905175b3b4cce6f204d47 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Jul 2022 17:31:40 +0200
Subject: [PATCH 08/19] Prune event entity when redacted

---
 .../sdk/internal/session/room/prune/RedactionEventProcessor.kt   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
index e33fbb56b1..a0f227bb9f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
@@ -74,6 +74,7 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
             when (typeToPrune) {
                 EventType.ENCRYPTED,
                 EventType.MESSAGE,
+                in EventType.STATE_ROOM_BEACON_INFO,
                 in EventType.POLL_START -> {
                     Timber.d("REDACTION for message ${eventToPrune.eventId}")
                     val unsignedData = EventMapper.map(eventToPrune).unsignedData

From f6415b0a5d4641be639db20800b1db9e0f411ab0 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 6 Jul 2022 15:02:23 +0200
Subject: [PATCH 09/19] Deleting summaries related to a redacted live location
 sharing

---
 .../EventAnnotationsSummaryEntityQuery.kt     |   9 ++
 .../sdk/internal/session/SessionModule.kt     |   5 +
 ...iveLocationShareRedactionEventProcessor.kt |  65 +++++++++++
 ...ocationShareRedactionEventProcessorTest.kt | 106 ++++++++++++++++++
 .../android/sdk/test/fakes/FakeRealm.kt       |  10 ++
 5 files changed, 195 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessor.kt
 create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessorTest.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt
index 6caa832110..1c19c21de2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt
@@ -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)
@@ -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()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index f8a52f0b7e..b9f56cbc9f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -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
@@ -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
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessor.kt
new file mode 100644
index 0000000000..fa3479ed3c
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessor.kt
@@ -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()
+                }
+            }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessorTest.kt
new file mode 100644
index 0000000000..24d9c30039
--- /dev/null
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/LiveLocationShareRedactionEventProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.session.room.location
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.amshove.kluent.shouldBe
+import org.junit.Test
+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.internal.database.model.EventAnnotationsSummaryEntity
+import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
+import org.matrix.android.sdk.internal.database.model.EventEntity
+import org.matrix.android.sdk.internal.database.model.EventEntityFields
+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.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
+import org.matrix.android.sdk.test.fakes.FakeRealm
+import org.matrix.android.sdk.test.fakes.givenDelete
+import org.matrix.android.sdk.test.fakes.givenEqualTo
+import org.matrix.android.sdk.test.fakes.givenFindFirst
+
+private const val AN_EVENT_ID = "event-id"
+private const val A_REDACTED_EVENT_ID = "redacted-event-id"
+
+@ExperimentalCoroutinesApi
+class LiveLocationShareRedactionEventProcessorTest {
+
+    private val liveLocationShareRedactionEventProcessor = LiveLocationShareRedactionEventProcessor()
+    private val fakeRealm = FakeRealm()
+
+    @Test
+    fun `given an event when checking if it should be processed then only event of type REDACTED is processed`() {
+        val eventId = AN_EVENT_ID
+        val eventType = EventType.REDACTION
+        val insertType = EventInsertType.INCREMENTAL_SYNC
+
+        val result = liveLocationShareRedactionEventProcessor.shouldProcess(
+                eventId = eventId,
+                eventType = eventType,
+                insertType = insertType
+        )
+
+        result shouldBe true
+    }
+
+    @Test
+    fun `given an event when checking if it should be processed then local echo is not processed`() {
+        val eventId = AN_EVENT_ID
+        val eventType = EventType.REDACTION
+        val insertType = EventInsertType.LOCAL_ECHO
+
+        val result = liveLocationShareRedactionEventProcessor.shouldProcess(
+                eventId = eventId,
+                eventType = eventType,
+                insertType = insertType
+        )
+
+        result shouldBe false
+    }
+
+    @Test
+    fun `given a redacted live location share event when processing it then related summaries are deleted from database`() = runTest {
+        val event = Event(eventId = AN_EVENT_ID, redacts = A_REDACTED_EVENT_ID)
+        val redactedEventEntity = EventEntity(eventId = A_REDACTED_EVENT_ID, type = EventType.STATE_ROOM_BEACON_INFO.first())
+        fakeRealm.givenWhere<EventEntity>()
+                .givenEqualTo(EventEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
+                .givenFindFirst(redactedEventEntity)
+        val liveSummary = mockk<LiveLocationShareAggregatedSummaryEntity>()
+        every { liveSummary.eventId } returns A_REDACTED_EVENT_ID
+        liveSummary.givenDelete()
+        fakeRealm.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
+                .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
+                .givenFindFirst(liveSummary)
+        val annotationsSummary = mockk<EventAnnotationsSummaryEntity>()
+        every { annotationsSummary.eventId } returns A_REDACTED_EVENT_ID
+        annotationsSummary.givenDelete()
+        fakeRealm.givenWhere<EventAnnotationsSummaryEntity>()
+                .givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, A_REDACTED_EVENT_ID)
+                .givenFindFirst(annotationsSummary)
+
+        liveLocationShareRedactionEventProcessor.process(fakeRealm.instance, event = event)
+
+        verify {
+            liveSummary.deleteFromRealm()
+            annotationsSummary.deleteFromRealm()
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt
index 0ebff87278..cb40889fb7 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeRealm.kt
@@ -18,10 +18,13 @@ package org.matrix.android.sdk.test.fakes
 
 import io.mockk.MockKVerificationScope
 import io.mockk.every
+import io.mockk.just
 import io.mockk.mockk
+import io.mockk.runs
 import io.mockk.verify
 import io.realm.Realm
 import io.realm.RealmModel
+import io.realm.RealmObject
 import io.realm.RealmQuery
 import io.realm.RealmResults
 import io.realm.kotlin.where
@@ -97,3 +100,10 @@ inline fun <reified T : RealmModel> RealmQuery<T>.givenIsNotNull(
     every { isNotNull(fieldName) } returns this
     return this
 }
+
+/**
+ * Should be called on a mocked RealmObject and not on a real RealmObject so that the underlying final method is mocked.
+ */
+fun RealmObject.givenDelete() {
+    every { deleteFromRealm() } just runs
+}

From 63626b79de3fdd40a40303925bff6c7ef6dbcacb Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 8 Jul 2022 16:39:50 +0200
Subject: [PATCH 10/19] Redact beacon info event first to refresh timeline
 faster

---
 .../room/location/RedactLiveLocationShareTask.kt      | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
index 9883d1204b..61a9a81c03 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
@@ -45,6 +45,11 @@ internal class DefaultRedactLiveLocationShareTask @Inject constructor(
         val relatedEventIds = getRelatedEventIdsOfLive(params.beaconInfoEventId)
         Timber.d("beacon with id ${params.beaconInfoEventId} has related event ids: ${relatedEventIds.joinToString(", ")}")
 
+        redactEvent(
+                eventId = params.beaconInfoEventId,
+                roomId = params.roomId,
+                reason = params.reason
+        )
         relatedEventIds.forEach { eventId ->
             redactEvent(
                     eventId = eventId,
@@ -52,12 +57,6 @@ internal class DefaultRedactLiveLocationShareTask @Inject constructor(
                     reason = params.reason
             )
         }
-
-        redactEvent(
-                eventId = params.beaconInfoEventId,
-                roomId = params.roomId,
-                reason = params.reason
-        )
     }
 
     private suspend fun getRelatedEventIdsOfLive(beaconInfoEventId: String): List<String> {

From 8fb402ab101ee674e3ddd7b20a0f77d07db84ee2 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 11 Jul 2022 14:42:33 +0200
Subject: [PATCH 11/19] Stop sharing live location if live is redacted

---
 .../location/LocationSharingAndroidService.kt | 10 +++-
 ...eckIfLiveLocationShareIsRedactedUseCase.kt | 39 ++++++++++++
 .../GetLiveLocationShareSummaryUseCase.kt     |  6 +-
 ...fLiveLocationShareIsRedactedUseCaseTest.kt | 60 +++++++++++++++++++
 .../GetLiveLocationShareSummaryUseCaseTest.kt | 15 ++++-
 .../vector/app/test/fakes/FakeEventService.kt | 29 +++++++++
 .../test/fakes/FakeLocationSharingService.kt  |  2 +-
 .../im/vector/app/test/fakes/FakeSession.kt   |  2 +
 8 files changed, 156 insertions(+), 7 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt
 create mode 100644 vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt
 create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeEventService.kt

diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
index 69ffc0e89e..b3c88ccd93 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
@@ -24,6 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.services.VectorAndroidService
+import im.vector.app.features.location.live.CheckIfLiveLocationShareIsRedactedUseCase
 import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.session.coroutineScope
@@ -55,6 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     @Inject lateinit var locationTracker: LocationTracker
     @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
+    @Inject lateinit var checkIfLiveLocationShareIsRedactedUseCase: CheckIfLiveLocationShareIsRedactedUseCase
 
     private val binder = LocalBinder()
 
@@ -203,14 +205,18 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     private fun listenForLiveSummaryChanges(roomId: String, beaconEventId: String) {
         launchWithActiveSession { session ->
             val job = getLiveLocationShareSummaryUseCase.execute(roomId, beaconEventId)
-                    .distinctUntilChangedBy { it.isActive }
-                    .filter { it.isActive == false }
+                    .distinctUntilChangedBy { it?.isActive }
+                    .filter { it?.isActive == false || (it == null && isLiveRedacted(roomId, beaconEventId)) }
                     .onEach { stopSharingLocation(beaconEventId) }
                     .launchIn(session.coroutineScope)
             jobs.add(job)
         }
     }
 
+    private suspend fun isLiveRedacted(roomId: String, beaconEventId: String): Boolean {
+        return checkIfLiveLocationShareIsRedactedUseCase.execute(roomId = roomId, eventId = beaconEventId)
+    }
+
     private fun launchWithActiveSession(block: suspend CoroutineScope.(Session) -> Unit) =
             activeSessionHolder
                     .getSafeActiveSession()
diff --git a/vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt b/vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt
new file mode 100644
index 0000000000..123e1be7a3
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ * 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 im.vector.app.features.location.live
+
+import org.matrix.android.sdk.api.session.Session
+import timber.log.Timber
+import javax.inject.Inject
+
+class CheckIfLiveLocationShareIsRedactedUseCase @Inject constructor(
+        private val session: Session,
+) {
+
+    suspend fun execute(roomId: String, eventId: String): Boolean {
+        Timber.d("checking if event is redacted for roomId=$roomId and eventId=$eventId")
+        return try {
+            session.eventService()
+                    .getEvent(roomId, eventId)
+                    .isRedacted()
+                    .also { Timber.d("event isRedacted=$it") }
+        } catch (error: Exception) {
+            Timber.e(error, "error when getting event, it may not exist yet")
+            false
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCase.kt b/vector/src/main/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCase.kt
index 0d8b70ccda..bc38889d7f 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCase.kt
@@ -19,7 +19,7 @@ package im.vector.app.features.location.live
 import androidx.lifecycle.asFlow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.withContext
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.getRoom
@@ -31,13 +31,13 @@ class GetLiveLocationShareSummaryUseCase @Inject constructor(
         private val session: Session,
 ) {
 
-    suspend fun execute(roomId: String, eventId: String): Flow<LiveLocationShareAggregatedSummary> = withContext(session.coroutineDispatchers.main) {
+    suspend fun execute(roomId: String, eventId: String): Flow<LiveLocationShareAggregatedSummary?> = withContext(session.coroutineDispatchers.main) {
         Timber.d("getting flow for roomId=$roomId and eventId=$eventId")
         session.getRoom(roomId)
                 ?.locationSharingService()
                 ?.getLiveLocationShareSummary(eventId)
                 ?.asFlow()
-                ?.mapNotNull { it.getOrNull() }
+                ?.map { it.getOrNull() }
                 ?: emptyFlow()
     }
 }
diff --git a/vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt
new file mode 100644
index 0000000000..0cb6a09ad5
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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 im.vector.app.features.location.live
+
+import im.vector.app.test.fakes.FakeSession
+import kotlinx.coroutines.test.runTest
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.UnsignedData
+
+private const val A_ROOM_ID = "room_id"
+private const val AN_EVENT_ID = "event_id"
+
+class CheckIfLiveLocationShareIsRedactedUseCaseTest {
+
+    private val fakeSession = FakeSession()
+
+    private val checkIfLiveLocationShareIsRedactedUseCase = CheckIfLiveLocationShareIsRedactedUseCase(
+            session = fakeSession
+    )
+
+    @Test
+    fun `given a room id and event id for redacted event when calling use case then true is returned`() = runTest {
+        val event = Event(
+                unsignedData = UnsignedData(age = 123, redactedEvent = Event())
+        )
+        fakeSession.eventService()
+                .givenGetEventReturns(event)
+
+        val result = checkIfLiveLocationShareIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
+
+        result shouldBeEqualTo true
+    }
+
+    @Test
+    fun `given a room id and event id for non redacted event when calling use case then false is returned`() = runTest {
+        val event = Event()
+        fakeSession.eventService()
+                .givenGetEventReturns(event)
+
+        val result = checkIfLiveLocationShareIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
+
+        result shouldBeEqualTo false
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCaseTest.kt
index fed825154c..ed1bcebf16 100644
--- a/vector/src/test/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/location/live/GetLiveLocationShareSummaryUseCaseTest.kt
@@ -53,7 +53,7 @@ class GetLiveLocationShareSummaryUseCaseTest {
     }
 
     @Test
-    fun `given a room id and event id when calling use case then live data on summary is returned`() = runTest {
+    fun `given a room id and event id when calling use case then flow on summary is returned`() = runTest {
         val summary = LiveLocationShareAggregatedSummary(
                 userId = "userId",
                 isActive = true,
@@ -70,4 +70,17 @@ class GetLiveLocationShareSummaryUseCaseTest {
 
         result shouldBeEqualTo summary
     }
+
+    @Test
+    fun `given a room id, event id and a null summary when calling use case then null is emitted in the flow`() = runTest {
+        fakeSession.roomService()
+                .getRoom(A_ROOM_ID)
+                .locationSharingService()
+                .givenLiveLocationShareSummaryReturns(AN_EVENT_ID, null)
+                .givenAsFlowReturns(Optional(null))
+
+        val result = getLiveLocationShareSummaryUseCase.execute(A_ROOM_ID, AN_EVENT_ID).first()
+
+        result shouldBeEqualTo null
+    }
 }
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeEventService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeEventService.kt
new file mode 100644
index 0000000000..167f1d624b
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeEventService.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021 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.app.test.fakes
+
+import io.mockk.coEvery
+import io.mockk.mockk
+import org.matrix.android.sdk.api.session.events.EventService
+import org.matrix.android.sdk.api.session.events.model.Event
+
+class FakeEventService : EventService by mockk() {
+
+    fun givenGetEventReturns(event: Event) {
+        coEvery { getEvent(any(), any()) } returns event
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
index accb3be877..d85403a274 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
@@ -41,7 +41,7 @@ class FakeLocationSharingService : LocationSharingService by mockk() {
 
     fun givenLiveLocationShareSummaryReturns(
             eventId: String,
-            summary: LiveLocationShareAggregatedSummary
+            summary: LiveLocationShareAggregatedSummary?
     ): LiveData<Optional<LiveLocationShareAggregatedSummary>> {
         return MutableLiveData(Optional(summary)).also {
             every { getLiveLocationShareSummary(eventId) } returns it
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
index 3af15a7e5c..65295af3dd 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt
@@ -35,6 +35,7 @@ class FakeSession(
         val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
         val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
         private val fakeRoomService: FakeRoomService = FakeRoomService(),
+        private val fakeEventService: FakeEventService = FakeEventService(),
 ) : Session by mockk(relaxed = true) {
 
     init {
@@ -50,6 +51,7 @@ class FakeSession(
     override fun homeServerCapabilitiesService(): HomeServerCapabilitiesService = fakeHomeServerCapabilitiesService
     override fun sharedSecretStorageService() = fakeSharedSecretStorageService
     override fun roomService() = fakeRoomService
+    override fun eventService() = fakeEventService
 
     fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
         coEvery {

From 127737649689b2fa0913fb6ccdeea2e3a349de1a Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 12 Jul 2022 16:58:02 +0200
Subject: [PATCH 12/19] Fix missing handling of redacted beacon location data
 events

---
 .../session/room/prune/RedactionEventProcessor.kt      |  1 +
 .../detail/timeline/factory/TimelineItemFactory.kt     | 10 ++++++++--
 .../timeline/helper/TimelineDisplayableEvents.kt       |  2 +-
 .../timeline/helper/TimelineEventVisibilityHelper.kt   |  4 ++++
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
index a0f227bb9f..cc86679cbc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt
@@ -75,6 +75,7 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
                 EventType.ENCRYPTED,
                 EventType.MESSAGE,
                 in EventType.STATE_ROOM_BEACON_INFO,
+                in EventType.BEACON_LOCATION_DATA,
                 in EventType.POLL_START -> {
                     Timber.d("REDACTION for message ${eventToPrune.eventId}")
                     val unsignedData = EventMapper.map(eventToPrune).unsignedData
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
index 97ae3b634e..6c5a66d39d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
@@ -113,8 +113,14 @@ class TimelineItemFactory @Inject constructor(
                     EventType.CALL_NEGOTIATE,
                     EventType.REACTION,
                     in EventType.POLL_RESPONSE,
-                    in EventType.POLL_END,
-                    in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
+                    in EventType.POLL_END -> noticeItemFactory.create(params)
+                    in EventType.BEACON_LOCATION_DATA -> {
+                        if (event.root.isRedacted()) {
+                            messageItemFactory.create(params)
+                        } else {
+                            noticeItemFactory.create(params)
+                        }
+                    }
                     // Calls
                     EventType.CALL_INVITE,
                     EventType.CALL_HANGUP,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
index e9f8e35dc9..23db2a721c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
@@ -51,7 +51,7 @@ object TimelineDisplayableEvents {
             EventType.STATE_ROOM_JOIN_RULES,
             EventType.KEY_VERIFICATION_DONE,
             EventType.KEY_VERIFICATION_CANCEL,
-    ) + EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
+    ) + EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO + EventType.BEACON_LOCATION_DATA
 }
 
 fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
index 8af708fca1..e6765bf35a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt
@@ -241,6 +241,10 @@ class TimelineEventVisibilityHelper @Inject constructor(
             } else root.eventId != rootThreadEventId
         }
 
+        if (root.getClearType() in EventType.BEACON_LOCATION_DATA) {
+            return !root.isRedacted()
+        }
+
         return false
     }
 

From 17227f1ae62db2b3a2ad162fae8d7ef7ee7d110a Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 10:14:47 +0200
Subject: [PATCH 13/19] DB migration

---
 .../database/RealmSessionStoreMigration.kt    |  4 ++-
 .../database/migration/MigrateSessionTo033.kt | 33 +++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo033.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index b54aec26b2..9784412761 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -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
@@ -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.
@@ -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()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo033.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo033.kt
new file mode 100644
index 0000000000..0e3a8599c5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo033.kt
@@ -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)
+    }
+}

From 7b15193eff28b867567c26b674241e1f6de1e930 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 10:56:34 +0200
Subject: [PATCH 14/19] Renaming a method to reflect its implementation

---
 .../session/room/location/RedactLiveLocationShareTask.kt  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
index 61a9a81c03..ac855b81e7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/location/RedactLiveLocationShareTask.kt
@@ -45,13 +45,13 @@ internal class DefaultRedactLiveLocationShareTask @Inject constructor(
         val relatedEventIds = getRelatedEventIdsOfLive(params.beaconInfoEventId)
         Timber.d("beacon with id ${params.beaconInfoEventId} has related event ids: ${relatedEventIds.joinToString(", ")}")
 
-        redactEvent(
+        postRedactionWithLocalEcho(
                 eventId = params.beaconInfoEventId,
                 roomId = params.roomId,
                 reason = params.reason
         )
         relatedEventIds.forEach { eventId ->
-            redactEvent(
+            postRedactionWithLocalEcho(
                     eventId = eventId,
                     roomId = params.roomId,
                     reason = params.reason
@@ -69,8 +69,8 @@ internal class DefaultRedactLiveLocationShareTask @Inject constructor(
         }
     }
 
-    private fun redactEvent(eventId: String, roomId: String, reason: String?) {
-        Timber.d("redacting event of id $eventId")
+    private fun postRedactionWithLocalEcho(eventId: String, roomId: String, reason: String?) {
+        Timber.d("posting redaction for event of id $eventId")
         val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, eventId, reason)
         localEchoEventFactory.createLocalEcho(redactionEcho)
         eventSenderProcessor.postRedaction(redactionEcho, reason)

From 667b30f145d1eeef9d5f953d11f6d09c185c3817 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 12:00:09 +0200
Subject: [PATCH 15/19] Refactoring redact task unit tests

---
 .../DefaultRedactLiveLocationShareTaskTest.kt | 94 ++++++++++---------
 1 file changed, 52 insertions(+), 42 deletions(-)

diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
index 66428ac064..b8618d1a79 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/session/room/location/DefaultRedactLiveLocationShareTaskTest.kt
@@ -22,6 +22,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Test
+import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
 import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
 import org.matrix.android.sdk.test.fakes.FakeEventSenderProcessor
@@ -58,59 +59,68 @@ class DefaultRedactLiveLocationShareTaskTest {
     }
 
     @Test
-    fun `given parameters when calling the task then it is correctly executed`() = runTest {
-        val params = RedactLiveLocationShareTask.Params(
-                roomId = A_ROOM_ID,
-                beaconInfoEventId = AN_EVENT_ID,
-                reason = A_REASON
-        )
-        fakeRealmConfiguration.givenAwaitTransaction<List<String>>(fakeRealm.instance)
+    fun `given parameters when redacting then post redact events and related and creates redact local echos`() = runTest {
+        val params = createParams()
         val relatedEventIds = listOf(AN_EVENT_ID_1, AN_EVENT_ID_2, AN_EVENT_ID_3)
-        val aggregatedSummaryEntity = LiveLocationShareAggregatedSummaryEntity(
-                eventId = AN_EVENT_ID,
-                relatedEventIds = RealmList(*relatedEventIds.toTypedArray()),
-        )
-        fakeRealm.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
-                .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, AN_EVENT_ID)
-                .givenFindFirst(aggregatedSummaryEntity)
-        val redactedEvent = fakeLocalEchoEventFactory.givenCreateRedactEvent(
-                eventId = AN_EVENT_ID,
-                withLocalEcho = true
-        )
-        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent, reason = A_REASON)
-        val redactedEvent1 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
-                eventId = AN_EVENT_ID_1,
-                withLocalEcho = true
-        )
-        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent1, reason = A_REASON)
-        val redactedEvent2 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
-                eventId = AN_EVENT_ID_2,
-                withLocalEcho = true
-        )
-        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent2, reason = A_REASON)
-        val redactedEvent3 = fakeLocalEchoEventFactory.givenCreateRedactEvent(
-                eventId = AN_EVENT_ID_3,
-                withLocalEcho = true
-        )
-        fakeEventSenderProcessor.givenPostRedaction(event = redactedEvent3, reason = A_REASON)
+        val aggregatedSummaryEntity = createSummary(relatedEventIds)
+        givenSummaryForId(AN_EVENT_ID, aggregatedSummaryEntity)
+        fakeRealmConfiguration.givenAwaitTransaction<List<String>>(fakeRealm.instance)
+        val redactEvents = givenCreateRedactEventWithLocalEcho(relatedEventIds + AN_EVENT_ID)
+        givenPostRedaction(redactEvents)
 
         defaultRedactLiveLocationShareTask.execute(params)
 
-        fakeLocalEchoEventFactory.verifyCreateRedactEvent(
-                roomId = A_ROOM_ID,
+        verifyCreateRedactEventForEventIds(relatedEventIds + AN_EVENT_ID)
+        verifyCreateLocalEchoForEvents(redactEvents)
+    }
+
+    private fun createParams() = RedactLiveLocationShareTask.Params(
+            roomId = A_ROOM_ID,
+            beaconInfoEventId = AN_EVENT_ID,
+            reason = A_REASON
+    )
+
+    private fun createSummary(relatedEventIds: List<String>): LiveLocationShareAggregatedSummaryEntity {
+        return LiveLocationShareAggregatedSummaryEntity(
                 eventId = AN_EVENT_ID,
-                reason = A_REASON
+                relatedEventIds = RealmList(*relatedEventIds.toTypedArray()),
         )
-        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent)
-        relatedEventIds.forEach { eventId ->
+    }
+
+    private fun givenSummaryForId(eventId: String, aggregatedSummaryEntity: LiveLocationShareAggregatedSummaryEntity) {
+        fakeRealm.givenWhere<LiveLocationShareAggregatedSummaryEntity>()
+                .givenEqualTo(LiveLocationShareAggregatedSummaryEntityFields.EVENT_ID, eventId)
+                .givenFindFirst(aggregatedSummaryEntity)
+    }
+
+    private fun givenCreateRedactEventWithLocalEcho(eventIds: List<String>): List<Event> {
+        return eventIds.map { eventId ->
+            fakeLocalEchoEventFactory.givenCreateRedactEvent(
+                    eventId = eventId,
+                    withLocalEcho = true
+            )
+        }
+    }
+
+    private fun givenPostRedaction(redactEvents: List<Event>) {
+        redactEvents.forEach {
+            fakeEventSenderProcessor.givenPostRedaction(event = it, reason = A_REASON)
+        }
+    }
+
+    private fun verifyCreateRedactEventForEventIds(eventIds: List<String>) {
+        eventIds.forEach { eventId ->
             fakeLocalEchoEventFactory.verifyCreateRedactEvent(
                     roomId = A_ROOM_ID,
                     eventId = eventId,
                     reason = A_REASON
             )
         }
-        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent1)
-        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent2)
-        fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactedEvent3)
+    }
+
+    private fun verifyCreateLocalEchoForEvents(events: List<Event>) {
+        events.forEach { redactionEvent ->
+            fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactionEvent)
+        }
     }
 }

From 99fc4b4a212c38d3300922e826d16302ad6f917f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 14:43:42 +0200
Subject: [PATCH 16/19] Simplify logic of the use case to check if event can be
 redacted

---
 .../timeline/action/CheckIfCanRedactEventUseCase.kt      | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
index 222df8f914..3bc3a5e351 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
@@ -29,10 +29,9 @@ class CheckIfCanRedactEventUseCase @Inject constructor(
         // Only some event types are supported for the moment
         val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER) +
                 EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
-        if (event.root.getClearType() !in canRedactEventTypes) return false
-        // Message sent by the current user can always be redacted
-        if (event.root.senderId == activeSessionHolder.getActiveSession().myUserId) return true
-        // Check permission for messages sent by other users
-        return actionPermissions.canRedact
+
+        return event.root.getClearType() in canRedactEventTypes &&
+                // Message sent by the current user can always be redacted, else check permission for messages sent by other users
+                (event.root.senderId == activeSessionHolder.getActiveSession().myUserId || actionPermissions.canRedact)
     }
 }

From 2121ec5739d6ce448af49c89f921dd5cd0945c76 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 14:53:44 +0200
Subject: [PATCH 17/19] Moving use case to check if event is redacted into a
 dedicated package

---
 .../features/location/LocationSharingAndroidService.kt |  6 +++---
 .../CheckIfEventIsRedactedUseCase.kt}                  |  4 ++--
 .../CheckIfEventIsRedactedUseCaseTest.kt}              | 10 +++++-----
 3 files changed, 10 insertions(+), 10 deletions(-)
 rename vector/src/main/java/im/vector/app/features/{location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt => redaction/CheckIfEventIsRedactedUseCase.kt} (91%)
 rename vector/src/test/java/im/vector/app/features/{location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt => redaction/CheckIfEventIsRedactedUseCaseTest.kt} (81%)

diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
index b3c88ccd93..bf022e0088 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
@@ -24,7 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.services.VectorAndroidService
-import im.vector.app.features.location.live.CheckIfLiveLocationShareIsRedactedUseCase
+import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
 import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.session.coroutineScope
@@ -56,7 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     @Inject lateinit var locationTracker: LocationTracker
     @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
-    @Inject lateinit var checkIfLiveLocationShareIsRedactedUseCase: CheckIfLiveLocationShareIsRedactedUseCase
+    @Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase
 
     private val binder = LocalBinder()
 
@@ -214,7 +214,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     }
 
     private suspend fun isLiveRedacted(roomId: String, beaconEventId: String): Boolean {
-        return checkIfLiveLocationShareIsRedactedUseCase.execute(roomId = roomId, eventId = beaconEventId)
+        return checkIfEventIsRedactedUseCase.execute(roomId = roomId, eventId = beaconEventId)
     }
 
     private fun launchWithActiveSession(block: suspend CoroutineScope.(Session) -> Unit) =
diff --git a/vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt b/vector/src/main/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCase.kt
similarity index 91%
rename from vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt
rename to vector/src/main/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCase.kt
index 123e1be7a3..ac77455d66 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCase.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.location.live
+package im.vector.app.features.redaction
 
 import org.matrix.android.sdk.api.session.Session
 import timber.log.Timber
 import javax.inject.Inject
 
-class CheckIfLiveLocationShareIsRedactedUseCase @Inject constructor(
+class CheckIfEventIsRedactedUseCase @Inject constructor(
         private val session: Session,
 ) {
 
diff --git a/vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCaseTest.kt
similarity index 81%
rename from vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt
rename to vector/src/test/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCaseTest.kt
index 0cb6a09ad5..7dffd78516 100644
--- a/vector/src/test/java/im/vector/app/features/location/live/CheckIfLiveLocationShareIsRedactedUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/redaction/CheckIfEventIsRedactedUseCaseTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.location.live
+package im.vector.app.features.redaction
 
 import im.vector.app.test.fakes.FakeSession
 import kotlinx.coroutines.test.runTest
@@ -26,11 +26,11 @@ import org.matrix.android.sdk.api.session.events.model.UnsignedData
 private const val A_ROOM_ID = "room_id"
 private const val AN_EVENT_ID = "event_id"
 
-class CheckIfLiveLocationShareIsRedactedUseCaseTest {
+class CheckIfEventIsRedactedUseCaseTest {
 
     private val fakeSession = FakeSession()
 
-    private val checkIfLiveLocationShareIsRedactedUseCase = CheckIfLiveLocationShareIsRedactedUseCase(
+    private val checkIfEventIsRedactedUseCase = CheckIfEventIsRedactedUseCase(
             session = fakeSession
     )
 
@@ -42,7 +42,7 @@ class CheckIfLiveLocationShareIsRedactedUseCaseTest {
         fakeSession.eventService()
                 .givenGetEventReturns(event)
 
-        val result = checkIfLiveLocationShareIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
+        val result = checkIfEventIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
 
         result shouldBeEqualTo true
     }
@@ -53,7 +53,7 @@ class CheckIfLiveLocationShareIsRedactedUseCaseTest {
         fakeSession.eventService()
                 .givenGetEventReturns(event)
 
-        val result = checkIfLiveLocationShareIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
+        val result = checkIfEventIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
 
         result shouldBeEqualTo false
     }

From 79615258690ac371041454dfaf3cd2d2928fbb45 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 15:02:30 +0200
Subject: [PATCH 18/19] Unit tests: adding verification that redaction does not
 happen when event id is empty

---
 .../location/RedactLiveLocationShareEventUseCaseTest.kt  | 2 ++
 .../vector/app/test/fakes/FakeLocationSharingService.kt  | 9 +++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt
index 89b619fe3c..2ca285ef50 100644
--- a/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/home/room/detail/location/RedactLiveLocationShareEventUseCaseTest.kt
@@ -45,5 +45,7 @@ class RedactLiveLocationShareEventUseCaseTest {
         val event = Event(eventId = "")
 
         redactLiveLocationShareEventUseCase.execute(event = event, room = fakeRoom, reason = A_REASON)
+
+        fakeRoom.locationSharingService().verifyRedactLiveLocationShare(inverse = true, beaconInfoEventId = "", reason = A_REASON)
     }
 }
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
index d85403a274..ce498a715a 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingService.kt
@@ -56,7 +56,12 @@ class FakeLocationSharingService : LocationSharingService by mockk() {
         coEvery { redactLiveLocationShare(beaconInfoEventId, reason) } just runs
     }
 
-    fun verifyRedactLiveLocationShare(beaconInfoEventId: String, reason: String?) {
-        coVerify { redactLiveLocationShare(beaconInfoEventId, reason) }
+    /**
+     * @param inverse when true it will check redaction of the live did not happen
+     * @param beaconInfoEventId event id of the beacon related to the live
+     * @param reason reason explaining the redaction
+     */
+    fun verifyRedactLiveLocationShare(inverse: Boolean = false, beaconInfoEventId: String, reason: String?) {
+        coVerify(inverse = inverse) { redactLiveLocationShare(beaconInfoEventId, reason) }
     }
 }

From b2d7ef9fbf18f894ac1af1e7a46bf3fc8e0b7c6e Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 18 Jul 2022 15:04:57 +0200
Subject: [PATCH 19/19] Re-arranging imports

---
 .../app/features/location/LocationSharingAndroidService.kt      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
index bf022e0088..fb749c2581 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingAndroidService.kt
@@ -24,9 +24,9 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.services.VectorAndroidService
-import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
 import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
 import im.vector.app.features.notifications.NotificationUtils
+import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
 import im.vector.app.features.session.coroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job