mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-02-07 14:18:25 +00:00
- Instead of reporting the Display name inside the remoteHandle of CXCallUpdate it now reports the SIP URI - The display name is now inserted into localizedCallerName The benefit of doing this is that is that it enables calling from the Phone Call History, and it is required if tel URI are activated (unless calls are not reported in phone history) This commit also enables the ability to place calls using Linphone by long pressing tel URIs.
618 lines
24 KiB
Swift
618 lines
24 KiB
Swift
/*
|
|
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
|
*
|
|
* This file is part of linphone-iphone
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import Foundation
|
|
import linphonesw
|
|
import UserNotifications
|
|
import os
|
|
import CallKit
|
|
import AVFoundation
|
|
|
|
@objc class CallAppData: NSObject {
|
|
@objc var batteryWarningShown = false
|
|
@objc var videoRequested = false /*set when user has requested for video*/
|
|
}
|
|
|
|
/*
|
|
* CallManager is a class that manages application calls and supports callkit.
|
|
* There is only one CallManager by calling CallManager.instance().
|
|
*/
|
|
@objc class CallManager: NSObject {
|
|
static var theCallManager: CallManager?
|
|
let providerDelegate: ProviderDelegate! // to support callkit
|
|
let callController: CXCallController! // to support callkit
|
|
let manager: CoreManagerDelegate! // callbacks of the linphonecore
|
|
var lc: Core?
|
|
@objc var speakerBeforePause : Bool = false
|
|
@objc var speakerEnabled : Bool = false
|
|
@objc var bluetoothEnabled : Bool = false
|
|
@objc var nextCallIsTransfer: Bool = false
|
|
@objc var alreadyRegisteredForNotification: Bool = false
|
|
var referedFromCall: String?
|
|
var referedToCall: String?
|
|
var endCallkit: Bool = false
|
|
|
|
|
|
fileprivate override init() {
|
|
providerDelegate = ProviderDelegate()
|
|
callController = CXCallController()
|
|
manager = CoreManagerDelegate()
|
|
}
|
|
|
|
@objc static func instance() -> CallManager {
|
|
if (theCallManager == nil) {
|
|
theCallManager = CallManager()
|
|
}
|
|
return theCallManager!
|
|
}
|
|
|
|
@objc func setCore(core: OpaquePointer) {
|
|
lc = Core.getSwiftObject(cObject: core)
|
|
lc?.addDelegate(delegate: manager)
|
|
}
|
|
|
|
@objc static func getAppData(call: OpaquePointer) -> CallAppData? {
|
|
let sCall = Call.getSwiftObject(cObject: call)
|
|
return getAppData(sCall: sCall)
|
|
}
|
|
|
|
static func getAppData(sCall:Call) -> CallAppData? {
|
|
if (sCall.userData == nil) {
|
|
return nil
|
|
}
|
|
return Unmanaged<CallAppData>.fromOpaque(sCall.userData!).takeUnretainedValue()
|
|
}
|
|
|
|
@objc static func setAppData(call:OpaquePointer, appData: CallAppData) {
|
|
let sCall = Call.getSwiftObject(cObject: call)
|
|
setAppData(sCall: sCall, appData: appData)
|
|
}
|
|
|
|
static func setAppData(sCall:Call, appData:CallAppData?) {
|
|
if (sCall.userData != nil) {
|
|
Unmanaged<CallAppData>.fromOpaque(sCall.userData!).release()
|
|
}
|
|
if (appData == nil) {
|
|
sCall.userData = nil
|
|
} else {
|
|
sCall.userData = UnsafeMutableRawPointer(Unmanaged.passRetained(appData!).toOpaque())
|
|
}
|
|
}
|
|
|
|
@objc func findCall(callId: String?) -> OpaquePointer? {
|
|
let call = callByCallId(callId: callId)
|
|
return call?.getCobject
|
|
}
|
|
|
|
func callByCallId(callId: String?) -> Call? {
|
|
if (callId == nil) {
|
|
return nil
|
|
}
|
|
let calls = lc?.calls
|
|
if let callTmp = calls?.first(where: { $0.callLog?.callId == callId }) {
|
|
return callTmp
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@objc static func callKitEnabled() -> Bool {
|
|
#if !targetEnvironment(simulator)
|
|
if ConfigManager.instance().lpConfigBoolForKey(key: "use_callkit", section: "app") {
|
|
return true
|
|
}
|
|
#endif
|
|
return false
|
|
}
|
|
|
|
@objc func allowSpeaker() -> Bool {
|
|
if (UIDevice.current.userInterfaceIdiom == .pad) {
|
|
// For now, ipad support only speaker.
|
|
return true
|
|
}
|
|
|
|
var allow = true
|
|
let newRoute = AVAudioSession.sharedInstance().currentRoute
|
|
if (newRoute.outputs.count > 0) {
|
|
let route = newRoute.outputs[0].portType
|
|
allow = !( route == .lineOut || route == .headphones || (AudioHelper.bluetoothRoutes() as Array).contains(where: {($0 as! AVAudioSession.Port) == route}))
|
|
}
|
|
|
|
return allow
|
|
}
|
|
|
|
@objc func enableSpeaker(enable: Bool) {
|
|
speakerEnabled = enable
|
|
do {
|
|
if (enable && allowSpeaker()) {
|
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
|
UIDevice.current.isProximityMonitoringEnabled = false
|
|
bluetoothEnabled = false
|
|
} else {
|
|
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
|
|
let buildinPort = AudioHelper.builtinAudioDevice()
|
|
try AVAudioSession.sharedInstance().setPreferredInput(buildinPort)
|
|
UIDevice.current.isProximityMonitoringEnabled = (lc!.callsNb > 0)
|
|
}
|
|
} catch {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Failed to change audio route: err \(error)")
|
|
}
|
|
}
|
|
|
|
func requestTransaction(_ transaction: CXTransaction, action: String) {
|
|
callController.request(transaction) { error in
|
|
if let error = error {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Requested transaction \(action) failed because: \(error)")
|
|
} else {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Requested transaction \(action) successfully")
|
|
}
|
|
}
|
|
}
|
|
|
|
// From ios13, display the callkit view when the notification is received.
|
|
@objc func displayIncomingCall(callId: String) {
|
|
let uuid = CallManager.instance().providerDelegate.uuids["\(callId)"]
|
|
if (uuid != nil) {
|
|
let callInfo = providerDelegate.callInfos[uuid!]
|
|
if (callInfo?.declined ?? false) {
|
|
// This call was declined.
|
|
providerDelegate.reportIncomingCall(call:nil, uuid: uuid!, handle: "Calling", hasVideo: true, displayName: callInfo?.displayName ?? "Calling")
|
|
providerDelegate.endCall(uuid: uuid!)
|
|
}
|
|
return
|
|
}
|
|
|
|
let call = CallManager.instance().callByCallId(callId: callId)
|
|
if (call != nil) {
|
|
let displayName = FastAddressBook.displayName(for: call?.remoteAddress?.getCobject) ?? "Unknow"
|
|
let video = UIApplication.shared.applicationState == .active && (lc!.videoActivationPolicy?.automaticallyAccept ?? false) && (call!.remoteParams?.videoEnabled ?? false)
|
|
displayIncomingCall(call: call, handle: (call!.remoteAddress?.asStringUriOnly())!, hasVideo: video, callId: callId, displayName: displayName)
|
|
} else {
|
|
displayIncomingCall(call: nil, handle: "Calling", hasVideo: true, callId: callId, displayName: "Calling")
|
|
}
|
|
}
|
|
|
|
func displayIncomingCall(call:Call?, handle: String, hasVideo: Bool, callId: String, displayName:String) {
|
|
let uuid = UUID()
|
|
let callInfo = CallInfo.newIncomingCallInfo(callId: callId)
|
|
|
|
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
|
providerDelegate.uuids.updateValue(uuid, forKey: callId)
|
|
providerDelegate.reportIncomingCall(call:call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName)
|
|
|
|
}
|
|
|
|
@objc func acceptCall(call: OpaquePointer?, hasVideo:Bool) {
|
|
if (call == nil) {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Can not accept null call!")
|
|
return
|
|
}
|
|
let call = Call.getSwiftObject(cObject: call!)
|
|
acceptCall(call: call, hasVideo: hasVideo)
|
|
}
|
|
|
|
func acceptCall(call: Call, hasVideo:Bool) {
|
|
do {
|
|
let callParams = try lc!.createCallParams(call: call)
|
|
callParams.videoEnabled = hasVideo
|
|
if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) {
|
|
let low_bandwidth = (AppManager.network() == .network_2g)
|
|
if (low_bandwidth) {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Low bandwidth mode")
|
|
}
|
|
callParams.lowBandwidthEnabled = low_bandwidth
|
|
}
|
|
|
|
//We set the record file name here because we can't do it after the call is started.
|
|
let address = call.callLog?.fromAddress
|
|
let writablePath = AppManager.recordingFilePathFromCall(address: address?.username ?? "")
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Record file path: \(String(describing: writablePath))")
|
|
callParams.recordFile = writablePath
|
|
|
|
try call.acceptWithParams(params: callParams)
|
|
} catch {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "accept call failed \(error)")
|
|
}
|
|
}
|
|
|
|
// for outgoing call. There is not yet callId
|
|
@objc func startCall(addr: OpaquePointer?, isSas: Bool) {
|
|
if (addr == nil) {
|
|
print("Can not start a call with null address!")
|
|
return
|
|
}
|
|
|
|
let sAddr = Address.getSwiftObject(cObject: addr!)
|
|
if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer) {
|
|
let uuid = UUID()
|
|
let name = FastAddressBook.displayName(for: addr) ?? "unknow"
|
|
let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly())
|
|
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
|
let transaction = CXTransaction(action: startCallAction)
|
|
|
|
let callInfo = CallInfo.newOutgoingCallInfo(addr: sAddr, isSas: isSas, displayName: name)
|
|
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
|
providerDelegate.uuids.updateValue(uuid, forKey: "")
|
|
|
|
setHeldOtherCalls(exceptCallid: "")
|
|
requestTransaction(transaction, action: "startCall")
|
|
}else {
|
|
try? doCall(addr: sAddr, isSas: isSas)
|
|
}
|
|
}
|
|
|
|
func doCall(addr: Address, isSas: Bool) throws {
|
|
let displayName = FastAddressBook.displayName(for: addr.getCobject)
|
|
|
|
let lcallParams = try CallManager.instance().lc!.createCallParams(call: nil)
|
|
if ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference") && AppManager.network() == .network_2g {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Enabling low bandwidth mode")
|
|
lcallParams.lowBandwidthEnabled = true
|
|
}
|
|
|
|
if (displayName != nil) {
|
|
try addr.setDisplayname(newValue: displayName!)
|
|
}
|
|
|
|
if(ConfigManager.instance().lpConfigBoolForKey(key: "override_domain_with_default_one")) {
|
|
try addr.setDomain(newValue: ConfigManager.instance().lpConfigStringForKey(key: "domain", section: "assistant"))
|
|
}
|
|
|
|
if (CallManager.instance().nextCallIsTransfer) {
|
|
let call = CallManager.instance().lc!.currentCall
|
|
try call?.transfer(referTo: addr.asString())
|
|
CallManager.instance().nextCallIsTransfer = false
|
|
} else {
|
|
//We set the record file name here because we can't do it after the call is started.
|
|
let writablePath = AppManager.recordingFilePathFromCall(address: addr.username )
|
|
Log.directLog(BCTBX_LOG_DEBUG, text: "record file path: \(writablePath)")
|
|
lcallParams.recordFile = writablePath
|
|
if (isSas) {
|
|
lcallParams.mediaEncryption = .ZRTP
|
|
}
|
|
let call = CallManager.instance().lc!.inviteAddressWithParams(addr: addr, params: lcallParams)
|
|
if (call != nil) {
|
|
// The LinphoneCallAppData object should be set on call creation with callback
|
|
// - (void)onCall:StateChanged:withMessage:. If not, we are in big trouble and expect it to crash
|
|
// We are NOT responsible for creating the AppData.
|
|
let data = CallManager.getAppData(sCall: call!)
|
|
if (data == nil) {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "New call instanciated but app data was not set. Expect it to crash.")
|
|
/* will be used later to notify user if video was not activated because of the linphone core*/
|
|
} else {
|
|
data!.videoRequested = lcallParams.videoEnabled
|
|
CallManager.setAppData(sCall: call!, appData: data)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func groupCall() {
|
|
if (CallManager.callKitEnabled()) {
|
|
let calls = lc?.calls
|
|
if (calls == nil || calls!.isEmpty) {
|
|
return
|
|
}
|
|
let firstCall = calls!.first?.callLog?.callId ?? ""
|
|
let lastCall = (calls!.count > 1) ? calls!.last?.callLog?.callId ?? "" : ""
|
|
|
|
let currentUuid = CallManager.instance().providerDelegate.uuids["\(firstCall)"]
|
|
if (currentUuid == nil) {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Can not find correspondant call to group.")
|
|
return
|
|
}
|
|
|
|
let newUuid = CallManager.instance().providerDelegate.uuids["\(lastCall)"]
|
|
let groupAction = CXSetGroupCallAction(call: currentUuid!, callUUIDToGroupWith: newUuid)
|
|
let transcation = CXTransaction(action: groupAction)
|
|
requestTransaction(transcation, action: "groupCall")
|
|
} else {
|
|
try? lc?.addAllToConference()
|
|
}
|
|
}
|
|
|
|
@objc func removeAllCallInfos() {
|
|
providerDelegate.callInfos.removeAll()
|
|
providerDelegate.uuids.removeAll()
|
|
}
|
|
|
|
// To be removed.
|
|
static func configAudioSession(audioSession: AVAudioSession) {
|
|
do {
|
|
try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.voiceChat, options: AVAudioSession.CategoryOptions(rawValue: AVAudioSession.CategoryOptions.allowBluetooth.rawValue | AVAudioSession.CategoryOptions.allowBluetoothA2DP.rawValue))
|
|
try audioSession.setMode(AVAudioSession.Mode.voiceChat)
|
|
try audioSession.setPreferredSampleRate(48000.0)
|
|
try AVAudioSession.sharedInstance().setActive(true, options: [])
|
|
} catch {
|
|
Log.directLog(BCTBX_LOG_WARNING, text: "CallKit: Unable to config audio session because : \(error)")
|
|
}
|
|
}
|
|
|
|
@objc func terminateCall(call: OpaquePointer?) {
|
|
if (call == nil) {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Can not terminate null call!")
|
|
return
|
|
}
|
|
let call = Call.getSwiftObject(cObject: call!)
|
|
do {
|
|
try call.terminate()
|
|
Log.directLog(BCTBX_LOG_DEBUG, text: "Call terminated")
|
|
} catch {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Failed to terminate call failed because \(error)")
|
|
}
|
|
if (UIApplication.shared.applicationState == .background) {
|
|
CoreManager.instance().stopLinphoneCore()
|
|
}
|
|
}
|
|
|
|
@objc func markCallAsDeclined(callId: String) {
|
|
if !CallManager.callKitEnabled() {
|
|
return
|
|
}
|
|
|
|
let uuid = providerDelegate.uuids["\(callId)"]
|
|
if (uuid == nil) {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Mark call \(callId) as declined.")
|
|
let uuid = UUID()
|
|
providerDelegate.uuids.updateValue(uuid, forKey: callId)
|
|
let callInfo = CallInfo.newIncomingCallInfo(callId: callId)
|
|
callInfo.declined = true
|
|
callInfo.reason = Reason.Busy
|
|
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
|
} else {
|
|
// end call
|
|
providerDelegate.endCall(uuid: uuid!)
|
|
}
|
|
}
|
|
|
|
@objc func setHeld(call: OpaquePointer, hold: Bool) {
|
|
let sCall = Call.getSwiftObject(cObject: call)
|
|
if (!hold) {
|
|
setHeldOtherCalls(exceptCallid: sCall.callLog?.callId ?? "")
|
|
}
|
|
setHeld(call: sCall, hold: hold)
|
|
}
|
|
|
|
func setHeld(call: Call, hold: Bool) {
|
|
let callid = call.callLog?.callId ?? ""
|
|
let uuid = providerDelegate.uuids["\(callid)"]
|
|
if (uuid == nil) {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Can not find correspondant call to set held.")
|
|
return
|
|
}
|
|
let setHeldAction = CXSetHeldCallAction(call: uuid!, onHold: hold)
|
|
let transaction = CXTransaction(action: setHeldAction)
|
|
requestTransaction(transaction, action: "setHeld")
|
|
}
|
|
|
|
@objc func setHeldOtherCalls(exceptCallid: String) {
|
|
for call in CallManager.instance().lc!.calls {
|
|
if (call.callLog?.callId != exceptCallid && call.state != .Paused && call.state != .Pausing && call.state != .PausedByRemote) {
|
|
setHeld(call: call, hold: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func performActionWhenCoreIsOn(action: @escaping ()->Void ) {
|
|
if (manager.globalState == .On) {
|
|
action()
|
|
} else {
|
|
manager.actionsToPerformOnceWhenCoreIsOn.append(action)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class CoreManagerDelegate: CoreDelegate {
|
|
static var speaker_already_enabled : Bool = false
|
|
var globalState : GlobalState = .Off
|
|
var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = []
|
|
|
|
override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) {
|
|
if (gstate == .On) {
|
|
actionsToPerformOnceWhenCoreIsOn.forEach {
|
|
$0()
|
|
}
|
|
actionsToPerformOnceWhenCoreIsOn.removeAll()
|
|
}
|
|
globalState = gstate
|
|
}
|
|
|
|
override func onRegistrationStateChanged(lc: Core, cfg: ProxyConfig, cstate: RegistrationState, message: String) {
|
|
if lc.proxyConfigList.count == 1 && (cstate == .Failed || cstate == .Cleared){
|
|
// terminate callkit immediately when registration failed or cleared, supporting single proxy configuration
|
|
CallManager.instance().endCallkit = true
|
|
for call in CallManager.instance().providerDelegate.uuids {
|
|
CallManager.instance().providerDelegate.endCall(uuid: call.value)
|
|
}
|
|
} else {
|
|
CallManager.instance().endCallkit = false
|
|
}
|
|
}
|
|
|
|
override func onCallStateChanged(lc: Core, call: Call, cstate: Call.State, message: String) {
|
|
let addr = call.remoteAddress;
|
|
let displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow"
|
|
let callLog = call.callLog
|
|
let callId = callLog?.callId
|
|
let video = UIApplication.shared.applicationState == .active && (lc.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false)
|
|
// we keep the speaker auto-enabled state in this static so that we don't
|
|
// force-enable it on ICE re-invite if the user disabled it.
|
|
CoreManagerDelegate.speaker_already_enabled = false
|
|
|
|
if (call.userData == nil) {
|
|
let appData = CallAppData()
|
|
CallManager.setAppData(sCall: call, appData: appData)
|
|
}
|
|
|
|
|
|
switch cstate {
|
|
case .IncomingReceived:
|
|
if (CallManager.callKitEnabled()) {
|
|
let uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
|
if (uuid != nil) {
|
|
// Tha app is now registered, updated the call already existed.
|
|
CallManager.instance().providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName)
|
|
let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!]
|
|
if (callInfo?.declined ?? false) {
|
|
DispatchQueue.main.asyncAfter(deadline: .now()) {try? call.decline(reason: callInfo!.reason)}
|
|
} else if (callInfo?.accepted ?? false) {
|
|
// The call is already answered.
|
|
CallManager.instance().acceptCall(call: call, hasVideo: video)
|
|
}
|
|
} else {
|
|
CallManager.instance().displayIncomingCall(call: call, handle: addr!.asStringUriOnly(), hasVideo: video, callId: callId!, displayName: displayName)
|
|
}
|
|
} else if (UIApplication.shared.applicationState != .active) {
|
|
// not support callkit , use notif
|
|
let content = UNMutableNotificationContent()
|
|
content.title = NSLocalizedString("Incoming call", comment: "")
|
|
content.body = displayName
|
|
content.sound = UNNotificationSound.init(named: UNNotificationSoundName.init("notes_of_the_optimistic.caf"))
|
|
content.categoryIdentifier = "call_cat"
|
|
content.userInfo = ["CallId" : callId!]
|
|
let req = UNNotificationRequest.init(identifier: "call_request", content: content, trigger: nil)
|
|
UNUserNotificationCenter.current().add(req, withCompletionHandler: nil)
|
|
}
|
|
break
|
|
case .StreamsRunning:
|
|
if (CallManager.callKitEnabled()) {
|
|
let uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
|
if (uuid != nil) {
|
|
let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!]
|
|
if (callInfo != nil && callInfo!.isOutgoing && !callInfo!.connected) {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call connected with uuid \(uuid!) and callId \(callId!)")
|
|
CallManager.instance().providerDelegate.reportOutgoingCallConnected(uuid: uuid!)
|
|
callInfo!.connected = true
|
|
CallManager.instance().providerDelegate.callInfos.updateValue(callInfo!, forKey: uuid!)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CallManager.instance().speakerBeforePause) {
|
|
CallManager.instance().speakerBeforePause = false
|
|
CallManager.instance().enableSpeaker(enable: true)
|
|
CoreManagerDelegate.speaker_already_enabled = true
|
|
}
|
|
break
|
|
case .OutgoingInit,
|
|
.OutgoingProgress,
|
|
.OutgoingRinging,
|
|
.OutgoingEarlyMedia:
|
|
if (CallManager.callKitEnabled()) {
|
|
let uuid = CallManager.instance().providerDelegate.uuids[""]
|
|
if (uuid != nil) {
|
|
let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!]
|
|
callInfo!.callId = callId!
|
|
CallManager.instance().providerDelegate.callInfos.updateValue(callInfo!, forKey: uuid!)
|
|
CallManager.instance().providerDelegate.uuids.removeValue(forKey: "")
|
|
CallManager.instance().providerDelegate.uuids.updateValue(uuid!, forKey: callId!)
|
|
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId!)")
|
|
CallManager.instance().providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
|
|
} else {
|
|
CallManager.instance().referedToCall = callId
|
|
}
|
|
}
|
|
break
|
|
case .End,
|
|
.Error:
|
|
UIDevice.current.isProximityMonitoringEnabled = false
|
|
CoreManagerDelegate.speaker_already_enabled = false
|
|
if (CallManager.instance().lc!.callsNb == 0) {
|
|
CallManager.instance().enableSpeaker(enable: false)
|
|
// disable this because I don't find anygood reason for it: _bluetoothAvailable = FALSE;
|
|
// furthermore it introduces a bug when calling multiple times since route may not be
|
|
// reconfigured between cause leading to bluetooth being disabled while it should not
|
|
CallManager.instance().bluetoothEnabled = false
|
|
}
|
|
|
|
if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) {
|
|
// Configure the notification's payload.
|
|
let content = UNMutableNotificationContent()
|
|
content.title = NSString.localizedUserNotificationString(forKey: NSLocalizedString("Missed call", comment: ""), arguments: nil)
|
|
content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil)
|
|
|
|
// Deliver the notification.
|
|
let request = UNNotificationRequest(identifier: "call_request", content: content, trigger: nil) // Schedule the notification.
|
|
let center = UNUserNotificationCenter.current()
|
|
center.add(request) { (error : Error?) in
|
|
if error != nil {
|
|
Log.directLog(BCTBX_LOG_ERROR, text: "Error while adding notification request : \(error!.localizedDescription)")
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CallManager.callKitEnabled()) {
|
|
var uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
|
if (callId == CallManager.instance().referedToCall) {
|
|
// refered call ended before connecting
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Callkit: end refered to call : \(String(describing: CallManager.instance().referedToCall))")
|
|
CallManager.instance().referedFromCall = nil
|
|
CallManager.instance().referedToCall = nil
|
|
}
|
|
if uuid == nil {
|
|
// the call not yet connected
|
|
uuid = CallManager.instance().providerDelegate.uuids[""]
|
|
}
|
|
if (uuid != nil) {
|
|
if (callId == CallManager.instance().referedFromCall) {
|
|
Log.directLog(BCTBX_LOG_MESSAGE, text: "Callkit: end refered from call : \(String(describing: CallManager.instance().referedFromCall))")
|
|
CallManager.instance().referedFromCall = nil
|
|
let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!]
|
|
callInfo!.callId = CallManager.instance().referedToCall ?? ""
|
|
CallManager.instance().providerDelegate.callInfos.updateValue(callInfo!, forKey: uuid!)
|
|
CallManager.instance().providerDelegate.uuids.removeValue(forKey: callId!)
|
|
CallManager.instance().providerDelegate.uuids.updateValue(uuid!, forKey: callInfo!.callId)
|
|
CallManager.instance().referedToCall = nil
|
|
break
|
|
}
|
|
|
|
let transaction = CXTransaction(action:
|
|
CXEndCallAction(call: uuid!))
|
|
CallManager.instance().requestTransaction(transaction, action: "endCall")
|
|
}
|
|
}
|
|
break
|
|
case .Released:
|
|
call.userData = nil
|
|
break
|
|
case .Referred:
|
|
CallManager.instance().referedFromCall = call.callLog?.callId
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
|
|
if (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) {
|
|
if ((call.currentParams?.videoEnabled ?? false) && !CoreManagerDelegate.speaker_already_enabled && !CallManager.instance().bluetoothEnabled) {
|
|
CallManager.instance().enableSpeaker(enable: true)
|
|
CoreManagerDelegate.speaker_already_enabled = true
|
|
}
|
|
}
|
|
|
|
// post Notification kLinphoneCallUpdate
|
|
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self, userInfo: [
|
|
AnyHashable("call"): NSValue.init(pointer:UnsafeRawPointer(call.getCobject)),
|
|
AnyHashable("state"): NSNumber(value: cstate.rawValue),
|
|
AnyHashable("message"): message
|
|
])
|
|
}
|
|
}
|
|
|
|
|