mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
implement the start, send and stop functions
This commit is contained in:
@@ -15,6 +15,15 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
|
||||
|
||||
private let authorizationStatusSubject: CurrentValueSubject<CLAuthorizationStatus, Never>
|
||||
|
||||
/// Cached joined room proxies keyed by room ID, kept in sync with the active sessions dictionary.
|
||||
private var activeRoomProxies = [String: JoinedRoomProxyProtocol]()
|
||||
|
||||
/// The running task that iterates over live location updates.
|
||||
@CancellableTask
|
||||
private var locationUpdatesTask: Task<Void, Never>?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> {
|
||||
authorizationStatusSubject.asCurrentValuePublisher()
|
||||
}
|
||||
@@ -33,6 +42,11 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
|
||||
super.init()
|
||||
|
||||
locationManager.delegate = self
|
||||
locationManager.allowsBackgroundLocationUpdates = true
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
|
||||
locationManager.pausesLocationUpdatesAutomatically = false
|
||||
|
||||
setupSubscriptions()
|
||||
}
|
||||
|
||||
// MARK: - LiveLocationManagerProtocol
|
||||
@@ -45,6 +59,39 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
|
||||
return true
|
||||
}
|
||||
|
||||
func startLiveLocation(roomID: String, durationMillis: UInt64) async -> Result<Void, LiveLocationManagerError> {
|
||||
guard case .joined(let roomProxy) = await clientProxy.roomForIdentifier(roomID) else {
|
||||
MXLog.error("Failed to resolve joined room for identifier: \(roomID)")
|
||||
return .failure(.roomNotJoined)
|
||||
}
|
||||
|
||||
let result = await roomProxy.startLiveLocationShare(durationMillis: durationMillis)
|
||||
|
||||
guard case .success = result else {
|
||||
MXLog.error("Failed to start live location share in room: \(roomID)")
|
||||
return .failure(.startFailed)
|
||||
}
|
||||
|
||||
let timeoutDate = Date().addingTimeInterval(TimeInterval(durationMillis) / 1000.0)
|
||||
appSettings.liveLocationSharingTimeoutDatesByRoomID[roomID] = timeoutDate
|
||||
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func stopLiveLocation(roomID: String) async {
|
||||
// Best effort: send the stop event to the room regardless of tracking state.
|
||||
if let roomProxy = await resolveRoomProxy(for: roomID) {
|
||||
let result = await roomProxy.stopLiveLocationShare()
|
||||
if case .failure(let error) = result {
|
||||
MXLog.error("Failed to stop live location share in room \(roomID): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Always clean up locally.
|
||||
appSettings.liveLocationSharingTimeoutDatesByRoomID.removeValue(forKey: roomID)
|
||||
activeRoomProxies.removeValue(forKey: roomID)
|
||||
}
|
||||
|
||||
// MARK: - CLLocationManagerDelegate
|
||||
|
||||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||||
@@ -53,6 +100,114 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
|
||||
if manager.authorizationStatus == .notDetermined {
|
||||
appSettings.hasRequestedLocationAlwaysLocationAuthorization = false
|
||||
}
|
||||
|
||||
// If authorization was revoked, stop all active sessions.
|
||||
if manager.authorizationStatus != .authorizedAlways {
|
||||
stopAllSessions()
|
||||
}
|
||||
|
||||
authorizationStatusSubject.send(manager.authorizationStatus)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupSubscriptions() {
|
||||
appSettings.$liveLocationSharingTimeoutDatesByRoomID
|
||||
.removeDuplicates()
|
||||
.sink { [weak self] sessions in
|
||||
guard let self else { return }
|
||||
syncActiveRoomProxies(with: sessions)
|
||||
|
||||
if sessions.isEmpty {
|
||||
locationUpdatesTask = nil
|
||||
} else {
|
||||
startLocationUpdatesIfNeeded()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$liveLocationSharingEnabled
|
||||
.filter { !$0 }
|
||||
.sink { [weak self] _ in
|
||||
guard let self else { return }
|
||||
appSettings.liveLocationSharingTimeoutDatesByRoomID.removeAll()
|
||||
activeRoomProxies.removeAll()
|
||||
locationUpdatesTask = nil
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func syncActiveRoomProxies(with sessions: [String: Date]) {
|
||||
// Remove proxies for rooms no longer in the dictionary.
|
||||
let activeRoomIDs = Set(sessions.keys)
|
||||
for roomID in activeRoomProxies.keys where !activeRoomIDs.contains(roomID) {
|
||||
activeRoomProxies.removeValue(forKey: roomID)
|
||||
}
|
||||
}
|
||||
|
||||
private func startLocationUpdatesIfNeeded() {
|
||||
guard locationUpdatesTask == nil else { return }
|
||||
|
||||
locationUpdatesTask = Task { [weak self] in
|
||||
do {
|
||||
for try await update in CLLocationUpdate.liveUpdates() {
|
||||
guard let self, !Task.isCancelled else { break }
|
||||
|
||||
await self.sendLocationToActiveRooms(update)
|
||||
}
|
||||
} catch {
|
||||
MXLog.error("Live location updates failed with error: \(error)")
|
||||
self?.stopAllSessions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func sendLocationToActiveRooms(_ update: CLLocationUpdate) async {
|
||||
let sessions = appSettings.liveLocationSharingTimeoutDatesByRoomID
|
||||
let geoURI = update.location.map { GeoURI(coordinate: $0.coordinate, uncertainty: $0.horizontalAccuracy) }
|
||||
|
||||
for (roomID, timeoutDate) in sessions {
|
||||
if Date() >= timeoutDate {
|
||||
MXLog.info("Live location session expired for room: \(roomID)")
|
||||
await stopLiveLocation(roomID: roomID)
|
||||
continue
|
||||
}
|
||||
|
||||
guard let geoURI else { continue }
|
||||
|
||||
let roomProxy = await resolveRoomProxy(for: roomID)
|
||||
guard let roomProxy else {
|
||||
MXLog.error("Failed to resolve room proxy for live location update in room: \(roomID)")
|
||||
continue
|
||||
}
|
||||
|
||||
let result = await roomProxy.sendLiveLocation(geoURI: geoURI)
|
||||
if case .failure(let error) = result {
|
||||
MXLog.error("Failed to send live location update to room \(roomID): \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveRoomProxy(for roomID: String) async -> JoinedRoomProxyProtocol? {
|
||||
if let cached = activeRoomProxies[roomID] {
|
||||
return cached
|
||||
}
|
||||
|
||||
guard case .joined(let roomProxy) = await clientProxy.roomForIdentifier(roomID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
activeRoomProxies[roomID] = roomProxy
|
||||
return roomProxy
|
||||
}
|
||||
|
||||
private func stopAllSessions() {
|
||||
let roomIDs = Array(appSettings.liveLocationSharingTimeoutDatesByRoomID.keys)
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
for roomID in roomIDs {
|
||||
await stopLiveLocation(roomID: roomID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user