From 81e14c7c3b6529f08d2cf0b0cacc5a548c836c12 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 21 Jun 2022 09:43:29 +0200 Subject: [PATCH] Observing live status in DB from location sharing Android service --- .../room/location/LocationSharingService.kt | 3 ++ .../location/LocationSharingService.kt | 53 ++++++++++--------- .../LocationSharingServiceConnection.kt | 4 -- .../GetLiveLocationShareSummaryUseCase.kt | 4 ++ .../live/StopLiveLocationShareUseCase.kt | 9 +--- .../live/StopLiveLocationShareUseCaseTest.kt | 6 --- .../FakeLocationSharingServiceConnection.kt | 12 ----- 7 files changed, 37 insertions(+), 54 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt index 1ddaa87d34..ada3dc85d7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/location/LocationSharingService.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.api.session.room.location +import androidx.annotation.MainThread import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary import org.matrix.android.sdk.api.util.Cancelable @@ -60,11 +61,13 @@ interface LocationSharingService { /** * Returns a LiveData on the list of current running live location shares. */ + @MainThread fun getRunningLiveLocationShareSummaries(): LiveData> /** * Returns a LiveData on the live location share summary with the given eventId. * @param beaconInfoEventId event id of the initial beacon info state event */ + @MainThread fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData> } diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 4adac6846d..3ba5031200 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -18,22 +18,27 @@ package im.vector.app.features.location import android.content.Intent import android.os.Binder +import android.os.Handler import android.os.IBinder import android.os.Parcelable import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.services.VectorService +import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import timber.log.Timber -import java.util.Timer -import java.util.TimerTask import javax.inject.Inject @AndroidEntryPoint @@ -49,6 +54,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { @Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var locationTracker: LocationTracker @Inject lateinit var activeSessionHolder: ActiveSessionHolder + @Inject lateinit var getLiveLocationShareSummaryUseCase: GetLiveLocationShareSummaryUseCase private val binder = LocalBinder() @@ -56,8 +62,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { * Keep track of a map between beacon event Id starting the live and RoomArgs. */ private val roomArgsMap = mutableMapOf() - private val timers = mutableListOf() var callback: Callback? = null + private val jobs = mutableListOf() + private val mainHandler by lazy { Handler(mainLooper) } override fun onCreate() { super.onCreate() @@ -78,9 +85,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { val notification = notificationUtils.buildLiveLocationSharingNotification() startForeground(roomArgs.roomId.hashCode(), notification) - // Schedule a timer to stop sharing - scheduleTimer(roomArgs.roomId, roomArgs.durationMillis) - // Send beacon info state event launchInIO { session -> sendStartingLiveBeaconInfo(session, roomArgs) @@ -101,6 +105,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { when (result) { is UpdateLiveLocationShareResult.Success -> { addRoomArgs(result.beaconEventId, roomArgs) + listenForLiveSummaryChanges(roomArgs.roomId, result.beaconEventId) locationTracker.requestLastKnownLocation() } is UpdateLiveLocationShareResult.Failure -> { @@ -115,22 +120,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } } - private fun scheduleTimer(roomId: String, durationMillis: Long) { - Timer() - .apply { - schedule(object : TimerTask() { - override fun run() { - stopSharingLocation(roomId) - timers.remove(this@apply) - } - }, durationMillis) - } - .also { - timers.add(it) - } - } - - fun stopSharingLocation(roomId: String) { + private fun stopSharingLocation(roomId: String) { Timber.i("### LocationSharingService.stopSharingLocation for $roomId") removeRoomArgs(roomId) tryToDestroyMe() @@ -177,9 +167,9 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { } private fun destroyMe() { + jobs.forEach { it.cancel() } + jobs.clear() locationTracker.removeCallback(this) - timers.forEach { it.cancel() } - timers.clear() stopSelf() } @@ -202,6 +192,21 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { beaconIds.forEach { roomArgsMap.remove(it) } } + private fun listenForLiveSummaryChanges(roomId: String, eventId: String) { + activeSessionHolder + .getSafeActiveSession() + ?.let { session -> + mainHandler.post { + val job = getLiveLocationShareSummaryUseCase.execute(roomId, eventId) + .distinctUntilChangedBy { it.isActive } + .filter { it.isActive == false } + .onEach { stopSharingLocation(roomId) } + .launchIn(session.coroutineScope) + jobs.add(job) + } + } + } + private fun launchInIO(block: suspend CoroutineScope.(Session) -> Unit) = activeSessionHolder .getSafeActiveSession() diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt index 33d4fbbb49..817216f0b4 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingServiceConnection.kt @@ -55,10 +55,6 @@ class LocationSharingServiceConnection @Inject constructor( removeCallback(callback) } - fun stopLiveLocationSharing(roomId: String) { - locationSharingService?.stopSharingLocation(roomId) - } - override fun onServiceConnected(className: ComponentName, binder: IBinder) { locationSharingService = (binder as LocationSharingService.LocalBinder).getService().also { it.callback = this 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 0687189925..d2696f62fd 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 @@ -16,6 +16,7 @@ package im.vector.app.features.location.live +import androidx.annotation.MainThread import androidx.lifecycle.asFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow @@ -23,13 +24,16 @@ import kotlinx.coroutines.flow.mapNotNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary +import timber.log.Timber import javax.inject.Inject class GetLiveLocationShareSummaryUseCase @Inject constructor( private val session: Session, ) { + @MainThread fun execute(roomId: String, eventId: String): Flow { + Timber.d("getting flow for roomId=$roomId and eventId=$eventId") return session.getRoom(roomId) ?.locationSharingService() ?.getLiveLocationShareSummary(eventId) diff --git a/vector/src/main/java/im/vector/app/features/location/live/StopLiveLocationShareUseCase.kt b/vector/src/main/java/im/vector/app/features/location/live/StopLiveLocationShareUseCase.kt index f4ad48089c..b5e0e6ef02 100644 --- a/vector/src/main/java/im/vector/app/features/location/live/StopLiveLocationShareUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/location/live/StopLiveLocationShareUseCase.kt @@ -16,24 +16,17 @@ package im.vector.app.features.location.live -import im.vector.app.features.location.LocationSharingServiceConnection import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.location.UpdateLiveLocationShareResult import javax.inject.Inject class StopLiveLocationShareUseCase @Inject constructor( - private val locationSharingServiceConnection: LocationSharingServiceConnection, private val session: Session ) { suspend fun execute(roomId: String): UpdateLiveLocationShareResult? { - val result = sendStoppedBeaconInfo(session, roomId) - when (result) { - is UpdateLiveLocationShareResult.Success -> locationSharingServiceConnection.stopLiveLocationSharing(roomId) - else -> Unit - } - return result + return sendStoppedBeaconInfo(session, roomId) } private suspend fun sendStoppedBeaconInfo(session: Session, roomId: String): UpdateLiveLocationShareResult? { diff --git a/vector/src/test/java/im/vector/app/features/location/live/StopLiveLocationShareUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/location/live/StopLiveLocationShareUseCaseTest.kt index 9ebddc76eb..e111771683 100644 --- a/vector/src/test/java/im/vector/app/features/location/live/StopLiveLocationShareUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/location/live/StopLiveLocationShareUseCaseTest.kt @@ -16,7 +16,6 @@ package im.vector.app.features.location.live -import im.vector.app.test.fakes.FakeLocationSharingServiceConnection import im.vector.app.test.fakes.FakeSession import io.mockk.unmockkAll import kotlinx.coroutines.test.runTest @@ -30,11 +29,9 @@ private const val AN_EVENT_ID = "event_id" class StopLiveLocationShareUseCaseTest { - private val fakeLocationSharingServiceConnection = FakeLocationSharingServiceConnection() private val fakeSession = FakeSession() private val stopLiveLocationShareUseCase = StopLiveLocationShareUseCase( - locationSharingServiceConnection = fakeLocationSharingServiceConnection.instance, session = fakeSession ) @@ -45,7 +42,6 @@ class StopLiveLocationShareUseCaseTest { @Test fun `given a room id when calling use case then the current live is stopped with success`() = runTest { - fakeLocationSharingServiceConnection.givenStopLiveLocationSharing() val updateLiveResult = UpdateLiveLocationShareResult.Success(AN_EVENT_ID) fakeSession.roomService() .getRoom(A_ROOM_ID) @@ -55,7 +51,6 @@ class StopLiveLocationShareUseCaseTest { val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID) result shouldBeEqualTo updateLiveResult - fakeLocationSharingServiceConnection.verifyStopLiveLocationSharing(A_ROOM_ID) } @Test @@ -70,6 +65,5 @@ class StopLiveLocationShareUseCaseTest { val result = stopLiveLocationShareUseCase.execute(A_ROOM_ID) result shouldBeEqualTo updateLiveResult - fakeLocationSharingServiceConnection.verifyStopLiveLocationSharingNotCalled(A_ROOM_ID) } } diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingServiceConnection.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingServiceConnection.kt index d0d148a9e2..db27a894f9 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingServiceConnection.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeLocationSharingServiceConnection.kt @@ -34,16 +34,4 @@ class FakeLocationSharingServiceConnection { fun verifyBind(callback: LocationSharingServiceConnection.Callback) { verify { instance.bind(callback) } } - - fun givenStopLiveLocationSharing() { - every { instance.stopLiveLocationSharing(any()) } just runs - } - - fun verifyStopLiveLocationSharing(roomId: String) { - verify { instance.stopLiveLocationSharing(roomId) } - } - - fun verifyStopLiveLocationSharingNotCalled(roomId: String) { - verify(inverse = true) { instance.stopLiveLocationSharing(roomId) } - } }