Merge pull request #6486 from vector-im/feature/mna/delete-lls
[Location sharing] - Delete action on a live message (PSG-523)
This commit is contained in:
commit
c3105c8bb1
1
changelog.d/6437.feature
Normal file
1
changelog.d/6437.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
[Location sharing] - Delete action on a live message
|
@ -371,6 +371,8 @@ fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClear
|
|||||||
|
|
||||||
fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER
|
fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER
|
||||||
|
|
||||||
|
fun Event.isLiveLocation(): Boolean = getClearType() in EventType.STATE_ROOM_BEACON_INFO
|
||||||
|
|
||||||
fun Event.getRelationContent(): RelationDefaultContent? {
|
fun Event.getRelationContent(): RelationDefaultContent? {
|
||||||
return if (isEncrypted()) {
|
return if (isEncrypted()) {
|
||||||
content.toModel<EncryptedEventContent>()?.relatesTo
|
content.toModel<EncryptedEventContent>()?.relatesTo
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.api.session.room.location
|
package org.matrix.android.sdk.api.session.room.location
|
||||||
|
|
||||||
import androidx.annotation.MainThread
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
import org.matrix.android.sdk.api.util.Cancelable
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
@ -59,16 +58,21 @@ interface LocationSharingService {
|
|||||||
*/
|
*/
|
||||||
suspend fun stopLiveLocationShare(): UpdateLiveLocationShareResult
|
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.
|
* Returns a LiveData on the list of current running live location shares.
|
||||||
*/
|
*/
|
||||||
@MainThread
|
|
||||||
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a LiveData on the live location share summary with the given eventId.
|
* Returns a LiveData on the live location share summary with the given eventId.
|
||||||
* @param beaconInfoEventId event id of the initial beacon info state event
|
* @param beaconInfoEventId event id of the initial beacon info state event
|
||||||
*/
|
*/
|
||||||
@MainThread
|
|
||||||
fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>>
|
fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>>
|
||||||
}
|
}
|
||||||
|
@ -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.RelationType
|
||||||
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
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.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.isPoll
|
||||||
import org.matrix.android.sdk.api.session.events.model.isReply
|
import org.matrix.android.sdk.api.session.events.model.isReply
|
||||||
import org.matrix.android.sdk.api.session.events.model.isSticker
|
import org.matrix.android.sdk.api.session.events.model.isSticker
|
||||||
@ -165,6 +166,10 @@ fun TimelineEvent.isSticker(): Boolean {
|
|||||||
return root.isSticker()
|
return root.isSticker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TimelineEvent.isLiveLocation(): Boolean {
|
||||||
|
return root.isLiveLocation()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not the event is a root thread event.
|
* Returns whether or not the event is a root thread event.
|
||||||
*/
|
*/
|
||||||
|
@ -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.MigrateSessionTo030
|
||||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo031
|
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.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.Normalizer
|
||||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -57,7 +58,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||||||
private val normalizer: Normalizer
|
private val normalizer: Normalizer
|
||||||
) : MatrixRealmMigration(
|
) : MatrixRealmMigration(
|
||||||
dbName = "Session",
|
dbName = "Session",
|
||||||
schemaVersion = 32L,
|
schemaVersion = 33L,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Forces all RealmSessionStoreMigration instances to be equal.
|
* Forces all RealmSessionStoreMigration instances to be equal.
|
||||||
@ -99,5 +100,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||||||
if (oldVersion < 30) MigrateSessionTo030(realm).perform()
|
if (oldVersion < 30) MigrateSessionTo030(realm).perform()
|
||||||
if (oldVersion < 31) MigrateSessionTo031(realm).perform()
|
if (oldVersion < 31) MigrateSessionTo031(realm).perform()
|
||||||
if (oldVersion < 32) MigrateSessionTo032(realm).perform()
|
if (oldVersion < 32) MigrateSessionTo032(realm).perform()
|
||||||
|
if (oldVersion < 33) MigrateSessionTo033(realm).perform()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.database.model.livelocation
|
package org.matrix.android.sdk.internal.database.model.livelocation
|
||||||
|
|
||||||
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
@ -29,6 +30,11 @@ internal open class LiveLocationShareAggregatedSummaryEntity(
|
|||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
var eventId: String = "",
|
var eventId: String = "",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of event ids used to compute the aggregated summary data.
|
||||||
|
*/
|
||||||
|
var relatedEventIds: RealmList<String> = RealmList(),
|
||||||
|
|
||||||
var roomId: String = "",
|
var roomId: String = "",
|
||||||
|
|
||||||
var userId: String = "",
|
var userId: String = "",
|
||||||
|
@ -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.EventAnnotationsSummaryEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
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> {
|
internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
|
||||||
return realm.where<EventAnnotationsSummaryEntity>()
|
return realm.where<EventAnnotationsSummaryEntity>()
|
||||||
.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
|
.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
|
||||||
@ -44,3 +49,7 @@ internal fun EventAnnotationsSummaryEntity.Companion.getOrCreate(realm: Realm, r
|
|||||||
return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||||
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun EventAnnotationsSummaryEntity.Companion.get(realm: Realm, eventId: String): EventAnnotationsSummaryEntity? {
|
||||||
|
return EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||||
|
}
|
||||||
|
@ -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.LiveLocationShareAggregatedSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntityFields
|
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(
|
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.where(
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
@ -72,6 +80,13 @@ internal fun LiveLocationShareAggregatedSummaryEntity.Companion.get(
|
|||||||
return LiveLocationShareAggregatedSummaryEntity.where(realm, roomId, eventId).findFirst()
|
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(
|
internal fun LiveLocationShareAggregatedSummaryEntity.Companion.findActiveLiveInRoomForUser(
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
|
@ -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.DefaultPollAggregationProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
|
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.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.prune.RedactionEventProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessorCoroutine
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessorCoroutine
|
||||||
@ -321,6 +322,10 @@ internal abstract class SessionModule {
|
|||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindEventRedactionProcessor(processor: RedactionEventProcessor): EventInsertLiveProcessor
|
abstract fun bindEventRedactionProcessor(processor: RedactionEventProcessor): EventInsertLiveProcessor
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
abstract fun bindLiveLocationShareRedactionEventProcessor(processor: LiveLocationShareRedactionEventProcessor): EventInsertLiveProcessor
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoSet
|
@IntoSet
|
||||||
abstract fun bindEventRelationsAggregationProcessor(processor: EventRelationsAggregationProcessor): EventInsertLiveProcessor
|
abstract fun bindEventRelationsAggregationProcessor(processor: EventRelationsAggregationProcessor): EventInsertLiveProcessor
|
||||||
|
@ -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.CheckIfExistingActiveLiveTask
|
||||||
import org.matrix.android.sdk.internal.session.room.location.DefaultCheckIfExistingActiveLiveTask
|
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.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.DefaultSendLiveLocationTask
|
||||||
import org.matrix.android.sdk.internal.session.room.location.DefaultSendStaticLocationTask
|
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.DefaultStartLiveLocationShareTask
|
||||||
import org.matrix.android.sdk.internal.session.room.location.DefaultStopLiveLocationShareTask
|
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.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.SendLiveLocationTask
|
||||||
import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask
|
import org.matrix.android.sdk.internal.session.room.location.SendStaticLocationTask
|
||||||
import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask
|
import org.matrix.android.sdk.internal.session.room.location.StartLiveLocationShareTask
|
||||||
@ -339,4 +341,7 @@ internal abstract class RoomModule {
|
|||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindCheckIfExistingActiveLiveTask(task: DefaultCheckIfExistingActiveLiveTask): CheckIfExistingActiveLiveTask
|
abstract fun bindCheckIfExistingActiveLiveTask(task: DefaultCheckIfExistingActiveLiveTask): CheckIfExistingActiveLiveTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindRedactLiveLocationShareTask(task: DefaultRedactLiveLocationShareTask): RedactLiveLocationShareTask
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
|
|||||||
|
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmList
|
||||||
import org.matrix.android.sdk.api.extensions.orTrue
|
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.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
@ -73,6 +74,11 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
|
|||||||
eventId = targetEventId
|
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
|
// remote event can stay with isLive == true while the local summary is no more active
|
||||||
val isActive = aggregatedSummary.isActive.orTrue() && isLive
|
val isActive = aggregatedSummary.isActive.orTrue() && isLive
|
||||||
val endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
|
val endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
|
||||||
@ -144,6 +150,11 @@ internal class LiveLocationAggregationProcessor @Inject constructor(
|
|||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
eventId = relatedEventId
|
eventId = relatedEventId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!event.eventId.isNullOrEmpty()) {
|
||||||
|
addRelatedEventId(event.eventId, aggregatedSummary)
|
||||||
|
}
|
||||||
|
|
||||||
val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
|
val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
|
||||||
val currentLocationTimestamp = ContentMapper
|
val currentLocationTimestamp = ContentMapper
|
||||||
.map(aggregatedSummary.lastLocationContent)
|
.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) {
|
private fun deactivateAllPreviousBeacons(realm: Realm, roomId: String, userId: String, currentEventId: String) {
|
||||||
LiveLocationShareAggregatedSummaryEntity
|
LiveLocationShareAggregatedSummaryEntity
|
||||||
.findActiveLiveInRoomForUser(
|
.findActiveLiveInRoomForUser(
|
||||||
|
@ -42,6 +42,7 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
|
|||||||
private val startLiveLocationShareTask: StartLiveLocationShareTask,
|
private val startLiveLocationShareTask: StartLiveLocationShareTask,
|
||||||
private val stopLiveLocationShareTask: StopLiveLocationShareTask,
|
private val stopLiveLocationShareTask: StopLiveLocationShareTask,
|
||||||
private val checkIfExistingActiveLiveTask: CheckIfExistingActiveLiveTask,
|
private val checkIfExistingActiveLiveTask: CheckIfExistingActiveLiveTask,
|
||||||
|
private val redactLiveLocationShareTask: RedactLiveLocationShareTask,
|
||||||
private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
|
private val liveLocationShareAggregatedSummaryMapper: LiveLocationShareAggregatedSummaryMapper,
|
||||||
) : LocationSharingService {
|
) : LocationSharingService {
|
||||||
|
|
||||||
@ -102,6 +103,15 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
|
|||||||
return stopLiveLocationShareTask.execute(params)
|
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>> {
|
override fun getRunningLiveLocationShareSummaries(): LiveData<List<LiveLocationShareAggregatedSummary>> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
return monarchy.findAllMappedWithChanges(
|
||||||
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) },
|
{ LiveLocationShareAggregatedSummaryEntity.findRunningLiveInRoom(it, roomId = roomId) },
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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(", ")}")
|
||||||
|
|
||||||
|
postRedactionWithLocalEcho(
|
||||||
|
eventId = params.beaconInfoEventId,
|
||||||
|
roomId = params.roomId,
|
||||||
|
reason = params.reason
|
||||||
|
)
|
||||||
|
relatedEventIds.forEach { eventId ->
|
||||||
|
postRedactionWithLocalEcho(
|
||||||
|
eventId = eventId,
|
||||||
|
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 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)
|
||||||
|
}
|
||||||
|
}
|
@ -74,6 +74,8 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr
|
|||||||
when (typeToPrune) {
|
when (typeToPrune) {
|
||||||
EventType.ENCRYPTED,
|
EventType.ENCRYPTED,
|
||||||
EventType.MESSAGE,
|
EventType.MESSAGE,
|
||||||
|
in EventType.STATE_ROOM_BEACON_INFO,
|
||||||
|
in EventType.BEACON_LOCATION_DATA,
|
||||||
in EventType.POLL_START -> {
|
in EventType.POLL_START -> {
|
||||||
Timber.d("REDACTION for message ${eventToPrune.eventId}")
|
Timber.d("REDACTION for message ${eventToPrune.eventId}")
|
||||||
val unsignedData = EventMapper.map(eventToPrune).unsignedData
|
val unsignedData = EventMapper.map(eventToPrune).unsignedData
|
||||||
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
|
|||||||
|
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.amshove.kluent.shouldContain
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.UnsignedData
|
import org.matrix.android.sdk.api.session.events.model.UnsignedData
|
||||||
@ -199,9 +200,10 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
age = 123,
|
age = 123,
|
||||||
replacesState = AN_EVENT_ID
|
replacesState = AN_EVENT_ID
|
||||||
)
|
)
|
||||||
|
val stateEventId = "state-event-id"
|
||||||
val event = Event(
|
val event = Event(
|
||||||
senderId = A_SENDER_ID,
|
senderId = A_SENDER_ID,
|
||||||
eventId = "",
|
eventId = stateEventId,
|
||||||
unsignedData = unsignedData
|
unsignedData = unsignedData
|
||||||
)
|
)
|
||||||
val beaconInfo = MessageBeaconInfoContent(
|
val beaconInfo = MessageBeaconInfoContent(
|
||||||
@ -237,6 +239,7 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID
|
aggregatedEntity.roomId shouldBeEqualTo A_ROOM_ID
|
||||||
aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID
|
aggregatedEntity.userId shouldBeEqualTo A_SENDER_ID
|
||||||
aggregatedEntity.isActive shouldBeEqualTo false
|
aggregatedEntity.isActive shouldBeEqualTo false
|
||||||
|
aggregatedEntity.relatedEventIds shouldContain stateEventId
|
||||||
aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS
|
aggregatedEntity.endOfLiveTimestampMillis shouldBeEqualTo A_TIMESTAMP + A_TIMEOUT_MILLIS
|
||||||
aggregatedEntity.lastLocationContent shouldBeEqualTo null
|
aggregatedEntity.lastLocationContent shouldBeEqualTo null
|
||||||
previousEntities.forEach { entity ->
|
previousEntities.forEach { entity ->
|
||||||
@ -324,7 +327,7 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
val lastBeaconLocationContent = MessageBeaconLocationDataContent(
|
val lastBeaconLocationContent = MessageBeaconLocationDataContent(
|
||||||
unstableTimestampMillis = A_TIMESTAMP
|
unstableTimestampMillis = A_TIMESTAMP
|
||||||
)
|
)
|
||||||
givenLastSummaryQueryReturns(
|
val aggregatedEntity = givenLastSummaryQueryReturns(
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
beaconLocationContent = lastBeaconLocationContent
|
beaconLocationContent = lastBeaconLocationContent
|
||||||
@ -340,6 +343,7 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
result shouldBeEqualTo false
|
result shouldBeEqualTo false
|
||||||
|
aggregatedEntity.relatedEventIds shouldContain AN_EVENT_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -353,7 +357,7 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
val lastBeaconLocationContent = MessageBeaconLocationDataContent(
|
val lastBeaconLocationContent = MessageBeaconLocationDataContent(
|
||||||
unstableTimestampMillis = A_TIMESTAMP - 60_000
|
unstableTimestampMillis = A_TIMESTAMP - 60_000
|
||||||
)
|
)
|
||||||
val entity = givenLastSummaryQueryReturns(
|
val aggregatedEntity = givenLastSummaryQueryReturns(
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
roomId = A_ROOM_ID,
|
roomId = A_ROOM_ID,
|
||||||
beaconLocationContent = lastBeaconLocationContent
|
beaconLocationContent = lastBeaconLocationContent
|
||||||
@ -369,7 +373,8 @@ internal class LiveLocationAggregationProcessorTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
result shouldBeEqualTo true
|
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?.getBestTimestampMillis() shouldBeEqualTo A_TIMESTAMP
|
||||||
savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI
|
savedLocationData?.getBestLocationInfo()?.geoUri shouldBeEqualTo A_GEO_URI
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,10 @@ import androidx.lifecycle.Transformations
|
|||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.coVerify
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.runs
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import io.mockk.unmockkAll
|
import io.mockk.unmockkAll
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
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 AN_UNCERTAINTY = 5.0
|
||||||
private const val A_TIMEOUT = 15_000L
|
private const val A_TIMEOUT = 15_000L
|
||||||
private const val A_DESCRIPTION = "description"
|
private const val A_DESCRIPTION = "description"
|
||||||
|
private const val A_REASON = "reason"
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
internal class DefaultLocationSharingServiceTest {
|
internal class DefaultLocationSharingServiceTest {
|
||||||
@ -62,6 +65,7 @@ internal class DefaultLocationSharingServiceTest {
|
|||||||
private val startLiveLocationShareTask = mockk<StartLiveLocationShareTask>()
|
private val startLiveLocationShareTask = mockk<StartLiveLocationShareTask>()
|
||||||
private val stopLiveLocationShareTask = mockk<StopLiveLocationShareTask>()
|
private val stopLiveLocationShareTask = mockk<StopLiveLocationShareTask>()
|
||||||
private val checkIfExistingActiveLiveTask = mockk<CheckIfExistingActiveLiveTask>()
|
private val checkIfExistingActiveLiveTask = mockk<CheckIfExistingActiveLiveTask>()
|
||||||
|
private val redactLiveLocationShareTask = mockk<RedactLiveLocationShareTask>()
|
||||||
private val fakeLiveLocationShareAggregatedSummaryMapper = mockk<LiveLocationShareAggregatedSummaryMapper>()
|
private val fakeLiveLocationShareAggregatedSummaryMapper = mockk<LiveLocationShareAggregatedSummaryMapper>()
|
||||||
|
|
||||||
private val defaultLocationSharingService = DefaultLocationSharingService(
|
private val defaultLocationSharingService = DefaultLocationSharingService(
|
||||||
@ -72,6 +76,7 @@ internal class DefaultLocationSharingServiceTest {
|
|||||||
startLiveLocationShareTask = startLiveLocationShareTask,
|
startLiveLocationShareTask = startLiveLocationShareTask,
|
||||||
stopLiveLocationShareTask = stopLiveLocationShareTask,
|
stopLiveLocationShareTask = stopLiveLocationShareTask,
|
||||||
checkIfExistingActiveLiveTask = checkIfExistingActiveLiveTask,
|
checkIfExistingActiveLiveTask = checkIfExistingActiveLiveTask,
|
||||||
|
redactLiveLocationShareTask = redactLiveLocationShareTask,
|
||||||
liveLocationShareAggregatedSummaryMapper = fakeLiveLocationShareAggregatedSummaryMapper
|
liveLocationShareAggregatedSummaryMapper = fakeLiveLocationShareAggregatedSummaryMapper
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -209,6 +214,20 @@ internal class DefaultLocationSharingServiceTest {
|
|||||||
coVerify { stopLiveLocationShareTask.execute(expectedParams) }
|
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
|
@Test
|
||||||
fun `livedata of live summaries is correctly computed`() {
|
fun `livedata of live summaries is correctly computed`() {
|
||||||
val entity = LiveLocationShareAggregatedSummaryEntity()
|
val entity = LiveLocationShareAggregatedSummaryEntity()
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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.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
|
||||||
|
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 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 = 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)
|
||||||
|
|
||||||
|
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,
|
||||||
|
relatedEventIds = RealmList(*relatedEventIds.toTypedArray()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyCreateLocalEchoForEvents(events: List<Event>) {
|
||||||
|
events.forEach { redactionEvent ->
|
||||||
|
fakeLocalEchoEventFactory.verifyCreateLocalEcho(redactionEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,4 +27,8 @@ internal class FakeEventSenderProcessor : EventSenderProcessor by mockk() {
|
|||||||
fun givenPostEventReturns(event: Event, cancelable: Cancelable) {
|
fun givenPostEventReturns(event: Event, cancelable: Cancelable) {
|
||||||
every { postEvent(event) } returns cancelable
|
every { postEvent(event) } returns cancelable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenPostRedaction(event: Event, reason: String?) {
|
||||||
|
every { postRedaction(event, reason) } returns mockk()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,24 +46,6 @@ internal class FakeLocalEchoEventFactory {
|
|||||||
return event
|
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(
|
fun verifyCreateStaticLocationEvent(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
latitude: Double,
|
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(
|
fun verifyCreateLiveLocationEvent(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
beaconInfoEventId: 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) {
|
fun verifyCreateLocalEcho(event: Event) {
|
||||||
verify { instance.createLocalEcho(event) }
|
verify { instance.createLocalEcho(event) }
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,13 @@ package org.matrix.android.sdk.test.fakes
|
|||||||
|
|
||||||
import io.mockk.MockKVerificationScope
|
import io.mockk.MockKVerificationScope
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmModel
|
import io.realm.RealmModel
|
||||||
|
import io.realm.RealmObject
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
@ -97,3 +100,10 @@ inline fun <reified T : RealmModel> RealmQuery<T>.givenIsNotNull(
|
|||||||
every { isNotNull(fieldName) } returns this
|
every { isNotNull(fieldName) } returns this
|
||||||
return 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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -48,6 +48,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
|
|||||||
import im.vector.app.features.createdirect.DirectRoomHelper
|
import im.vector.app.features.createdirect.DirectRoomHelper
|
||||||
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
|
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
|
||||||
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
|
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.sticker.StickerPickerActionHandler
|
||||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
||||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
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.read.ReadService
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
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.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.sync.SyncRequestState
|
||||||
import org.matrix.android.sdk.api.session.threads.ThreadNotificationBadgeState
|
import org.matrix.android.sdk.api.session.threads.ThreadNotificationBadgeState
|
||||||
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
import org.matrix.android.sdk.api.session.threads.ThreadNotificationState
|
||||||
@ -135,6 +137,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
private val locationSharingServiceConnection: LocationSharingServiceConnection,
|
||||||
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
private val stopLiveLocationShareUseCase: StopLiveLocationShareUseCase,
|
||||||
|
private val redactLiveLocationShareEventUseCase: RedactLiveLocationShareEventUseCase,
|
||||||
timelineFactory: TimelineFactory,
|
timelineFactory: TimelineFactory,
|
||||||
appStateHandler: AppStateHandler,
|
appStateHandler: AppStateHandler,
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
@ -770,7 +773,13 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
|
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
|
||||||
val event = room.getTimelineEvent(action.targetEventId) ?: return
|
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) {
|
private fun handleUndoReact(action: RoomDetailAction.UndoReaction) {
|
||||||
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||||||
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
|
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val checkIfCanReplyEventUseCase: CheckIfCanReplyEventUseCase,
|
private val checkIfCanReplyEventUseCase: CheckIfCanReplyEventUseCase,
|
||||||
|
private val checkIfCanRedactEventUseCase: CheckIfCanRedactEventUseCase,
|
||||||
) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
|
) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
private val informationData = initialState.informationData
|
private val informationData = initialState.informationData
|
||||||
@ -518,12 +519,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun canRedact(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
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
|
return checkIfCanRedactEventUseCase.execute(event, actionPermissions)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
||||||
|
@ -113,8 +113,14 @@ class TimelineItemFactory @Inject constructor(
|
|||||||
EventType.CALL_NEGOTIATE,
|
EventType.CALL_NEGOTIATE,
|
||||||
EventType.REACTION,
|
EventType.REACTION,
|
||||||
in EventType.POLL_RESPONSE,
|
in EventType.POLL_RESPONSE,
|
||||||
in EventType.POLL_END,
|
in EventType.POLL_END -> noticeItemFactory.create(params)
|
||||||
in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
|
in EventType.BEACON_LOCATION_DATA -> {
|
||||||
|
if (event.root.isRedacted()) {
|
||||||
|
messageItemFactory.create(params)
|
||||||
|
} else {
|
||||||
|
noticeItemFactory.create(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Calls
|
// Calls
|
||||||
EventType.CALL_INVITE,
|
EventType.CALL_INVITE,
|
||||||
EventType.CALL_HANGUP,
|
EventType.CALL_HANGUP,
|
||||||
|
@ -51,7 +51,7 @@ object TimelineDisplayableEvents {
|
|||||||
EventType.STATE_ROOM_JOIN_RULES,
|
EventType.STATE_ROOM_JOIN_RULES,
|
||||||
EventType.KEY_VERIFICATION_DONE,
|
EventType.KEY_VERIFICATION_DONE,
|
||||||
EventType.KEY_VERIFICATION_CANCEL,
|
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 {
|
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
|
||||||
|
@ -241,6 +241,10 @@ class TimelineEventVisibilityHelper @Inject constructor(
|
|||||||
} else root.eventId != rootThreadEventId
|
} else root.eventId != rootThreadEventId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (root.getClearType() in EventType.BEACON_LOCATION_DATA) {
|
||||||
|
return !root.isRedacted()
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import im.vector.app.core.di.ActiveSessionHolder
|
|||||||
import im.vector.app.core.services.VectorAndroidService
|
import im.vector.app.core.services.VectorAndroidService
|
||||||
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
|
import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
|
import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -55,6 +56,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
@Inject lateinit var locationTracker: LocationTracker
|
@Inject lateinit var locationTracker: LocationTracker
|
||||||
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
|
||||||
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
@Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase
|
||||||
|
@Inject lateinit var checkIfEventIsRedactedUseCase: CheckIfEventIsRedactedUseCase
|
||||||
|
|
||||||
private val binder = LocalBinder()
|
private val binder = LocalBinder()
|
||||||
|
|
||||||
@ -203,14 +205,18 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
|
|||||||
private fun listenForLiveSummaryChanges(roomId: String, beaconEventId: String) {
|
private fun listenForLiveSummaryChanges(roomId: String, beaconEventId: String) {
|
||||||
launchWithActiveSession { session ->
|
launchWithActiveSession { session ->
|
||||||
val job = getLiveLocationShareSummaryUseCase.execute(roomId, beaconEventId)
|
val job = getLiveLocationShareSummaryUseCase.execute(roomId, beaconEventId)
|
||||||
.distinctUntilChangedBy { it.isActive }
|
.distinctUntilChangedBy { it?.isActive }
|
||||||
.filter { it.isActive == false }
|
.filter { it?.isActive == false || (it == null && isLiveRedacted(roomId, beaconEventId)) }
|
||||||
.onEach { stopSharingLocation(beaconEventId) }
|
.onEach { stopSharingLocation(beaconEventId) }
|
||||||
.launchIn(session.coroutineScope)
|
.launchIn(session.coroutineScope)
|
||||||
jobs.add(job)
|
jobs.add(job)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun isLiveRedacted(roomId: String, beaconEventId: String): Boolean {
|
||||||
|
return checkIfEventIsRedactedUseCase.execute(roomId = roomId, eventId = beaconEventId)
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchWithActiveSession(block: suspend CoroutineScope.(Session) -> Unit) =
|
private fun launchWithActiveSession(block: suspend CoroutineScope.(Session) -> Unit) =
|
||||||
activeSessionHolder
|
activeSessionHolder
|
||||||
.getSafeActiveSession()
|
.getSafeActiveSession()
|
||||||
|
@ -19,7 +19,7 @@ package im.vector.app.features.location.live
|
|||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
@ -31,13 +31,13 @@ class GetLiveLocationShareSummaryUseCase @Inject constructor(
|
|||||||
private val session: Session,
|
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")
|
Timber.d("getting flow for roomId=$roomId and eventId=$eventId")
|
||||||
session.getRoom(roomId)
|
session.getRoom(roomId)
|
||||||
?.locationSharingService()
|
?.locationSharingService()
|
||||||
?.getLiveLocationShareSummary(eventId)
|
?.getLiveLocationShareSummary(eventId)
|
||||||
?.asFlow()
|
?.asFlow()
|
||||||
?.mapNotNull { it.getOrNull() }
|
?.map { it.getOrNull() }
|
||||||
?: emptyFlow()
|
?: emptyFlow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.redaction
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class CheckIfEventIsRedactedUseCase @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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
fakeRoom.locationSharingService().verifyRedactLiveLocationShare(inverse = true, beaconInfoEventId = "", reason = A_REASON)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -53,7 +53,7 @@ class GetLiveLocationShareSummaryUseCaseTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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(
|
val summary = LiveLocationShareAggregatedSummary(
|
||||||
userId = "userId",
|
userId = "userId",
|
||||||
isActive = true,
|
isActive = true,
|
||||||
@ -70,4 +70,17 @@ class GetLiveLocationShareSummaryUseCaseTest {
|
|||||||
|
|
||||||
result shouldBeEqualTo summary
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.redaction
|
||||||
|
|
||||||
|
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 CheckIfEventIsRedactedUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeSession = FakeSession()
|
||||||
|
|
||||||
|
private val checkIfEventIsRedactedUseCase = CheckIfEventIsRedactedUseCase(
|
||||||
|
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 = checkIfEventIsRedactedUseCase.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 = checkIfEventIsRedactedUseCase.execute(A_ROOM_ID, AN_EVENT_ID)
|
||||||
|
|
||||||
|
result shouldBeEqualTo false
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,11 @@ package im.vector.app.test.fakes
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
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.LocationSharingService
|
||||||
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult
|
||||||
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
|
||||||
@ -38,7 +41,7 @@ class FakeLocationSharingService : LocationSharingService by mockk() {
|
|||||||
|
|
||||||
fun givenLiveLocationShareSummaryReturns(
|
fun givenLiveLocationShareSummaryReturns(
|
||||||
eventId: String,
|
eventId: String,
|
||||||
summary: LiveLocationShareAggregatedSummary
|
summary: LiveLocationShareAggregatedSummary?
|
||||||
): LiveData<Optional<LiveLocationShareAggregatedSummary>> {
|
): LiveData<Optional<LiveLocationShareAggregatedSummary>> {
|
||||||
return MutableLiveData(Optional(summary)).also {
|
return MutableLiveData(Optional(summary)).also {
|
||||||
every { getLiveLocationShareSummary(eventId) } returns it
|
every { getLiveLocationShareSummary(eventId) } returns it
|
||||||
@ -48,4 +51,17 @@ class FakeLocationSharingService : LocationSharingService by mockk() {
|
|||||||
fun givenStopLiveLocationShareReturns(result: UpdateLiveLocationShareResult) {
|
fun givenStopLiveLocationShareReturns(result: UpdateLiveLocationShareResult) {
|
||||||
coEvery { stopLiveLocationShare() } returns result
|
coEvery { stopLiveLocationShare() } returns result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenRedactLiveLocationShare(beaconInfoEventId: String, reason: String?) {
|
||||||
|
coEvery { redactLiveLocationShare(beaconInfoEventId, reason) } just runs
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ class FakeSession(
|
|||||||
val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
|
val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
|
||||||
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
|
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
|
||||||
private val fakeRoomService: FakeRoomService = FakeRoomService(),
|
private val fakeRoomService: FakeRoomService = FakeRoomService(),
|
||||||
|
private val fakeEventService: FakeEventService = FakeEventService(),
|
||||||
) : Session by mockk(relaxed = true) {
|
) : Session by mockk(relaxed = true) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -50,6 +51,7 @@ class FakeSession(
|
|||||||
override fun homeServerCapabilitiesService(): HomeServerCapabilitiesService = fakeHomeServerCapabilitiesService
|
override fun homeServerCapabilitiesService(): HomeServerCapabilitiesService = fakeHomeServerCapabilitiesService
|
||||||
override fun sharedSecretStorageService() = fakeSharedSecretStorageService
|
override fun sharedSecretStorageService() = fakeSharedSecretStorageService
|
||||||
override fun roomService() = fakeRoomService
|
override fun roomService() = fakeRoomService
|
||||||
|
override fun eventService() = fakeEventService
|
||||||
|
|
||||||
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
||||||
coEvery {
|
coEvery {
|
||||||
|
Loading…
Reference in New Issue
Block a user