diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index e99c9bca98..b9be784f01 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4697,15 +4697,15 @@ class ElementCallServiceMock: ElementCallServiceProtocol { //MARK: - setupCallSession - var setupCallSessionTitleUnderlyingCallsCount = 0 - var setupCallSessionTitleCallsCount: Int { + var setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount = 0 + var setupCallSessionRoomIDRoomDisplayNameCallsCount: Int { get { if Thread.isMainThread { - return setupCallSessionTitleUnderlyingCallsCount + return setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = setupCallSessionTitleUnderlyingCallsCount + returnValue = setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount } return returnValue! @@ -4713,28 +4713,28 @@ class ElementCallServiceMock: ElementCallServiceProtocol { } set { if Thread.isMainThread { - setupCallSessionTitleUnderlyingCallsCount = newValue + setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - setupCallSessionTitleUnderlyingCallsCount = newValue + setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount = newValue } } } } - var setupCallSessionTitleCalled: Bool { - return setupCallSessionTitleCallsCount > 0 + var setupCallSessionRoomIDRoomDisplayNameCalled: Bool { + return setupCallSessionRoomIDRoomDisplayNameCallsCount > 0 } - var setupCallSessionTitleReceivedTitle: String? - var setupCallSessionTitleReceivedInvocations: [String] = [] - var setupCallSessionTitleClosure: ((String) async -> Void)? + var setupCallSessionRoomIDRoomDisplayNameReceivedArguments: (roomID: String, roomDisplayName: String)? + var setupCallSessionRoomIDRoomDisplayNameReceivedInvocations: [(roomID: String, roomDisplayName: String)] = [] + var setupCallSessionRoomIDRoomDisplayNameClosure: ((String, String) async -> Void)? - func setupCallSession(title: String) async { - setupCallSessionTitleCallsCount += 1 - setupCallSessionTitleReceivedTitle = title + func setupCallSession(roomID: String, roomDisplayName: String) async { + setupCallSessionRoomIDRoomDisplayNameCallsCount += 1 + setupCallSessionRoomIDRoomDisplayNameReceivedArguments = (roomID: roomID, roomDisplayName: roomDisplayName) DispatchQueue.main.async { - self.setupCallSessionTitleReceivedInvocations.append(title) + self.setupCallSessionRoomIDRoomDisplayNameReceivedInvocations.append((roomID: roomID, roomDisplayName: roomDisplayName)) } - await setupCallSessionTitleClosure?(title) + await setupCallSessionRoomIDRoomDisplayNameClosure?(roomID, roomDisplayName) } //MARK: - tearDownCallSession diff --git a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift index c46c08af54..e2691ab000 100644 --- a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift +++ b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift @@ -102,7 +102,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol return } - await elementCallService.setupCallSession(title: roomProxy.roomTitle) + await elementCallService.setupCallSession(roomID: roomProxy.id, roomDisplayName: roomProxy.roomTitle) let _ = await roomProxy.sendCallNotificationIfNeeeded() } diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 19576cfde5..5ab5089b97 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -28,7 +28,7 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe private var callProvider: CXProvider? private var ongoingCallID: UUID? - private var incomingCallRoomID: String? + private var ongoingCallRoomID: String? private var endUnansweredCallTask: Task? @@ -46,15 +46,14 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe pushRegistry.desiredPushTypes = [.voIP] } - func setupCallSession(title: String) async { - guard ongoingCallID == nil else { - return - } - + func setupCallSession(roomID: String, roomDisplayName: String) async { let callID = UUID() ongoingCallID = callID + ongoingCallRoomID = roomID - let handle = CXHandle(type: .generic, value: title) + callProvider = setupCallProvider(callID: callID, roomID: roomID, roomDisplayName: roomDisplayName, isCallIncoming: false) + + let handle = CXHandle(type: .generic, value: roomDisplayName) let startCallAction = CXStartCallAction(call: callID, handle: handle) startCallAction.isVideo = true @@ -85,6 +84,10 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe MXLog.error("Failed transaction with error: \(error)") } } + + self.ongoingCallID = nil + ongoingCallRoomID = nil + callProvider = nil } // MARK: - PKPushRegistryDelegate @@ -99,45 +102,28 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe let callID = UUID() ongoingCallID = callID + ongoingCallRoomID = roomID - incomingCallRoomID = roomID - - let configuration = CXProviderConfiguration() - configuration.supportsVideo = true - configuration.includesCallsInRecents = true - // Provide image icon if available - configuration.iconTemplateImageData = nil - - // https://stackoverflow.com/a/46077628/730924 - configuration.supportedHandleTypes = [.generic] - - let update = CXCallUpdate() - update.hasVideo = true + let roomDisplayName = payload.dictionaryPayload[ElementCallServiceNotificationKey.roomDisplayName.rawValue] as? String - update.localizedCallerName = payload.dictionaryPayload[ElementCallServiceNotificationKey.roomDisplayName.rawValue] as? String - - // https://stackoverflow.com/a/41230020/730924 - update.remoteHandle = .init(type: .generic, value: roomID) - - let callProvider = CXProvider(configuration: configuration) - callProvider.setDelegate(self, queue: nil) - callProvider.reportNewIncomingCall(with: callID, update: update) { error in - if let error { - MXLog.error("Failed reporting new incoming call with error: \(error)") - } - + callProvider = setupCallProvider(callID: callID, roomID: roomID, roomDisplayName: roomDisplayName, isCallIncoming: true) { completion() } - endUnansweredCallTask = Task { [weak self, callProvider, callID] in + endUnansweredCallTask = Task { [weak self] in try? await Task.sleep(for: .seconds(15)) - guard !Task.isCancelled else { + + guard let self, !Task.isCancelled else { return } - if let ongoingCallID = self?.ongoingCallID, ongoingCallID == callID { - callProvider.reportCall(with: callID, endedAt: .now, reason: .unanswered) + if let ongoingCallID, ongoingCallID == callID { + callProvider?.reportCall(with: callID, endedAt: .now, reason: .unanswered) } + + ongoingCallID = nil + ongoingCallRoomID = nil + callProvider = nil } } @@ -148,12 +134,12 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe } func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { - if let incomingCallRoomID { + if let ongoingCallRoomID { Task { // Dispatch to next run loop so it doesn't conflict with `setupCallSession` - actionsSubject.send(.answerCall(roomID: incomingCallRoomID)) + actionsSubject.send(.answerCall(roomID: ongoingCallRoomID)) } - self.incomingCallRoomID = nil + self.ongoingCallRoomID = nil endUnansweredCallTask?.cancel() } else { MXLog.error("Failed answering incoming call, missing room ID") @@ -163,13 +149,52 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { - if let incomingCallRoomID { - actionsSubject.send(.declineCall(roomID: incomingCallRoomID)) - self.incomingCallRoomID = nil + if let ongoingCallRoomID { + actionsSubject.send(.declineCall(roomID: ongoingCallRoomID)) + self.ongoingCallRoomID = nil } else { MXLog.error("Failed declining incoming call, missing room ID") } action.fulfill() } + + // MARK: - Private + + private func setupCallProvider(callID: UUID, roomID: String, roomDisplayName: String?, isCallIncoming: Bool, completion: (() -> Void)? = nil) -> CXProvider { + let configuration = CXProviderConfiguration() + configuration.supportsVideo = true + configuration.includesCallsInRecents = true + // Provide image icon if available + configuration.iconTemplateImageData = nil + + // https://stackoverflow.com/a/46077628/730924 + configuration.supportedHandleTypes = [.generic] + + let callProvider = CXProvider(configuration: configuration) + callProvider.setDelegate(self, queue: nil) + + if isCallIncoming { + let update = CXCallUpdate() + update.hasVideo = true + + update.localizedCallerName = roomDisplayName + + // https://stackoverflow.com/a/41230020/730924 + update.remoteHandle = .init(type: .generic, value: roomID) + + callProvider.reportNewIncomingCall(with: callID, update: update) { error in + if let error { + MXLog.error("Failed reporting new incoming call with error: \(error)") + } + + completion?() + } + } else { + callProvider.reportOutgoingCall(with: callID, connectedAt: .now) + completion?() + } + + return callProvider + } } diff --git a/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift b/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift index c05d593f17..402f01d9b7 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift @@ -32,7 +32,7 @@ let ElementCallServiceNotificationDiscardDelta = 10.0 protocol ElementCallServiceProtocol { var actions: AnyPublisher { get } - func setupCallSession(title: String) async + func setupCallSession(roomID: String, roomDisplayName: String) async func tearDownCallSession() }