Stop live location share on location tick when past expiry

This commit is contained in:
ganfra
2026-07-10 12:44:51 +02:00
parent 8923ffb7f1
commit ac0ad510c8
@@ -52,9 +52,11 @@ class DefaultActiveLiveLocationShareManager(
private val clock: SystemClock, private val clock: SystemClock,
private val sessionObserver: SessionObserver, private val sessionObserver: SessionObserver,
) : ActiveLiveLocationShareManager, LiveLocationReceiver { ) : ActiveLiveLocationShareManager, LiveLocationReceiver {
private data class Timeout(val expiresAt: Instant, val job: Job)
private val isSetup = AtomicBoolean(false) private val isSetup = AtomicBoolean(false)
private val cachedRooms = ConcurrentHashMap<RoomId, JoinedRoom>() private val cachedRooms = ConcurrentHashMap<RoomId, JoinedRoom>()
private val timeoutJobs = ConcurrentHashMap<RoomId, Job>() private val timeouts = ConcurrentHashMap<RoomId, Timeout>()
private val syncedActiveShareIds = MutableStateFlow<Set<BeaconId>>(emptySet()) private val syncedActiveShareIds = MutableStateFlow<Set<BeaconId>>(emptySet())
private val localSharingRoomIds = MutableStateFlow<Set<RoomId>>(emptySet()) private val localSharingRoomIds = MutableStateFlow<Set<RoomId>>(emptySet())
override val sharingRoomIds: StateFlow<Set<RoomId>> = localSharingRoomIds override val sharingRoomIds: StateFlow<Set<RoomId>> = localSharingRoomIds
@@ -139,9 +141,18 @@ class DefaultActiveLiveLocationShareManager(
} }
override suspend fun onLocationUpdate(location: Location) { 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)") 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") Timber.d("ActiveLiveLocationShareManager sending location to room $roomId")
sendLiveLocation(roomId, location) sendLiveLocation(roomId, location)
.onFailure { .onFailure {
@@ -192,20 +203,21 @@ class DefaultActiveLiveLocationShareManager(
} }
private fun scheduleTimeout(roomId: RoomId, expiresAt: Instant) { private fun scheduleTimeout(roomId: RoomId, expiresAt: Instant) {
timeoutJobs.remove(roomId)?.cancel() timeouts.remove(roomId)?.job?.cancel()
val delayMillis = expiresAt.toEpochMilliseconds() - clock.epochMillis() val delayMillis = expiresAt.toEpochMilliseconds() - clock.epochMillis()
timeoutJobs[roomId] = matrixClient.sessionCoroutineScope.launch { val job = matrixClient.sessionCoroutineScope.launch {
delay(delayMillis) delay(delayMillis)
stopShare(roomId) stopShare(roomId)
.onFailure { error -> .onFailure { error ->
Timber.e(error, "ActiveLiveLocationShareManager failed to stop timed out share for room $roomId") 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) { private suspend fun stopLocalShare(roomId: RoomId) {
Timber.d("ActiveLiveLocationShareManager stop local share in $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() val wasSharing = localSharingRoomIds.getAndUpdate { it - roomId }.isNotEmpty()
cachedRooms.remove(roomId)?.close() cachedRooms.remove(roomId)?.close()
liveLocationStore.removeLiveLocationExpiry(roomId) liveLocationStore.removeLiveLocationExpiry(roomId)
@@ -220,11 +232,11 @@ class DefaultActiveLiveLocationShareManager(
sessionObserver.removeListener(sessionListener) sessionObserver.removeListener(sessionListener)
coordinator.unregister(matrixClient.sessionId) coordinator.unregister(matrixClient.sessionId)
liveLocationStore.clear() liveLocationStore.clear()
timeouts.values.forEach { it.job.cancel() }
timeouts.clear()
for (room in cachedRooms.values) { for (room in cachedRooms.values) {
room.close() room.close()
timeoutJobs[room.roomId]?.cancel()
} }
timeoutJobs.clear()
cachedRooms.clear() cachedRooms.clear()
localSharingRoomIds.value = emptySet() localSharingRoomIds.value = emptySet()
syncedActiveShareIds.value = emptySet() syncedActiveShareIds.value = emptySet()