From 43f303fa4341d14976475025d0dfdd5017fcc297 Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Wed, 2 Dec 2020 01:18:18 +0100 Subject: [PATCH] This commit modifies the way Linphone reports the calls to CallKit : - Instead of reporting the Display name inside the remoteHandle of CXCallUpdate it now reports the SIP URI - The display name is now inserted into localizedCallerName The benefit of doing this is that is that it enables calling from the Phone Call History, and it is required if tel URI are activated (unless calls are not reported in phone history) This commit also enables the ability to place calls using Linphone by long pressing tel URIs. --- Classes/CallManager.swift | 48 +++++++++++++++++++++++++--------- Classes/LinphoneAppDelegate.m | 17 +++++++++++- Classes/ProviderDelegate.swift | 20 +++++++++++--- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 2be08ed05..7cb376bc8 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -171,7 +171,7 @@ import AVFoundation let callInfo = providerDelegate.callInfos[uuid!] if (callInfo?.declined ?? false) { // This call was declined. - providerDelegate.reportIncomingCall(call:nil, uuid: uuid!, handle: "Calling", hasVideo: true) + providerDelegate.reportIncomingCall(call:nil, uuid: uuid!, handle: "Calling", hasVideo: true, displayName: callInfo?.displayName ?? "Calling") providerDelegate.endCall(uuid: uuid!) } return @@ -179,21 +179,22 @@ import AVFoundation let call = CallManager.instance().callByCallId(callId: callId) if (call != nil) { - let addr = FastAddressBook.displayName(for: call?.remoteAddress?.getCobject) ?? "Unknow" + let displayName = FastAddressBook.displayName(for: call?.remoteAddress?.getCobject) ?? "Unknow" let video = UIApplication.shared.applicationState == .active && (lc!.videoActivationPolicy?.automaticallyAccept ?? false) && (call!.remoteParams?.videoEnabled ?? false) - displayIncomingCall(call: call, handle: addr, hasVideo: video, callId: callId) + displayIncomingCall(call: call, handle: (call!.remoteAddress?.asStringUriOnly())!, hasVideo: video, callId: callId, displayName: displayName) } else { - displayIncomingCall(call: nil, handle: "Calling", hasVideo: true, callId: callId) + displayIncomingCall(call: nil, handle: "Calling", hasVideo: true, callId: callId, displayName: "Calling") } } - func displayIncomingCall(call:Call?, handle: String, hasVideo: Bool, callId: String) { + func displayIncomingCall(call:Call?, handle: String, hasVideo: Bool, callId: String, displayName:String) { let uuid = UUID() let callInfo = CallInfo.newIncomingCallInfo(callId: callId) providerDelegate.callInfos.updateValue(callInfo, forKey: uuid) providerDelegate.uuids.updateValue(uuid, forKey: callId) - providerDelegate.reportIncomingCall(call:call, uuid: uuid, handle: handle, hasVideo: hasVideo) + providerDelegate.reportIncomingCall(call:call, uuid: uuid, handle: handle, hasVideo: hasVideo, displayName: displayName) + } @objc func acceptCall(call: OpaquePointer?, hasVideo:Bool) { @@ -240,11 +241,11 @@ import AVFoundation if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer) { let uuid = UUID() let name = FastAddressBook.displayName(for: addr) ?? "unknow" - let handle = CXHandle(type: .generic, value: name) + let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly()) let startCallAction = CXStartCallAction(call: uuid, handle: handle) let transaction = CXTransaction(action: startCallAction) - let callInfo = CallInfo.newOutgoingCallInfo(addr: sAddr, isSas: isSas) + let callInfo = CallInfo.newOutgoingCallInfo(addr: sAddr, isSas: isSas, displayName: name) providerDelegate.callInfos.updateValue(callInfo, forKey: uuid) providerDelegate.uuids.updateValue(uuid, forKey: "") @@ -406,10 +407,31 @@ import AVFoundation } } } + + @objc func performActionWhenCoreIsOn(action: @escaping ()->Void ) { + if (manager.globalState == .On) { + action() + } else { + manager.actionsToPerformOnceWhenCoreIsOn.append(action) + } + } + } class CoreManagerDelegate: CoreDelegate { static var speaker_already_enabled : Bool = false + var globalState : GlobalState = .Off + var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] + + override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) { + if (gstate == .On) { + actionsToPerformOnceWhenCoreIsOn.forEach { + $0() + } + actionsToPerformOnceWhenCoreIsOn.removeAll() + } + globalState = gstate + } override func onRegistrationStateChanged(lc: Core, cfg: ProxyConfig, cstate: RegistrationState, message: String) { if lc.proxyConfigList.count == 1 && (cstate == .Failed || cstate == .Cleared){ @@ -425,7 +447,7 @@ class CoreManagerDelegate: CoreDelegate { 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 displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow" let callLog = call.callLog let callId = callLog?.callId let video = UIApplication.shared.applicationState == .active && (lc.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) @@ -445,7 +467,7 @@ class CoreManagerDelegate: CoreDelegate { 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) + CallManager.instance().providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName) let callInfo = CallManager.instance().providerDelegate.callInfos[uuid!] if (callInfo?.declined ?? false) { DispatchQueue.main.asyncAfter(deadline: .now()) {try? call.decline(reason: callInfo!.reason)} @@ -454,13 +476,13 @@ class CoreManagerDelegate: CoreDelegate { CallManager.instance().acceptCall(call: call, hasVideo: video) } } else { - CallManager.instance().displayIncomingCall(call: call, handle: address, hasVideo: video, callId: callId!) + CallManager.instance().displayIncomingCall(call: call, handle: addr!.asStringUriOnly(), hasVideo: video, callId: callId!, displayName: displayName) } } else if (UIApplication.shared.applicationState != .active) { // not support callkit , use notif let content = UNMutableNotificationContent() content.title = NSLocalizedString("Incoming call", comment: "") - content.body = address + content.body = displayName content.sound = UNNotificationSound.init(named: UNNotificationSoundName.init("notes_of_the_optimistic.caf")) content.categoryIdentifier = "call_cat" content.userInfo = ["CallId" : callId!] @@ -524,7 +546,7 @@ class CoreManagerDelegate: CoreDelegate { // 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) + content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil) // Deliver the notification. let request = UNNotificationRequest(identifier: "call_request", content: content, trigger: nil) // Schedule the notification. diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 8a600ecf3..a1aa761c4 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -31,6 +31,9 @@ #include "LinphoneManager.h" #include "linphone/linphonecore.h" +#import +#import + #ifdef USE_CRASHLYTICS #include "FIRApp.h" #endif @@ -256,7 +259,6 @@ #ifdef USE_CRASHLYTICS [FIRApp configure]; #endif - UIApplication *app = [UIApplication sharedApplication]; UIApplicationState state = app.applicationState; @@ -404,11 +406,24 @@ // 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 setOn]; } + if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) { // tel URI handler. + INInteraction *interaction = userActivity.interaction; + INStartAudioCallIntent *startAudioCallIntent = (INStartAudioCallIntent *)interaction.intent; + INPerson *contact = startAudioCallIntent.contacts[0]; + INPersonHandle *personHandle = contact.personHandle; + [CallManager.instance performActionWhenCoreIsOnAction:^(void) { + [LinphoneManager.instance call: [LinphoneUtils normalizeSipOrPhoneAddress:personHandle.value]]; + }]; + + } + return YES; } diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 2fa978f2b..1b7fdd467 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -33,6 +33,7 @@ import os var declined = false var connected = false var reason: Reason = Reason.None + var displayName: String? static func newIncomingCallInfo(callId: String) -> CallInfo { let callInfo = CallInfo() @@ -40,11 +41,12 @@ import os return callInfo } - static func newOutgoingCallInfo(addr: Address, isSas: Bool) -> CallInfo { + static func newOutgoingCallInfo(addr: Address, isSas: Bool, displayName: String) -> CallInfo { let callInfo = CallInfo() callInfo.isOutgoing = true callInfo.sasEnabled = isSas callInfo.toAddr = addr + callInfo.displayName = displayName return callInfo } } @@ -68,7 +70,7 @@ class ProviderDelegate: NSObject { providerConfiguration.ringtoneSound = "notes_of_the_optimistic.caf" providerConfiguration.supportsVideo = true providerConfiguration.iconTemplateImageData = UIImage(named: "callkit_logo")?.pngData() - providerConfiguration.supportedHandleTypes = [.generic] + providerConfiguration.supportedHandleTypes = [.generic, .phoneNumber, .emailAddress] providerConfiguration.maximumCallsPerCallGroup = 10 providerConfiguration.maximumCallGroups = 2 @@ -79,10 +81,11 @@ class ProviderDelegate: NSObject { return providerConfiguration }() - func reportIncomingCall(call:Call?, uuid: UUID, handle: String, hasVideo: Bool) { + func reportIncomingCall(call:Call?, uuid: UUID, handle: String, hasVideo: Bool, displayName:String) { let update = CXCallUpdate() update.remoteHandle = CXHandle(type:.generic, value: handle) update.hasVideo = hasVideo + update.localizedCallerName = displayName let callInfo = callInfos[uuid] let callId = callInfo?.callId @@ -112,9 +115,10 @@ class ProviderDelegate: NSObject { } } - func updateCall(uuid: UUID, handle: String, hasVideo: Bool = false) { + func updateCall(uuid: UUID, handle: String, hasVideo: Bool = false, displayName:String) { let update = CXCallUpdate() update.remoteHandle = CXHandle(type:.generic, value:handle) + update.localizedCallerName = displayName update.hasVideo = hasVideo provider.reportCall(with:uuid, updated:update); } @@ -221,9 +225,17 @@ extension ProviderDelegate: CXProviderDelegate { } func provider(_ provider: CXProvider, perform action: CXStartCallAction) { + + do { + let uuid = action.callUUID let callInfo = callInfos[uuid] + let update = CXCallUpdate() + update.remoteHandle = action.handle + update.localizedCallerName = callInfo?.displayName + self.provider.reportCall(with: action.callUUID, updated: update) + let addr = callInfo?.toAddr if (addr == nil) { Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: can not call a null address!")