diff --git a/Classes/AppManager.swift b/Classes/AppManager.swift new file mode 100644 index 000000000..5d87b0983 --- /dev/null +++ b/Classes/AppManager.swift @@ -0,0 +1,63 @@ +/* +* 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 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 AppManager: NSObject { + 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 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. + } +} diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 5b3ee5b38..527a5a56e 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -23,37 +23,24 @@ 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 var batteryWarningShown = false + @objc 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] = [:] + var lc: Core? @objc var speakerBeforePause : Bool = false @objc var speakerEnabled : Bool = false @objc var bluetoothEnabled : Bool = false + @objc var nextCallIsTransfer: Bool = false + var callAppDatas: [String : CallAppData] = [:] - var lc: Core? - var config: Config? fileprivate override init() { providerDelegate = ProviderDelegate() @@ -68,6 +55,11 @@ enum NetworkType: Int { return theCallManager! } + @objc func setCore(core: OpaquePointer) { + lc = Core.getSwiftObject(cObject: core) + lc?.addDelegate(delegate: manager) + } + @objc func getAppData (callId : String) -> CallAppData? { return CallManager.instance().callAppDatas["\(callId)"] } @@ -76,12 +68,6 @@ enum NetworkType: Int { 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 @@ -100,24 +86,13 @@ enum NetworkType: Int { @objc static func callKitEnabled() -> Bool { #if !targetEnvironment(simulator) - if CallManager.instance().lpConfigBoolForKey(key: "use_callkit", section: "app") { + if ConfigManager.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. @@ -150,26 +125,6 @@ enum NetworkType: Int { 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 @@ -208,8 +163,8 @@ enum NetworkType: Int { 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 (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) { + let low_bandwidth = (AppManager.network() == .network_2g) if (low_bandwidth) { Log.directLog(BCTBX_LOG_MESSAGE, text: "Low bandwidth mode") } @@ -218,7 +173,7 @@ enum NetworkType: Int { //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 ?? "") + let writablePath = AppManager.recordingFilePathFromCall(address: address?.username ?? "") Log.directLog(BCTBX_LOG_MESSAGE, text: "Record file path: \(String(describing: writablePath))") callParams.recordFile = writablePath @@ -257,7 +212,7 @@ enum NetworkType: Int { 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 { + if ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference") && AppManager.network() == .network_2g { Log.directLog(BCTBX_LOG_MESSAGE, text: "Enabling low bandwidth mode") lcallParams.lowBandwidthEnabled = true } @@ -266,17 +221,17 @@ enum NetworkType: Int { 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(ConfigManager.instance().lpConfigBoolForKey(key: "override_domain_with_default_one")) { + try addr.setDomain(newValue: ConfigManager.instance().lpConfigStringForKey(key: "domain", section: "assistant")) } - if (CallManager.nextCallIsTransfer) { + if (CallManager.instance().nextCallIsTransfer) { let call = CallManager.instance().lc!.currentCall try call?.transfer(referTo: addr.asString()) - CallManager.nextCallIsTransfer = false + CallManager.instance().nextCallIsTransfer = false } else { //We set the record file name here because we can't do it after the call is started. - let writablePath = CallManager.recordingFilePathFromCall(address: addr.username ) + let writablePath = AppManager.recordingFilePathFromCall(address: addr.username ) Log.directLog(BCTBX_LOG_DEBUG, text: "record file path: \(writablePath)") lcallParams.recordFile = writablePath if (isSas) { @@ -343,93 +298,6 @@ enum NetworkType: Int { 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 { @@ -457,8 +325,12 @@ class CoreManager: CoreDelegate { // Tha app is now registered, updated the call already existed. CallManager.instance().providerDelegate.updateCall(uuid: uuid!, handle: address, hasVideo: video) let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!] - let connected = callInfo?.connected ?? false - if (connected) { + let accepted = callInfo?.accepted ?? false + let declined = callInfo?.declined ?? false + if (declined) { + // The call is already declined. + try? call.decline(reason: Reason.Unknown) + } else if (accepted) { // The call is already answered. CallManager.instance().acceptCall(call: call, hasVideo: video) } diff --git a/Classes/CallView.m b/Classes/CallView.m index 552f19deb..5ae9f5749 100644 --- a/Classes/CallView.m +++ b/Classes/CallView.m @@ -139,7 +139,7 @@ static UICompositeViewDescription *compositeDescription = nil; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _waitView.hidden = TRUE; - CallManager.nextCallIsTransfer = FALSE; + CallManager.instance.nextCallIsTransfer = FALSE; callRecording = FALSE; _recordButtonOnView.hidden = TRUE; @@ -844,7 +844,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { [self hideOptions:TRUE animated:TRUE]; DialerView *view = VIEW(DialerView); [view setAddress:@""]; - CallManager.nextCallIsTransfer = TRUE; + CallManager.instance.nextCallIsTransfer = TRUE; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; } @@ -852,7 +852,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { [self hideOptions:TRUE animated:TRUE]; DialerView *view = VIEW(DialerView); [view setAddress:@""]; - CallManager.nextCallIsTransfer = FALSE; + CallManager.instance.nextCallIsTransfer = FALSE; [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; } diff --git a/Classes/ConfigManager.swift b/Classes/ConfigManager.swift new file mode 100644 index 000000000..90ff5cb0e --- /dev/null +++ b/Classes/ConfigManager.swift @@ -0,0 +1,125 @@ +/* +* 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 + +@objc class ConfigManager: NSObject { + static var theConfigManager: ConfigManager? + var config: Config? + let applicationKey = "app" + + @objc static func instance() -> ConfigManager { + if (theConfigManager == nil) { + theConfigManager = ConfigManager() + } + return theConfigManager! + } + + @objc func setDb(db:OpaquePointer) { + config = Config.getSwiftObject(cObject: db) + } + + //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) + } +} diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index 6762d418e..e68813a09 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -1445,8 +1445,8 @@ 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]; + [CallManager.instance setCoreWithCore:theLinphoneCore]; + [ConfigManager.instance setDbWithDb:_configDb]; linphone_core_start(theLinphoneCore); diff --git a/Classes/LinphoneUI/UICallButton.m b/Classes/LinphoneUI/UICallButton.m index 0e48c16b6..5515c22c7 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 (CallManager.nextCallIsTransfer) { + if (CallManager.instance.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/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 00d2af92b..9b576f232 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -24,12 +24,14 @@ import linphonesw import AVFoundation import os -class CallInfo: NSObject { +@objc class CallInfo: NSObject { var callId: String = "" - var connected = false + var accepted = false var toAddr: Address? var isOutgoing = false var sasEnabled = false + var declined = false + static func newIncomingCallInfo(callId: String) -> CallInfo { let callInfo = CallInfo() @@ -48,17 +50,12 @@ class CallInfo: NSObject { class ProviderDelegate: NSObject { private let provider: CXProvider - private let callController: CXCallController - var uuids: [String : UUID] = [:] var callInfos: [UUID : CallInfo] = [:] override init() { provider = CXProvider(configuration: ProviderDelegate.providerConfiguration) - callController = CXCallController() - super.init() - provider.setDelegate(self, queue: nil) } @@ -83,16 +80,22 @@ class ProviderDelegate: NSObject { update.remoteHandle = CXHandle(type:.generic, value: handle) update.hasVideo = hasVideo - let callId = callInfos[uuid]?.callId + let callInfo = callInfos[uuid] + let callId = callInfo?.callId 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)]") + if (call == nil) { + callInfo?.declined = true + self.callInfos.updateValue(callInfo!, forKey: uuid) + return + } let code = (error as NSError?)?.code if code == CXErrorCodeIncomingCallError.filteredByBlockList.rawValue || code == CXErrorCodeIncomingCallError.filteredByDoNotDisturb.rawValue { try? call?.decline(reason: Reason.Busy) - } else { + } else { try? call?.decline(reason: Reason.Unknown) } } @@ -103,7 +106,6 @@ class ProviderDelegate: NSObject { let update = CXCallUpdate() update.remoteHandle = CXHandle(type:.generic, value:handle) update.hasVideo = hasVideo - provider.reportCall(with:uuid, updated:update); } @@ -147,10 +149,10 @@ extension ProviderDelegate: CXProviderDelegate { 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. + if (call == nil || call?.state != Call.State.IncomingReceived) { + // The application is not yet registered or the call is not yet received, mark the call as accepted. The audio session must be configured here. CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) - callInfo?.connected = true + callInfo?.accepted = true callInfos.updateValue(callInfo!, forKey: uuid) } else { CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false) @@ -211,7 +213,6 @@ extension ProviderDelegate: CXProviderDelegate { let uuid = action.callUUID let callId = callInfos[uuid]?.callId 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() } @@ -220,7 +221,6 @@ extension ProviderDelegate: CXProviderDelegate { let uuid = action.callUUID let callId = callInfos[uuid]?.callId 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])! diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 518ea680c..06024a9a4 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -82,6 +82,8 @@ 5EEE8F9B20C80C23006E4176 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EF0C33820C806A5005081B0 /* NotificationCenter.framework */; }; 5EEE8F9F20C80C23006E4176 /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EEE8F9E20C80C23006E4176 /* TodayViewController.m */; }; 5EEE8FA220C80C23006E4176 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EEE8FA020C80C23006E4176 /* MainInterface.storyboard */; }; + 6134812D2406CECC00695B41 /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6134812C2406CECC00695B41 /* ConfigManager.swift */; }; + 6134812F2407B35200695B41 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6134812E2407B35200695B41 /* AppManager.swift */; }; 614C087823D1A35F00217F80 /* ProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C087723D1A35F00217F80 /* ProviderDelegate.swift */; }; 614C087A23D1A37400217F80 /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 614C087923D1A37400217F80 /* CallManager.swift */; }; 614D09CE21E74D5400C43EDF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 614D09CD21E74D5400C43EDF /* GoogleService-Info.plist */; }; @@ -962,6 +964,8 @@ 5EEE8FB320C81334006E4176 /* latestCallsWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = latestCallsWidget.entitlements; sourceTree = ""; }; 5EF0C33820C806A5005081B0 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; 6130C85B22BBB493009CC79C /* LaunchScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchScreen.h; sourceTree = ""; }; + 6134812C2406CECC00695B41 /* ConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = ""; }; + 6134812E2407B35200695B41 /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; 614C087623D1A35E00217F80 /* linphone-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "linphone-Bridging-Header.h"; sourceTree = ""; }; 614C087723D1A35F00217F80 /* ProviderDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderDelegate.swift; sourceTree = ""; }; 614C087923D1A37400217F80 /* CallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = ""; }; @@ -2183,6 +2187,8 @@ 34216F3F1547EBCD00EA9777 /* VideoZoomHandler.m */, 614C087723D1A35F00217F80 /* ProviderDelegate.swift */, 614C087923D1A37400217F80 /* CallManager.swift */, + 6134812C2406CECC00695B41 /* ConfigManager.swift */, + 6134812E2407B35200695B41 /* AppManager.swift */, 614C087623D1A35E00217F80 /* linphone-Bridging-Header.h */, ); path = Classes; @@ -4404,6 +4410,7 @@ 633671611BCBAAD200BFCBDE /* ChatConversationCreateView.m in Sources */, 634610061B61330300548952 /* UILabel+Boldify.m in Sources */, 2248E90E12F7E4CF00220D9C /* UIDigitButton.m in Sources */, + 6134812F2407B35200695B41 /* AppManager.swift in Sources */, 633756391B67BAF400E21BAD /* SideMenuTableView.m in Sources */, 2214EB7A12F846B1002A5394 /* UICallButton.m in Sources */, 6352A5751BE0D4B800594C1C /* CallSideMenuView.m in Sources */, @@ -4413,6 +4420,7 @@ 8C9C5E111F83BD97006987FA /* UIChatCreateCollectionViewCell.m in Sources */, 22968A5F12F875C600588287 /* UISpeakerButton.m in Sources */, 63701DDF1BA32039006A9AE3 /* UIConfirmationDialog.m in Sources */, + 6134812D2406CECC00695B41 /* ConfigManager.swift in Sources */, 22C755601317E59C007BC101 /* UIBluetoothButton.m in Sources */, 22AA8B0113D83F6300B30535 /* UICamSwitch.m in Sources */, 63B8D6A21BCBF43100C12B09 /* UIChatCreateCell.m in Sources */,