mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 15:45:11 +00:00
implemented the LiveLocationManager as part of the user session and setup of permissions in the plist.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreLocation
|
||||
|
||||
class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationManagerDelegate {
|
||||
private let clientProxy: ClientProxyProtocol
|
||||
private let locationManager: CLLocationManager
|
||||
private let appSettings: AppSettings
|
||||
|
||||
private let authorizationStatusSubject: CurrentValueSubject<CLAuthorizationStatus, Never>
|
||||
|
||||
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> {
|
||||
authorizationStatusSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
init(clientProxy: ClientProxyProtocol,
|
||||
appSettings: AppSettings) {
|
||||
self.clientProxy = clientProxy
|
||||
self.appSettings = appSettings
|
||||
// Very important, the CLLocationManager needs to be initialised on the main thread
|
||||
// or the delegate functions won't be handled!
|
||||
// https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate
|
||||
locationManager = CLLocationManager()
|
||||
authorizationStatusSubject = CurrentValueSubject(locationManager.authorizationStatus)
|
||||
|
||||
super.init()
|
||||
|
||||
locationManager.delegate = self
|
||||
}
|
||||
|
||||
// MARK: - LiveLocationManagerProtocol
|
||||
|
||||
@discardableResult
|
||||
func requestAlwaysAuthorizationIfPossible() -> Bool {
|
||||
guard !appSettings.hasRequestedAlwaysLocationAuthorization else { return false }
|
||||
appSettings.hasRequestedAlwaysLocationAuthorization = true
|
||||
locationManager.requestAlwaysAuthorization()
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - CLLocationManagerDelegate
|
||||
|
||||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
||||
// If the system resets authorization to notDetermined (e.g. after app reinstall or
|
||||
// settings reset), clear the flag so we can request again.
|
||||
if manager.authorizationStatus == .notDetermined {
|
||||
appSettings.hasRequestedAlwaysLocationAuthorization = false
|
||||
}
|
||||
authorizationStatusSubject.send(manager.authorizationStatus)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// TODO: Add CLLocationManager location update handling to forward updates to rooms
|
||||
// TODO: Track which rooms are currently sharing live location
|
||||
// TODO: Send location updates to all active rooms via clientProxy
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol LiveLocationManagerProtocol: AnyObject {
|
||||
/// Publishes the current "Always" location authorization status.
|
||||
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> { get }
|
||||
|
||||
/// Requests "Always" location authorization from the user if the system allows it.
|
||||
///
|
||||
/// - Returns: `true` if the request was forwarded to the system and a prompt will be shown;
|
||||
/// `false` if the request was already made before and iOS would silently ignore it.
|
||||
@discardableResult
|
||||
func requestAlwaysAuthorizationIfPossible() -> Bool
|
||||
}
|
||||
@@ -17,6 +17,7 @@ class UserSession: UserSessionProtocol {
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||
let liveLocationManager: LiveLocationManagerProtocol
|
||||
|
||||
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
||||
|
||||
@@ -25,10 +26,11 @@ class UserSession: UserSessionProtocol {
|
||||
sessionSecurityStateSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol) {
|
||||
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, liveLocationManager: LiveLocationManagerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
self.mediaProvider = mediaProvider
|
||||
self.voiceMessageMediaManager = voiceMessageMediaManager
|
||||
self.liveLocationManager = liveLocationManager
|
||||
|
||||
authErrorCancellable = clientProxy.actionsPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
||||
@@ -23,6 +23,7 @@ protocol UserSessionProtocol {
|
||||
var clientProxy: ClientProxyProtocol { get }
|
||||
var mediaProvider: MediaProviderProtocol { get }
|
||||
var voiceMessageMediaManager: VoiceMessageMediaManagerProtocol { get }
|
||||
var liveLocationManager: LiveLocationManagerProtocol { get }
|
||||
|
||||
var sessionSecurityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never> { get }
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
switch await restorePreviousLogin(credentials) {
|
||||
case .success(let clientProxy):
|
||||
return .success(buildUserSessionWithClient(clientProxy))
|
||||
return await .success(buildUserSessionWithClient(clientProxy))
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed restoring login with error: \(error)")
|
||||
|
||||
@@ -84,7 +84,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
MXLog.info("Set up session for user \(userID) at: \(sessionDirectories)")
|
||||
|
||||
return .success(buildUserSessionWithClient(clientProxy))
|
||||
return await .success(buildUserSessionWithClient(clientProxy))
|
||||
} catch {
|
||||
MXLog.error("Failed creating user session with error: \(error)")
|
||||
return .failure(.failedSettingUpSession)
|
||||
@@ -103,16 +103,22 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) -> UserSessionProtocol {
|
||||
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) async -> UserSessionProtocol {
|
||||
let mediaProvider = MediaProvider(mediaLoader: clientProxy.mediaLoader,
|
||||
imageCache: .onlyInMemory,
|
||||
homeserverReachabilityPublisher: clientProxy.homeserverReachabilityPublisher)
|
||||
|
||||
let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider)
|
||||
|
||||
let liveLocationManager = await MainActor.run {
|
||||
LiveLocationManager(clientProxy: clientProxy,
|
||||
appSettings: appSettings)
|
||||
}
|
||||
|
||||
return UserSession(clientProxy: clientProxy,
|
||||
mediaProvider: mediaProvider,
|
||||
voiceMessageMediaManager: voiceMessageMediaManager)
|
||||
voiceMessageMediaManager: voiceMessageMediaManager,
|
||||
liveLocationManager: liveLocationManager)
|
||||
}
|
||||
|
||||
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
||||
|
||||
Reference in New Issue
Block a user