mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-21 17:12:45 +02:00
b298dedc22
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19 * commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1': finish version++ version++ comments update submodule remove obsolete tests removed unused code update submodule fix Libolm removal update license macro update license Prepare for new sprint # Conflicts: # Config/AppVersion.xcconfig # IDETemplateMacros.plist # LICENSE # README.md # Riot/Categories/MXSession+Riot.m # Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift # Riot/Managers/KeyValueStorage/Extensions/Keychain.swift # Riot/Managers/KeyValueStorage/KeyValueStore.swift # Riot/Managers/KeyValueStorage/KeychainStore.swift # Riot/Managers/KeyValueStorage/MemoryStore.swift # Riot/Managers/PushNotification/PushNotificationService.m # Riot/Managers/Settings/RiotSettings.swift # Riot/Managers/Settings/Shared/RiotSharedSettings.swift # Riot/Modules/Analytics/AnalyticsUIElement.swift # Riot/Modules/Application/AppCoordinator.swift # Riot/Modules/Application/LegacyAppDelegate.h # Riot/Modules/Application/LegacyAppDelegate.m # Riot/Modules/Authentication/Legacy/AuthenticationViewController.h # Riot/Modules/Authentication/Legacy/AuthenticationViewController.m # Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h # Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m # Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m # Riot/Modules/Common/Recents/RecentsViewController.m # Riot/Modules/Common/WebViewController/WebViewViewController.m # Riot/Modules/Contacts/Details/ContactDetailsViewController.m # Riot/Modules/Contacts/Views/ContactTableViewCell.m # Riot/Modules/Favorites/FavouritesViewController.h # Riot/Modules/Favorites/FavouritesViewController.m # Riot/Modules/GlobalSearch/UnifiedSearchViewController.m # Riot/Modules/People/PeopleViewController.h # Riot/Modules/People/PeopleViewController.m # Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift # Riot/Modules/Room/DataSources/RoomDataSource.m # Riot/Modules/Room/Files/RoomFilesViewController.m # Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m # Riot/Modules/Room/Members/RoomParticipantsViewController.m # Riot/Modules/Room/RoomViewController.m # Riot/Modules/Room/Settings/RoomSettingsViewController.m # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift # Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift # Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h # Riot/Modules/Rooms/RoomsViewController.h # Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift # Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift # Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift # Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift # Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift # Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift # Riot/Modules/SetPinCode/PinCodePreferences.swift # Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift # Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m # Riot/Modules/Settings/Security/SecurityViewController.m # Riot/Modules/Settings/SettingsViewController.m # Riot/Modules/SplitView/SplitViewCoordinator.swift # Riot/Modules/SplitView/SplitViewCoordinatorType.swift # Riot/Modules/StartChat/StartChatViewController.m # Riot/Modules/TabBar/MasterTabBarController.h # Riot/Modules/TabBar/MasterTabBarController.m # Riot/Utils/EventFormatter.m # Riot/Utils/HTMLFormatter.swift # Riot/Utils/Tools.m # RiotNSE/NotificationService.swift
278 lines
9.5 KiB
Swift
278 lines
9.5 KiB
Swift
//
|
|
// Copyright 2022-2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Please see LICENSE in the repository root for full details.
|
|
//
|
|
|
|
import Foundation
|
|
import CoreLocation
|
|
import MatrixSDK
|
|
|
|
/// UserLocationService handles live location sharing for the current user
|
|
class UserLocationService: UserLocationServiceProtocol {
|
|
|
|
// MARK: - Constants
|
|
|
|
private enum Constants {
|
|
|
|
/// Minimum delay in milliseconds to send consecutive location for a beacon info
|
|
static let beaconSendMinInterval: UInt64 = 5000 // 5s
|
|
|
|
/// Delay to check for experied beacons
|
|
static let beaconExpiredVerificationInterval: TimeInterval = 5 // 5s
|
|
}
|
|
|
|
// MARK: - Properties
|
|
|
|
// MARK: Private
|
|
|
|
private let locationManager: LocationManager
|
|
private let session: MXSession
|
|
|
|
/// All active beacon info summaries that belongs to this device
|
|
/// Do not update location for beacon info started on another device
|
|
private var deviceBeaconInfoSummaries: [MXBeaconInfoSummaryProtocol] = []
|
|
|
|
private var beaconInfoSummaryListener: Any?
|
|
|
|
private var expiredBeaconVerificationTimer: Timer?
|
|
|
|
// MARK: Public
|
|
|
|
// MARK: - Setup
|
|
|
|
init(session: MXSession) {
|
|
// (bwi #4549): its ok to only test the buildsetting here. Wellknown check is handled in UI
|
|
self.locationManager = LocationManager(accuracy: .full, allowsBackgroundLocationUpdates: BWIBuildSettings.shared.locationSharingEnabled)
|
|
self.session = session
|
|
}
|
|
|
|
// MARK: - Public
|
|
|
|
func requestAuthorization(_ handler: @escaping LocationAuthorizationHandler) {
|
|
|
|
self.locationManager.requestAuthorization(handler)
|
|
}
|
|
|
|
func start() {
|
|
self.locationManager.delegate = self
|
|
|
|
// Check for existing beacon info summaries for the current device and start location tracking if needed
|
|
self.setupDeviceBeaconSummaries()
|
|
|
|
self.startListeningBeaconInfoSummaries()
|
|
self.startVerifyingExpiredBeaconInfoSummaries()
|
|
}
|
|
|
|
func stop() {
|
|
self.stopLocationTracking()
|
|
self.stopListeningBeaconInfoSummaries()
|
|
self.stopVerifyingExpiredBeaconInfoSummaries()
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
// MARK: Beacon info summary
|
|
|
|
private func startVerifyingExpiredBeaconInfoSummaries() {
|
|
|
|
let timer = Timer.scheduledTimer(withTimeInterval: Constants.beaconExpiredVerificationInterval, repeats: true) { [weak self] _ in
|
|
|
|
self?.verifyExpiredBeaconInfoSummaries()
|
|
}
|
|
|
|
self.expiredBeaconVerificationTimer = timer
|
|
}
|
|
|
|
private func stopVerifyingExpiredBeaconInfoSummaries() {
|
|
self.expiredBeaconVerificationTimer?.invalidate()
|
|
self.expiredBeaconVerificationTimer = nil
|
|
}
|
|
|
|
private func verifyExpiredBeaconInfoSummaries() {
|
|
|
|
for beaconInfoSummary in deviceBeaconInfoSummaries where beaconInfoSummary.isActive == false && beaconInfoSummary.hasStopped == false {
|
|
|
|
// TODO: Prevent to stop several times
|
|
// Wait for isStopping status
|
|
self.session.locationService.stopUserLocationSharing(withBeaconInfoEventId: beaconInfoSummary.id, roomId: beaconInfoSummary.roomId) { response in
|
|
|
|
}
|
|
}
|
|
|
|
// Remove non active beacon info summaries
|
|
self.deviceBeaconInfoSummaries = self.deviceBeaconInfoSummaries.filter({ beaconInfoSummary in
|
|
return beaconInfoSummary.isActive
|
|
})
|
|
}
|
|
|
|
private func getExistingDeviceBeaconSummaries() -> [MXBeaconInfoSummaryProtocol] {
|
|
guard let userId = self.session.myUserId else {
|
|
return []
|
|
}
|
|
|
|
return self.session.locationService.getBeaconInfoSummaries(for: userId).filter { summary in
|
|
return self.isDeviceBeaconInfoSummary(summary) && summary.isActive
|
|
}
|
|
}
|
|
|
|
private func setupDeviceBeaconSummaries() {
|
|
let existingDeviceBeaconInfoSummaries = self.getExistingDeviceBeaconSummaries()
|
|
|
|
self.deviceBeaconInfoSummaries = existingDeviceBeaconInfoSummaries
|
|
|
|
self.updateLocationTrackingIfNeeded()
|
|
|
|
for summary in existingDeviceBeaconInfoSummaries {
|
|
self.didReceiveDeviceNewBeaconInfoSummary(summary)
|
|
}
|
|
}
|
|
|
|
private func startListeningBeaconInfoSummaries() {
|
|
|
|
let beaconInfoSummaryListener = self.session.aggregations.beaconAggregations.listenToBeaconInfoSummaryUpdate { [weak self] roomId, beaconInfoSummary in
|
|
|
|
self?.didReceiveBeaconInfoSummary(beaconInfoSummary)
|
|
}
|
|
|
|
self.beaconInfoSummaryListener = beaconInfoSummaryListener
|
|
}
|
|
|
|
private func stopListeningBeaconInfoSummaries() {
|
|
|
|
if let listener = self.beaconInfoSummaryListener {
|
|
self.session.aggregations.beaconAggregations.removeListener(listener)
|
|
}
|
|
}
|
|
|
|
|
|
private func isDeviceBeaconInfoSummary(_ beaconInfoSummary: MXBeaconInfoSummaryProtocol) -> Bool {
|
|
return beaconInfoSummary.userId == self.session.myUserId && beaconInfoSummary.deviceId == self.session.myDeviceId
|
|
}
|
|
|
|
private func didReceiveBeaconInfoSummary(_ beaconInfoSummary: MXBeaconInfoSummaryProtocol) {
|
|
|
|
guard self.isDeviceBeaconInfoSummary(beaconInfoSummary) else {
|
|
return
|
|
}
|
|
|
|
let existingIndex = self.deviceBeaconInfoSummaries.firstIndex(where: { beaconInfoSum in
|
|
beaconInfoSum.id == beaconInfoSummary.id
|
|
})
|
|
|
|
if beaconInfoSummary.isActive {
|
|
|
|
if let index = existingIndex {
|
|
self.deviceBeaconInfoSummaries[index] = beaconInfoSummary
|
|
} else {
|
|
self.deviceBeaconInfoSummaries.append(beaconInfoSummary)
|
|
|
|
// Send location if possible to a new beacon info summary
|
|
self.didReceiveDeviceNewBeaconInfoSummary(beaconInfoSummary)
|
|
}
|
|
} else {
|
|
|
|
if let index = existingIndex {
|
|
self.deviceBeaconInfoSummaries.remove(at: index)
|
|
}
|
|
}
|
|
|
|
self.updateLocationTrackingIfNeeded()
|
|
}
|
|
|
|
private func didReceiveDeviceNewBeaconInfoSummary(_ beaconInfoSummary: MXBeaconInfoSummaryProtocol) {
|
|
|
|
guard let lastLocation = self.locationManager.lastLocation else {
|
|
return
|
|
}
|
|
|
|
self.sendLocation(lastLocation, for: beaconInfoSummary)
|
|
}
|
|
|
|
// MARK: Location sending
|
|
|
|
private func sendLocation(_ location: CLLocation, for beaconInfoSummary: MXBeaconInfoSummaryProtocol) {
|
|
guard self.canSendBeaconRequest(for: beaconInfoSummary) else {
|
|
return
|
|
}
|
|
|
|
var localEcho: MXEvent?
|
|
|
|
self.session.locationService.sendLocation(withBeaconInfoEventId: beaconInfoSummary.id,
|
|
latitude: location.coordinate.latitude,
|
|
longitude: location.coordinate.longitude,
|
|
inRoomWithId: beaconInfoSummary.roomId,
|
|
localEcho: &localEcho) { response in
|
|
|
|
switch response {
|
|
case .success:
|
|
break
|
|
case .failure(let error):
|
|
MXLog.error("Fail to send location", context: error)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func didReceiveLocation(_ location: CLLocation) {
|
|
|
|
for deviceBaconInfoSummary in deviceBeaconInfoSummaries {
|
|
self.sendLocation(location, for: deviceBaconInfoSummary)
|
|
}
|
|
}
|
|
|
|
private func canSendBeaconRequest(for beaconInfoSummary: MXBeaconInfoSummaryProtocol) -> Bool {
|
|
|
|
// Check if location manager is started
|
|
guard self.locationManager.isUpdatingLocation else {
|
|
return false
|
|
}
|
|
|
|
let canSendBeaconRequest: Bool
|
|
|
|
if let lastBeaconTimestamp = beaconInfoSummary.lastBeacon?.timestamp {
|
|
|
|
let currentTimestamp = Date().timeIntervalSince1970 * 1000
|
|
|
|
canSendBeaconRequest = UInt64(currentTimestamp) - lastBeaconTimestamp >= Constants.beaconSendMinInterval
|
|
} else {
|
|
// The beacon info summary have no last beacon, we can send a request immediatly
|
|
canSendBeaconRequest = true
|
|
}
|
|
|
|
return canSendBeaconRequest
|
|
}
|
|
|
|
// MARK: Device location
|
|
|
|
private func stopLocationTracking() {
|
|
self.locationManager.stop()
|
|
self.locationManager.delegate = nil
|
|
}
|
|
|
|
private func updateLocationTrackingIfNeeded() {
|
|
|
|
if self.deviceBeaconInfoSummaries.isEmpty {
|
|
|
|
// Stop location tracking if there is no active beacon info summaries
|
|
if self.locationManager.isUpdatingLocation {
|
|
self.locationManager.stop()
|
|
}
|
|
} else {
|
|
|
|
// Start location tracking if there is beacon info summaries and location tracking is stopped
|
|
if self.locationManager.isUpdatingLocation == false {
|
|
self.locationManager.start()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - LocationManagerDelegate
|
|
extension UserLocationService: LocationManagerDelegate {
|
|
|
|
func locationManager(_ manager: LocationManager, didUpdateLocation location: CLLocation) {
|
|
self.didReceiveLocation(location)
|
|
}
|
|
}
|