diff --git a/Session/Calls/Call Management/SessionCall.swift b/Session/Calls/Call Management/SessionCall.swift index a2d0104ab..e2e8b4a20 100644 --- a/Session/Calls/Call Management/SessionCall.swift +++ b/Session/Calls/Call Management/SessionCall.swift @@ -128,6 +128,11 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { WebRTCSession.current = self.webRTCSession super.init() self.webRTCSession.delegate = self + if AppEnvironment.shared.callManager.currentCall == nil { + AppEnvironment.shared.callManager.currentCall = self + } else { + SNLog("[Calls] A call is ongoing.") + } } func reportIncomingCallIfNeeded(completion: @escaping (Error?) -> Void) { @@ -147,7 +152,7 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { } // MARK: Actions - func startSessionCall(completion: (() -> Void)?) { + func startSessionCall() { guard case .offer = mode else { return } var promise: Promise! Storage.write(with: { transaction in @@ -161,10 +166,9 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { } } }) - completion?() } - func answerSessionCall(completion: (() -> Void)?) { + func answerSessionCall() { guard case .answer = mode else { return } hasStartedConnecting = true if let sdp = remoteSDP { @@ -172,7 +176,6 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate { } else { isWaitingForRemoteSDP = true } - completion?() } func endSessionCall() { diff --git a/Session/Calls/Call Management/SessionCallManager+CXCallController.swift b/Session/Calls/Call Management/SessionCallManager+CXCallController.swift index c543f5026..62607e08b 100644 --- a/Session/Calls/Call Management/SessionCallManager+CXCallController.swift +++ b/Session/Calls/Call Management/SessionCallManager+CXCallController.swift @@ -2,7 +2,7 @@ import CallKit import SessionUtilitiesKit extension SessionCallManager { - public func startCall(_ call: SessionCall, completion: (() -> Void)?) { + public func startCall(_ call: SessionCall, completion: ((Error?) -> Void)?) { guard case .offer = call.mode else { return } let handle = CXHandle(type: .generic, value: call.sessionID) let startCallAction = CXStartCallAction(call: call.uuid, handle: handle) @@ -13,17 +13,23 @@ extension SessionCallManager { transaction.addAction(startCallAction) reportOutgoingCall(call) - requestTransaction(transaction) - completion?() + requestTransaction(transaction, completion: completion) + } + + public func answerCall(_ call: SessionCall, completion: ((Error?) -> Void)?) { + let answerCallAction = CXAnswerCallAction(call: call.uuid) + let transaction = CXTransaction() + transaction.addAction(answerCallAction) + + requestTransaction(transaction, completion: completion) } - public func endCall(_ call: SessionCall, completion: (() -> Void)?) { + public func endCall(_ call: SessionCall, completion: ((Error?) -> Void)?) { let endCallAction = CXEndCallAction(call: call.uuid) let transaction = CXTransaction() transaction.addAction(endCallAction) - requestTransaction(transaction) - completion?() + requestTransaction(transaction, completion: completion) } // Not currently in use @@ -35,13 +41,14 @@ extension SessionCallManager { requestTransaction(transaction) } - private func requestTransaction(_ transaction: CXTransaction) { + private func requestTransaction(_ transaction: CXTransaction, completion: ((Error?) -> Void)? = nil) { callController.request(transaction) { error in if let error = error { SNLog("Error requesting transaction: \(error)") } else { SNLog("Requested transaction successfully") } + completion?(error) } } } diff --git a/Session/Calls/Call Management/SessionCallManager+CXProvider.swift b/Session/Calls/Call Management/SessionCallManager+CXProvider.swift index 3b0cfa9b7..23d69e6bc 100644 --- a/Session/Calls/Call Management/SessionCallManager+CXProvider.swift +++ b/Session/Calls/Call Management/SessionCallManager+CXProvider.swift @@ -9,18 +9,22 @@ extension SessionCallManager: CXProviderDelegate { public func provider(_ provider: CXProvider, perform action: CXStartCallAction) { AssertIsOnMainThread() guard let call = self.currentCall else { return action.fail() } - call.startSessionCall(completion: nil) + call.startSessionCall() action.fulfill() } public func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { AssertIsOnMainThread() - guard let _ = self.currentCall else { return action.fail() } - let userDefaults = UserDefaults.standard - if userDefaults[.hasSeenCallIPExposureWarning] { - showCallVC() + guard let call = self.currentCall else { return action.fail() } + if let _ = CurrentAppContext().frontmostViewController() as? CallVC { + call.answerSessionCall() } else { - showCallModal() + let userDefaults = UserDefaults.standard + if userDefaults[.hasSeenCallIPExposureWarning] { + showCallVC() + } else { + showCallModal() + } } action.fulfill() } diff --git a/Session/Calls/Call Management/SessionCallManager.swift b/Session/Calls/Call Management/SessionCallManager.swift index 72978f143..7e4c2ffc0 100644 --- a/Session/Calls/Call Management/SessionCallManager.swift +++ b/Session/Calls/Call Management/SessionCallManager.swift @@ -4,7 +4,19 @@ import SessionMessagingKit public final class SessionCallManager: NSObject { let provider: CXProvider let callController = CXCallController() - var currentCall: SessionCall? + var currentCall: SessionCall? = nil { + willSet { + if (newValue != nil) { + DispatchQueue.main.async { + UIApplication.shared.isIdleTimerDisabled = true + } + } else { + DispatchQueue.main.async { + UIApplication.shared.isIdleTimerDisabled = false + } + } + } + } private static var _sharedProvider: CXProvider? class func sharedProvider(useSystemCallLog: Bool) -> CXProvider { @@ -46,12 +58,13 @@ public final class SessionCallManager: NSObject { public func reportOutgoingCall(_ call: SessionCall) { AssertIsOnMainThread() - self.currentCall = call - call.hasStartedConnectingDidChange = { - self.provider.reportOutgoingCall(with: call.uuid, startedConnectingAt: call.connectingDate) - } - call.hasConnectedDidChange = { - self.provider.reportOutgoingCall(with: call.uuid, connectedAt: call.connectedDate) + call.stateDidChange = { + if call.hasStartedConnecting { + self.provider.reportOutgoingCall(with: call.uuid, startedConnectingAt: call.connectingDate) + } + if call.hasConnected { + self.provider.reportOutgoingCall(with: call.uuid, connectedAt: call.connectedDate) + } } } @@ -69,11 +82,11 @@ public final class SessionCallManager: NSObject { // Report the incoming call to the system self.provider.reportNewIncomingCall(with: call.uuid, update: update) { error in guard error == nil else { + self.currentCall = nil completion(error) Logger.error("failed to report new incoming call, error: \(error!)") return } - self.currentCall = call completion(nil) } } diff --git a/Session/Calls/CallVC.swift b/Session/Calls/CallVC.swift index e724cd338..ba35b2379 100644 --- a/Session/Calls/CallVC.swift +++ b/Session/Calls/CallVC.swift @@ -167,6 +167,15 @@ final class CallVC : UIViewController, VideoPreviewDelegate { } } } + self.call.hasStartedConnectingDidChange = { + DispatchQueue.main.async { + self.callInfoLabel.text = "Connecting..." + self.answerButton.alpha = 0 + UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseIn, animations: { + self.answerButton.isHidden = true + }, completion: nil) + } + } self.call.hasConnectedDidChange = { DispatchQueue.main.async { self.callInfoLabel.text = "Connected" @@ -180,8 +189,10 @@ final class CallVC : UIViewController, VideoPreviewDelegate { } } self.call.hasEndedDidChange = { - self.conversationVC?.showInputAccessoryView() - self.presentingViewController?.dismiss(animated: true, completion: nil) + DispatchQueue.main.async { + self.conversationVC?.showInputAccessoryView() + self.presentingViewController?.dismiss(animated: true, completion: nil) + } } } @@ -194,9 +205,16 @@ final class CallVC : UIViewController, VideoPreviewDelegate { if shouldRestartCamera { cameraManager.prepare() } touch(call.videoCapturer) titleLabel.text = self.call.contactName - AppEnvironment.shared.callManager.startCall(call) { - self.callInfoLabel.text = "Ringing..." - self.answerButton.isHidden = true + AppEnvironment.shared.callManager.startCall(call) { error in + DispatchQueue.main.async { + if let _ = error { + self.callInfoLabel.text = "Can't start a call." + self.endCall() + } else { + self.callInfoLabel.text = "Ringing..." + self.answerButton.isHidden = true + } + } } if shouldAnswer { answerCall() } } @@ -305,12 +323,13 @@ final class CallVC : UIViewController, VideoPreviewDelegate { @objc private func answerCall() { let userDefaults = UserDefaults.standard if userDefaults[.hasSeenCallIPExposureWarning] { - self.call.answerSessionCall{ - self.callInfoLabel.text = "Connecting..." - self.answerButton.alpha = 0 - UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseIn, animations: { - self.answerButton.isHidden = true - }, completion: nil) + AppEnvironment.shared.callManager.answerCall(call) { error in + DispatchQueue.main.async { + if let _ = error { + self.callInfoLabel.text = "Can't answer the call." + self.endCall() + } + } } } else { userDefaults[.hasSeenCallIPExposureWarning] = true @@ -319,7 +338,12 @@ final class CallVC : UIViewController, VideoPreviewDelegate { } @objc private func endCall() { - AppEnvironment.shared.callManager.endCall(call, completion: nil) + AppEnvironment.shared.callManager.endCall(call) { error in + if let _ = error { + self.call.endSessionCall() + AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: nil) + } + } } @objc private func minimize() { diff --git a/Session/Calls/Views & Modals/IncomingCallBanner.swift b/Session/Calls/Views & Modals/IncomingCallBanner.swift index 9ebdee16d..569ffdb1f 100644 --- a/Session/Calls/Views & Modals/IncomingCallBanner.swift +++ b/Session/Calls/Views & Modals/IncomingCallBanner.swift @@ -155,7 +155,11 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate { } @objc private func endCall() { - AppEnvironment.shared.callManager.endCall(call) { + AppEnvironment.shared.callManager.endCall(call) { error in + if let _ = error { + self.call.endSessionCall() + AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: nil) + } self.dismiss() } } diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index efc0f6ced..7be62ee46 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -31,6 +31,7 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc let userDefaults = UserDefaults.standard if userDefaults[.hasSeenCallIPExposureWarning] { guard let contactSessionID = (thread as? TSContactThread)?.contactSessionID() else { return } + guard AppEnvironment.shared.callManager.currentCall == nil else { return } let call = SessionCall(for: contactSessionID, uuid: UUID().uuidString, mode: .offer) let callVC = CallVC(for: call) callVC.conversationVC = self diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index d6c994d10..afe933801 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -17,7 +17,6 @@ extension AppDelegate { conversationVC.inputAccessoryView?.isHidden = true conversationVC.inputAccessoryView?.alpha = 0 presentingVC.present(callVC, animated: true, completion: nil) - return } } call.reportIncomingCallIfNeeded{ error in