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