diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift
new file mode 100644
index 000000000..9e7622643
--- /dev/null
+++ b/Classes/CallManager.swift
@@ -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 .
+*/
+
+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
+ ])
+ }
+}
+
+
diff --git a/Classes/CallOutgoingView.m b/Classes/CallOutgoingView.m
index c9d674674..97bfeb68f 100644
--- a/Classes/CallOutgoingView.m
+++ b/Classes/CallOutgoingView.m
@@ -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) {
diff --git a/Classes/CallView.m b/Classes/CallView.m
index 07f0aaaee..552f19deb 100644
--- a/Classes/CallView.m
+++ b/Classes/CallView.m
@@ -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
diff --git a/Classes/LinphoneAppDelegate.h b/Classes/LinphoneAppDelegate.h
index c34ad8775..9728720cd 100644
--- a/Classes/LinphoneAppDelegate.h
+++ b/Classes/LinphoneAppDelegate.h
@@ -21,10 +21,11 @@
#import
#import "LinphoneCoreSettingsStore.h"
-#import "ProviderDelegate.h"
#import
#import
#import
+#import "linphoneapp-Swift.h"
+
@interface LinphoneAppDelegate : NSObject {
@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;
diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m
index 4d499f9d3..61163d01a 100644
--- a/Classes/LinphoneAppDelegate.m
+++ b/Classes/LinphoneAppDelegate.m
@@ -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> * _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:@"", [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
diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h
index af065a3bb..ec77c3153 100644
--- a/Classes/LinphoneManager.h
+++ b/Classes/LinphoneManager.h
@@ -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;
diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m
index 21c011a1f..6762d418e 100644
--- a/Classes/LinphoneManager.m
+++ b/Classes/LinphoneManager.m
@@ -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)) {
diff --git a/Classes/LinphoneUI/UICallButton.m b/Classes/LinphoneUI/UICallButton.m
index 6d1bab3d6..0e48c16b6 100644
--- a/Classes/LinphoneUI/UICallButton.m
+++ b/Classes/LinphoneUI/UICallButton.m
@@ -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) {
diff --git a/Classes/LinphoneUI/UIDeviceCell.m b/Classes/LinphoneUI/UIDeviceCell.m
index e9b2aac38..4161fbaad 100644
--- a/Classes/LinphoneUI/UIDeviceCell.m
+++ b/Classes/LinphoneUI/UIDeviceCell.m
@@ -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
diff --git a/Classes/LinphoneUI/UIDevicesDetails.m b/Classes/LinphoneUI/UIDevicesDetails.m
index 8f528900d..7478204b4 100644
--- a/Classes/LinphoneUI/UIDevicesDetails.m
+++ b/Classes/LinphoneUI/UIDevicesDetails.m
@@ -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
diff --git a/Classes/LinphoneUI/UIPauseButton.m b/Classes/LinphoneUI/UIPauseButton.m
index 480d10d64..4b5da117e 100644
--- a/Classes/LinphoneUI/UIPauseButton.m
+++ b/Classes/LinphoneUI/UIPauseButton.m
@@ -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];
diff --git a/Classes/LinphoneUI/UISpeakerButton.m b/Classes/LinphoneUI/UISpeakerButton.m
index 7b39211b1..3bdd5b70d 100644
--- a/Classes/LinphoneUI/UISpeakerButton.m
+++ b/Classes/LinphoneUI/UISpeakerButton.m
@@ -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
diff --git a/Classes/LinphoneUI/UIVideoButton.m b/Classes/LinphoneUI/UIVideoButton.m
index c0431b6bd..69aa119fb 100644
--- a/Classes/LinphoneUI/UIVideoButton.m
+++ b/Classes/LinphoneUI/UIVideoButton.m
@@ -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];
diff --git a/Classes/Log.h b/Classes/Log.h
index c2f264af2..81b6c2007 100644
--- a/Classes/Log.h
+++ b/Classes/Log.h
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-#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
\ No newline at end of file
diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m
index 3c421795d..0f4a3687d 100644
--- a/Classes/PhoneMainView.m
+++ b/Classes/PhoneMainView.m
@@ -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 {
diff --git a/Classes/ProviderDelegate.h b/Classes/ProviderDelegate.h
deleted file mode 100644
index b56968111..000000000
--- a/Classes/ProviderDelegate.h
+++ /dev/null
@@ -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 .
- */
-
-#import
-
-#ifndef ProviderDelegate_h
-#define ProviderDelegate_h
-
-@interface ProviderDelegate : NSObject
-
-@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 */
diff --git a/Classes/ProviderDelegate.m b/Classes/ProviderDelegate.m
deleted file mode 100644
index bd417f6e4..000000000
--- a/Classes/ProviderDelegate.m
+++ /dev/null
@@ -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 .
- */
-
-#import "ProviderDelegate.h"
-#import "LinphoneManager.h"
-#import "PhoneMainView.h"
-#include "linphone/linphonecore.h"
-#import
-#import
-
-@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
diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift
new file mode 100644
index 000000000..2945321fd
--- /dev/null
+++ b/Classes/ProviderDelegate.swift
@@ -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 .
+*/
+
+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)
+ }
+}
+
diff --git a/Classes/Utils/Log.m b/Classes/Utils/Log.m
index fc4b7ff43..43c69275b 100644
--- a/Classes/Utils/Log.m
+++ b/Classes/Utils/Log.m
@@ -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) {
diff --git a/Classes/Utils/Utils.h b/Classes/Utils/Utils.h
index ee47e89fd..c2c7f94c5 100644
--- a/Classes/Utils/Utils.h
+++ b/Classes/Utils/Utils.h
@@ -52,7 +52,6 @@ typedef enum {
+ (NSMutableDictionary *)photoAssetsDictionary;
-+ (NSString *)recordingFilePathFromCall:(const LinphoneAddress *)iaddr;
+ (NSArray *)parseRecordingName:(NSString *)filename;
@end
diff --git a/Classes/Utils/Utils.m b/Classes/Utils/Utils.m
index 116bb13d1..a15a3f8ac 100644
--- a/Classes/Utils/Utils.m
+++ b/Classes/Utils/Utils.m
@@ -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
diff --git a/Classes/linphone-Bridging-Header.h b/Classes/linphone-Bridging-Header.h
new file mode 100644
index 000000000..a520acc8e
--- /dev/null
+++ b/Classes/linphone-Bridging-Header.h
@@ -0,0 +1,11 @@
+//
+// Use this file to import your target's public headers that you would like to expose to Swift.
+//
+
+
+
+#import
+#import "FastAddressBook.h"
+#import "Log.h"
+#import "AudioHelper.h"
+
diff --git a/Resources/linphonerc b/Resources/linphonerc
index e65093c7f..ffc700895 100644
--- a/Resources/linphonerc
+++ b/Resources/linphonerc
@@ -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
diff --git a/Resources/linphonerc-factory b/Resources/linphonerc-factory
index 38eedc0c5..0299da623 100644
--- a/Resources/linphonerc-factory
+++ b/Resources/linphonerc-factory
@@ -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