Files
simplex-chat/apps/ios/Shared/Views/Call/CallController.swift
Evgeny Poberezkin 8469f921b7 ios: notification actions for calls and contact requests with NSE (#829)
* ios: notification actions for calls and contact requests with NSE

* update contact request if already in the list
2022-07-22 08:10:37 +01:00

228 lines
8.8 KiB
Swift

//
// CallController.swift
// SimpleX (iOS)
//
// Created by Evgeny on 21/05/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import Foundation
//import CallKit
import AVFoundation
import SimpleXChat
//class CallController: NSObject, CXProviderDelegate, ObservableObject {
class CallController: NSObject, ObservableObject {
static let useCallKit = false
static let shared = CallController()
// private let provider = CXProvider(configuration: CallController.configuration)
// private let controller = CXCallController()
private let callManager = CallManager()
@Published var activeCallInvitation: RcvCallInvitation?
// PKPushRegistry will be used from notification service extension
// let registry = PKPushRegistry(queue: nil)
// static let configuration: CXProviderConfiguration = {
// let configuration = CXProviderConfiguration()
// configuration.supportsVideo = true
// configuration.supportedHandleTypes = [.generic]
// configuration.includesCallsInRecents = true // TODO disable or add option
// configuration.maximumCallsPerCallGroup = 1
// return configuration
// }()
override init() {
super.init()
// self.provider.setDelegate(self, queue: nil)
// self.registry.delegate = self
// self.registry.desiredPushTypes = [.voIP]
}
// func providerDidReset(_ provider: CXProvider) {
// }
// func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
// logger.debug("CallController.provider CXStartCallAction")
// if callManager.startOutgoingCall(callUUID: action.callUUID) {
// action.fulfill()
// provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
// } else {
// action.fail()
// }
// }
// func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
// logger.debug("CallController.provider CXAnswerCallAction")
// if callManager.answerIncomingCall(callUUID: action.callUUID) {
// action.fulfill()
// } else {
// action.fail()
// }
// }
// func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
// logger.debug("CallController.provider CXEndCallAction")
// callManager.endCall(callUUID: action.callUUID) { ok in
// if ok {
// action.fulfill()
// } else {
// action.fail()
// }
// }
// }
// func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
// print("timed out", #function)
// action.fulfill()
// }
// func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
// print("received", #function)
//// do {
//// try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers)
//// logger.debug("audioSession category set")
//// try audioSession.setActive(true)
//// logger.debug("audioSession activated")
//// } catch {
//// print(error)
//// logger.error("failed activating audio session")
//// }
// }
// func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
// print("received", #function)
// }
// func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
//
// }
// This will be needed when we have notification service extension
// func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
// if type == .voIP {
// // Extract the call information from the push notification payload
// if let displayName = payload.dictionaryPayload["displayName"] as? String,
// let contactId = payload.dictionaryPayload["contactId"] as? String,
// let uuidStr = payload.dictionaryPayload["uuid"] as? String,
// let uuid = UUID(uuidString: uuidStr) {
// let callUpdate = CXCallUpdate()
// callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: displayName)
// provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
// if error != nil {
// let m = ChatModel.shared
// m.callInvitations.removeValue(forKey: contactId)
// }
// // Tell PushKit that the notification is handled.
// completion()
// })
// }
// }
// }
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
logger.debug("CallController.reportNewIncomingCall")
// if CallController.useCallKit, let uuid = invitation.callkitUUID {
// let update = CXCallUpdate()
// update.remoteHandle = CXHandle(type: .generic, value: invitation.contact.displayName)
// update.hasVideo = invitation.peerMedia == .video
// provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
// } else {
NtfManager.shared.notifyCallInvitation(invitation)
if invitation.callTs.timeIntervalSinceNow >= -180 {
activeCallInvitation = invitation
}
// }
}
// func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
// if CallController.useCallKit, let uuid = call.callkitUUID {
// provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
// }
// }
func reportCallRemoteEnded(invitation: RcvCallInvitation) {
// if CallController.useCallKit, let uuid = invitation.callkitUUID {
// provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
// } else if invitation.contact.id == activeCallInvitation?.contact.id {
activeCallInvitation = nil
// }
}
// func reportCallRemoteEnded(call: Call) {
// if CallController.useCallKit, let uuid = call.callkitUUID {
// provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
// }
// }
func startCall(_ contact: Contact, _ media: CallMediaType) {
logger.debug("CallController.startCall")
let uuid = callManager.newOutgoingCall(contact, media)
// if CallController.useCallKit {
// let handle = CXHandle(type: .generic, value: contact.displayName)
// let action = CXStartCallAction(call: uuid, handle: handle)
// action.isVideo = media == .video
// requestTransaction(with: action)
// } else if callManager.startOutgoingCall(callUUID: uuid) {
if callManager.startOutgoingCall(callUUID: uuid) {
logger.debug("CallController.startCall: call started")
} else {
logger.error("CallController.startCall: no active call")
}
}
func answerCall(invitation: RcvCallInvitation) {
callManager.answerIncomingCall(invitation: invitation)
if invitation.contact.id == self.activeCallInvitation?.contact.id {
self.activeCallInvitation = nil
}
}
func endCall(callUUID: UUID) {
// if CallController.useCallKit {
// requestTransaction(with: CXEndCallAction(call: callUUID))
// } else {
callManager.endCall(callUUID: callUUID) { ok in
if ok {
logger.debug("CallController.endCall: call ended")
} else {
logger.error("CallController.endCall: no actove call pr call invitation to end")
}
}
// }
}
func endCall(invitation: RcvCallInvitation) {
callManager.endCall(invitation: invitation) {
if invitation.contact.id == self.activeCallInvitation?.contact.id {
DispatchQueue.main.async {
self.activeCallInvitation = nil
}
}
}
}
func endCall(call: Call, completed: @escaping () -> Void) {
callManager.endCall(call: call, completed: completed)
}
func callAction(invitation: RcvCallInvitation, action: NtfCallAction) {
switch action {
case .accept: answerCall(invitation: invitation)
case .reject: endCall(invitation: invitation)
}
}
// private func requestTransaction(with action: CXAction) {
// let t = CXTransaction()
// t.addAction(action)
// controller.request(t) { error in
// if let error = error {
// logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
// } else {
// logger.debug("CallController.requestTransaction requested transaction successfully")
// }
// }
// }
}