session-ios/Session/Calls/Call Management/SessionCallManager.swift

154 lines
6 KiB
Swift
Raw Normal View History

2021-10-28 08:02:41 +02:00
import CallKit
import SessionMessagingKit
2021-11-07 23:12:18 +01:00
public final class SessionCallManager: NSObject {
let provider: CXProvider
let callController = CXCallController()
2021-11-11 06:51:54 +01:00
var callTimeOutTimer: Timer? = nil
2021-11-09 01:53:38 +01:00
var currentCall: SessionCall? = nil {
willSet {
if (newValue != nil) {
DispatchQueue.main.async {
UIApplication.shared.isIdleTimerDisabled = true
}
} else {
DispatchQueue.main.async {
UIApplication.shared.isIdleTimerDisabled = false
}
}
}
}
2021-10-28 08:02:41 +02:00
private static var _sharedProvider: CXProvider?
class func sharedProvider(useSystemCallLog: Bool) -> CXProvider {
let configuration = buildProviderConfiguration(useSystemCallLog: useSystemCallLog)
if let sharedProvider = self._sharedProvider {
sharedProvider.configuration = configuration
return sharedProvider
} else {
SwiftSingletons.register(self)
let provider = CXProvider(configuration: configuration)
_sharedProvider = provider
return provider
}
}
class func buildProviderConfiguration(useSystemCallLog: Bool) -> CXProviderConfiguration {
let localizedName = NSLocalizedString("APPLICATION_NAME", comment: "Name of application")
let providerConfiguration = CXProviderConfiguration(localizedName: localizedName)
providerConfiguration.supportsVideo = true
2021-11-10 01:17:35 +01:00
providerConfiguration.maximumCallGroups = 1
2021-10-28 08:02:41 +02:00
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.generic]
let iconMaskImage = #imageLiteral(resourceName: "SessionGreen32")
providerConfiguration.iconTemplateImageData = iconMaskImage.pngData()
providerConfiguration.includesCallsInRecents = useSystemCallLog
return providerConfiguration
}
init(useSystemCallLog: Bool = false) {
AssertIsOnMainThread()
self.provider = type(of: self).sharedProvider(useSystemCallLog: useSystemCallLog)
super.init()
// We cannot assert singleton here, because this class gets rebuilt when the user changes relevant call settings
self.provider.setDelegate(self, queue: nil)
}
2021-11-11 01:09:52 +01:00
// MARK: Report calls
2021-10-29 06:22:23 +02:00
public func reportOutgoingCall(_ call: SessionCall) {
2021-10-28 08:02:41 +02:00
AssertIsOnMainThread()
2021-11-09 01:53:38 +01:00
call.stateDidChange = {
if call.hasStartedConnecting {
self.provider.reportOutgoingCall(with: call.callID, startedConnectingAt: call.connectingDate)
2021-11-09 01:53:38 +01:00
}
if call.hasConnected {
self.provider.reportOutgoingCall(with: call.callID, connectedAt: call.connectedDate)
2021-11-09 01:53:38 +01:00
}
2021-10-28 08:02:41 +02:00
}
2021-11-29 06:32:02 +01:00
callTimeOutTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false) { _ in
2021-11-11 06:51:54 +01:00
guard let currentCall = self.currentCall else { return }
currentCall.didTimeout = true
self.endCall(currentCall) { error in
self.callTimeOutTimer = nil
}
}
2021-10-28 08:02:41 +02:00
}
public func reportIncomingCall(_ call: SessionCall, callerName: String, completion: @escaping (Error?) -> Void) {
AssertIsOnMainThread()
// Construct a CXCallUpdate describing the incoming call, including the caller.
let update = CXCallUpdate()
update.localizedCallerName = callerName
update.remoteHandle = CXHandle(type: .generic, value: call.callID.uuidString)
2021-11-08 05:09:45 +01:00
update.hasVideo = false
2021-10-28 08:02:41 +02:00
disableUnsupportedFeatures(callUpdate: update)
// Report the incoming call to the system
self.provider.reportNewIncomingCall(with: call.callID, update: update) { error in
2021-10-28 08:02:41 +02:00
guard error == nil else {
self.reportCurrentCallEnded(reason: .failed)
2021-10-28 08:02:41 +02:00
completion(error)
return
}
completion(nil)
}
}
2021-11-07 23:12:18 +01:00
public func reportCurrentCallEnded(reason: CXCallEndedReason?) {
guard let call = currentCall else { return }
2021-12-02 03:30:41 +01:00
invalidateTimeoutTimer()
2021-11-07 23:12:18 +01:00
if let reason = reason {
self.provider.reportCall(with: call.callID, endedAt: nil, reason: reason)
2021-11-18 03:19:47 +01:00
switch (reason) {
2021-11-19 01:50:14 +01:00
case .answeredElsewhere: call.updateCallMessage(mode: .answeredElsewhere)
2021-11-18 03:19:47 +01:00
case .unanswered: call.updateCallMessage(mode: .unanswered)
case .declinedElsewhere: call.updateCallMessage(mode: .local)
default: call.updateCallMessage(mode: .remote)
2021-11-11 06:51:54 +01:00
}
2021-11-09 06:05:23 +01:00
} else {
call.updateCallMessage(mode: .local)
2021-11-07 23:12:18 +01:00
}
2022-02-15 03:59:01 +01:00
call.webRTCSession.dropConnection()
2021-10-29 06:22:23 +02:00
self.currentCall = nil
2021-11-07 23:12:18 +01:00
WebRTCSession.current = nil
2021-10-29 06:22:23 +02:00
}
2021-10-28 08:02:41 +02:00
// MARK: Util
private func disableUnsupportedFeatures(callUpdate: CXCallUpdate) {
// Call Holding is failing to restart audio when "swapping" calls on the CallKit screen
// until user returns to in-app call screen.
callUpdate.supportsHolding = false
// Not yet supported
callUpdate.supportsGrouping = false
callUpdate.supportsUngrouping = false
// Is there any reason to support this?
callUpdate.supportsDTMF = false
}
2021-11-07 23:12:18 +01:00
2021-11-26 06:57:57 +01:00
public func handleIncomingCallOfferInBusyState(offerMessage: CallMessage, using transaction: YapDatabaseReadWriteTransaction) {
2021-11-11 01:09:52 +01:00
guard let caller = offerMessage.sender, let thread = TSContactThread.fetch(for: caller, using: transaction) else { return }
let message = CallMessage()
message.uuid = offerMessage.uuid
message.kind = .endCall
print("[Calls] Sending end call message.")
MessageSender.sendNonDurably(message, in: thread, using: transaction).retainUntilComplete()
2021-11-29 02:10:33 +01:00
let infoMessage = TSInfoMessage.from(offerMessage, associatedWith: thread)
infoMessage.save(with: transaction)
infoMessage.updateCallInfoMessage(.missed, using: transaction)
2021-11-11 01:09:52 +01:00
}
2021-11-11 06:51:54 +01:00
public func invalidateTimeoutTimer() {
callTimeOutTimer?.invalidate()
callTimeOutTimer = nil
}
2021-10-28 08:02:41 +02:00
}
2021-11-07 23:12:18 +01:00