mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
implemented the LiveLocationManager as part of the user session and setup of permissions in the plist.
This commit is contained in:
@@ -36,6 +36,7 @@
|
|||||||
02A92F8F4538CECDFB4F2607 /* RoomDirectorySearchScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1562EAF6231151A675BED7A9 /* RoomDirectorySearchScreenCoordinator.swift */; };
|
02A92F8F4538CECDFB4F2607 /* RoomDirectorySearchScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1562EAF6231151A675BED7A9 /* RoomDirectorySearchScreenCoordinator.swift */; };
|
||||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
|
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
|
||||||
0307469D99B5FE6C7043AE39 /* KnockRequestsListScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E8A8EC299E12490588B07C /* KnockRequestsListScreenCoordinator.swift */; };
|
0307469D99B5FE6C7043AE39 /* KnockRequestsListScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E8A8EC299E12490588B07C /* KnockRequestsListScreenCoordinator.swift */; };
|
||||||
|
03364DE2568860586A713472 /* LiveLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D17F49E39CC38DAB7B305701 /* LiveLocationManager.swift */; };
|
||||||
03631FC946FF0BBAD37E22BF /* preview_avatar_user.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 87FC42213E86E8182CFD3A49 /* preview_avatar_user.jpg */; };
|
03631FC946FF0BBAD37E22BF /* preview_avatar_user.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 87FC42213E86E8182CFD3A49 /* preview_avatar_user.jpg */; };
|
||||||
037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */; };
|
037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */; };
|
||||||
038AB2E86960FD240231D4C2 /* GeneratedPreviewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A2E4BD7C0CAD25EF924A4C /* GeneratedPreviewTests.swift */; };
|
038AB2E86960FD240231D4C2 /* GeneratedPreviewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A2E4BD7C0CAD25EF924A4C /* GeneratedPreviewTests.swift */; };
|
||||||
@@ -1188,6 +1189,7 @@
|
|||||||
CC961529F9F1854BEC3272C9 /* LayoutMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC8AA23D4F37CC26564F63C5 /* LayoutMocks.swift */; };
|
CC961529F9F1854BEC3272C9 /* LayoutMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC8AA23D4F37CC26564F63C5 /* LayoutMocks.swift */; };
|
||||||
CCBEC2100CAF2EEBE9DB4156 /* TemplateScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA40B98B098B6F0371B750B3 /* TemplateScreenModels.swift */; };
|
CCBEC2100CAF2EEBE9DB4156 /* TemplateScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA40B98B098B6F0371B750B3 /* TemplateScreenModels.swift */; };
|
||||||
CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E97E9615A158C76B2AB77 /* DateTests.swift */; };
|
CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E97E9615A158C76B2AB77 /* DateTests.swift */; };
|
||||||
|
CD077E14FAADC444C5A80068 /* LiveLocationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33752AE856E93CE62412B7A1 /* LiveLocationManagerProtocol.swift */; };
|
||||||
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; };
|
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; };
|
||||||
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */; };
|
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */; };
|
||||||
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
|
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
|
||||||
@@ -1865,6 +1867,7 @@
|
|||||||
33035418BB35754232985871 /* TimelineReadReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReadReceiptsView.swift; sourceTree = "<group>"; };
|
33035418BB35754232985871 /* TimelineReadReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReadReceiptsView.swift; sourceTree = "<group>"; };
|
||||||
330AF4D121C3396F7A14B21D /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/SAS.strings; sourceTree = "<group>"; };
|
330AF4D121C3396F7A14B21D /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObject.swift; sourceTree = "<group>"; };
|
3339B1DDB1341E833D2555BC /* AVMetadataMachineReadableCodeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVMetadataMachineReadableCodeObject.swift; sourceTree = "<group>"; };
|
||||||
|
33752AE856E93CE62412B7A1 /* LiveLocationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveLocationManagerProtocol.swift; sourceTree = "<group>"; };
|
||||||
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; sourceTree = "<group>"; };
|
33AE897D86784CCA5E4E9227 /* ElementCallService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallService.swift; sourceTree = "<group>"; };
|
||||||
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -2719,6 +2722,7 @@
|
|||||||
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
||||||
D09227E671DB30795C43FFFD /* KnockRequestsListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreenViewModel.swift; sourceTree = "<group>"; };
|
D09227E671DB30795C43FFFD /* KnockRequestsListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
D0C2D52E36AD614B3C003EF6 /* RoomTimelineItemViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemViewState.swift; sourceTree = "<group>"; };
|
D0C2D52E36AD614B3C003EF6 /* RoomTimelineItemViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemViewState.swift; sourceTree = "<group>"; };
|
||||||
|
D17F49E39CC38DAB7B305701 /* LiveLocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveLocationManager.swift; sourceTree = "<group>"; };
|
||||||
D1896F6288D80E1F3EFB3DF8 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ka; path = ka.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
D1896F6288D80E1F3EFB3DF8 /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ka; path = ka.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = "<group>"; };
|
D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||||
D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
|
D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
|
||||||
@@ -3192,6 +3196,7 @@
|
|||||||
39557ADF21345E18F3865B9E /* Emojis */,
|
39557ADF21345E18F3865B9E /* Emojis */,
|
||||||
CA555F7C7CA382ACACF0D82B /* Keychain */,
|
CA555F7C7CA382ACACF0D82B /* Keychain */,
|
||||||
DCF22D1E268F1A36EC3431E4 /* LinkMetadata */,
|
DCF22D1E268F1A36EC3431E4 /* LinkMetadata */,
|
||||||
|
90FD376E9373246E4925CE95 /* Location */,
|
||||||
79E560F5113ED25D172E550C /* Media */,
|
79E560F5113ED25D172E550C /* Media */,
|
||||||
6709362D60732DED2069AE0F /* MediaPlayer */,
|
6709362D60732DED2069AE0F /* MediaPlayer */,
|
||||||
6DE13A7AE6587B079F4049D7 /* Notification */,
|
6DE13A7AE6587B079F4049D7 /* Notification */,
|
||||||
@@ -5388,6 +5393,15 @@
|
|||||||
path = LoginScreen;
|
path = LoginScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
90FD376E9373246E4925CE95 /* Location */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D17F49E39CC38DAB7B305701 /* LiveLocationManager.swift */,
|
||||||
|
33752AE856E93CE62412B7A1 /* LiveLocationManagerProtocol.swift */,
|
||||||
|
);
|
||||||
|
path = Location;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
92E99C57D7F92ED16F73282C /* ElementCall */ = {
|
92E99C57D7F92ED16F73282C /* ElementCall */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -8293,6 +8307,8 @@
|
|||||||
A37BFB32EAB8AEF6DD5BA0DC /* LinkNewDeviceService.swift in Sources */,
|
A37BFB32EAB8AEF6DD5BA0DC /* LinkNewDeviceService.swift in Sources */,
|
||||||
74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */,
|
74DF5BC17DE9F51E077FD457 /* LinkNewDeviceServiceMock.swift in Sources */,
|
||||||
866FA35E7A2339EF8B6D91CA /* LinkPreviewView.swift in Sources */,
|
866FA35E7A2339EF8B6D91CA /* LinkPreviewView.swift in Sources */,
|
||||||
|
03364DE2568860586A713472 /* LiveLocationManager.swift in Sources */,
|
||||||
|
CD077E14FAADC444C5A80068 /* LiveLocationManagerProtocol.swift in Sources */,
|
||||||
C8D0AC22E03F652118A2BB73 /* LiveLocationRoomTimelineItem.swift in Sources */,
|
C8D0AC22E03F652118A2BB73 /* LiveLocationRoomTimelineItem.swift in Sources */,
|
||||||
F7977C53B2B1D73030C69761 /* LiveLocationRoomTimelineView.swift in Sources */,
|
F7977C53B2B1D73030C69761 /* LiveLocationRoomTimelineView.swift in Sources */,
|
||||||
6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */,
|
6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"NSCameraUsageDescription" = "Каб зрабіць фота ці відэа і адправіць іх у выглядзе паведамлення, Element X патрабуе доступу да камеры.";
|
"NSCameraUsageDescription" = "Каб зрабіць фота ці відэа і адправіць іх у выглядзе паведамлення, Element X патрабуе доступу да камеры.";
|
||||||
"NSFaceIDUsageDescription" = "Face ID выкарыстоўваецца для доступу да праграмы.";
|
"NSFaceIDUsageDescription" = "Face ID выкарыстоўваецца для доступу да праграмы.";
|
||||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "To share your live location, Element X needs location access when the app is in the background.";
|
"NSLocationAlwaysAndWhenInUseUsageDescription" = "To share your live location, Element X needs location access when the app is in the background.";
|
||||||
"NSLocationWhenInUseUsageDescription" = "Дайце доступ да месцазнаходжання, каб Element X мог падзяліцца вашым месцазнаходжаннем.";
|
"NSLocationWhenInUseUsageDescription" = "Дайце доступ да месцазнаходжання, каб Element X мог падзяліцца вашым месцазнаходжаннем.";
|
||||||
"NSMicrophoneUsageDescription" = "Каб запісваць і адпраўляць паведамленні з гукам, Element X патрабуе доступу да мікрафона.";
|
"NSMicrophoneUsageDescription" = "Каб запісваць і адпраўляць паведамленні з гукам, Element X патрабуе доступу да мікрафона.";
|
||||||
"NSPhotoLibraryUsageDescription" = "Дазваляе захоўваць фатаграфіі і відэа ў вашу бібліятэку.";
|
"NSPhotoLibraryUsageDescription" = "Дазваляе захоўваць фатаграфіі і відэа ў вашу бібліятэку.";
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ final class AppSettings {
|
|||||||
case analyticsConsentState
|
case analyticsConsentState
|
||||||
case hasRunNotificationPermissionsOnboarding
|
case hasRunNotificationPermissionsOnboarding
|
||||||
case hasRunIdentityConfirmationOnboarding
|
case hasRunIdentityConfirmationOnboarding
|
||||||
|
case hasRequestedAlwaysLocationAuthorization
|
||||||
|
|
||||||
case frequentlyUsedSystemEmojis
|
case frequentlyUsedSystemEmojis
|
||||||
|
|
||||||
@@ -343,6 +344,9 @@ final class AppSettings {
|
|||||||
@UserPreference(key: UserDefaultsKeys.hasRunIdentityConfirmationOnboarding, defaultValue: false, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.hasRunIdentityConfirmationOnboarding, defaultValue: false, storageType: .userDefaults(store))
|
||||||
var hasRunIdentityConfirmationOnboarding
|
var hasRunIdentityConfirmationOnboarding
|
||||||
|
|
||||||
|
@UserPreference(key: UserDefaultsKeys.hasRequestedAlwaysLocationAuthorization, defaultValue: false, storageType: .userDefaults(store))
|
||||||
|
var hasRequestedAlwaysLocationAuthorization
|
||||||
|
|
||||||
@UserPreference(key: UserDefaultsKeys.frequentlyUsedSystemEmojis, defaultValue: [FrequentlyUsedEmoji](), storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.frequentlyUsedSystemEmojis, defaultValue: [FrequentlyUsedEmoji](), storageType: .userDefaults(store))
|
||||||
var frequentlyUsedSystemEmojis
|
var frequentlyUsedSystemEmojis
|
||||||
|
|
||||||
|
|||||||
@@ -11100,6 +11100,79 @@ class LinkNewDeviceServiceMock: LinkNewDeviceServiceProtocol, @unchecked Sendabl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class LiveLocationManagerMock: LiveLocationManagerProtocol, @unchecked Sendable {
|
||||||
|
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> {
|
||||||
|
get { return underlyingAuthorizationStatus }
|
||||||
|
set(value) { underlyingAuthorizationStatus = value }
|
||||||
|
}
|
||||||
|
var underlyingAuthorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never>!
|
||||||
|
|
||||||
|
//MARK: - requestAlwaysAuthorizationIfPossible
|
||||||
|
|
||||||
|
var requestAlwaysAuthorizationIfPossibleUnderlyingCallsCount = 0
|
||||||
|
var requestAlwaysAuthorizationIfPossibleCallsCount: Int {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return requestAlwaysAuthorizationIfPossibleUnderlyingCallsCount
|
||||||
|
} else {
|
||||||
|
var returnValue: Int? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = requestAlwaysAuthorizationIfPossibleUnderlyingCallsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
requestAlwaysAuthorizationIfPossibleUnderlyingCallsCount = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
requestAlwaysAuthorizationIfPossibleUnderlyingCallsCount = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestAlwaysAuthorizationIfPossibleCalled: Bool {
|
||||||
|
return requestAlwaysAuthorizationIfPossibleCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestAlwaysAuthorizationIfPossibleUnderlyingReturnValue: Bool!
|
||||||
|
var requestAlwaysAuthorizationIfPossibleReturnValue: Bool! {
|
||||||
|
get {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
return requestAlwaysAuthorizationIfPossibleUnderlyingReturnValue
|
||||||
|
} else {
|
||||||
|
var returnValue: Bool? = nil
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
returnValue = requestAlwaysAuthorizationIfPossibleUnderlyingReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if Thread.isMainThread {
|
||||||
|
requestAlwaysAuthorizationIfPossibleUnderlyingReturnValue = newValue
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
requestAlwaysAuthorizationIfPossibleUnderlyingReturnValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestAlwaysAuthorizationIfPossibleClosure: (() -> Bool)?
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func requestAlwaysAuthorizationIfPossible() -> Bool {
|
||||||
|
requestAlwaysAuthorizationIfPossibleCallsCount += 1
|
||||||
|
if let requestAlwaysAuthorizationIfPossibleClosure = requestAlwaysAuthorizationIfPossibleClosure {
|
||||||
|
return requestAlwaysAuthorizationIfPossibleClosure()
|
||||||
|
} else {
|
||||||
|
return requestAlwaysAuthorizationIfPossibleReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
class MediaLoaderMock: MediaLoaderProtocol, @unchecked Sendable {
|
class MediaLoaderMock: MediaLoaderProtocol, @unchecked Sendable {
|
||||||
|
|
||||||
//MARK: - loadMediaContentForSource
|
//MARK: - loadMediaContentForSource
|
||||||
@@ -20236,6 +20309,11 @@ class UserSessionMock: UserSessionProtocol, @unchecked Sendable {
|
|||||||
set(value) { underlyingVoiceMessageMediaManager = value }
|
set(value) { underlyingVoiceMessageMediaManager = value }
|
||||||
}
|
}
|
||||||
var underlyingVoiceMessageMediaManager: VoiceMessageMediaManagerProtocol!
|
var underlyingVoiceMessageMediaManager: VoiceMessageMediaManagerProtocol!
|
||||||
|
var liveLocationManager: LiveLocationManagerProtocol {
|
||||||
|
get { return underlyingLiveLocationManager }
|
||||||
|
set(value) { underlyingLiveLocationManager = value }
|
||||||
|
}
|
||||||
|
var underlyingLiveLocationManager: LiveLocationManagerProtocol!
|
||||||
var sessionSecurityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never> {
|
var sessionSecurityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never> {
|
||||||
get { return underlyingSessionSecurityStatePublisher }
|
get { return underlyingSessionSecurityStatePublisher }
|
||||||
set(value) { underlyingSessionSecurityStatePublisher = value }
|
set(value) { underlyingSessionSecurityStatePublisher = value }
|
||||||
|
|||||||
@@ -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 clientProxy: ClientProxyProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||||
|
let liveLocationManager: LiveLocationManagerProtocol
|
||||||
|
|
||||||
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
||||||
|
|
||||||
@@ -25,10 +26,11 @@ class UserSession: UserSessionProtocol {
|
|||||||
sessionSecurityStateSubject.asCurrentValuePublisher()
|
sessionSecurityStateSubject.asCurrentValuePublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol) {
|
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, liveLocationManager: LiveLocationManagerProtocol) {
|
||||||
self.clientProxy = clientProxy
|
self.clientProxy = clientProxy
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
self.voiceMessageMediaManager = voiceMessageMediaManager
|
self.voiceMessageMediaManager = voiceMessageMediaManager
|
||||||
|
self.liveLocationManager = liveLocationManager
|
||||||
|
|
||||||
authErrorCancellable = clientProxy.actionsPublisher
|
authErrorCancellable = clientProxy.actionsPublisher
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ protocol UserSessionProtocol {
|
|||||||
var clientProxy: ClientProxyProtocol { get }
|
var clientProxy: ClientProxyProtocol { get }
|
||||||
var mediaProvider: MediaProviderProtocol { get }
|
var mediaProvider: MediaProviderProtocol { get }
|
||||||
var voiceMessageMediaManager: VoiceMessageMediaManagerProtocol { get }
|
var voiceMessageMediaManager: VoiceMessageMediaManagerProtocol { get }
|
||||||
|
var liveLocationManager: LiveLocationManagerProtocol { get }
|
||||||
|
|
||||||
var sessionSecurityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never> { get }
|
var sessionSecurityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never> { get }
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
|
|
||||||
switch await restorePreviousLogin(credentials) {
|
switch await restorePreviousLogin(credentials) {
|
||||||
case .success(let clientProxy):
|
case .success(let clientProxy):
|
||||||
return .success(buildUserSessionWithClient(clientProxy))
|
return await .success(buildUserSessionWithClient(clientProxy))
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
MXLog.error("Failed restoring login with error: \(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)")
|
MXLog.info("Set up session for user \(userID) at: \(sessionDirectories)")
|
||||||
|
|
||||||
return .success(buildUserSessionWithClient(clientProxy))
|
return await .success(buildUserSessionWithClient(clientProxy))
|
||||||
} catch {
|
} catch {
|
||||||
MXLog.error("Failed creating user session with error: \(error)")
|
MXLog.error("Failed creating user session with error: \(error)")
|
||||||
return .failure(.failedSettingUpSession)
|
return .failure(.failedSettingUpSession)
|
||||||
@@ -103,16 +103,22 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) -> UserSessionProtocol {
|
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) async -> UserSessionProtocol {
|
||||||
let mediaProvider = MediaProvider(mediaLoader: clientProxy.mediaLoader,
|
let mediaProvider = MediaProvider(mediaLoader: clientProxy.mediaLoader,
|
||||||
imageCache: .onlyInMemory,
|
imageCache: .onlyInMemory,
|
||||||
homeserverReachabilityPublisher: clientProxy.homeserverReachabilityPublisher)
|
homeserverReachabilityPublisher: clientProxy.homeserverReachabilityPublisher)
|
||||||
|
|
||||||
let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider)
|
let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider)
|
||||||
|
|
||||||
|
let liveLocationManager = await MainActor.run {
|
||||||
|
LiveLocationManager(clientProxy: clientProxy,
|
||||||
|
appSettings: appSettings)
|
||||||
|
}
|
||||||
|
|
||||||
return UserSession(clientProxy: clientProxy,
|
return UserSession(clientProxy: clientProxy,
|
||||||
mediaProvider: mediaProvider,
|
mediaProvider: mediaProvider,
|
||||||
voiceMessageMediaManager: voiceMessageMediaManager)
|
voiceMessageMediaManager: voiceMessageMediaManager,
|
||||||
|
liveLocationManager: liveLocationManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user