diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt index c24d3f9cc2..36744664cb 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt @@ -52,9 +52,11 @@ class DefaultActiveLiveLocationShareManager( private val clock: SystemClock, private val sessionObserver: SessionObserver, ) : ActiveLiveLocationShareManager, LiveLocationReceiver { + private data class Timeout(val expiresAt: Instant, val job: Job) + private val isSetup = AtomicBoolean(false) private val cachedRooms = ConcurrentHashMap() - private val timeoutJobs = ConcurrentHashMap() + private val timeouts = ConcurrentHashMap() private val syncedActiveShareIds = MutableStateFlow>(emptySet()) private val localSharingRoomIds = MutableStateFlow>(emptySet()) override val sharingRoomIds: StateFlow> = localSharingRoomIds @@ -139,9 +141,18 @@ class DefaultActiveLiveLocationShareManager( } override suspend fun onLocationUpdate(location: Location) { - val activeSharesCount = localSharingRoomIds.value.size + val nowMillis = clock.epochMillis() + val (expired, active) = localSharingRoomIds.value.partition { roomId -> + val timeout = timeouts[roomId] ?: return@partition false + timeout.expiresAt.toEpochMilliseconds() <= nowMillis + } + expired.forEach { roomId -> + Timber.d("ActiveLiveLocationShareManager location tick detected expired share for room $roomId, stopping") + stopShare(roomId) + } + val activeSharesCount = active.size Timber.d("ActiveLiveLocationShareManager received location update for $activeSharesCount active share(s)") - localSharingRoomIds.value.forEach { roomId -> + active.forEach { roomId -> Timber.d("ActiveLiveLocationShareManager sending location to room $roomId") sendLiveLocation(roomId, location) .onFailure { @@ -192,20 +203,21 @@ class DefaultActiveLiveLocationShareManager( } private fun scheduleTimeout(roomId: RoomId, expiresAt: Instant) { - timeoutJobs.remove(roomId)?.cancel() + timeouts.remove(roomId)?.job?.cancel() val delayMillis = expiresAt.toEpochMilliseconds() - clock.epochMillis() - timeoutJobs[roomId] = matrixClient.sessionCoroutineScope.launch { + val job = matrixClient.sessionCoroutineScope.launch { delay(delayMillis) stopShare(roomId) .onFailure { error -> Timber.e(error, "ActiveLiveLocationShareManager failed to stop timed out share for room $roomId") } } + timeouts[roomId] = Timeout(expiresAt = expiresAt, job = job) } private suspend fun stopLocalShare(roomId: RoomId) { Timber.d("ActiveLiveLocationShareManager stop local share in $roomId") - timeoutJobs.remove(roomId)?.cancel() + timeouts.remove(roomId)?.job?.cancel() val wasSharing = localSharingRoomIds.getAndUpdate { it - roomId }.isNotEmpty() cachedRooms.remove(roomId)?.close() liveLocationStore.removeLiveLocationExpiry(roomId) @@ -220,11 +232,11 @@ class DefaultActiveLiveLocationShareManager( sessionObserver.removeListener(sessionListener) coordinator.unregister(matrixClient.sessionId) liveLocationStore.clear() + timeouts.values.forEach { it.job.cancel() } + timeouts.clear() for (room in cachedRooms.values) { room.close() - timeoutJobs[room.roomId]?.cancel() } - timeoutJobs.clear() cachedRooms.clear() localSharingRoomIds.value = emptySet() syncedActiveShareIds.value = emptySet()