mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-07 05:53:06 +00:00
callkit in Swift
This commit is contained in:
parent
c4b5d1207f
commit
98a7938b86
24 changed files with 940 additions and 1092 deletions
566
Classes/CallManager.swift
Normal file
566
Classes/CallManager.swift
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2019 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
|
||||
import CoreTelephony
|
||||
|
||||
enum NetworkType: Int {
|
||||
case network_none = 0
|
||||
case network_2g = 1
|
||||
case network_3g = 2
|
||||
case network_4g = 3
|
||||
case network_lte = 4
|
||||
case network_wifi = 5
|
||||
}
|
||||
|
||||
@objc class CallAppData: NSObject {
|
||||
@objc public var batteryWarningShown = false
|
||||
@objc public var videoRequested = false /*set when user has requested for video*/
|
||||
}
|
||||
|
||||
@objc class CallManager: NSObject {
|
||||
static var theCallManager: CallManager?
|
||||
@objc static public var nextCallIsTransfer: Bool = false
|
||||
|
||||
let providerDelegate: ProviderDelegate!
|
||||
let callController: CXCallController!
|
||||
let manager: CoreManager!
|
||||
let applicationKey = "app"
|
||||
var callAppDatas: [String : CallAppData] = [:]
|
||||
@objc var speakerBeforePause : Bool = false
|
||||
@objc var speakerEnabled : Bool = false
|
||||
@objc var bluetoothEnabled : Bool = false
|
||||
|
||||
var lc: Core?
|
||||
var config: Config?
|
||||
|
||||
fileprivate override init() {
|
||||
providerDelegate = ProviderDelegate()
|
||||
callController = CXCallController()
|
||||
manager = CoreManager()
|
||||
}
|
||||
|
||||
@objc static func instance() -> CallManager {
|
||||
if (theCallManager == nil) {
|
||||
theCallManager = CallManager()
|
||||
}
|
||||
return theCallManager!
|
||||
}
|
||||
|
||||
@objc func getAppData (callId : String) -> CallAppData? {
|
||||
return CallManager.instance().callAppDatas["\(callId)"]
|
||||
}
|
||||
|
||||
@objc func setAppData (callId: String, appData: CallAppData) {
|
||||
CallManager.instance().callAppDatas.updateValue(appData, forKey: callId)
|
||||
}
|
||||
|
||||
@objc func configCallManager(core: OpaquePointer, db:OpaquePointer) {
|
||||
lc = Core.getSwiftObject(cObject: core)
|
||||
lc?.addDelegate(delegate: manager)
|
||||
config = Config.getSwiftObject(cObject: db)
|
||||
}
|
||||
|
||||
@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 CallManager.instance().lpConfigBoolForKey(key: "use_callkit", section: "app") {
|
||||
return true
|
||||
}
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
|
||||
static func network() -> NetworkType {
|
||||
let info = CTTelephonyNetworkInfo()
|
||||
let currentRadio = info.currentRadioAccessTechnology
|
||||
if (currentRadio == CTRadioAccessTechnologyEdge) {
|
||||
return NetworkType.network_2g
|
||||
} else if (currentRadio == CTRadioAccessTechnologyLTE) {
|
||||
return NetworkType.network_4g
|
||||
}
|
||||
return NetworkType.network_3g
|
||||
}
|
||||
|
||||
@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 setSpeakerEnabled(enable: Bool) {
|
||||
speakerEnabled = enable
|
||||
do {
|
||||
if (enable && allowSpeaker()) {
|
||||
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
||||
UIDevice.current.isProximityMonitoringEnabled = false
|
||||
bluetoothEnabled = false
|
||||
} else {
|
||||
let buildinPort = AudioHelper.bluetoothAudioDevice()
|
||||
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)")
|
||||
}
|
||||
}
|
||||
|
||||
@objc static func recordingFilePathFromCall(address: String) -> String {
|
||||
var filePath = "recording_"
|
||||
filePath = filePath.appending(address.isEmpty ? address : "unknow")
|
||||
let now = Date()
|
||||
let dateFormat = DateFormatter()
|
||||
dateFormat.dateFormat = "E-d-MMM-yyyy-HH-mm-ss"
|
||||
let date = dateFormat.string(from: now)
|
||||
|
||||
filePath = filePath.appending("_\(date).mkv")
|
||||
|
||||
let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
|
||||
var writablePath = paths[0]
|
||||
writablePath = writablePath.appending("/\(filePath)")
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "file path is \(writablePath)")
|
||||
return writablePath
|
||||
//file name is recording_contact-name_dayName-day-monthName-year-hour-minutes-seconds
|
||||
//The recording prefix is used to identify recordings in the cache directory.
|
||||
//We will use name_dayName-day-monthName-year to separate recordings by days, then hour-minutes-seconds to order them in each day.
|
||||
}
|
||||
|
||||
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) {
|
||||
displayIncomingCall(call: nil, handle: "Calling", hasVideo: false, callId: callId)
|
||||
}
|
||||
|
||||
func displayIncomingCall(call:Call?, handle: String, hasVideo: Bool, callId: String) {
|
||||
let uuid = UUID()
|
||||
providerDelegate.uuids.updateValue(uuid, forKey: callId)
|
||||
providerDelegate.calls.updateValue(callId, forKey: uuid)
|
||||
providerDelegate.connecteds.updateValue(false, forKey: uuid)
|
||||
providerDelegate.reportIncomingCall(call:call, uuid: uuid, handle: handle, hasVideo: hasVideo)
|
||||
}
|
||||
|
||||
@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 (lpConfigBoolForKey(key: "edge_opt_preference")) {
|
||||
let low_bandwidth = (CallManager.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 = CallManager.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()) {
|
||||
let uuid = UUID()
|
||||
let name = FastAddressBook.displayName(for: addr) ?? "unknow"
|
||||
let handle = CXHandle(type: .generic, value: name)
|
||||
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
||||
let transaction = CXTransaction(action: startCallAction)
|
||||
|
||||
providerDelegate.addrs.updateValue(sAddr, forKey: uuid)
|
||||
providerDelegate.outgoingUuids.updateValue(uuid, forKey: sAddr.asStringUriOnly())
|
||||
providerDelegate.isSas.updateValue(isSas, forKey: uuid)
|
||||
|
||||
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 CallManager.instance().lpConfigBoolForKey(key: "edge_opt_preference") && CallManager.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(CallManager.instance().lpConfigBoolForKey(key: "override_domain_with_default_one")) {
|
||||
try addr.setDomain(newValue: CallManager.instance().lpConfigStringForKey(key: "domain", section: "assistant"))
|
||||
}
|
||||
|
||||
if (CallManager.nextCallIsTransfer) {
|
||||
let call = CallManager.instance().lc!.currentCall
|
||||
try call?.transfer(referTo: addr.asString())
|
||||
CallManager.nextCallIsTransfer = false
|
||||
} else {
|
||||
//We set the record file name here because we can't do it after the call is started.
|
||||
let writablePath = CallManager.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) {
|
||||
let callId = call!.callLog?.callId
|
||||
// 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.instance().getAppData(callId: callId ?? "")
|
||||
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.instance().setAppData(callId: callId!, 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")
|
||||
|
||||
// To simulate the real group call action
|
||||
heldCall(uuid: currentUuid!, onHold: false)
|
||||
} else {
|
||||
try? lc?.addAllToConference()
|
||||
}
|
||||
}
|
||||
|
||||
func heldCall(uuid: UUID, onHold: Bool) {
|
||||
let heldAction = CXSetHeldCallAction(call: uuid, onHold: onHold)
|
||||
let otherTransacation = CXTransaction(action: heldAction)
|
||||
requestTransaction(otherTransacation, action: "heldCall")
|
||||
}
|
||||
|
||||
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)")
|
||||
}
|
||||
}
|
||||
|
||||
//pragma mark - LPConfig Functions
|
||||
@objc func lpConfigSetString(value:String, key:String, section:String) {
|
||||
if (!key.isEmpty) {
|
||||
config?.setString(section: section, key: key, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func lpConfigSetString(value:String, key:String) {
|
||||
lpConfigSetString(value: value, key: key, section: applicationKey)
|
||||
}
|
||||
|
||||
@objc func lpConfigStringForKey(key:String, section:String, defaultValue:String) -> String {
|
||||
if (key.isEmpty) {
|
||||
return defaultValue
|
||||
}
|
||||
return config?.getString(section: section, key: key, defaultString: "") ?? defaultValue
|
||||
}
|
||||
|
||||
@objc func lpConfigStringForKey(key:String, section:String) -> String {
|
||||
return lpConfigStringForKey(key: key, section: section, defaultValue: "")
|
||||
}
|
||||
|
||||
@objc func lpConfigStringForKey(key:String, defaultValue:String) -> String {
|
||||
return lpConfigStringForKey(key: key, section: applicationKey, defaultValue: defaultValue)
|
||||
}
|
||||
|
||||
@objc func lpConfigStringForKey(key:String) -> String {
|
||||
return lpConfigStringForKey(key: key, defaultValue: "")
|
||||
}
|
||||
|
||||
@objc func lpConfigSetInt(value:Int, key:String, section:String) {
|
||||
if(!key.isEmpty) {
|
||||
config?.setInt(section: section, key: key, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func lpConfigSetInt(value:Int, key:String) {
|
||||
lpConfigSetInt(value: value, key: key, section: applicationKey)
|
||||
}
|
||||
|
||||
@objc func lpConfigIntForKey(key:String, section:String, defaultValue:Int) -> Int {
|
||||
if (key.isEmpty) {
|
||||
return defaultValue
|
||||
}
|
||||
return config?.getInt(section: section, key: key, defaultValue: defaultValue) ?? defaultValue
|
||||
}
|
||||
|
||||
@objc func lpConfigIntForKey(key:String, section:String) -> Int {
|
||||
return lpConfigIntForKey(key: key, section: section, defaultValue: -1)
|
||||
}
|
||||
|
||||
@objc func lpConfigIntForKey(key:String, defaultValue:Int) -> Int {
|
||||
return lpConfigIntForKey(key: key, section: applicationKey, defaultValue: defaultValue)
|
||||
}
|
||||
|
||||
@objc func lpConfigIntForKey(key:String) -> Int {
|
||||
return lpConfigIntForKey(key: key, defaultValue: -1)
|
||||
}
|
||||
|
||||
@objc func lpConfigSetBool(value:Bool, key:String, section:String) {
|
||||
lpConfigSetInt(value: value ? 1:0, key: key, section: section)
|
||||
}
|
||||
|
||||
@objc func lpConfigSetBool(value:Bool, key:String) {
|
||||
lpConfigSetBool(value: value, key: key, section: applicationKey)
|
||||
}
|
||||
|
||||
@objc func lpConfigBoolForKey(key:String, section:String, defaultValue:Bool) -> Bool {
|
||||
if (key.isEmpty) {
|
||||
return defaultValue
|
||||
}
|
||||
let val = lpConfigIntForKey(key: key, section: section, defaultValue: -1)
|
||||
return (val != -1) ? (val == 1) : defaultValue
|
||||
}
|
||||
|
||||
@objc func lpConfigBoolForKey(key:String, section:String) -> Bool {
|
||||
return lpConfigBoolForKey(key: key, section: section, defaultValue: false)
|
||||
}
|
||||
|
||||
@objc func lpConfigBoolForKey(key:String, defaultValue:Bool) -> Bool {
|
||||
return lpConfigBoolForKey(key: key, section: applicationKey, defaultValue: defaultValue)
|
||||
}
|
||||
|
||||
@objc func lpConfigBoolForKey(key:String) -> Bool {
|
||||
return lpConfigBoolForKey(key: key, defaultValue: false)
|
||||
}
|
||||
}
|
||||
|
||||
class CoreManager: CoreDelegate {
|
||||
static var speaker_already_enabled : Bool = false
|
||||
|
||||
override func onCallStateChanged(lc: Core, call: Call, cstate: Call.State, message: String) {
|
||||
let addr = call.remoteAddress;
|
||||
let address = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow"
|
||||
let callLog = call.callLog
|
||||
let callId = callLog?.callId
|
||||
let video = call.params?.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.
|
||||
CoreManager.speaker_already_enabled = false
|
||||
|
||||
if (callId != nil && CallManager.instance().callAppDatas["\(callId!)"] == nil) {
|
||||
CallManager.instance().callAppDatas.updateValue(CallAppData(), forKey: callId!)
|
||||
}
|
||||
|
||||
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: address, hasVideo: video)
|
||||
let connected = CallManager.instance().providerDelegate.connecteds[uuid!] ?? false
|
||||
if (connected) {
|
||||
// The call is already answered.
|
||||
CallManager.instance().acceptCall(call: call, hasVideo: video)
|
||||
}
|
||||
} else {
|
||||
// Nothing happped before, display a new Incoming call.
|
||||
CallManager.instance().displayIncomingCall(call: call, handle: address, hasVideo: video, callId: callId!)
|
||||
}
|
||||
} else if (UIApplication.shared.applicationState != .active) {
|
||||
// not support callkit , use notif
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSLocalizedString("Incoming call", comment: "")
|
||||
content.body = address
|
||||
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.outgoingUuids["\(addr!.asStringUriOnly())"]
|
||||
if (uuid != nil) {
|
||||
CallManager.instance().providerDelegate.uuids.updateValue(uuid!, forKey: callId!)
|
||||
CallManager.instance().providerDelegate.calls.updateValue(callId!, forKey: uuid!)
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call connected with uuid \(uuid!) and callId \(callId!)")
|
||||
CallManager.instance().providerDelegate.reportOutgoingCallConnected(uuid: uuid!)
|
||||
}
|
||||
}
|
||||
|
||||
if (CallManager.instance().speakerBeforePause) {
|
||||
CallManager.instance().speakerBeforePause = false
|
||||
CallManager.instance().setSpeakerEnabled(enable: true)
|
||||
CoreManager.speaker_already_enabled = true
|
||||
}
|
||||
break
|
||||
case .OutgoingRinging:
|
||||
if (CallManager.callKitEnabled()) {
|
||||
let uuid = CallManager.instance().providerDelegate.outgoingUuids["\(addr!.asStringUriOnly())"]
|
||||
if (uuid != nil) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId!)")
|
||||
CallManager.instance().providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
|
||||
}
|
||||
}
|
||||
break
|
||||
case .End,
|
||||
.Error:
|
||||
UIDevice.current.isProximityMonitoringEnabled = false
|
||||
CoreManager.speaker_already_enabled = false
|
||||
if (CallManager.instance().lc!.callsNb == 0) {
|
||||
CallManager.instance().setSpeakerEnabled(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 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: address, 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()) {
|
||||
// end CallKit
|
||||
let uuid = CallManager.instance().providerDelegate.uuids["\(callId!)"]
|
||||
if (uuid != nil) {
|
||||
let transaction = CXTransaction(action:
|
||||
CXEndCallAction(call: uuid!))
|
||||
CallManager.instance().requestTransaction(transaction, action: "endCall")
|
||||
}
|
||||
}
|
||||
break
|
||||
case .Released:
|
||||
CallManager.instance().callAppDatas.removeValue(forKey: callId ?? "")
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) {
|
||||
if (video && CoreManager.speaker_already_enabled && CallManager.instance().bluetoothEnabled) {
|
||||
CallManager.instance().setSpeakerEnabled(enable: true)
|
||||
CoreManager.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
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -93,20 +93,20 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
|
||||
- (IBAction)onRoutesBluetoothClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:TRUE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesEarpieceClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:FALSE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesSpeakerClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:FALSE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:TRUE];
|
||||
[CallManager.instance setSpeakerEnabled:TRUE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesClick:(id)sender {
|
||||
|
|
@ -131,8 +131,8 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
[_routesButton setOn];
|
||||
}
|
||||
|
||||
_routesBluetoothButton.selected = LinphoneManager.instance.bluetoothEnabled;
|
||||
_routesSpeakerButton.selected = LinphoneManager.instance.speakerEnabled;
|
||||
_routesBluetoothButton.selected = CallManager.instance.bluetoothEnabled;
|
||||
_routesSpeakerButton.selected = CallManager.instance.speakerEnabled;
|
||||
_routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected;
|
||||
|
||||
if (hidden != _routesView.hidden) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#include "linphone/linphonecore.h"
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
const NSInteger SECURE_BUTTON_TAG = 5;
|
||||
|
||||
@implementation CallView {
|
||||
|
|
@ -137,7 +139,7 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
_waitView.hidden = TRUE;
|
||||
LinphoneManager.instance.nextCallIsTransfer = NO;
|
||||
CallManager.nextCallIsTransfer = FALSE;
|
||||
|
||||
callRecording = FALSE;
|
||||
_recordButtonOnView.hidden = TRUE;
|
||||
|
|
@ -241,7 +243,7 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
}
|
||||
onConfirmationClick:^() {
|
||||
LinphoneAddress *addr = linphone_address_new(address.UTF8String);
|
||||
[LinphoneManager.instance doCallWithSas:addr isSas:TRUE];
|
||||
[CallManager.instance startCallWithAddr:addr isSas:TRUE];
|
||||
linphone_address_unref(addr);
|
||||
} ];
|
||||
[securityDialog.securityImage setImage:[UIImage imageNamed:@"security_alert_indicator.png"]];
|
||||
|
|
@ -488,8 +490,8 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
[_routesButton setOn];
|
||||
}
|
||||
|
||||
_routesBluetoothButton.selected = LinphoneManager.instance.bluetoothEnabled;
|
||||
_routesSpeakerButton.selected = LinphoneManager.instance.speakerEnabled;
|
||||
_routesBluetoothButton.selected = CallManager.instance.bluetoothEnabled;
|
||||
_routesSpeakerButton.selected = CallManager.instance.speakerEnabled;
|
||||
_routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected;
|
||||
|
||||
if (hidden != _routesView.hidden) {
|
||||
|
|
@ -582,13 +584,16 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
case LinphoneCallOutgoingInit:
|
||||
case LinphoneCallConnected:
|
||||
case LinphoneCallStreamsRunning: {
|
||||
// check video
|
||||
// check video, because video can be disabled because of the low bandwidth.
|
||||
if (!linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
|
||||
const LinphoneCallParams *param = linphone_call_get_current_params(call);
|
||||
const LinphoneCallAppData *callAppData =
|
||||
(__bridge const LinphoneCallAppData *)(linphone_call_get_user_data(call));
|
||||
if (state == LinphoneCallStreamsRunning && callAppData->videoRequested &&
|
||||
linphone_call_params_low_bandwidth_enabled(param)) {
|
||||
CallAppData *data = nil;
|
||||
const char *callId = linphone_call_log_get_call_id(linphone_call_get_call_log(call));
|
||||
if (callId) {
|
||||
data = [CallManager.instance getAppDataWithCallId:[NSString stringWithUTF8String:callId]];
|
||||
}
|
||||
|
||||
if (state == LinphoneCallStreamsRunning && data && data.videoRequested && linphone_call_params_low_bandwidth_enabled(param)) {
|
||||
// too bad video was not enabled because low bandwidth
|
||||
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Low bandwidth", nil)
|
||||
message:NSLocalizedString(@"Video cannot be activated because of low bandwidth "
|
||||
|
|
@ -602,7 +607,8 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
|
||||
[errView addAction:defaultAction];
|
||||
[self presentViewController:errView animated:YES completion:nil];
|
||||
callAppData->videoRequested = FALSE; /*reset field*/
|
||||
data.videoRequested = FALSE;
|
||||
[CallManager.instance setAppDataWithCallId:[NSString stringWithUTF8String:callId] appData:data];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -802,20 +808,20 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
|
||||
- (IBAction)onRoutesBluetoothClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:TRUE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesEarpieceClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:FALSE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesSpeakerClick:(id)sender {
|
||||
[self hideRoutes:TRUE animated:TRUE];
|
||||
[LinphoneManager.instance setBluetoothEnabled:FALSE];
|
||||
[LinphoneManager.instance setSpeakerEnabled:TRUE];
|
||||
[CallManager.instance setSpeakerEnabled:TRUE];
|
||||
}
|
||||
|
||||
- (IBAction)onRoutesClick:(id)sender {
|
||||
|
|
@ -838,7 +844,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
[self hideOptions:TRUE animated:TRUE];
|
||||
DialerView *view = VIEW(DialerView);
|
||||
[view setAddress:@""];
|
||||
LinphoneManager.instance.nextCallIsTransfer = YES;
|
||||
CallManager.nextCallIsTransfer = TRUE;
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
}
|
||||
|
||||
|
|
@ -846,13 +852,13 @@ static void hideSpinner(LinphoneCall *call, void *user_data) {
|
|||
[self hideOptions:TRUE animated:TRUE];
|
||||
DialerView *view = VIEW(DialerView);
|
||||
[view setAddress:@""];
|
||||
LinphoneManager.instance.nextCallIsTransfer = NO;
|
||||
CallManager.nextCallIsTransfer = FALSE;
|
||||
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
|
||||
}
|
||||
|
||||
- (IBAction)onOptionsConferenceClick:(id)sender {
|
||||
[self hideOptions:TRUE animated:TRUE];
|
||||
linphone_core_add_all_to_conference(LC);
|
||||
[CallManager.instance groupCall];
|
||||
}
|
||||
|
||||
#pragma mark - Animation
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@
|
|||
#import <PushKit/PushKit.h>
|
||||
|
||||
#import "LinphoneCoreSettingsStore.h"
|
||||
#import "ProviderDelegate.h"
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#import <UserNotificationsUI/UserNotificationsUI.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
|
||||
@interface LinphoneAppDelegate : NSObject <UIApplicationDelegate, PKPushRegistryDelegate, UNUserNotificationCenterDelegate, CLLocationManagerDelegate> {
|
||||
@private
|
||||
|
|
@ -39,7 +40,6 @@
|
|||
@property (nonatomic, retain) NSString *configURL;
|
||||
@property (nonatomic, strong) UIWindow* window;
|
||||
@property PKPushRegistry* voipRegistry;
|
||||
@property ProviderDelegate *del;
|
||||
@property BOOL alreadyRegisteredForNotification;
|
||||
@property BOOL onlyPortrait;
|
||||
@property UIApplicationShortcutItem *shortcutItem;
|
||||
|
|
|
|||
|
|
@ -111,13 +111,6 @@
|
|||
instance->currentCallContextBeforeGoingBackground.call = 0;
|
||||
} else if (linphone_call_get_state(call) ==
|
||||
LinphoneCallIncomingReceived) {
|
||||
LinphoneCallAppData *data =
|
||||
(__bridge LinphoneCallAppData *)linphone_call_get_user_data(
|
||||
call);
|
||||
if (data && data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) {
|
||||
if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) {
|
||||
linphone_call_accept(call);
|
||||
|
|
@ -125,7 +118,8 @@
|
|||
} else {
|
||||
[PhoneMainView.instance displayIncomingCall:call];
|
||||
}
|
||||
} else if (linphone_core_get_calls_nb(LC) > 1) {
|
||||
} else {
|
||||
// Click the call notification when callkit is disabled, show app view.
|
||||
[PhoneMainView.instance displayIncomingCall:call];
|
||||
}
|
||||
|
||||
|
|
@ -268,10 +262,6 @@
|
|||
BOOL background_mode = [instance lpConfigBoolForKey:@"backgroundmode_preference"];
|
||||
BOOL start_at_boot = [instance lpConfigBoolForKey:@"start_at_boot_preference"];
|
||||
[self registerForNotifications]; // Register for notifications must be done ASAP to give a chance for first SIP register to be done with right token. Specially true in case of remote provisionning or re-install with new type of signing certificate, like debug to release.
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
self.del = [[ProviderDelegate alloc] init];
|
||||
[LinphoneManager.instance setProviderDelegate:self.del];
|
||||
}
|
||||
|
||||
if (state == UIApplicationStateBackground) {
|
||||
// we've been woken up directly to background;
|
||||
|
|
@ -411,6 +401,17 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
// used for callkit. Called when active video.
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
|
||||
{
|
||||
if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
LOGI(@"CallKit: satrt video.");
|
||||
CallView *view = VIEW(CallView);
|
||||
[view.videoButton toggle];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)valueForKey:(NSString *)key fromQueryItems:(NSArray *)queryItems {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", key];
|
||||
NSURLQueryItem *queryItem = [[queryItems filteredArrayUsingPredicate:predicate] firstObject];
|
||||
|
|
@ -445,12 +446,6 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Tell the core to make sure that we are registered.
|
||||
// It will initiate socket connections, which seems to be required.
|
||||
// Indeed it is observed that if no network action is done in the notification handler, then
|
||||
// iOS kills us.
|
||||
linphone_core_ensure_registered(LC);
|
||||
|
||||
NSString *uuid = [NSString stringWithFormat:@"<urn:uuid:%@>", [LinphoneManager.instance lpConfigStringForKey:@"uuid" inSection:@"misc" withDefault:NULL]];
|
||||
NSString *sipInstance = [aps objectForKey:@"uuid"];
|
||||
if (sipInstance && uuid && ![sipInstance isEqualToString:uuid]) {
|
||||
|
|
@ -486,10 +481,20 @@
|
|||
notification.alertTitle = @"APN Pusher";
|
||||
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
[LinphoneManager.instance addPushCallId:callId];
|
||||
if ([loc_key isEqualToString:@"IC_MSG"] && [CallManager callKitEnabled]) {
|
||||
// callkit only for call not message.
|
||||
[CallManager.instance displayIncomingCallWithCallId:callId];
|
||||
}
|
||||
}
|
||||
|
||||
LOGI(@"Notification [%p] processed", userInfo);
|
||||
// Tell the core to make sure that we are registered.
|
||||
// It will initiate socket connections, which seems to be required.
|
||||
// Indeed it is observed that if no network action is done in the notification handler, then
|
||||
// iOS kills us.
|
||||
linphone_core_ensure_registered(LC);
|
||||
}
|
||||
|
||||
- (BOOL)addLongTaskIDforCallID:(NSString *)callId {
|
||||
|
|
@ -588,14 +593,7 @@
|
|||
if (!callId)
|
||||
return;
|
||||
|
||||
LinphoneCall *call = [LinphoneManager.instance callByCallId:callId];
|
||||
if (call) {
|
||||
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
}
|
||||
LinphoneCall *call = [CallManager.instance findCallWithCallId:callId];
|
||||
|
||||
if ([response.actionIdentifier isEqual:@"Answer"]) {
|
||||
// use the standard handler
|
||||
|
|
@ -757,13 +755,6 @@
|
|||
completionHandler:(void (^)(void))completionHandler {
|
||||
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call) {
|
||||
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
}
|
||||
LOGI(@"%@", NSStringFromSelector(_cmd));
|
||||
if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_9_0) {
|
||||
LOGI(@"%@", NSStringFromSelector(_cmd));
|
||||
|
|
@ -805,13 +796,6 @@
|
|||
completionHandler:(void (^)(void))completionHandler {
|
||||
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call) {
|
||||
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
}
|
||||
if ([notification.category isEqualToString:@"incoming_call"]) {
|
||||
if ([identifier isEqualToString:@"answer"]) {
|
||||
// use the standard handler
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
#include "linphone/linphonecore.h"
|
||||
#include "bctoolbox/list.h"
|
||||
#import "OrderedDictionary.h"
|
||||
#import "ProviderDelegate.h"
|
||||
|
||||
#import "linphoneapp-Swift.h"
|
||||
|
||||
extern NSString *const LINPHONERC_APPLICATION_KEY;
|
||||
|
||||
|
|
@ -76,16 +77,6 @@ typedef struct _CallContext {
|
|||
bool_t cameraIsEnabled;
|
||||
} CallContext;
|
||||
|
||||
@interface LinphoneCallAppData :NSObject {
|
||||
@public
|
||||
bool_t batteryWarningShown;
|
||||
UILocalNotification *notification;
|
||||
NSMutableDictionary *userInfos;
|
||||
bool_t videoRequested; /*set when user has requested for video*/
|
||||
NSTimer* timer;
|
||||
};
|
||||
@end
|
||||
|
||||
typedef struct _LinphoneManagerSounds {
|
||||
SystemSoundID vibrate;
|
||||
} LinphoneManagerSounds;
|
||||
|
|
@ -132,15 +123,12 @@ typedef struct _LinphoneManagerSounds {
|
|||
- (void)configurePushTokenForProxyConfig: (LinphoneProxyConfig*)cfg;
|
||||
- (BOOL)popPushCallID:(NSString*) callId;
|
||||
- (void)acceptCallForCallId:(NSString*)callid;
|
||||
- (LinphoneCall *)callByCallId:(NSString *)call_id;
|
||||
- (void)cancelLocalNotifTimerForCallId:(NSString*)callid;
|
||||
- (void)startPushLongRunningTask:(NSString *)loc_key callId:(NSString *)callId;
|
||||
+ (BOOL)langageDirectionIsRTL;
|
||||
|
||||
- (void)refreshRegisters;
|
||||
|
||||
- (bool)allowSpeaker;
|
||||
|
||||
- (void)configureVbrCodecs;
|
||||
|
||||
+ (BOOL)copyFile:(NSString*)src destination:(NSString*)dst override:(BOOL)override ignore:(BOOL)ignore;
|
||||
|
|
@ -151,11 +139,8 @@ typedef struct _LinphoneManagerSounds {
|
|||
+ (NSString*)dataFile:(NSString*)file;
|
||||
+ (NSString*)cacheDirectory;
|
||||
|
||||
- (void)acceptCall:(LinphoneCall *)call evenWithVideo:(BOOL)video;
|
||||
- (void)send:(NSString *)replyText toChatRoom:(LinphoneChatRoom *)room;
|
||||
- (void)call:(const LinphoneAddress *)address;
|
||||
- (BOOL)doCall:(const LinphoneAddress *)iaddr;
|
||||
- (BOOL)doCallWithSas:(const LinphoneAddress *)iaddr isSas:(BOOL)isSas;
|
||||
|
||||
+(id)getMessageAppDataForKey:(NSString*)key inMessage:(LinphoneChatMessage*)msg;
|
||||
+(void)setValueInMessageAppData:(id)value forKey:(NSString*)key inMessage:(LinphoneChatMessage*)msg;
|
||||
|
|
@ -189,8 +174,6 @@ typedef struct _LinphoneManagerSounds {
|
|||
|
||||
- (void)shouldPresentLinkPopup;
|
||||
|
||||
- (void)setProviderDelegate:(ProviderDelegate *)del;
|
||||
|
||||
- (void) setLinphoneManagerAddressBookMap:(OrderedDictionary*) addressBook;
|
||||
- (OrderedDictionary*) getLinphoneManagerAddressBookMap;
|
||||
|
||||
|
|
@ -202,7 +185,9 @@ typedef struct _LinphoneManagerSounds {
|
|||
- (void)loadAvatar;
|
||||
- (void)migrationPerAccount;
|
||||
|
||||
@property ProviderDelegate *providerDelegate;
|
||||
- (void)setupGSMInteraction;
|
||||
- (void)setBluetoothEnabled:(BOOL)enable;
|
||||
- (BOOL)isCTCallCenterExist;
|
||||
|
||||
@property (readonly) BOOL isTesting;
|
||||
@property(readonly, strong) FastAddressBook *fastAddressBook;
|
||||
|
|
@ -214,10 +199,7 @@ typedef struct _LinphoneManagerSounds {
|
|||
@property(nonatomic, strong) NSData *pushNotificationToken;
|
||||
@property (readonly) LinphoneManagerSounds sounds;
|
||||
@property (readonly) NSMutableArray *logs;
|
||||
@property (nonatomic, assign) BOOL speakerBeforePause;
|
||||
@property (nonatomic, assign) BOOL speakerEnabled;
|
||||
@property (nonatomic, assign) BOOL bluetoothAvailable;
|
||||
@property (nonatomic, assign) BOOL bluetoothEnabled;
|
||||
@property (readonly) NSString* contactSipField;
|
||||
@property (readonly,copy) NSString* contactFilter;
|
||||
@property (copy) void (^silentPushCompletion)(UIBackgroundFetchResult);
|
||||
|
|
@ -225,7 +207,6 @@ typedef struct _LinphoneManagerSounds {
|
|||
@property (readonly) LpConfig *configDb;
|
||||
@property(readonly) InAppProductsManager *iapManager;
|
||||
@property(strong, nonatomic) NSMutableArray *fileTransferDelegates;
|
||||
@property BOOL nextCallIsTransfer;
|
||||
@property BOOL conf;
|
||||
@property NSDictionary *pushDict;
|
||||
@property(strong, nonatomic) OrderedDictionary *linphoneManagerAddressBookMap;
|
||||
|
|
|
|||
|
|
@ -95,19 +95,6 @@ extern void libmscodec2_init(MSFactory *factory);
|
|||
NSString *const kLinphoneOldChatDBFilename = @"chat_database.sqlite";
|
||||
NSString *const kLinphoneInternalChatDBFilename = @"linphone_chats.db";
|
||||
|
||||
@implementation LinphoneCallAppData
|
||||
- (id)init {
|
||||
if ((self = [super init])) {
|
||||
batteryWarningShown = FALSE;
|
||||
notification = nil;
|
||||
videoRequested = FALSE;
|
||||
userInfos = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface LinphoneManager ()
|
||||
@property(strong, nonatomic) AVAudioPlayer *messagePlayer;
|
||||
@end
|
||||
|
|
@ -255,9 +242,6 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
|
|||
_logs = [[NSMutableArray alloc] init];
|
||||
_pushDict = [[NSMutableDictionary alloc] init];
|
||||
_database = NULL;
|
||||
_speakerEnabled = FALSE;
|
||||
_speakerBeforePause = FALSE;
|
||||
_bluetoothEnabled = FALSE;
|
||||
_conf = FALSE;
|
||||
_fileTransferDelegates = [[NSMutableArray alloc] init];
|
||||
_linphoneManagerAddressBookMap = [[OrderedDictionary alloc] init];
|
||||
|
|
@ -576,340 +560,6 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)onCall:(LinphoneCall *)call StateChanged:(LinphoneCallState)state withMessage:(const char *)message {
|
||||
// Handling wrapper
|
||||
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (!data) {
|
||||
data = [[LinphoneCallAppData alloc] init];
|
||||
linphone_call_set_user_data(call, (void *)CFBridgingRetain(data));
|
||||
}
|
||||
|
||||
#pragma deploymate push "ignored-api-availability"
|
||||
if (_silentPushCompletion) {
|
||||
// we were woken up by a silent push. Call the completion handler with NEWDATA
|
||||
// so that the push is notified to the user
|
||||
LOGI(@"onCall - handler %p", _silentPushCompletion);
|
||||
_silentPushCompletion(UIBackgroundFetchResultNewData);
|
||||
_silentPushCompletion = nil;
|
||||
}
|
||||
#pragma deploymate pop
|
||||
|
||||
const LinphoneAddress *addr = linphone_call_get_remote_address(call);
|
||||
NSString *address = [FastAddressBook displayNameForAddress:addr];
|
||||
|
||||
if (state == LinphoneCallIncomingReceived) {
|
||||
LinphoneCallLog *callLog = linphone_call_get_call_log(call);
|
||||
NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog)];
|
||||
int index = [(NSNumber *)[_pushDict objectForKey:callId] intValue] - 1;
|
||||
LOGI(@"Decrementing index of long running task for call id : %@ with index : %d", callId, index);
|
||||
[_pushDict setValue:[NSNumber numberWithInt:index] forKey:callId];
|
||||
BOOL need_bg_task = FALSE;
|
||||
for (NSString *key in [_pushDict allKeys]) {
|
||||
int value = [(NSNumber *)[_pushDict objectForKey:key] intValue];
|
||||
if (value > 0) {
|
||||
need_bg_task = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pushBgTaskCall && !need_bg_task) {
|
||||
LOGI(@"Call received, stopping call background task for call-id [%@]", callId);
|
||||
[[UIApplication sharedApplication] endBackgroundTask:pushBgTaskCall];
|
||||
pushBgTaskCall = 0;
|
||||
}
|
||||
/*first step is to re-enable ctcall center*/
|
||||
CTCallCenter *lCTCallCenter = [[CTCallCenter alloc] init];
|
||||
|
||||
/*should we reject this call ?*/
|
||||
if ([lCTCallCenter currentCalls] != nil &&
|
||||
floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
char *tmp = linphone_call_get_remote_address_as_string(call);
|
||||
if (tmp) {
|
||||
LOGI(@"Mobile call ongoing... rejecting call from [%s]", tmp);
|
||||
ms_free(tmp);
|
||||
}
|
||||
linphone_call_decline(call, LinphoneReasonBusy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if (call && (linphone_core_get_calls_nb(LC) < 2)) {
|
||||
if ([LinphoneManager.instance lpConfigBoolForKey:@"accept_early_media" inSection:@"app"] && [LinphoneManager.instance lpConfigBoolForKey:@"pref_accept_early_media"]) {
|
||||
[PhoneMainView.instance displayIncomingCall:call];
|
||||
} else {
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
NSUUID *uuid = [NSUUID UUID];
|
||||
[LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid];
|
||||
[LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId];
|
||||
BOOL video = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive &&
|
||||
linphone_video_activation_policy_get_automatically_accept(linphone_core_get_video_activation_policy(LC)) &&
|
||||
linphone_call_params_video_enabled(linphone_call_get_remote_params(call)));
|
||||
[LinphoneManager.instance.providerDelegate reportIncomingCall:call withUUID:uuid handle:address video:video];
|
||||
#else
|
||||
[PhoneMainView.instance displayIncomingCall:call];
|
||||
#endif
|
||||
}
|
||||
} else if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
|
||||
// Create a UNNotification
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
content.title = NSLocalizedString(@"Incoming call", nil);
|
||||
content.body = address;
|
||||
content.sound = [UNNotificationSound soundNamed:@"notes_of_the_optimistic.caf"];
|
||||
content.categoryIdentifier = @"call_cat";
|
||||
content.userInfo = @{ @"CallId" : callId };
|
||||
UNNotificationRequest *req =
|
||||
[UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req
|
||||
withCompletionHandler:^(NSError *err){
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
|
||||
// if (![LinphoneManager.instance popPushCallID:callId]) {
|
||||
// case where a remote notification is not already received
|
||||
// Create a new local notification
|
||||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
UIMutableUserNotificationAction *answer = [[UIMutableUserNotificationAction alloc] init];
|
||||
answer.identifier = @"answer";
|
||||
answer.title = NSLocalizedString(@"Answer", nil);
|
||||
answer.activationMode = UIUserNotificationActivationModeForeground;
|
||||
answer.destructive = NO;
|
||||
answer.authenticationRequired = YES;
|
||||
|
||||
UIMutableUserNotificationAction *decline = [[UIMutableUserNotificationAction alloc] init];
|
||||
decline.identifier = @"decline";
|
||||
decline.title = NSLocalizedString(@"Decline", nil);
|
||||
decline.activationMode = UIUserNotificationActivationModeBackground;
|
||||
decline.destructive = YES;
|
||||
decline.authenticationRequired = NO;
|
||||
|
||||
NSArray *callactions = @[ decline, answer ];
|
||||
|
||||
UIMutableUserNotificationCategory *callcat = [[UIMutableUserNotificationCategory alloc] init];
|
||||
callcat.identifier = @"incoming_call";
|
||||
[callcat setActions:callactions forContext:UIUserNotificationActionContextDefault];
|
||||
[callcat setActions:callactions forContext:UIUserNotificationActionContextMinimal];
|
||||
|
||||
NSSet *categories = [NSSet setWithObjects:callcat, nil];
|
||||
|
||||
UIUserNotificationSettings *set = [UIUserNotificationSettings
|
||||
settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge |
|
||||
UIUserNotificationTypeSound)
|
||||
categories:categories];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:set];
|
||||
data->notification = [[UILocalNotification alloc] init];
|
||||
if (data->notification) {
|
||||
// iOS8 doesn't need the timer trick for the local notification.
|
||||
data->notification.category = @"incoming_call";
|
||||
if ([[UIDevice currentDevice].systemVersion floatValue] >= 8 &&
|
||||
[self lpConfigBoolForKey:@"repeat_call_notification"] == NO) {
|
||||
NSString *ring = ([LinphoneManager bundleFile:[self lpConfigStringForKey:@"local_ring"
|
||||
inSection:@"sound"]
|
||||
.lastPathComponent]
|
||||
?: [LinphoneManager bundleFile:@"notes_of_the_optimistic.caf"])
|
||||
.lastPathComponent;
|
||||
data->notification.soundName = ring;
|
||||
} else {
|
||||
data->notification.soundName = @"shortring.caf";
|
||||
data->timer = [NSTimer scheduledTimerWithTimeInterval:5
|
||||
target:self
|
||||
selector:@selector(localNotifContinue:)
|
||||
userInfo:data->notification
|
||||
repeats:TRUE];
|
||||
}
|
||||
|
||||
data->notification.repeatInterval = 0;
|
||||
|
||||
data->notification.alertBody =
|
||||
[NSString stringWithFormat:NSLocalizedString(@"IC_MSG", nil), address];
|
||||
// data->notification.alertAction = NSLocalizedString(@"Answer", nil);
|
||||
data->notification.userInfo = @{ @"callId" : callId, @"timer" : [NSNumber numberWithInt:1] };
|
||||
data->notification.applicationIconBadgeNumber = 1;
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
LOGI([app currentUserNotificationSettings].description);
|
||||
[app presentLocalNotificationNow:data->notification];
|
||||
|
||||
if (!incallBgTask) {
|
||||
incallBgTask =
|
||||
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
||||
LOGW(@"Call cannot ring any more, too late");
|
||||
[[UIApplication sharedApplication] endBackgroundTask:incallBgTask];
|
||||
incallBgTask = 0;
|
||||
}];
|
||||
|
||||
if (data->timer) {
|
||||
[[NSRunLoop currentRunLoop] addTimer:data->timer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
static BOOL speaker_already_enabled = FALSE;
|
||||
|
||||
// Disable speaker when no more call
|
||||
if ((state == LinphoneCallEnd || state == LinphoneCallError)) {
|
||||
[HistoryListTableView saveDataToUserDefaults];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE];
|
||||
speaker_already_enabled = FALSE;
|
||||
if (linphone_core_get_calls_nb(theLinphoneCore) == 0) {
|
||||
[self setSpeakerEnabled:FALSE];
|
||||
[self removeCTCallCenterCb];
|
||||
// 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
|
||||
_bluetoothEnabled = FALSE;
|
||||
}
|
||||
|
||||
if (incallBgTask) {
|
||||
[[UIApplication sharedApplication] endBackgroundTask:incallBgTask];
|
||||
incallBgTask = 0;
|
||||
}
|
||||
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
|
||||
if (data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
LinphoneCallLog *UNlog = linphone_call_get_call_log(call);
|
||||
if ((UNlog == NULL
|
||||
|| linphone_call_log_get_status(UNlog) == LinphoneCallMissed
|
||||
|| linphone_call_log_get_status(UNlog) == LinphoneCallAborted
|
||||
|| linphone_call_log_get_status(UNlog) == LinphoneCallEarlyAborted)) {
|
||||
UNMutableNotificationContent *missed_content = [[UNMutableNotificationContent alloc] init];
|
||||
missed_content.title = NSLocalizedString(@"Missed call", nil);
|
||||
missed_content.body = address;
|
||||
UNNotificationRequest *missed_req = [UNNotificationRequest requestWithIdentifier:@"call_request"
|
||||
content:missed_content
|
||||
trigger:NULL];
|
||||
[UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:missed_req
|
||||
withCompletionHandler:^(NSError *_Nullable error)
|
||||
{if (error) LOGD(@"Error while adding notification request : %@", error.description);}];
|
||||
}
|
||||
}
|
||||
LinphoneCallLog *callLog2 = linphone_call_get_call_log(call);
|
||||
const char *call_id2 = linphone_call_log_get_call_id(callLog2);
|
||||
NSString *callId2 = call_id2
|
||||
? [NSString stringWithUTF8String:call_id2]
|
||||
: @"";
|
||||
NSUUID *uuid = (NSUUID *)[self.providerDelegate.uuids objectForKey:callId2];
|
||||
if (uuid) {
|
||||
LinphoneCall *callKit_call = (LinphoneCall *)linphone_core_get_calls(LC)
|
||||
? linphone_core_get_calls(LC)->data
|
||||
: NULL;
|
||||
const char *callKit_callId = callKit_call
|
||||
? linphone_call_log_get_call_id(linphone_call_get_call_log(callKit_call))
|
||||
: NULL;
|
||||
if (callKit_callId && !_conf) {
|
||||
// Create a CallKit call because there's not !
|
||||
NSString *callKit_callIdNS = [NSString stringWithUTF8String:callKit_callId];
|
||||
NSUUID *callKit_uuid = [NSUUID UUID];
|
||||
[LinphoneManager.instance.providerDelegate.uuids setObject:callKit_uuid forKey:callKit_callIdNS];
|
||||
[LinphoneManager.instance.providerDelegate.calls setObject:callKit_callIdNS forKey:callKit_uuid];
|
||||
NSString *address = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(callKit_call)];
|
||||
CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:address];
|
||||
CXStartCallAction *act = [[CXStartCallAction alloc] initWithCallUUID:callKit_uuid handle:handle];
|
||||
CXTransaction *tr = [[CXTransaction alloc] initWithAction:act];
|
||||
[LinphoneManager.instance.providerDelegate.controller requestTransaction:tr completion:^(NSError *err){}];
|
||||
[LinphoneManager.instance.providerDelegate.provider reportOutgoingCallWithUUID:callKit_uuid startedConnectingAtDate:nil];
|
||||
[LinphoneManager.instance.providerDelegate.provider reportOutgoingCallWithUUID:callKit_uuid connectedAtDate:nil];
|
||||
}
|
||||
|
||||
CXEndCallAction *act = [[CXEndCallAction alloc] initWithCallUUID:uuid];
|
||||
CXTransaction *tr = [[CXTransaction alloc] initWithAction:act];
|
||||
[LinphoneManager.instance.providerDelegate.controller requestTransaction:tr completion:^(NSError *err){}];
|
||||
LOGI(@"CallKit - clearing CK as call ended on uuid %@",uuid);
|
||||
[LinphoneManager.instance.providerDelegate.provider reportOutgoingCallWithUUID:uuid connectedAtDate:[NSDate date]];
|
||||
[self.providerDelegate.uuids removeObjectForKey:callId2];
|
||||
[self.providerDelegate.calls removeObjectForKey:uuid];
|
||||
[self.providerDelegate.provider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:(state == LinphoneCallError ? CXCallEndedReasonFailed : CXCallEndedReasonRemoteEnded)];
|
||||
} else { // Can happen when Call-ID changes (Replaces header)
|
||||
if (linphone_core_get_calls_nb(LC) ==0) { // Need to clear all CK calls
|
||||
for (NSUUID *myUuid in self.providerDelegate.calls) {
|
||||
[self.providerDelegate.provider reportCallWithUUID:myUuid
|
||||
endedAtDate:NULL
|
||||
reason:(state == LinphoneCallError
|
||||
? CXCallEndedReasonFailed
|
||||
: CXCallEndedReasonRemoteEnded)];
|
||||
}
|
||||
[self.providerDelegate.uuids removeAllObjects];
|
||||
[self.providerDelegate.calls removeAllObjects];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (data != nil && data->notification != nil) {
|
||||
LinphoneCallLog *log = linphone_call_get_call_log(call);
|
||||
// cancel local notif if needed
|
||||
if (data->timer) {
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
}
|
||||
[[UIApplication sharedApplication] cancelLocalNotification:data->notification];
|
||||
data->notification = nil;
|
||||
|
||||
if (log == NULL || linphone_call_log_get_status(log) == LinphoneCallMissed) {
|
||||
UILocalNotification *notification = [[UILocalNotification alloc] init];
|
||||
notification.repeatInterval = 0;
|
||||
notification.alertBody = [NSString stringWithFormat:
|
||||
NSLocalizedString(@"You missed a call from %@",nil), address];
|
||||
notification.alertAction = NSLocalizedString(@"Show", nil);
|
||||
notification.userInfo = [NSDictionary dictionaryWithObject: [NSString stringWithUTF8String:linphone_call_log_get_call_id(log)]
|
||||
forKey:@"callLog"];
|
||||
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state == LinphoneCallError)
|
||||
[PhoneMainView.instance popCurrentView];
|
||||
}
|
||||
if (state == LinphoneCallReleased) {
|
||||
if (data != NULL) {
|
||||
linphone_call_set_user_data(call, NULL);
|
||||
CFBridgingRelease((__bridge CFTypeRef)(data));
|
||||
}
|
||||
}
|
||||
// Enable speaker when video
|
||||
if (state == LinphoneCallIncomingReceived || state == LinphoneCallOutgoingInit ||
|
||||
state == LinphoneCallConnected || state == LinphoneCallStreamsRunning) {
|
||||
if (linphone_call_params_video_enabled( linphone_call_get_current_params(call)) && !speaker_already_enabled && !_bluetoothEnabled) {
|
||||
[self setSpeakerEnabled:TRUE];
|
||||
speaker_already_enabled = TRUE;
|
||||
}
|
||||
}
|
||||
if (state == LinphoneCallStreamsRunning) {
|
||||
if (_speakerBeforePause) {
|
||||
_speakerBeforePause = FALSE;
|
||||
[self setSpeakerEnabled:TRUE];
|
||||
speaker_already_enabled = TRUE;
|
||||
}
|
||||
}
|
||||
if (state == LinphoneCallConnected && !mCallCenter) {
|
||||
/*only register CT call center CB for connected call*/
|
||||
[self setupGSMInteraction];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:!(_speakerEnabled || _bluetoothEnabled)];
|
||||
}
|
||||
|
||||
// Post event
|
||||
NSDictionary *dict = @{@"call" : [NSValue valueWithPointer:call],
|
||||
@"state" : [NSNumber numberWithInt:state],
|
||||
@"message" : [NSString stringWithUTF8String:message]};
|
||||
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate
|
||||
object:self
|
||||
userInfo:dict];
|
||||
}
|
||||
|
||||
static void linphone_iphone_call_state(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state,
|
||||
const char *message) {
|
||||
[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onCall:call StateChanged:state withMessage:message];
|
||||
}
|
||||
|
||||
#pragma mark - Transfert State Functions
|
||||
|
||||
static void linphone_iphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state) {
|
||||
|
|
@ -1563,15 +1213,6 @@ static void linphone_iphone_is_composing_received(LinphoneCore *lc, LinphoneChat
|
|||
[[UIApplication sharedApplication] endBackgroundTask:coreIterateTaskId];
|
||||
}
|
||||
|
||||
- (void)audioSessionInterrupted:(NSNotification *)notification {
|
||||
int interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue];
|
||||
if (interruptionType == AVAudioSessionInterruptionTypeBegan) {
|
||||
[self beginInterruption];
|
||||
} else if (interruptionType == AVAudioSessionInterruptionTypeEnded) {
|
||||
[self endInterruption];
|
||||
}
|
||||
}
|
||||
|
||||
/** Should be called once per linphone_core_new() */
|
||||
- (void)finishCoreConfiguration {
|
||||
//Force keep alive to workaround push notif on chat message
|
||||
|
|
@ -1663,7 +1304,6 @@ static BOOL libStarted = FALSE;
|
|||
|
||||
// create linphone core
|
||||
[self createLinphoneCore];
|
||||
[self.providerDelegate config];
|
||||
_iapManager = [[InAppProductsManager alloc] init];
|
||||
|
||||
// - Security fix - remove multi transport migration, because it enables tcp or udp, if by factoring settings only
|
||||
|
|
@ -1786,7 +1426,6 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
|
|||
|
||||
LinphoneFactory *factory = linphone_factory_get();
|
||||
LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(factory);
|
||||
linphone_core_cbs_set_call_state_changed(cbs, linphone_iphone_call_state);
|
||||
linphone_core_cbs_set_registration_state_changed(cbs,linphone_iphone_registration_state);
|
||||
linphone_core_cbs_set_notify_presence_received_for_uri_or_tel(cbs, linphone_iphone_notify_presence_received_for_uri_or_tel);
|
||||
linphone_core_cbs_set_authentication_requested(cbs, linphone_iphone_popup_password_request);
|
||||
|
|
@ -1805,6 +1444,10 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
|
|||
|
||||
theLinphoneCore = linphone_factory_create_core_with_config_3(factory, _configDb, NULL);
|
||||
linphone_core_add_callbacks(theLinphoneCore, cbs);
|
||||
|
||||
// Add call changed callback by swift
|
||||
[CallManager.instance configCallManagerWithCore:theLinphoneCore db:_configDb];
|
||||
|
||||
linphone_core_start(theLinphoneCore);
|
||||
|
||||
// Let the core handle cbs
|
||||
|
|
@ -1832,10 +1475,6 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
|
|||
(or skipped).
|
||||
Wait for this to finish the code configuration */
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(audioSessionInterrupted:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:nil];
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(globalStateChangedNotificationHandler:)
|
||||
name:kLinphoneGlobalStateUpdate
|
||||
|
|
@ -1896,31 +1535,10 @@ static int comp_call_id(const LinphoneCall *call, const char *callid) {
|
|||
return strcmp(linphone_call_log_get_call_id(linphone_call_get_call_log(call)), callid);
|
||||
}
|
||||
|
||||
- (LinphoneCall *)callByCallId:(NSString *)call_id {
|
||||
const bctbx_list_t *calls = linphone_core_get_calls(theLinphoneCore);
|
||||
if (!calls || !call_id) {
|
||||
return NULL;
|
||||
}
|
||||
bctbx_list_t *call_tmp = bctbx_list_find_custom(calls, (bctbx_compare_func)comp_call_id, [call_id UTF8String]);
|
||||
if (!call_tmp) {
|
||||
return NULL;
|
||||
}
|
||||
LinphoneCall *call = (LinphoneCall *)call_tmp->data;
|
||||
return call;
|
||||
}
|
||||
|
||||
- (void)cancelLocalNotifTimerForCallId:(NSString *)callid {
|
||||
// first, make sure this callid is not already involved in a call
|
||||
const bctbx_list_t *calls = linphone_core_get_calls(theLinphoneCore);
|
||||
bctbx_list_t *call = bctbx_list_find_custom(calls, (bctbx_compare_func)comp_call_id, [callid UTF8String]);
|
||||
if (call != NULL) {
|
||||
LinphoneCallAppData *data =
|
||||
(__bridge LinphoneCallAppData *)(linphone_call_get_user_data((LinphoneCall *)call->data));
|
||||
if (data->timer)
|
||||
[data->timer invalidate];
|
||||
data->timer = nil;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)acceptCallForCallId:(NSString *)callid {
|
||||
|
|
@ -1930,7 +1548,7 @@ static int comp_call_id(const LinphoneCall *call, const char *callid) {
|
|||
if (call != NULL) {
|
||||
const LinphoneVideoPolicy *video_policy = linphone_core_get_video_policy(theLinphoneCore);
|
||||
bool with_video = video_policy->automatically_accept;
|
||||
[self acceptCall:(LinphoneCall *)call->data evenWithVideo:with_video];
|
||||
[CallManager.instance acceptCallWithCall:(LinphoneCall *)call->data hasVideo:with_video];
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
|
@ -2198,19 +1816,6 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
[self enableProxyPublish:YES];
|
||||
}
|
||||
|
||||
- (void)beginInterruption {
|
||||
LinphoneCall *c = linphone_core_get_current_call(theLinphoneCore);
|
||||
LOGI(@"Sound interruption detected!");
|
||||
if (c && linphone_call_get_state(c) == LinphoneCallStreamsRunning) {
|
||||
_speakerBeforePause = _speakerEnabled;
|
||||
linphone_call_pause(c);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)endInterruption {
|
||||
LOGI(@"Sound interruption ended!");
|
||||
}
|
||||
|
||||
- (void)refreshRegisters {
|
||||
linphone_core_refresh_registers(theLinphoneCore); // just to make sure REGISTRATION is up to date
|
||||
}
|
||||
|
|
@ -2278,21 +1883,6 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
}
|
||||
#pragma mark - Audio route Functions
|
||||
|
||||
- (bool)allowSpeaker {
|
||||
if (IPAD)
|
||||
return true;
|
||||
|
||||
bool allow = true;
|
||||
AVAudioSessionRouteDescription *newRoute = [AVAudioSession sharedInstance].currentRoute;
|
||||
if (newRoute && newRoute.outputs.count > 0) {
|
||||
NSString *route = newRoute.outputs[0].portType;
|
||||
allow = !([route isEqualToString:AVAudioSessionPortLineOut] ||
|
||||
[route isEqualToString:AVAudioSessionPortHeadphones] ||
|
||||
[[AudioHelper bluetoothRoutes] containsObject:route]);
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
- (void)audioRouteChangeListenerCallback:(NSNotification *)notif {
|
||||
if (IPAD)
|
||||
return;
|
||||
|
|
@ -2311,12 +1901,12 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
NSString *route = newRoute.outputs[0].portType;
|
||||
LOGI(@"Current audio route is [%s]", [route UTF8String]);
|
||||
|
||||
_speakerEnabled = [route isEqualToString:AVAudioSessionPortBuiltInSpeaker];
|
||||
if (([[AudioHelper bluetoothRoutes] containsObject:route]) && !_speakerEnabled) {
|
||||
CallManager.instance.speakerEnabled = [route isEqualToString:AVAudioSessionPortBuiltInSpeaker];
|
||||
if (([[AudioHelper bluetoothRoutes] containsObject:route]) && !CallManager.instance.speakerEnabled) {
|
||||
_bluetoothAvailable = TRUE;
|
||||
_bluetoothEnabled = TRUE;
|
||||
CallManager.instance.bluetoothEnabled = TRUE;
|
||||
} else
|
||||
_bluetoothEnabled = FALSE;
|
||||
CallManager.instance.bluetoothEnabled = FALSE;
|
||||
|
||||
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:_bluetoothAvailable], @"available", nil];
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneBluetoothAvailabilityUpdate
|
||||
|
|
@ -2325,77 +1915,30 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setSpeakerEnabled:(BOOL)enable {
|
||||
_speakerEnabled = enable;
|
||||
NSError *err = nil;
|
||||
|
||||
if (enable && [self allowSpeaker]) {
|
||||
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&err];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE];
|
||||
_bluetoothEnabled = FALSE;
|
||||
} else {
|
||||
AVAudioSessionPortDescription *builtinPort = [AudioHelper builtinAudioDevice];
|
||||
[[AVAudioSession sharedInstance] setPreferredInput:builtinPort error:&err];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:(linphone_core_get_calls_nb(LC) > 0)];
|
||||
}
|
||||
|
||||
if (err) {
|
||||
LOGE(@"Failed to change audio route: err %@", err.localizedDescription);
|
||||
err = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBluetoothEnabled:(BOOL)enable {
|
||||
if (_bluetoothAvailable) {
|
||||
// The change of route will be done in setSpeakerEnabled
|
||||
_bluetoothEnabled = enable;
|
||||
if (_bluetoothEnabled) {
|
||||
CallManager.instance.bluetoothEnabled = enable;
|
||||
if (CallManager.instance.bluetoothEnabled) {
|
||||
NSError *err = nil;
|
||||
AVAudioSessionPortDescription *_bluetoothPort = [AudioHelper bluetoothAudioDevice];
|
||||
[[AVAudioSession sharedInstance] setPreferredInput:_bluetoothPort error:&err];
|
||||
// if setting bluetooth failed, it must be because the device is not available
|
||||
// anymore (disconnected), so deactivate bluetooth.
|
||||
if (err) {
|
||||
_bluetoothEnabled = FALSE;
|
||||
CallManager.instance.bluetoothEnabled = FALSE;
|
||||
LOGE(@"Failed to enable bluetooth: err %@", err.localizedDescription);
|
||||
err = nil;
|
||||
} else {
|
||||
_speakerEnabled = FALSE;
|
||||
CallManager.instance.speakerEnabled = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[self setSpeakerEnabled:_speakerEnabled];
|
||||
[CallManager.instance setSpeakerEnabled:CallManager.instance.speakerEnabled];
|
||||
}
|
||||
|
||||
#pragma mark - Call Functions
|
||||
|
||||
- (void)acceptCall:(LinphoneCall *)call evenWithVideo:(BOOL)video {
|
||||
LinphoneCallParams *lcallParams = linphone_core_create_call_params(theLinphoneCore, call);
|
||||
if (!lcallParams) {
|
||||
LOGW(@"Could not create call parameters for %p, call has probably already ended.", call);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self lpConfigBoolForKey:@"edge_opt_preference"]) {
|
||||
bool low_bandwidth = self.network == network_2g;
|
||||
if (low_bandwidth) {
|
||||
LOGI(@"Low bandwidth mode");
|
||||
}
|
||||
linphone_call_params_enable_low_bandwidth(lcallParams, low_bandwidth);
|
||||
}
|
||||
linphone_call_params_enable_video(lcallParams, video);
|
||||
|
||||
//We set the record file name here because we can't do it after the call is started.
|
||||
NSString *writablePath = [LinphoneUtils recordingFilePathFromCall:linphone_call_log_get_from_address(linphone_call_get_call_log(call))];
|
||||
LOGD(@"record file path: %@\n", writablePath);
|
||||
|
||||
linphone_call_params_set_record_file(lcallParams, [writablePath cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
|
||||
linphone_call_accept_with_params(call, lcallParams);
|
||||
linphone_call_params_unref(lcallParams);
|
||||
}
|
||||
|
||||
- (void)send:(NSString *)replyText toChatRoom:(LinphoneChatRoom *)room {
|
||||
LinphoneChatMessage *msg = linphone_chat_room_create_message(room, replyText.UTF8String);
|
||||
linphone_chat_message_send(msg);
|
||||
|
|
@ -2443,81 +1986,8 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (linphone_core_get_calls_nb(theLinphoneCore) < 1 &&
|
||||
floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max &&
|
||||
self.providerDelegate.callKitCalls < 1) {
|
||||
self.providerDelegate.callKitCalls++;
|
||||
NSUUID *uuid = [NSUUID UUID];
|
||||
[LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:@""];
|
||||
[LinphoneManager.instance.providerDelegate.calls setObject:@"" forKey:uuid];
|
||||
LinphoneManager.instance.providerDelegate.pendingAddr = linphone_address_clone(iaddr);
|
||||
NSString *address = [FastAddressBook displayNameForAddress:iaddr];
|
||||
CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:address];
|
||||
CXStartCallAction *act = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:handle];
|
||||
CXTransaction *tr = [[CXTransaction alloc] initWithAction:act];
|
||||
[LinphoneManager.instance.providerDelegate.controller requestTransaction:tr
|
||||
completion:^(NSError *err){
|
||||
}];
|
||||
} else {
|
||||
[self doCall:iaddr];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)doCall:(const LinphoneAddress *)iaddr {
|
||||
return [self doCallWithSas:iaddr isSas:false];
|
||||
}
|
||||
|
||||
- (BOOL)doCallWithSas:(const LinphoneAddress *)iaddr isSas:(BOOL)isSas {
|
||||
LinphoneAddress *addr = linphone_address_clone(iaddr);
|
||||
NSString *displayName = [FastAddressBook displayNameForAddress:addr];
|
||||
|
||||
// Finally we can make the call
|
||||
LinphoneCallParams *lcallParams = linphone_core_create_call_params(theLinphoneCore, NULL);
|
||||
if ([self lpConfigBoolForKey:@"edge_opt_preference"] && (self.network == network_2g)) {
|
||||
LOGI(@"Enabling low bandwidth mode");
|
||||
linphone_call_params_enable_low_bandwidth(lcallParams, YES);
|
||||
}
|
||||
|
||||
if (displayName != nil) {
|
||||
linphone_address_set_display_name(addr, displayName.UTF8String);
|
||||
}
|
||||
if ([LinphoneManager.instance lpConfigBoolForKey:@"override_domain_with_default_one"]) {
|
||||
linphone_address_set_domain(
|
||||
addr, [[LinphoneManager.instance lpConfigStringForKey:@"domain" inSection:@"assistant"] UTF8String]);
|
||||
}
|
||||
|
||||
LinphoneCall *call;
|
||||
if (LinphoneManager.instance.nextCallIsTransfer) {
|
||||
char *caddr = linphone_address_as_string(addr);
|
||||
call = linphone_core_get_current_call(theLinphoneCore);
|
||||
linphone_call_transfer(call, caddr);
|
||||
LinphoneManager.instance.nextCallIsTransfer = NO;
|
||||
ms_free(caddr);
|
||||
} else {
|
||||
//We set the record file name here because we can't do it after the call is started.
|
||||
NSString *writablePath = [LinphoneUtils recordingFilePathFromCall:addr];
|
||||
LOGD(@"record file path: %@\n", writablePath);
|
||||
linphone_call_params_set_record_file(lcallParams, [writablePath cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
if (isSas)
|
||||
linphone_call_params_set_media_encryption(lcallParams, LinphoneMediaEncryptionZRTP);
|
||||
call = linphone_core_invite_address_with_params(theLinphoneCore, addr, lcallParams);
|
||||
if (call) {
|
||||
// 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.
|
||||
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (data == nil) {
|
||||
LOGE(@"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 = linphone_call_params_video_enabled(lcallParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
linphone_address_destroy(addr);
|
||||
linphone_call_params_destroy(lcallParams);
|
||||
|
||||
return TRUE;
|
||||
// For OutgoingCall, show CallOutgoingView
|
||||
[CallManager.instance startCallWithAddr:iaddr isSas:FALSE];
|
||||
}
|
||||
|
||||
#pragma mark - Property Functions
|
||||
|
|
@ -2835,6 +2305,10 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
mCallCenter = nil;
|
||||
}
|
||||
|
||||
- (BOOL)isCTCallCenterExist {
|
||||
return mCallCenter != nil;
|
||||
}
|
||||
|
||||
- (void)setupGSMInteraction {
|
||||
|
||||
[self removeCTCallCenterCb];
|
||||
|
|
@ -2858,7 +2332,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
if ([ct currentCalls] != nil) {
|
||||
if (call) {
|
||||
LOGI(@"Pausing SIP call because GSM call");
|
||||
_speakerBeforePause = _speakerEnabled;
|
||||
CallManager.instance.speakerBeforePause = CallManager.instance.speakerEnabled;
|
||||
linphone_call_pause(call);
|
||||
[self startCallPausedLongRunningTask];
|
||||
} else if (linphone_core_is_in_conference(theLinphoneCore)) {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
[self setImage:[UIImage imageNamed:@"call_audio_start_disabled.png"] forState:UIControlStateDisabled];
|
||||
}
|
||||
|
||||
if (LinphoneManager.instance.nextCallIsTransfer) {
|
||||
if (CallManager.nextCallIsTransfer) {
|
||||
[self setImage:[UIImage imageNamed:@"call_transfer_default.png"] forState:UIControlStateNormal];
|
||||
[self setImage:[UIImage imageNamed:@"call_transfer_disabled.png"] forState:UIControlStateDisabled];
|
||||
} else if (linphone_core_get_calls_nb(LC) > 0) {
|
||||
|
|
|
|||
|
|
@ -55,10 +55,7 @@
|
|||
|
||||
- (IBAction)onSecurityCallClick:(id)sender {
|
||||
const LinphoneAddress *addr = linphone_participant_device_get_address(_device);
|
||||
if (addr)
|
||||
[LinphoneManager.instance doCallWithSas:addr isSas:TRUE];
|
||||
else
|
||||
LOGE(@"CallKit : No call address");
|
||||
[CallManager.instance startCallWithAddr:(LinphoneAddress *)addr isSas:TRUE];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -60,10 +60,7 @@
|
|||
- (IBAction)onSecurityCallClick:(id)sender {
|
||||
LinphoneParticipantDevice *device = (LinphoneParticipantDevice *)bctbx_list_nth_data(_devices, 0);
|
||||
const LinphoneAddress *addr = linphone_participant_device_get_address(device);
|
||||
if (addr)
|
||||
[LinphoneManager.instance doCallWithSas:addr isSas:TRUE];
|
||||
else
|
||||
LOGE(@"CallKit : No call address");
|
||||
[CallManager.instance startCallWithAddr:(LinphoneAddress *)addr isSas:TRUE];
|
||||
}
|
||||
|
||||
#pragma mark - TableView
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@
|
|||
switch (type) {
|
||||
case UIPauseButtonType_Call: {
|
||||
if (call != nil) {
|
||||
LinphoneManager.instance.speakerBeforePause = LinphoneManager.instance.speakerEnabled;
|
||||
CallManager.instance.speakerBeforePause = CallManager.instance.speakerEnabled;
|
||||
linphone_call_pause(call);
|
||||
} else {
|
||||
LOGW(@"Cannot toggle pause buttton, because no current call");
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
case UIPauseButtonType_CurrentCall: {
|
||||
LinphoneCall *currentCall = [UIPauseButton getCall];
|
||||
if (currentCall != nil) {
|
||||
LinphoneManager.instance.speakerBeforePause = LinphoneManager.instance.speakerEnabled;
|
||||
CallManager.instance.speakerBeforePause = CallManager.instance.speakerEnabled;
|
||||
linphone_call_pause(currentCall);
|
||||
} else {
|
||||
LOGW(@"Cannot toggle pause buttton, because no current call");
|
||||
|
|
@ -120,18 +120,6 @@
|
|||
break;
|
||||
}
|
||||
case UIPauseButtonType_Conference: {
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
|
||||
NSString *key = (NSString *)[LinphoneManager.instance.providerDelegate.uuids allKeys][0];
|
||||
NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids objectForKey:key];
|
||||
if (!uuid) {
|
||||
return;
|
||||
}
|
||||
CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO];
|
||||
CXTransaction *tr = [[CXTransaction alloc] initWithAction:act];
|
||||
[LinphoneManager.instance.providerDelegate.controller requestTransaction:tr
|
||||
completion:^(NSError *err){
|
||||
}];
|
||||
}
|
||||
linphone_core_enter_conference(LC);
|
||||
// Fake event
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
|
||||
|
|
|
|||
|
|
@ -47,16 +47,16 @@ INIT_WITH_COMMON_CF {
|
|||
}
|
||||
|
||||
- (void)onOn {
|
||||
[LinphoneManager.instance setSpeakerEnabled:TRUE];
|
||||
[CallManager.instance setSpeakerEnabled:TRUE];
|
||||
}
|
||||
|
||||
- (void)onOff {
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
}
|
||||
|
||||
- (bool)onUpdate {
|
||||
self.enabled = [LinphoneManager.instance allowSpeaker];
|
||||
return [LinphoneManager.instance speakerEnabled];
|
||||
self.enabled = [CallManager.instance allowSpeaker];
|
||||
return CallManager.instance.speakerEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -42,9 +42,10 @@ INIT_WITH_COMMON_CF {
|
|||
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call) {
|
||||
LinphoneCallAppData *callAppData = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
callAppData->videoRequested =
|
||||
TRUE; /* will be used later to notify user if video was not activated because of the linphone core*/
|
||||
NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
CallAppData *data = [CallManager.instance getAppDataWithCallId:callId];
|
||||
data.videoRequested = TRUE;/* will be used later to notify user if video was not activated because of the linphone core*/
|
||||
[CallManager.instance setAppDataWithCallId:callId appData:data];
|
||||
LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
|
||||
linphone_call_params_enable_video(call_params, TRUE);
|
||||
linphone_call_update(call, call_params);
|
||||
|
|
@ -58,7 +59,7 @@ INIT_WITH_COMMON_CF {
|
|||
|
||||
if (!linphone_core_video_display_enabled(LC))
|
||||
return;
|
||||
[LinphoneManager.instance setSpeakerEnabled:FALSE];
|
||||
[CallManager.instance setSpeakerEnabled:FALSE];
|
||||
[self setEnabled:FALSE];
|
||||
[waitView startAnimating];
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "LinphoneManager.h"
|
||||
#import "linphone/core.h"
|
||||
|
||||
#define LOGV(level, ...) [Log log:level file:__FILE__ line:__LINE__ format:__VA_ARGS__]
|
||||
#define LOGD(...) LOGV(ORTP_DEBUG, __VA_ARGS__)
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
+ (void)log:(OrtpLogLevel)severity file:(const char *)file line:(int)line format:(NSString *)format, ...;
|
||||
+ (void)enableLogs:(OrtpLogLevel)level;
|
||||
+ (void)directLog:(OrtpLogLevel)level text:(NSString *)text;
|
||||
|
||||
void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args);
|
||||
@end
|
||||
|
|
@ -361,6 +361,10 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
|
||||
switch (state) {
|
||||
case LinphoneCallIncomingReceived:
|
||||
if (!CallManager.callKitEnabled) {
|
||||
[self displayIncomingCall:call];
|
||||
}
|
||||
break;
|
||||
case LinphoneCallIncomingEarlyMedia: {
|
||||
if (linphone_core_get_calls_nb(LC) > 1 ||
|
||||
(floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) {
|
||||
|
|
@ -374,36 +378,15 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
}
|
||||
case LinphoneCallPausedByRemote:
|
||||
case LinphoneCallConnected: {
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call) {
|
||||
NSString *callId =
|
||||
[NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
NSUUID *uuid = [LinphoneManager.instance.providerDelegate.uuids objectForKey:callId];
|
||||
if (uuid) {
|
||||
[LinphoneManager.instance.providerDelegate.provider reportOutgoingCallWithUUID:uuid
|
||||
startedConnectingAtDate:nil];
|
||||
}
|
||||
if (![LinphoneManager.instance isCTCallCenterExist]) {
|
||||
/*only register CT call center CB for connected call*/
|
||||
[LinphoneManager.instance setupGSMInteraction];
|
||||
[[UIDevice currentDevice] setProximityMonitoringEnabled:!(CallManager.instance.speakerEnabled || CallManager.instance.bluetoothEnabled)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallStreamsRunning: {
|
||||
[self changeCurrentView:CallView.compositeViewDescription];
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call) {
|
||||
NSString *callId =
|
||||
[NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
NSUUID *uuid = [LinphoneManager.instance.providerDelegate.uuids objectForKey:callId];
|
||||
if (uuid) {
|
||||
[LinphoneManager.instance.providerDelegate.provider reportOutgoingCallWithUUID:uuid
|
||||
connectedAtDate:nil];
|
||||
NSString *address = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)];
|
||||
CXCallUpdate *update = [[CXCallUpdate alloc] init];
|
||||
update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:address];
|
||||
update.supportsGrouping = TRUE;
|
||||
update.supportsDTMF = TRUE;
|
||||
update.supportsHolding = TRUE;
|
||||
update.supportsUngrouping = TRUE;
|
||||
[LinphoneManager.instance.providerDelegate.provider reportCallWithUUID:uuid updated:update];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallUpdatedByRemote: {
|
||||
|
|
@ -437,18 +420,6 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
break;
|
||||
case LinphoneCallOutgoingEarlyMedia:
|
||||
case LinphoneCallOutgoingProgress: {
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call &&
|
||||
(linphone_core_get_calls_nb(LC) < 2)) {
|
||||
// Link call ID to UUID
|
||||
NSString *callId =
|
||||
[NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
NSUUID *uuid = [LinphoneManager.instance.providerDelegate.uuids objectForKey:@""];
|
||||
if (uuid) {
|
||||
[LinphoneManager.instance.providerDelegate.uuids removeObjectForKey:@""];
|
||||
[LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId];
|
||||
[LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallOutgoingRinging:
|
||||
|
|
@ -458,19 +429,6 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
case LinphoneCallReleased:
|
||||
break;
|
||||
case LinphoneCallResuming: {
|
||||
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call) {
|
||||
NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids
|
||||
objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id(
|
||||
linphone_call_get_call_log(call))]];
|
||||
if (!uuid) {
|
||||
break;
|
||||
}
|
||||
CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO];
|
||||
CXTransaction *tr = [[CXTransaction alloc] initWithAction:act];
|
||||
[LinphoneManager.instance.providerDelegate.controller requestTransaction:tr
|
||||
completion:^(NSError *err){
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LinphoneCallUpdating:
|
||||
|
|
@ -800,7 +758,7 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
|
||||
if (callIDFromPush && autoAnswer) {
|
||||
// accept call automatically
|
||||
[lm acceptCall:call evenWithVideo:YES];
|
||||
[CallManager.instance acceptCallWithCall:call hasVideo:YES];
|
||||
} else {
|
||||
AudioServicesPlaySystemSound(lm.sounds.vibrate);
|
||||
CallIncomingView *view = VIEW(CallIncomingView);
|
||||
|
|
@ -818,10 +776,11 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
|
||||
LinphoneCall *call = linphone_core_get_current_call(LC);
|
||||
if (call && linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
|
||||
LinphoneCallAppData *callData = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
|
||||
if (callData != nil) {
|
||||
NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))];
|
||||
CallAppData *data = [CallManager.instance getAppDataWithCallId:callId];
|
||||
if (data != nil) {
|
||||
if (state == UIDeviceBatteryStateUnplugged) {
|
||||
if (level <= 0.2f && !callData->batteryWarningShown) {
|
||||
if (level <= 0.2f && !data.batteryWarningShown) {
|
||||
LOGI(@"Battery warning");
|
||||
DTActionSheet *sheet = [[DTActionSheet alloc]
|
||||
initWithTitle:NSLocalizedString(@"Battery is running low. Stop video ?", nil)];
|
||||
|
|
@ -836,12 +795,13 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
linphone_call_update(call, params);
|
||||
}];
|
||||
[sheet showInView:self.view];
|
||||
callData->batteryWarningShown = TRUE;
|
||||
data.batteryWarningShown = TRUE;
|
||||
}
|
||||
}
|
||||
if (level > 0.2f) {
|
||||
callData->batteryWarningShown = FALSE;
|
||||
data.batteryWarningShown = FALSE;
|
||||
}
|
||||
[CallManager.instance setAppDataWithCallId:callId appData:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -852,7 +812,7 @@ static RootViewManager *rootViewManagerInstance = nil;
|
|||
}
|
||||
|
||||
- (void)incomingCallAccepted:(LinphoneCall *)call evenWithVideo:(BOOL)video {
|
||||
[LinphoneManager.instance acceptCall:call evenWithVideo:video];
|
||||
[CallManager.instance acceptCallWithCall:call hasVideo:video];
|
||||
}
|
||||
|
||||
- (void)incomingCallDeclined:(LinphoneCall *)call {
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2019 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 <CallKit/CallKit.h>
|
||||
|
||||
#ifndef ProviderDelegate_h
|
||||
#define ProviderDelegate_h
|
||||
|
||||
@interface ProviderDelegate : NSObject <CXProviderDelegate, CXCallObserverDelegate>
|
||||
|
||||
@property CXProvider *provider;
|
||||
@property CXCallObserver *observer;
|
||||
@property CXCallController *controller;
|
||||
@property NSMutableDictionary *calls;
|
||||
@property NSMutableDictionary *uuids;
|
||||
@property(nonatomic) LinphoneCall *pendingCall;
|
||||
@property LinphoneAddress *pendingAddr;
|
||||
@property BOOL pendingCallVideo;
|
||||
@property int callKitCalls;
|
||||
|
||||
- (void)reportIncomingCall:(LinphoneCall *) call withUUID:(NSUUID *)uuid handle:(NSString *)handle video:(BOOL)video;
|
||||
- (void)config;
|
||||
- (void)configAudioSession:(AVAudioSession *)audioSession;
|
||||
@end
|
||||
|
||||
#endif /* ProviderDelegate_h */
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2019 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 "ProviderDelegate.h"
|
||||
#import "LinphoneManager.h"
|
||||
#import "PhoneMainView.h"
|
||||
#include "linphone/linphonecore.h"
|
||||
#import <AVFoundation/AVAudioSession.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@implementation ProviderDelegate
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.calls = [[NSMutableDictionary alloc] init];
|
||||
self.uuids = [[NSMutableDictionary alloc] init];
|
||||
self.pendingCall = NULL;
|
||||
self.pendingAddr = NULL;
|
||||
self.pendingCallVideo = FALSE;
|
||||
CXCallController *callController = [[CXCallController alloc] initWithQueue:dispatch_get_main_queue()];
|
||||
[callController.callObserver setDelegate:self queue:dispatch_get_main_queue()];
|
||||
self.controller = callController;
|
||||
self.callKitCalls = 0;
|
||||
|
||||
if (!self) {
|
||||
LOGD(@"ProviderDelegate not initialized...");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)config {
|
||||
CXProviderConfiguration *config = [[CXProviderConfiguration alloc]
|
||||
initWithLocalizedName:[NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]];
|
||||
config.ringtoneSound = @"notes_of_the_optimistic.caf";
|
||||
config.supportsVideo = FALSE;
|
||||
config.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:@"callkit_logo"]);
|
||||
|
||||
NSArray *ar = @[ [NSNumber numberWithInt:(int)CXHandleTypeGeneric] ];
|
||||
NSSet *handleTypes = [[NSSet alloc] initWithArray:ar];
|
||||
[config setSupportedHandleTypes:handleTypes];
|
||||
[config setMaximumCallGroups:2];
|
||||
[config setMaximumCallsPerCallGroup:1];
|
||||
//not show app's calls in tel's history
|
||||
//config.includesCallsInRecents = NO;
|
||||
self.provider = [[CXProvider alloc] initWithConfiguration:config];
|
||||
[self.provider setDelegate:self queue:dispatch_get_main_queue()];
|
||||
}
|
||||
|
||||
- (void)configAudioSession:(AVAudioSession *)audioSession {
|
||||
NSError *err = nil;
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
|
||||
mode:AVAudioSessionModeVoiceChat
|
||||
options:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowBluetoothA2DP
|
||||
error:&err];
|
||||
if (err) {
|
||||
LOGE(@"Unable to change audio session because: %@", err.localizedDescription);
|
||||
err = nil;
|
||||
}
|
||||
[audioSession setMode:AVAudioSessionModeVoiceChat error:&err];
|
||||
if (err) {
|
||||
LOGE(@"Unable to change audio mode because : %@", err.localizedDescription);
|
||||
err = nil;
|
||||
}
|
||||
double sampleRate = 48000.0;
|
||||
[audioSession setPreferredSampleRate:sampleRate error:&err];
|
||||
if (err) {
|
||||
LOGE(@"Unable to change preferred sample rate because : %@", err.localizedDescription);
|
||||
err = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reportIncomingCall:(LinphoneCall *) call withUUID:(NSUUID *)uuid handle:(NSString *)handle video:(BOOL)video; {
|
||||
// Create update to describe the incoming call and caller
|
||||
CXCallUpdate *update = [[CXCallUpdate alloc] init];
|
||||
update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
|
||||
update.supportsDTMF = TRUE;
|
||||
update.supportsHolding = TRUE;
|
||||
update.supportsGrouping = TRUE;
|
||||
update.supportsUngrouping = TRUE;
|
||||
update.hasVideo = _pendingCallVideo = video;
|
||||
|
||||
// Report incoming call to system
|
||||
LOGD(@"CallKit: report new incoming call with call-id: [%@] and UUID: [%@]", [_calls objectForKey:uuid], uuid);
|
||||
[self.provider reportNewIncomingCallWithUUID:uuid
|
||||
update:update
|
||||
completion:^(NSError *error) {
|
||||
if (error) {
|
||||
LOGE(@"CallKit: cannot complete incoming call with call-id: [%@] and UUID: [%@] from [%@] caused by [%@]",
|
||||
[_calls objectForKey:uuid], uuid, handle, [error localizedDescription]);
|
||||
if ([error code] == CXErrorCodeIncomingCallErrorFilteredByDoNotDisturb ||
|
||||
[error code] == CXErrorCodeIncomingCallErrorFilteredByBlockList)
|
||||
linphone_call_decline(call,LinphoneReasonBusy); /*to give a chance for other devices to answer*/
|
||||
else
|
||||
linphone_call_decline(call,LinphoneReasonUnknown);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setPendingCall:(LinphoneCall *)pendingCall {
|
||||
if (pendingCall) {
|
||||
_pendingCall = pendingCall;
|
||||
if (_pendingCall)
|
||||
linphone_call_ref(_pendingCall);
|
||||
} else if (_pendingCall) {
|
||||
linphone_call_unref(_pendingCall);
|
||||
_pendingCall = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - CXProviderDelegate Protocol
|
||||
|
||||
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
|
||||
NSUUID *uuid = action.callUUID;
|
||||
NSString *callID = [self.calls objectForKey:uuid]; // first, make sure this callid is not already involved in a call
|
||||
LOGD(@"CallKit: Answering call with call-id: [%@] and UUID: [%@]", callID, uuid);
|
||||
[self configAudioSession:[AVAudioSession sharedInstance]];
|
||||
[action fulfill];
|
||||
LinphoneCall *call = [LinphoneManager.instance callByCallId:callID];
|
||||
if (!call)
|
||||
return;
|
||||
|
||||
self.callKitCalls++;
|
||||
self.pendingCall = call;
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
|
||||
NSUUID *uuid = action.callUUID;
|
||||
NSString *callID = [self.calls objectForKey:uuid]; // first, make sure this callid is not already involved in a call
|
||||
LOGD(@"CallKit: Starting Call with call-id: [%@] and UUID: [%@]", callID, uuid);
|
||||
// To restart Audio Unit
|
||||
[self configAudioSession:[AVAudioSession sharedInstance]];
|
||||
[action fulfill];
|
||||
LinphoneCall *call;
|
||||
if (![callID isEqualToString:@""]) {
|
||||
call = linphone_core_get_current_call(LC);
|
||||
} else {
|
||||
call = [LinphoneManager.instance callByCallId:callID];
|
||||
}
|
||||
if (call != NULL) {
|
||||
self.callKitCalls++;
|
||||
self.pendingCall = call;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
|
||||
self.callKitCalls--;
|
||||
[action fulfill];
|
||||
if (linphone_core_is_in_conference(LC)) {
|
||||
LinphoneManager.instance.conf = TRUE;
|
||||
linphone_core_terminate_conference(LC);
|
||||
LOGD(@"CallKit: Ending the conference");
|
||||
} else if (linphone_core_get_calls_nb(LC) > 1) {
|
||||
LinphoneManager.instance.conf = TRUE;
|
||||
linphone_core_terminate_all_calls(LC);
|
||||
LOGD(@"CallKit: Ending all the ongoing calls");
|
||||
} else {
|
||||
NSUUID *uuid = action.callUUID;
|
||||
NSString *callID = [self.calls objectForKey:uuid];
|
||||
if (callID) {
|
||||
LOGD(@"CallKit: Ending the call with call-id: [%@] and UUID: [%@]", callID, uuid);
|
||||
LinphoneCall *call = [LinphoneManager.instance callByCallId:callID];
|
||||
if (call) {
|
||||
linphone_call_terminate((LinphoneCall *)call);
|
||||
}
|
||||
[self.uuids removeObjectForKey:callID];
|
||||
[self.calls removeObjectForKey:uuid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider performSetMutedCallAction:(nonnull CXSetMutedCallAction *)action {
|
||||
[action fulfill];
|
||||
if ([[PhoneMainView.instance currentView] equal:CallView.compositeViewDescription]) {
|
||||
CallView *view = (CallView *)[PhoneMainView.instance popToView:CallView.compositeViewDescription];
|
||||
[view.microButton toggle];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider performSetHeldCallAction:(nonnull CXSetHeldCallAction *)action {
|
||||
[action fulfill];
|
||||
if (linphone_core_is_in_conference(LC) && action.isOnHold) {
|
||||
linphone_core_leave_conference(LC);
|
||||
LOGD(@"CallKit: Leaving conference");
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
|
||||
return;
|
||||
}
|
||||
|
||||
if (linphone_core_get_calls_nb(LC) > 1 && action.isOnHold) {
|
||||
linphone_core_pause_all_calls(LC);
|
||||
LOGD(@"CallKit: Pausing all ongoing calls");
|
||||
return;
|
||||
}
|
||||
|
||||
NSUUID *uuid = action.callUUID;
|
||||
NSString *callID = [self.calls objectForKey:uuid];
|
||||
if (!callID) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD(@"CallKit: Call with call-id: [%@] and UUID: [%@] paused status changed to: []", callID, uuid, action.isOnHold ? @"Paused" : @"Resumed");
|
||||
LinphoneCall *call = [LinphoneManager.instance callByCallId:callID];
|
||||
if (!call)
|
||||
return;
|
||||
|
||||
if (action.isOnHold) {
|
||||
LinphoneManager.instance.speakerBeforePause = LinphoneManager.instance.speakerEnabled;
|
||||
linphone_call_pause((LinphoneCall *)call);
|
||||
} else {
|
||||
if (linphone_core_get_conference(LC) && linphone_core_get_calls_nb(LC) > 1) {
|
||||
linphone_core_enter_conference(LC);
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self];
|
||||
} else {
|
||||
[self configAudioSession:[AVAudioSession sharedInstance]];
|
||||
self.pendingCall = call;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action {
|
||||
[action fulfill];
|
||||
NSUUID *uuid = action.callUUID;
|
||||
NSString *callID = [self.calls objectForKey:uuid];
|
||||
LOGD(@"CallKit: playing DTMF for call with call-id: [%@] and UUID: [%@]", callID, uuid);
|
||||
LinphoneCall *call = [LinphoneManager.instance callByCallId:callID];
|
||||
char digit = action.digits.UTF8String[0];
|
||||
linphone_call_send_dtmf((LinphoneCall *)call, digit);
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
|
||||
LOGD(@"CallKit: Audio session activated");
|
||||
// Now we can (re)start the call
|
||||
if (self.pendingCall) {
|
||||
LinphoneCallState state = linphone_call_get_state(self.pendingCall);
|
||||
switch (state) {
|
||||
case LinphoneCallIncomingReceived:
|
||||
[LinphoneManager.instance acceptCall:(LinphoneCall *)self.pendingCall evenWithVideo:_pendingCallVideo];
|
||||
break;
|
||||
case LinphoneCallPaused:
|
||||
linphone_call_resume((LinphoneCall *)self.pendingCall);
|
||||
break;
|
||||
case LinphoneCallStreamsRunning:
|
||||
// May happen when multiple calls
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (_pendingAddr) {
|
||||
[LinphoneManager.instance doCall:_pendingAddr];
|
||||
} else {
|
||||
LOGE(@"CallKit: No pending call");
|
||||
}
|
||||
}
|
||||
|
||||
[self setPendingCall:NULL];
|
||||
if (_pendingAddr)
|
||||
linphone_address_unref(_pendingAddr);
|
||||
_pendingAddr = NULL;
|
||||
_pendingCallVideo = FALSE;
|
||||
}
|
||||
|
||||
- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(nonnull AVAudioSession *)audioSession {
|
||||
LOGD(@"CallKit : Audio session deactivated");
|
||||
[self setPendingCall:NULL];
|
||||
if (_pendingAddr)
|
||||
linphone_address_unref(_pendingAddr);
|
||||
_pendingAddr = NULL;
|
||||
_pendingCallVideo = FALSE;
|
||||
}
|
||||
|
||||
- (void)providerDidReset:(CXProvider *)provider {
|
||||
LOGD(@"CallKit: Provider reset");
|
||||
LinphoneManager.instance.conf = TRUE;
|
||||
linphone_core_terminate_all_calls(LC);
|
||||
[self.calls removeAllObjects];
|
||||
[self.uuids removeAllObjects];
|
||||
}
|
||||
|
||||
#pragma mark - CXCallObserverDelegate Protocol
|
||||
|
||||
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
|
||||
LOGD(@"CallKit: Call changed");
|
||||
}
|
||||
|
||||
@end
|
||||
244
Classes/ProviderDelegate.swift
Normal file
244
Classes/ProviderDelegate.swift
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2019 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 CallKit
|
||||
import UIKit
|
||||
import linphonesw
|
||||
import AVFoundation
|
||||
import os
|
||||
|
||||
class ProviderDelegate: NSObject {
|
||||
private let provider: CXProvider
|
||||
private let callController: CXCallController
|
||||
|
||||
var uuids: [String : UUID] = [:]
|
||||
var calls: [UUID : String] = [:]
|
||||
var connecteds: [UUID : Bool] = [:]
|
||||
var addrs: [UUID : Address] = [:]
|
||||
var outgoingUuids: [String : UUID] = [:]
|
||||
var isSas: [UUID : Bool] = [:]
|
||||
|
||||
|
||||
override init() {
|
||||
provider = CXProvider(configuration: ProviderDelegate.providerConfiguration)
|
||||
callController = CXCallController()
|
||||
|
||||
super.init()
|
||||
|
||||
provider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
static var providerConfiguration: CXProviderConfiguration = {
|
||||
let providerConfiguration = CXProviderConfiguration(localizedName: Bundle.main.infoDictionary!["CFBundleName"] as! String)
|
||||
providerConfiguration.ringtoneSound = "notes_of_the_optimistic.caf"
|
||||
providerConfiguration.supportsVideo = true
|
||||
providerConfiguration.iconTemplateImageData = UIImage(named: "callkit_logo")?.pngData()
|
||||
providerConfiguration.supportedHandleTypes = [.generic]
|
||||
|
||||
providerConfiguration.maximumCallsPerCallGroup = 3
|
||||
providerConfiguration.maximumCallGroups = 2
|
||||
|
||||
//not show app's calls in tel's history
|
||||
//providerConfiguration.includesCallsInRecents = YES;
|
||||
|
||||
return providerConfiguration
|
||||
}()
|
||||
|
||||
func reportIncomingCall(call:Call?, uuid: UUID, handle: String, hasVideo: Bool) {
|
||||
let update = CXCallUpdate()
|
||||
update.remoteHandle = CXHandle(type:.generic, value: handle)
|
||||
update.hasVideo = hasVideo
|
||||
|
||||
let callId = CallManager.instance().providerDelegate.calls[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: report new incoming call with call-id: [\(String(describing: callId))] and UUID: [\(uuid.description)]")
|
||||
provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
if error == nil {
|
||||
} else {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: cannot complete incoming call with call-id: [\(String(describing: callId))] and UUID: [\(uuid.description)] from [\(handle)] caused by [\(error!.localizedDescription)]")
|
||||
let code = (error as NSError?)?.code
|
||||
if code == CXErrorCodeIncomingCallError.filteredByBlockList.rawValue || code == CXErrorCodeIncomingCallError.filteredByDoNotDisturb.rawValue {
|
||||
try? call?.decline(reason: Reason.Busy)
|
||||
} else {
|
||||
try? call?.decline(reason: Reason.Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateCall(uuid: UUID, handle: String, hasVideo: Bool = false) {
|
||||
let update = CXCallUpdate()
|
||||
update.remoteHandle = CXHandle(type:.generic, value:handle)
|
||||
update.hasVideo = hasVideo
|
||||
|
||||
provider.reportCall(with:uuid, updated:update);
|
||||
}
|
||||
|
||||
func reportOutgoingCallStartedConnecting(uuid:UUID) {
|
||||
provider.reportOutgoingCall(with: uuid, startedConnectingAt: nil)
|
||||
}
|
||||
|
||||
func reportOutgoingCallConnected(uuid:UUID) {
|
||||
provider.reportOutgoingCall(with: uuid, connectedAt: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CXProviderDelegate
|
||||
extension ProviderDelegate: CXProviderDelegate {
|
||||
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = calls[uuid]
|
||||
let addr = addrs[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call ended with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
|
||||
// remove call infos first, otherwise CXEndCallAction will be called more than onece
|
||||
if (addr != nil) {
|
||||
addrs.removeValue(forKey: uuid)
|
||||
outgoingUuids.removeValue(forKey: addr!.asStringUriOnly())
|
||||
isSas.removeValue(forKey: uuid)
|
||||
}
|
||||
let call = CallManager.instance().callByCallId(callId: callId)
|
||||
if (callId != nil) {
|
||||
uuids.removeValue(forKey: callId!)
|
||||
}
|
||||
calls.removeValue(forKey: uuid)
|
||||
connecteds.removeValue(forKey: uuid)
|
||||
|
||||
if (call != nil) {
|
||||
do {
|
||||
try call!.terminate()
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call ended \(uuid) failed because \(error)")
|
||||
}
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = calls[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call with call-id: \(String(describing: callId)) and UUID: \(uuid.description).")
|
||||
|
||||
let call = CallManager.instance().callByCallId(callId: callId)
|
||||
if (call == nil) {
|
||||
// The application is not yet registered, mark the call as connected. The audio session must be configured here.
|
||||
CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance())
|
||||
CallManager.instance().providerDelegate.connecteds.updateValue(true, forKey: uuid)
|
||||
} else {
|
||||
CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false)
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = calls[uuid]
|
||||
let call = CallManager.instance().callByCallId(callId: callId)
|
||||
|
||||
if (call != nil && UIApplication.shared.applicationState != .active) {
|
||||
do {
|
||||
if action.isOnHold {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call paused with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
try call!.pause()
|
||||
} else {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call resumed with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
try call!.resume()
|
||||
}
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call set held (paused or resumed) \(uuid) failed because \(error)")
|
||||
}
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||
do {
|
||||
let uuid = action.callUUID
|
||||
let addr = addrs[uuid]
|
||||
if (addr == nil) {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: can not call a null address!")
|
||||
action.fail()
|
||||
}
|
||||
|
||||
try CallManager.instance().doCall(addr: addr!, isSas: CallManager.instance().providerDelegate.isSas[uuid] ?? false)
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call started failed because \(error)")
|
||||
action.fail()
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetGroupCallAction) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call grouped callUUid : \(action.callUUID) with callUUID: \(String(describing: action.callUUIDToGroupWith)).")
|
||||
do {
|
||||
try CallManager.instance().lc?.addAllToConference()
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call grouped failed because \(error)")
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = calls[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call muted with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
|
||||
CallManager.instance().lc!.micEnabled = !CallManager.instance().lc!.micEnabled
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = calls[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call send dtmf with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
|
||||
let call = CallManager.instance().callByCallId(callId: callId)
|
||||
if (call != nil) {
|
||||
let digit = (action.digits.cString(using: String.Encoding.utf8)?[0])!
|
||||
do {
|
||||
try call!.sendDtmf(dtmf: digit)
|
||||
} catch {
|
||||
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call send dtmf \(uuid) failed because \(error)")
|
||||
}
|
||||
}
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
|
||||
let uuid = action.uuid
|
||||
let callId = calls[uuid]
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call time out with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: did reset.")
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session activated.")
|
||||
CallManager.instance().lc?.audioSessionActivated(actived: true)
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session deactivated.")
|
||||
CallManager.instance().lc?.audioSessionActivated(actived: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
+ (void)directLog:(OrtpLogLevel)level text:(NSString *)text {
|
||||
bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", text.cString);
|
||||
}
|
||||
|
||||
#pragma mark - Logs Functions callbacks
|
||||
|
||||
void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ typedef enum {
|
|||
|
||||
+ (NSMutableDictionary <NSString *, PHAsset *> *)photoAssetsDictionary;
|
||||
|
||||
+ (NSString *)recordingFilePathFromCall:(const LinphoneAddress *)iaddr;
|
||||
+ (NSArray *)parseRecordingName:(NSString *)filename;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -492,30 +492,6 @@
|
|||
return addr;
|
||||
}
|
||||
|
||||
+ (NSString *)recordingFilePathFromCall:(const LinphoneAddress *)iaddr {
|
||||
NSString *filepath = @"recording_";
|
||||
const char *address = linphone_address_get_username(iaddr);
|
||||
filepath = [filepath stringByAppendingString:address? [NSString stringWithCString:address encoding:NSUTF8StringEncoding] : @"unknown"];
|
||||
NSDate * now = [NSDate date];
|
||||
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
|
||||
[dateFormat setDateFormat:@"E-d-MMM-yyyy-HH-mm-ss"];
|
||||
NSString *date = [dateFormat stringFromDate:now];
|
||||
|
||||
filepath = [filepath stringByAppendingString:@"_"];
|
||||
filepath = [filepath stringByAppendingString:date];
|
||||
filepath = [filepath stringByAppendingString:@".mkv"];
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
NSString *writablePath = [paths objectAtIndex:0];
|
||||
writablePath = [writablePath stringByAppendingString:@"/"];
|
||||
writablePath = [writablePath stringByAppendingString:filepath];
|
||||
LOGD(@"file path is: %@\n", writablePath);
|
||||
return writablePath;
|
||||
//file name is recording_contact-name_dayName-day-monthName-year-hour-minutes-seconds
|
||||
//The recording prefix is used to identify recordings in the cache directory.
|
||||
//We will use name_dayName-day-monthName-year to separate recordings by days, then hour-minutes-seconds to order them in each day.
|
||||
}
|
||||
|
||||
+ (NSArray *)parseRecordingName:(NSString *)filename {
|
||||
NSString *rec = @"recording_"; //key that helps find recordings
|
||||
NSString *subName = [filename substringFromIndex:[filename rangeOfString:rec].location]; //We remove the parent folders if they exist in the filename
|
||||
|
|
|
|||
11
Classes/linphone-Bridging-Header.h
Normal file
11
Classes/linphone-Bridging-Header.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import <UIKit/UIkit.h>
|
||||
#import "FastAddressBook.h"
|
||||
#import "Log.h"
|
||||
#import "AudioHelper.h"
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ display_phone_only=0
|
|||
auto_download_incoming_files_max_size=0
|
||||
lime_migration_done=0
|
||||
use_rls_presence=1
|
||||
use_callkit=1
|
||||
|
||||
[in_app_purchase]
|
||||
#set to 1 if in-app purchases are to be shown in the application
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ send_logs_include_linphonerc_and_chathistory=0
|
|||
publish_presence=0
|
||||
backgroundmode_preference=1
|
||||
use_rls_presence=1
|
||||
use_callkit=1
|
||||
|
||||
accept_early_media=0
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue