* ios: notification actions for calls and contact requests with NSE * update contact request if already in the list
228 lines
8.8 KiB
Swift
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")
|
|
// }
|
|
// }
|
|
// }
|
|
}
|