Force a stop before starting a new session and resend last location on new starts

This commit is contained in:
Mauro Romito
2026-04-23 16:07:52 +02:00
committed by Mauro
parent 95c32225a8
commit b0fa1afe51
2 changed files with 32 additions and 6 deletions
@@ -28,6 +28,8 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
private var isUpdatingLocation = false private var isUpdatingLocation = false
private var lastLocation: CLLocationCoordinate2D?
@MainActor @MainActor
init(clientProxy: ClientProxyProtocol, init(clientProxy: ClientProxyProtocol,
appSettings: AppSettings, appSettings: AppSettings,
@@ -76,10 +78,11 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
} }
func startLiveLocation(roomID: String, duration: Duration) async -> Result<Void, LiveLocationManagerError> { func startLiveLocation(roomID: String, duration: Duration) async -> Result<Void, LiveLocationManagerError> {
// Stop any existing session for this room first (e.g. one started from a different device) // Stop any existing session for this room first
// before starting a new one. var didAlreadyStopLocalSession = false
if appSettings.liveLocationSharingTimeoutDatesByRoomID[roomID] != nil { if appSettings.liveLocationSharingTimeoutDatesByRoomID[roomID] != nil {
await stopLiveLocation(roomID: roomID) await stopLiveLocation(roomID: roomID)
didAlreadyStopLocalSession = true
} }
guard case .joined(let roomProxy) = await clientProxy.roomForIdentifier(roomID) else { guard case .joined(let roomProxy) = await clientProxy.roomForIdentifier(roomID) else {
@@ -87,6 +90,11 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
return .failure(.roomNotJoined) return .failure(.roomNotJoined)
} }
if !didAlreadyStopLocalSession {
// In case an existing session has been started from another device, let's try to stop it.
// It's a best effort thing, so we don't care if no session is present or if it fails.
_ = await roomProxy.stopLiveLocationShare()
}
let result = await roomProxy.startLiveLocationShare(duration: duration) let result = await roomProxy.startLiveLocationShare(duration: duration)
guard case .success = result else { guard case .success = result else {
@@ -97,6 +105,13 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
let timeoutDate = Date().addingTimeInterval(TimeInterval(duration.seconds)) let timeoutDate = Date().addingTimeInterval(TimeInterval(duration.seconds))
appSettings.liveLocationSharingTimeoutDatesByRoomID[roomID] = timeoutDate appSettings.liveLocationSharingTimeoutDatesByRoomID[roomID] = timeoutDate
if isUpdatingLocation, let lastLocation {
// To make sure the newly started session is in sync with the existing ones,
// we re-send the last location received by the manager.
// Otherwise we would need to wait a distance filtered update.
locationUpdateSubject.send(lastLocation)
}
return .success(()) return .success(())
} }
@@ -145,6 +160,7 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
MXLog.verbose("Received location update via delegate, sending to rooms") MXLog.verbose("Received location update via delegate, sending to rooms")
locationUpdateSubject.send(location.coordinate) locationUpdateSubject.send(location.coordinate)
lastLocation = location.coordinate
} }
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
@@ -238,6 +254,7 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
MXLog.info("Stopping live location updates") MXLog.info("Stopping live location updates")
locationManager.stopUpdatingLocation() locationManager.stopUpdatingLocation()
isUpdatingLocation = false isUpdatingLocation = false
lastLocation = nil
} }
private func sendLocationToActiveRooms(_ coordinate: CLLocationCoordinate2D) async { private func sendLocationToActiveRooms(_ coordinate: CLLocationCoordinate2D) async {
@@ -28,16 +28,25 @@ final class LiveLocationManagerTests {
// MARK: - startLiveLocation // MARK: - startLiveLocation
@Test @Test
func startLiveLocationWithNoExistingSession() async throws { func startLiveLocationWithNoExistingLocalSession() async throws {
setUp() setUp()
let roomProxy = makeRoomProxy(roomID: "!room:matrix.org") let roomProxy = makeRoomProxy(roomID: "!room:matrix.org")
clientProxy.roomForIdentifierClosure = { _ in .joined(roomProxy) } clientProxy.roomForIdentifierClosure = { _ in .joined(roomProxy) }
var callOrder: [String] = []
roomProxy.stopLiveLocationShareClosure = {
callOrder.append("stop")
return .success(())
}
roomProxy.startLiveLocationShareDurationClosure = { _ in
callOrder.append("start")
return .success(())
}
let result = await manager.startLiveLocation(roomID: "!room:matrix.org", duration: .seconds(300)) let result = await manager.startLiveLocation(roomID: "!room:matrix.org", duration: .seconds(300))
try result.get() try result.get()
#expect(roomProxy.startLiveLocationShareDurationCalled) #expect(callOrder == ["stop", "start"])
#expect(!roomProxy.stopLiveLocationShareCalled)
#expect(appSettings.liveLocationSharingTimeoutDatesByRoomID["!room:matrix.org"] != nil) #expect(appSettings.liveLocationSharingTimeoutDatesByRoomID["!room:matrix.org"] != nil)
#expect(locationManagerMock.startUpdatingLocationCalled) #expect(locationManagerMock.startUpdatingLocationCalled)
} }
@@ -76,7 +85,7 @@ final class LiveLocationManagerTests {
_ = await manager.startLiveLocation(roomID: "!room1:matrix.org", duration: .seconds(300)) _ = await manager.startLiveLocation(roomID: "!room1:matrix.org", duration: .seconds(300))
#expect(!roomProxy.stopLiveLocationShareCalled) #expect(roomProxy.stopLiveLocationShareCalled)
#expect(appSettings.liveLocationSharingTimeoutDatesByRoomID["!room2:matrix.org"] != nil) #expect(appSettings.liveLocationSharingTimeoutDatesByRoomID["!room2:matrix.org"] != nil)
} }