refactor for CallKit
This commit is contained in:
parent
6f78d6dfbe
commit
bef20e2f9a
|
@ -148,6 +148,8 @@
|
|||
7B7CB190270FB2150079FF93 /* MiniCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB18F270FB2150079FF93 /* MiniCallView.swift */; };
|
||||
7B7CB192271508AD0079FF93 /* Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB191271508AD0079FF93 /* Vibration.swift */; };
|
||||
7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA68908272A27BE00EFC32F /* SessionCall.swift */; };
|
||||
7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */; };
|
||||
7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */; };
|
||||
7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; };
|
||||
7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; };
|
||||
|
@ -1133,6 +1135,8 @@
|
|||
7B7CB18F270FB2150079FF93 /* MiniCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniCallView.swift; sourceTree = "<group>"; };
|
||||
7B7CB191271508AD0079FF93 /* Vibration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vibration.swift; sourceTree = "<group>"; };
|
||||
7BA68908272A27BE00EFC32F /* SessionCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCall.swift; sourceTree = "<group>"; };
|
||||
7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXCallController.swift"; sourceTree = "<group>"; };
|
||||
7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXProvider.swift"; sourceTree = "<group>"; };
|
||||
7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
|
||||
7BC01A3F241F40AB00BC7C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -2071,8 +2075,10 @@
|
|||
7BA68907272A279900EFC32F /* Call Management */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BC707F127290ACB002817AD /* SessionCallManager.swift */,
|
||||
7BA68908272A27BE00EFC32F /* SessionCall.swift */,
|
||||
7BC707F127290ACB002817AD /* SessionCallManager.swift */,
|
||||
7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */,
|
||||
7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */,
|
||||
);
|
||||
path = "Call Management";
|
||||
sourceTree = "<group>";
|
||||
|
@ -4894,6 +4900,7 @@
|
|||
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
|
||||
346129991FD1E4DA00532771 /* SignalApp.m in Sources */,
|
||||
3496957121A301A100DCFE74 /* OWSBackupImportJob.m in Sources */,
|
||||
7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */,
|
||||
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */,
|
||||
C331FFFE2558FF3B00070591 /* ConversationCell.swift in Sources */,
|
||||
B8F5F72325F1B4CA003BF8D4 /* DownloadAttachmentModal.swift in Sources */,
|
||||
|
@ -4908,6 +4915,7 @@
|
|||
B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */,
|
||||
B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */,
|
||||
B877E24226CA12910007970A /* CallVC.swift in Sources */,
|
||||
7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */,
|
||||
C374EEEB25DA3CA70073A857 /* ConversationTitleView.swift in Sources */,
|
||||
B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */,
|
||||
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
import WebRTC
|
||||
import SessionMessagingKit
|
||||
import PromiseKit
|
||||
|
||||
public final class SessionCall: NSObject, WebRTCSessionDelegate {
|
||||
// MARK: Metadata Properties
|
||||
|
@ -147,14 +148,18 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate {
|
|||
// MARK: Actions
|
||||
func startSessionCall(completion: (() -> Void)?) {
|
||||
guard case .offer = mode else { return }
|
||||
AppEnvironment.shared.callManager.reportOutgoingCall(self)
|
||||
Storage.write { transaction in
|
||||
self.webRTCSession.sendPreOffer(to: self.sessionID, using: transaction).done {
|
||||
self.webRTCSession.sendOffer(to: self.sessionID, using: transaction).done {
|
||||
self.hasStartedConnecting = true
|
||||
}.retainUntilComplete()
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
var promise: Promise<Void>!
|
||||
Storage.write(with: { transaction in
|
||||
promise = self.webRTCSession.sendPreOffer(to: self.sessionID, using: transaction)
|
||||
}, completion: { [weak self] in
|
||||
let _ = promise.done {
|
||||
Storage.shared.write { transaction in
|
||||
self?.webRTCSession.sendOffer(to: self!.sessionID, using: transaction as! YapDatabaseReadWriteTransaction).done {
|
||||
self?.hasStartedConnecting = true
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
}
|
||||
})
|
||||
completion?()
|
||||
}
|
||||
|
||||
|
@ -175,7 +180,6 @@ public final class SessionCall: NSObject, WebRTCSessionDelegate {
|
|||
self.webRTCSession.endCall(with: self.sessionID, using: transaction)
|
||||
}
|
||||
hasEnded = true
|
||||
AppEnvironment.shared.callManager.reportCurrentCallEnded()
|
||||
}
|
||||
|
||||
// MARK: Renderer
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import CallKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
extension SessionCallManager {
|
||||
public func startCall(_ call: SessionCall, completion: (() -> Void)?) {
|
||||
guard case .offer = call.mode else { return }
|
||||
let handle = CXHandle(type: .generic, value: call.sessionID)
|
||||
let startCallAction = CXStartCallAction(call: call.uuid, handle: handle)
|
||||
|
||||
startCallAction.isVideo = false
|
||||
|
||||
let transaction = CXTransaction()
|
||||
transaction.addAction(startCallAction)
|
||||
|
||||
reportOutgoingCall(call)
|
||||
requestTransaction(transaction)
|
||||
completion?()
|
||||
}
|
||||
|
||||
public func endCall(_ call: SessionCall, completion: (() -> Void)?) {
|
||||
let endCallAction = CXEndCallAction(call: call.uuid)
|
||||
let transaction = CXTransaction()
|
||||
transaction.addAction(endCallAction)
|
||||
|
||||
requestTransaction(transaction)
|
||||
completion?()
|
||||
}
|
||||
|
||||
// Not currently in use
|
||||
public func setOnHoldStatus(for call: SessionCall) {
|
||||
let setHeldCallAction = CXSetHeldCallAction(call: call.uuid, onHold: true)
|
||||
let transaction = CXTransaction()
|
||||
transaction.addAction(setHeldCallAction)
|
||||
|
||||
requestTransaction(transaction)
|
||||
}
|
||||
|
||||
private func requestTransaction(_ transaction: CXTransaction) {
|
||||
callController.request(transaction) { error in
|
||||
if let error = error {
|
||||
SNLog("Error requesting transaction: \(error)")
|
||||
} else {
|
||||
SNLog("Requested transaction successfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import CallKit
|
||||
|
||||
extension SessionCallManager: CXProviderDelegate {
|
||||
public func providerDidReset(_ provider: CXProvider) {
|
||||
AssertIsOnMainThread()
|
||||
currentCall?.endSessionCall()
|
||||
}
|
||||
|
||||
public func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||
AssertIsOnMainThread()
|
||||
guard let call = self.currentCall else { return action.fail() }
|
||||
call.startSessionCall(completion: nil)
|
||||
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()
|
||||
} else {
|
||||
showCallModal()
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
public func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||
AssertIsOnMainThread()
|
||||
guard let call = self.currentCall else { return action.fail() }
|
||||
call.endSessionCall()
|
||||
reportCurrentCallEnded(reason: nil)
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
public func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
|
||||
// TODO: set on hold
|
||||
}
|
||||
|
||||
public func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
|
||||
// TODO: handle timeout
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import CallKit
|
||||
import SessionMessagingKit
|
||||
|
||||
public final class SessionCallManager: NSObject, CXProviderDelegate {
|
||||
private let provider: CXProvider
|
||||
public final class SessionCallManager: NSObject {
|
||||
let provider: CXProvider
|
||||
let callController = CXCallController()
|
||||
var currentCall: SessionCall?
|
||||
|
||||
private static var _sharedProvider: CXProvider?
|
||||
|
@ -43,11 +44,6 @@ public final class SessionCallManager: NSObject, CXProviderDelegate {
|
|||
self.provider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
public func providerDidReset(_ provider: CXProvider) {
|
||||
AssertIsOnMainThread()
|
||||
currentCall?.endSessionCall()
|
||||
}
|
||||
|
||||
public func reportOutgoingCall(_ call: SessionCall) {
|
||||
AssertIsOnMainThread()
|
||||
self.currentCall = call
|
||||
|
@ -82,8 +78,14 @@ public final class SessionCallManager: NSObject, CXProviderDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public func reportCurrentCallEnded() {
|
||||
public func reportCurrentCallEnded(reason: CXCallEndedReason?) {
|
||||
guard let call = currentCall else { return }
|
||||
if let reason = reason {
|
||||
self.provider.reportCall(with: call.uuid, endedAt: nil, reason: reason)
|
||||
}
|
||||
self.currentCall?.webRTCSession.dropConnection()
|
||||
self.currentCall = nil
|
||||
WebRTCSession.current = nil
|
||||
}
|
||||
|
||||
// MARK: Util
|
||||
|
@ -99,4 +101,27 @@ public final class SessionCallManager: NSObject, CXProviderDelegate {
|
|||
// Is there any reason to support this?
|
||||
callUpdate.supportsDTMF = false
|
||||
}
|
||||
|
||||
internal func showCallModal() {
|
||||
let callModal = CallModal() { [weak self] in
|
||||
self?.showCallVC()
|
||||
}
|
||||
callModal.modalPresentationStyle = .overFullScreen
|
||||
callModal.modalTransitionStyle = .crossDissolve
|
||||
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully
|
||||
presentingVC.present(callModal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
internal func showCallVC() {
|
||||
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully
|
||||
let callVC = CallVC(for: self.currentCall!)
|
||||
callVC.shouldAnswer = true
|
||||
if let conversationVC = presentingVC as? ConversationVC {
|
||||
callVC.conversationVC = conversationVC
|
||||
conversationVC.inputAccessoryView?.isHidden = true
|
||||
conversationVC.inputAccessoryView?.alpha = 0
|
||||
}
|
||||
presentingVC.present(callVC, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|||
if shouldRestartCamera { cameraManager.prepare() }
|
||||
touch(call.videoCapturer)
|
||||
titleLabel.text = self.call.contactName
|
||||
self.call.startSessionCall{
|
||||
AppEnvironment.shared.callManager.startCall(call) {
|
||||
self.callInfoLabel.text = "Ringing..."
|
||||
self.answerButton.isHidden = true
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|||
}
|
||||
|
||||
@objc private func endCall() {
|
||||
self.call.endSessionCall()
|
||||
AppEnvironment.shared.callManager.endCall(call, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func minimize() {
|
||||
|
|
|
@ -155,8 +155,9 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
|
|||
}
|
||||
|
||||
@objc private func endCall() {
|
||||
self.call.endSessionCall()
|
||||
self.dismiss()
|
||||
AppEnvironment.shared.callManager.endCall(call) {
|
||||
self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
public func showCallVC(answer: Bool) {
|
||||
|
|
|
@ -8,20 +8,24 @@ extension AppDelegate {
|
|||
// MARK: Call handling
|
||||
func createNewIncomingCall(caller: String, uuid: String) {
|
||||
let call = SessionCall(for: caller, uuid: uuid, mode: .answer)
|
||||
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully
|
||||
if let conversationVC = presentingVC as? ConversationVC, let contactThread = conversationVC.thread as? TSContactThread, contactThread.contactSessionID() == caller {
|
||||
let callVC = CallVC(for: call)
|
||||
callVC.conversationVC = conversationVC
|
||||
conversationVC.inputAccessoryView?.isHidden = true
|
||||
conversationVC.inputAccessoryView?.alpha = 0
|
||||
presentingVC.present(callVC, animated: true, completion: nil)
|
||||
} else {
|
||||
call.reportIncomingCallIfNeeded{ error in
|
||||
if let error = error {
|
||||
SNLog("[Calls] Failed to report incoming call to CallKit due to error: \(error)")
|
||||
let incomingCallBanner = IncomingCallBanner(for: call)
|
||||
incomingCallBanner.show()
|
||||
if CurrentAppContext().isMainAppAndActive {
|
||||
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // TODO: Handle more gracefully
|
||||
if let conversationVC = presentingVC as? ConversationVC, let contactThread = conversationVC.thread as? TSContactThread, contactThread.contactSessionID() == caller {
|
||||
DispatchQueue.main.async {
|
||||
let callVC = CallVC(for: call)
|
||||
callVC.conversationVC = conversationVC
|
||||
conversationVC.inputAccessoryView?.isHidden = true
|
||||
conversationVC.inputAccessoryView?.alpha = 0
|
||||
presentingVC.present(callVC, animated: true, completion: nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
call.reportIncomingCallIfNeeded{ error in
|
||||
if let error = error {
|
||||
SNLog("[Calls] Failed to report incoming call to CallKit due to error: \(error)")
|
||||
let incomingCallBanner = IncomingCallBanner(for: call)
|
||||
incomingCallBanner.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +57,7 @@ extension AppDelegate {
|
|||
if let currentBanner = IncomingCallBanner.current { currentBanner.dismiss() }
|
||||
if let callVC = CurrentAppContext().frontmostViewController() as? CallVC { callVC.handleEndCallMessage(message) }
|
||||
if let miniCallView = MiniCallView.current { miniCallView.dismiss() }
|
||||
WebRTCSession.current?.dropConnection()
|
||||
WebRTCSession.current = nil
|
||||
AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: .remoteEnded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ extension WebRTCSession {
|
|||
print("[Calls] Received remote SDP: \(sdp.sdp).")
|
||||
peerConnection.setRemoteDescription(sdp, completionHandler: { [weak self] error in
|
||||
if let error = error {
|
||||
SNLog("Couldn't set SDP due to error: \(error).")
|
||||
SNLog("[Calls] Couldn't set SDP due to error: \(error).")
|
||||
} else {
|
||||
guard let self = self,
|
||||
sdp.type == .offer, self.peerConnection.localDescription == nil else { return }
|
||||
|
|
|
@ -227,8 +227,6 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
message.kind = .endCall
|
||||
print("[Calls] Sending end call message.")
|
||||
MessageSender.sendNonDurably(message, in: thread, using: transaction).retainUntilComplete()
|
||||
dropConnection()
|
||||
WebRTCSession.current = nil
|
||||
}
|
||||
|
||||
public func dropConnection() {
|
||||
|
|
Loading…
Reference in New Issue