From cd93c10576cd4fa65d3443ed2d8cce7c663dde2a Mon Sep 17 00:00:00 2001 From: Benjamin Reis Date: Wed, 14 Dec 2016 16:29:02 +0100 Subject: [PATCH] support of CallKit for one call --- Classes/AssistantView.m | 3 +- Classes/CallOutgoingView.m | 12 + Classes/CallView.m | 98 +++++---- Classes/LinphoneAppDelegate.h | 4 +- Classes/LinphoneAppDelegate.m | 273 ++++++++++++++++++----- Classes/LinphoneManager.h | 7 + Classes/LinphoneManager.m | 329 +++++++++++++++++----------- Classes/LinphoneUI/StatusBarView.m | 55 +++-- Classes/LinphoneUI/UIHangUpButton.m | 3 +- Classes/LinphoneUI/UIPauseButton.m | 89 +++++++- Classes/PhoneMainView.m | 78 ++++++- Classes/ProviderDelegate.h | 26 +++ Classes/ProviderDelegate.m | 227 +++++++++++++++++++ Resources/images/callkit_logo.png | Bin 0 -> 1309 bytes linphone.xcodeproj/project.pbxproj | 23 +- 15 files changed, 964 insertions(+), 263 deletions(-) create mode 100644 Classes/ProviderDelegate.h create mode 100644 Classes/ProviderDelegate.m create mode 100644 Resources/images/callkit_logo.png diff --git a/Classes/AssistantView.m b/Classes/AssistantView.m index 35e6b025b..8787e3327 100644 --- a/Classes/AssistantView.m +++ b/Classes/AssistantView.m @@ -821,7 +821,8 @@ static UICompositeViewDescription *compositeDescription = nil; return NSLocalizedString(@"Unknown error, please try again later.", nil); } -- (void)showErrorPopup:(const char *)err { +- (void)showErrorPopup:(const char *)error { + const char *err = error ? error : ""; if (strcmp(err, "ERROR_BAD_CREDENTIALS") == 0) { UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Registration failure", nil) message:[AssistantView StringForXMLRPCError:err] diff --git a/Classes/CallOutgoingView.m b/Classes/CallOutgoingView.m index 00a891c3f..166382bb3 100644 --- a/Classes/CallOutgoingView.m +++ b/Classes/CallOutgoingView.m @@ -119,7 +119,19 @@ static UICompositeViewDescription *compositeDescription = nil; - (IBAction)onDeclineClick:(id)sender { LinphoneCall *call = linphone_core_get_current_call(LC); if (call) { + /*if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = [LinphoneManager.instance.providerDelegate.uuids objectForKey:[NSString + stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))]]; + if(!uuid) { + linphone_core_terminate_call(LC, call); + return; + } + CXEndCallAction *act = [[CXEndCallAction alloc] initWithCallUUID:uuid]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr completion:^(NSError *err){}]; + } else {*/ linphone_core_terminate_call(LC, call); + //} } } diff --git a/Classes/CallView.m b/Classes/CallView.m index d72579975..e99679082 100644 --- a/Classes/CallView.m +++ b/Classes/CallView.m @@ -17,12 +17,13 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#import #import -#import -#import +#import #import #import +#import +#import +#import #import "CallView.h" #import "CallSideMenuView.h" @@ -150,7 +151,6 @@ static UICompositeViewDescription *compositeDescription = nil; linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_videoPreview)); [self previewTouchLift]; - // Enable tap [singleFingerTap setEnabled:TRUE]; @@ -563,9 +563,11 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { const LinphoneCallParams *remote = linphone_call_get_remote_params(call); /* remote wants to add video */ - if (linphone_core_video_display_enabled(LC) && !linphone_call_params_video_enabled(current) && - linphone_call_params_video_enabled(remote) && - !linphone_core_get_video_policy(LC)->automatically_accept) { + if ((linphone_core_video_display_enabled(LC) && !linphone_call_params_video_enabled(current) && + linphone_call_params_video_enabled(remote)) && + (!linphone_core_get_video_policy(LC)->automatically_accept || + (([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) && + floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max))) { linphone_core_defer_call_update(LC, call); [self displayAskToEnableVideoCall:call]; } else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) { @@ -593,41 +595,61 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { #pragma mark - ActionSheet Functions - (void)displayAskToEnableVideoCall:(LinphoneCall *)call { - if (linphone_core_get_video_policy(LC)->automatically_accept) + if (linphone_core_get_video_policy(LC)->automatically_accept && + !([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)) return; NSString *username = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)]; NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%@ would like to enable video", nil), username]; - UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:title - cancelMessage:nil - confirmMessage:NSLocalizedString(@"ACCEPT", nil) - onCancelClick:^() { - LOGI(@"User declined video proposal"); - if (call == linphone_core_get_current_call(LC)) { - LinphoneCallParams *params = linphone_core_create_call_params(LC,call); - linphone_core_accept_call_update(LC, call, params); - linphone_call_params_destroy(params); - [videoDismissTimer invalidate]; - videoDismissTimer = nil; - } - } - onConfirmationClick:^() { - LOGI(@"User accept video proposal"); - if (call == linphone_core_get_current_call(LC)) { - LinphoneCallParams *params = linphone_core_create_call_params(LC,call); - linphone_call_params_enable_video(params, TRUE); - linphone_core_accept_call_update(LC, call, params); - linphone_call_params_destroy(params); - [videoDismissTimer invalidate]; - videoDismissTimer = nil; - } - } - inController:self]; - videoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:30 - target:self - selector:@selector(dismissVideoActionSheet:) - userInfo:sheet - repeats:NO]; + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground && + floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.title = NSLocalizedString(@"Video request", nil); + content.body = title; + content.categoryIdentifier = @"video_request"; + + UNNotificationRequest *req = + [UNNotificationRequest requestWithIdentifier:@"video_request" content:content trigger:NULL]; + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req + withCompletionHandler:^(NSError *_Nullable error) { + // Enable or disable features based on authorization. + if (error) { + LOGD(@"Error while adding notification request :"); + LOGD(error.description); + } + }]; + } else { + UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:title + cancelMessage:nil + confirmMessage:NSLocalizedString(@"ACCEPT", nil) + onCancelClick:^() { + LOGI(@"User declined video proposal"); + if (call == linphone_core_get_current_call(LC)) { + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + [videoDismissTimer invalidate]; + videoDismissTimer = nil; + } + } + onConfirmationClick:^() { + LOGI(@"User accept video proposal"); + if (call == linphone_core_get_current_call(LC)) { + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + [videoDismissTimer invalidate]; + videoDismissTimer = nil; + } + } + inController:self]; + videoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:30 + target:self + selector:@selector(dismissVideoActionSheet:) + userInfo:sheet + repeats:NO]; + } } - (void)dismissVideoActionSheet:(NSTimer *)timer { diff --git a/Classes/LinphoneAppDelegate.h b/Classes/LinphoneAppDelegate.h index 7b1d011c1..5a72874fb 100644 --- a/Classes/LinphoneAppDelegate.h +++ b/Classes/LinphoneAppDelegate.h @@ -22,6 +22,7 @@ #import #import "LinphoneCoreSettingsStore.h" +#import "ProviderDelegate.h" #import #import @@ -31,14 +32,13 @@ BOOL startedInBackground; } -- (void)processRemoteNotification:(NSDictionary*)userInfo; - (void)registerForNotifications:(UIApplication *)app; @property (nonatomic, retain) UIAlertController *waitingIndicator; @property (nonatomic, retain) NSString *configURL; @property (nonatomic, strong) UIWindow* window; @property PKPushRegistry* voipRegistry; - +@property ProviderDelegate *del; @end diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 726dd2d23..dabfbe4e9 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -45,6 +45,7 @@ startedInBackground = FALSE; } return self; + [[UIApplication sharedApplication] setDelegate:self]; } #pragma mark - @@ -113,7 +114,9 @@ } instance->currentCallContextBeforeGoingBackground.call = 0; } else if (linphone_call_get_state(call) == LinphoneCallIncomingReceived) { - [PhoneMainView.instance displayIncomingCall:call]; + if (linphone_core_get_calls_nb(LC) > 1) { + [PhoneMainView.instance displayIncomingCall:call]; + } // in this case, the ringing sound comes from the notification. // To stop it we have to do the iOS7 ring fix... [self fixRing]; @@ -200,25 +203,71 @@ - (void)registerForNotifications:(UIApplication *)app { - LinphoneManager *instance = [LinphoneManager instance]; - if (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_8_0) { - //[app unregisterForRemoteNotifications]; - // iOS8 and more : PushKit - self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; - self.voipRegistry.delegate = self; - - // Initiate registration. - self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; - } else { - /* iOS7 and below */ - if (!instance.isTesting) { - NSUInteger notifTypes = - UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge; - [app registerForRemoteNotificationTypes:notifTypes]; - } - } + self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; + self.voipRegistry.delegate = self; + + // Initiate registration. + self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + // Call category + UNNotificationAction *act_ans = + [UNNotificationAction actionWithIdentifier:@"Answer" + title:NSLocalizedString(@"Answer", nil) + options:UNNotificationActionOptionForeground]; + UNNotificationAction *act_dec = [UNNotificationAction actionWithIdentifier:@"Decline" + title:NSLocalizedString(@"Decline", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *cat_call = + [UNNotificationCategory categoryWithIdentifier:@"call_cat" + actions:[NSArray arrayWithObjects:act_ans, act_dec, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + + // Msg category + UNTextInputNotificationAction *act_reply = + [UNTextInputNotificationAction actionWithIdentifier:@"Reply" + title:NSLocalizedString(@"Reply", nil) + options:UNNotificationActionOptionNone]; + UNNotificationAction *act_seen = + [UNNotificationAction actionWithIdentifier:@"Seen" + title:NSLocalizedString(@"Mark as seen", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *cat_msg = + [UNNotificationCategory categoryWithIdentifier:@"msg_cat" + actions:[NSArray arrayWithObjects:act_reply, act_seen, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + + // Video Request Category + UNNotificationAction *act_accept = + [UNNotificationAction actionWithIdentifier:@"Accept" + title:NSLocalizedString(@"Accept", nil) + options:UNNotificationActionOptionForeground]; + + UNNotificationAction *act_refuse = [UNNotificationAction actionWithIdentifier:@"Cancel" + title:NSLocalizedString(@"Cancel", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *video_call = + [UNNotificationCategory categoryWithIdentifier:@"video_request" + actions:[NSArray arrayWithObjects:act_accept, act_refuse, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + + // ZRTP verification category + UNNotificationAction *act_confirm = [UNNotificationAction actionWithIdentifier:@"Confirm" + title:NSLocalizedString(@"Accept", nil) + options:UNNotificationActionOptionNone]; + + UNNotificationAction *act_deny = [UNNotificationAction actionWithIdentifier:@"Deny" + title:NSLocalizedString(@"Deny", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *cat_zrtp = + [UNNotificationCategory categoryWithIdentifier:@"zrtp_request" + actions:[NSArray arrayWithObjects:act_confirm, act_deny, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + [UNUserNotificationCenter currentNotificationCenter].delegate = self; [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | @@ -229,6 +278,8 @@ LOGD(error.description); } }]; + NSSet *categories = [NSSet setWithObjects:cat_call, cat_msg, video_call, cat_zrtp, nil]; + [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; } } #pragma deploymate pop @@ -242,6 +293,11 @@ BOOL start_at_boot = [instance lpConfigBoolForKey:@"start_at_boot_preference"]; [self registerForNotifications:app]; + 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; if (!start_at_boot || !background_mode) { @@ -264,14 +320,6 @@ [PhoneMainView.instance startUp]; [PhoneMainView.instance updateStatusBar:nil]; - if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_8_0) { - NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; - if (remoteNotif) { - LOGI(@"PushNotification from launch received."); - [self processRemoteNotification:remoteNotif]; - } - } - if (bgStartId != UIBackgroundTaskInvalid) [[UIApplication sharedApplication] endBackgroundTask:bgStartId]; @@ -290,7 +338,7 @@ - (void)applicationWillTerminate:(UIApplication *)application { LOGI(@"%@", NSStringFromSelector(_cmd)); - + LinphoneManager.instance.conf = TRUE; linphone_core_terminate_all_calls(LC); // destroyLinphoneCore automatically unregister proxies but if we are using @@ -536,8 +584,7 @@ didInvalidatePushTokenForType:(NSString *)type { forType:(NSString *)type { LOGI(@"PushKit : incoming voip notfication: %@", payload.dictionaryPayload); - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - // Call category + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { // Call category UNNotificationAction *act_ans = [UNNotificationAction actionWithIdentifier:@"Answer" title:NSLocalizedString(@"Answer", nil) @@ -550,7 +597,6 @@ didInvalidatePushTokenForType:(NSString *)type { actions:[NSArray arrayWithObjects:act_ans, act_dec, nil] intentIdentifiers:[[NSMutableArray alloc] init] options:UNNotificationCategoryOptionCustomDismissAction]; - // Msg category UNTextInputNotificationAction *act_reply = [UNTextInputNotificationAction actionWithIdentifier:@"Reply" @@ -566,6 +612,35 @@ didInvalidatePushTokenForType:(NSString *)type { intentIdentifiers:[[NSMutableArray alloc] init] options:UNNotificationCategoryOptionCustomDismissAction]; + // Video Request Category + UNNotificationAction *act_accept = + [UNNotificationAction actionWithIdentifier:@"Accept" + title:NSLocalizedString(@"Accept", nil) + options:UNNotificationActionOptionForeground]; + + UNNotificationAction *act_refuse = [UNNotificationAction actionWithIdentifier:@"Cancel" + title:NSLocalizedString(@"Cancel", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *video_call = + [UNNotificationCategory categoryWithIdentifier:@"video_request" + actions:[NSArray arrayWithObjects:act_accept, act_refuse, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + + // ZRTP verification category + UNNotificationAction *act_confirm = [UNNotificationAction actionWithIdentifier:@"Confirm" + title:NSLocalizedString(@"Accept", nil) + options:UNNotificationActionOptionNone]; + + UNNotificationAction *act_deny = [UNNotificationAction actionWithIdentifier:@"Deny" + title:NSLocalizedString(@"Deny", nil) + options:UNNotificationActionOptionNone]; + UNNotificationCategory *cat_zrtp = + [UNNotificationCategory categoryWithIdentifier:@"zrtp_request" + actions:[NSArray arrayWithObjects:act_confirm, act_deny, nil] + intentIdentifiers:[[NSMutableArray alloc] init] + options:UNNotificationCategoryOptionCustomDismissAction]; + [UNUserNotificationCenter currentNotificationCenter].delegate = self; [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | @@ -576,7 +651,7 @@ didInvalidatePushTokenForType:(NSString *)type { LOGD(error.description); } }]; - NSSet *categories = [NSSet setWithObjects:cat_call, cat_msg, nil]; + NSSet *categories = [NSSet setWithObjects:cat_call, cat_msg, video_call, cat_zrtp, nil]; [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; } [LinphoneManager.instance setupNetworkReachabilityCallback]; @@ -590,44 +665,45 @@ didInvalidatePushTokenForType:(NSString *)type { forType:(NSString *)type { LOGI(@"PushKit credentials updated"); LOGI(@"voip token: %@", (credentials.token)); - LOGI(@"%@ : %@", NSStringFromSelector(_cmd), credentials.token); dispatch_async(dispatch_get_main_queue(), ^{[LinphoneManager.instance setPushNotificationToken:credentials.token];}); } -#pragma mark - UserNotifications Framework +#pragma mark - UNUserNotifications Framework - (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionAlert); } - - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { LOGD(@"UN : response recieved"); LOGD(response.description); - - 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 ([response.actionIdentifier isEqual:@"Answer"]) { - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; - [LinphoneManager.instance acceptCallForCallId:[response.notification.request.content.userInfo objectForKey:@"callId"]]; - } else if ([response.actionIdentifier isEqual:@"Decline"]) { - linphone_core_decline_call(LC, call, LinphoneReasonDeclined); - } else if ([response.actionIdentifier isEqual:@"Reply"]) { - LinphoneCore *lc = [LinphoneManager getLc]; - NSString *replyText = [(UNTextInputNotificationResponse*)response userText]; - NSString *from = [response.notification.request.content.userInfo objectForKey:@"from_addr"]; - LinphoneChatRoom *room = linphone_core_get_chat_room_from_uri(lc, [from UTF8String]); - if (room) { - LinphoneChatMessage *msg = linphone_chat_room_create_message(room, replyText.UTF8String); - linphone_chat_room_send_chat_message(room, msg); + + NSString *callId = (NSString *)[response.notification.request.content.userInfo objectForKey:@"CallId"]; + 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; + } + } + + if ([response.actionIdentifier isEqual:@"Answer"]) { + // use the standard handler + [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + linphone_core_accept_call(LC, call); + } else if ([response.actionIdentifier isEqual:@"Decline"]) { + linphone_core_decline_call(LC, call, LinphoneReasonDeclined); + } else if ([response.actionIdentifier isEqual:@"Reply"]) { + LinphoneCore *lc = [LinphoneManager getLc]; + NSString *replyText = [(UNTextInputNotificationResponse *)response userText]; + NSString *from = [response.notification.request.content.userInfo objectForKey:@"from_addr"]; + LinphoneChatRoom *room = linphone_core_get_chat_room_from_uri(lc, [from UTF8String]); + if (room) { + LinphoneChatMessage *msg = linphone_chat_room_create_message(room, replyText.UTF8String); + linphone_chat_room_send_chat_message(room, msg); linphone_chat_room_mark_as_read(room); TabBarView *tab = (TabBarView *)[PhoneMainView.instance.mainViewController getCachedController:NSStringFromClass(TabBarView.class)]; @@ -645,18 +721,99 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response [PhoneMainView.instance updateApplicationBadgeNumber]; } + } else if ([response.actionIdentifier isEqual:@"Cancel"]) { + LOGI(@"User declined video proposal"); + if (call == linphone_core_get_current_call(LC)) { + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + } + } else if ([response.actionIdentifier isEqual:@"Accept"]) { + LOGI(@"User accept video proposal"); + if (call == linphone_core_get_current_call(LC)) { + [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; + [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + } + } else if ([response.actionIdentifier isEqual:@"Confirm"]) { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, YES); + } + } else if ([response.actionIdentifier isEqual:@"Deny"]) { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, NO); + } } else { // in this case the value is : com.apple.UNNotificationDefaultActionIdentifier if ([response.notification.request.content.categoryIdentifier isEqual:@"call_cat"]) { - + [PhoneMainView.instance displayIncomingCall:call]; } else if ([response.notification.request.content.categoryIdentifier isEqual:@"msg_cat"]) { [PhoneMainView.instance changeCurrentView:ChatsListView.compositeViewDescription]; + } else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) { + [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + NSTimer *videoDismissTimer = nil; + + UIConfirmationDialog *sheet = + [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body + cancelMessage:nil + confirmMessage:NSLocalizedString(@"ACCEPT", nil) + onCancelClick:^() { + LOGI(@"User declined video proposal"); + if (call == linphone_core_get_current_call(LC)) { + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + [videoDismissTimer invalidate]; + } + } + onConfirmationClick:^() { + LOGI(@"User accept video proposal"); + if (call == linphone_core_get_current_call(LC)) { + LinphoneCallParams *params = linphone_core_create_call_params(LC, call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(LC, call, params); + linphone_call_params_destroy(params); + [videoDismissTimer invalidate]; + } + } + inController:PhoneMainView.instance]; + + videoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:30 + target:self + selector:@selector(dismissVideoActionSheet:) + userInfo:sheet + repeats:NO]; + } else if ([response.notification.request.content.categoryIdentifier isEqual:@"zrtp_request"]) { + [UIConfirmationDialog + ShowWithMessage:[NSString stringWithFormat:NSLocalizedString( + @"Confirm the following SAS with peer:\n%s", nil), + linphone_call_get_authentication_token(call)] + cancelMessage:NSLocalizedString(@"DENY", nil) + confirmMessage:NSLocalizedString(@"ACCEPT", nil) + onCancelClick:^() { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, NO); + } + } + onConfirmationClick:^() { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, YES); + } + }]; } else { // Missed call [PhoneMainView.instance changeCurrentView:HistoryListView.compositeViewDescription]; } } } -#pragma mark - User notifications +- (void)dismissVideoActionSheet:(NSTimer *)timer { + UIConfirmationDialog *sheet = (UIConfirmationDialog *)timer.userInfo; + [sheet dismiss]; +} + +#pragma mark - NSUser notifications - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h index 6ffe88b29..c785def8c 100644 --- a/Classes/LinphoneManager.h +++ b/Classes/LinphoneManager.h @@ -34,6 +34,8 @@ #include "linphone/linphonecore.h" #include "bctoolbox/list.h" +#import "ProviderDelegate.h" + extern NSString *const LINPHONERC_APPLICATION_KEY; extern NSString *const kLinphoneCoreUpdate; @@ -136,6 +138,7 @@ typedef struct _LinphoneManagerSounds { - (void)configurePushTokenForProxyConfig: (LinphoneProxyConfig*)cfg; - (BOOL)popPushCallID:(NSString*) callId; - (void)acceptCallForCallId:(NSString*)callid; +- (LinphoneCall *)callByCallId:(NSString *)call_id; - (void)cancelLocalNotifTimerForCallId:(NSString*)callid; + (BOOL)langageDirectionIsRTL; @@ -188,6 +191,9 @@ typedef struct _LinphoneManagerSounds { - (void)shouldPresentLinkPopup; +- (void)setProviderDelegate:(ProviderDelegate *)del; +@property ProviderDelegate *providerDelegate; + @property (readonly) BOOL isTesting; @property(readonly, strong) FastAddressBook *fastAddressBook; @property Connectivity connectivity; @@ -211,5 +217,6 @@ typedef struct _LinphoneManagerSounds { @property(readonly) InAppProductsManager *iapManager; @property(strong, nonatomic) NSMutableArray *fileTransferDelegates; @property BOOL nextCallIsTransfer; +@property BOOL conf; @end diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index 4a54cd701..aa949b751 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -251,7 +251,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre _database = NULL; _speakerEnabled = FALSE; _bluetoothEnabled = FALSE; - + _conf = FALSE; _fileTransferDelegates = [[NSMutableArray alloc] init]; pushCallIDs = [[NSMutableArray alloc] init]; @@ -652,13 +652,16 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char const LinphoneAddress *addr = linphone_call_get_remote_address(call); NSString *address = [FastAddressBook displayNameForAddress:addr]; + LinphoneCallLog *callLog = linphone_call_get_call_log(call); + NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog)]; if (state == LinphoneCallIncomingReceived) { /*first step is to re-enable ctcall center*/ CTCallCenter *lCTCallCenter = [[CTCallCenter alloc] init]; /*should we reject this call ?*/ - if ([lCTCallCenter currentCalls] != nil) { + 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); @@ -668,40 +671,68 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char return; } - if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call && + (linphone_core_get_calls_nb(LC) < 2)) { + NSString *callId = + [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))]; + NSString *address = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)]; - LinphoneCallLog *callLog = linphone_call_get_call_log(call); - NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog)]; + NSUUID *uuid = [NSUUID UUID]; + [LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid]; + [LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId]; + BOOL video = FALSE; + video = (([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) && + linphone_core_get_video_policy(LC)->automatically_accept && + linphone_call_params_video_enabled(linphone_call_get_remote_params(call))); + [LinphoneManager.instance.providerDelegate reportIncomingCallwithUUID:uuid handle:address video:video]; + } else if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + // 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){ + }]; + } - //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]; + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + // 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. @@ -745,40 +776,6 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char } } } - } else { - 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}; - if ([self lpConfigBoolForKey:@"repeat_call_notification"] == YES) { - data->timer = [NSTimer scheduledTimerWithTimeInterval:5 - target:self - selector:@selector(userNotifContinue:) - userInfo:content - repeats:TRUE]; - } - UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req withCompletionHandler:^(NSError * _Nullable error) { - // Enable or disable features based on authorization. - if (error) { - LOGD(@"Error while adding notification request :"); - LOGD(error.description); - } - }]; - - 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]; - } - } } } @@ -800,55 +797,102 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char _bluetoothEnabled = FALSE; /*IOS specific*/ linphone_core_start_dtmf_stream(theLinphoneCore); - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)) { - 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) { - 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) { - // Enable or disable features based on authorization. - if (error) { - LOGD(@"Error while adding notification request :"); - LOGD(error.description); - } - }]; - } - linphone_core_set_network_reachable(LC, FALSE); - LinphoneManager.instance.connectivity = none; - } } + if (incallBgTask) { [[UIApplication sharedApplication] endBackgroundTask:incallBgTask]; incallBgTask = 0; } - 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; + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { + 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) { + 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) { + // Enable or disable features based on authorization. + if (error) { + LOGD(@"Error while adding notification request :"); + LOGD(error.description); + } + }]; + } + linphone_core_set_network_reachable(LC, FALSE); + LinphoneManager.instance.connectivity = none; } - [[UIApplication sharedApplication] cancelLocalNotification:data->notification]; + LinphoneCallLog *callLog2 = linphone_call_get_call_log(call); + NSString *callId2 = [NSString stringWithUTF8String:linphone_call_log_get_call_id(callLog2)]; + NSUUID *uuid = (NSUUID *)[self.providerDelegate.uuids objectForKey:callId2]; + if (uuid) { + // For security reasons do not display name + // CXCallUpdate *update = [[CXCallUpdate alloc] init]; + // update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:@"Unknown"]; + //[LinphoneManager.instance.providerDelegate.provider reportCallWithUUID:uuid updated:update]; - data->notification = nil; + if (linphone_core_get_calls_nb(LC) > 0 && !_conf) { + // Create a CallKit call because there's not ! + _conf = FALSE; + LinphoneCall *callKit_call = (LinphoneCall *)linphone_core_get_calls(LC)->data; + NSString *callKit_callId = [NSString + stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(callKit_call))]; + NSUUID *callKit_uuid = [NSUUID UUID]; + [LinphoneManager.instance.providerDelegate.uuids setObject:callKit_uuid forKey:callKit_callId]; + [LinphoneManager.instance.providerDelegate.calls setObject:callKit_callId 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]; + } - 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]; + [self.providerDelegate.uuids removeObjectForKey:callId2]; + [self.providerDelegate.calls removeObjectForKey:uuid]; + [self.providerDelegate.provider reportCallWithUUID:uuid + endedAtDate:NULL + reason:CXCallEndedReasonRemoteEnded]; + } + } 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]; + } } } } @@ -1704,7 +1748,7 @@ 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 @@ -1915,6 +1959,13 @@ 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(LC); + bctbx_list_t *call_tmp = bctbx_list_find_custom(calls, (bctbx_compare_func)comp_call_id, [call_id UTF8String]); + 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); @@ -2137,7 +2188,22 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { LinphoneCall *c = linphone_core_get_current_call(theLinphoneCore); LOGI(@"Sound interruption detected!"); if (c && linphone_call_get_state(c) == LinphoneCallStreamsRunning) { - linphone_core_pause_call(theLinphoneCore, c); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString + stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(c))]]; + if (!uuid) { + linphone_core_pause_call(theLinphoneCore, c); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_pause_call(theLinphoneCore, c); + } } } @@ -2330,16 +2396,16 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { // Then check that no GSM calls are in progress, abort otherwise. CTCallCenter *callCenter = [[CTCallCenter alloc] init]; - if ([callCenter currentCalls] != nil) { + if ([callCenter currentCalls] != nil && floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { LOGE(@"GSM call in progress, cancelling outgoing SIP call request"); UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Cannot make call", nil) message:NSLocalizedString(@"Please terminate GSM call first.", nil) preferredStyle:UIAlertControllerStyleAlert]; - + UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}]; - + [errView addAction:defaultAction]; [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; return FALSE; @@ -2360,6 +2426,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; return FALSE; } + LinphoneAddress *addr = linphone_address_clone(iaddr); NSString *displayName = [FastAddressBook displayNameForAddress:addr]; @@ -2378,13 +2445,15 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { addr, [[LinphoneManager.instance lpConfigStringForKey:@"domain" inSection:@"assistant"] UTF8String]); } + LinphoneCall *call; if (LinphoneManager.instance.nextCallIsTransfer) { char *caddr = linphone_address_as_string(addr); - linphone_core_transfer_call(theLinphoneCore, linphone_core_get_current_call(theLinphoneCore), caddr); + call = linphone_core_get_current_call(theLinphoneCore); + linphone_core_transfer_call(theLinphoneCore, call, caddr); LinphoneManager.instance.nextCallIsTransfer = NO; ms_free(caddr); } else { - LinphoneCall *call = linphone_core_invite_address_with_params(theLinphoneCore, addr, lcallParams); + 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 @@ -2704,20 +2773,22 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { } - (void)handleGSMCallInteration:(id)cCenter { - CTCallCenter *ct = (CTCallCenter *)cCenter; - /* pause current call, if any */ - LinphoneCall *call = linphone_core_get_current_call(theLinphoneCore); - if ([ct currentCalls] != nil) { - if (call) { - LOGI(@"Pausing SIP call because GSM call"); - linphone_core_pause_call(theLinphoneCore, call); - [self startCallPausedLongRunningTask]; - } else if (linphone_core_is_in_conference(theLinphoneCore)) { - LOGI(@"Leaving conference call because GSM call"); - linphone_core_leave_conference(theLinphoneCore); - [self startCallPausedLongRunningTask]; - } - } // else nop, keep call in paused state + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + CTCallCenter *ct = (CTCallCenter *)cCenter; + // pause current call, if any + LinphoneCall *call = linphone_core_get_current_call(theLinphoneCore); + if ([ct currentCalls] != nil) { + if (call) { + LOGI(@"Pausing SIP call because GSM call"); + linphone_core_pause_call(theLinphoneCore, call); + [self startCallPausedLongRunningTask]; + } else if (linphone_core_is_in_conference(theLinphoneCore)) { + LOGI(@"Leaving conference call because GSM call"); + linphone_core_leave_conference(theLinphoneCore); + [self startCallPausedLongRunningTask]; + } + } // else nop, keep call in paused state + } } - (NSString *)contactFilter { diff --git a/Classes/LinphoneUI/StatusBarView.m b/Classes/LinphoneUI/StatusBarView.m index 86975f227..92cc0631e 100644 --- a/Classes/LinphoneUI/StatusBarView.m +++ b/Classes/LinphoneUI/StatusBarView.m @@ -20,6 +20,7 @@ #import "StatusBarView.h" #import "LinphoneManager.h" #import "PhoneMainView.h" +#import @implementation StatusBarView { @@ -320,23 +321,43 @@ NSString *message = [NSString stringWithFormat:NSLocalizedString(@"Confirm the following SAS with peer:\n%s", nil), linphone_call_get_authentication_token(call)]; - if (securityDialog == nil) { - __block __strong StatusBarView *weakSelf = self; - securityDialog = [UIConfirmationDialog ShowWithMessage:message - cancelMessage:NSLocalizedString(@"DENY", nil) - confirmMessage:NSLocalizedString(@"ACCEPT", nil) - onCancelClick:^() { - if (linphone_core_get_current_call(LC) == call) { - linphone_call_set_authentication_token_verified(call, NO); - } - weakSelf->securityDialog = nil; - } - onConfirmationClick:^() { - if (linphone_core_get_current_call(LC) == call) { - linphone_call_set_authentication_token_verified(call, YES); - } - weakSelf->securityDialog = nil; - }]; + if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground && + floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.title = NSLocalizedString(@"ZRTP verification", nil); + content.body = message; + content.categoryIdentifier = @"zrtp_request"; + + UNNotificationRequest *req = + [UNNotificationRequest requestWithIdentifier:@"zrtp_request" content:content trigger:NULL]; + [[UNUserNotificationCenter currentNotificationCenter] + addNotificationRequest:req + withCompletionHandler:^(NSError *_Nullable error) { + // Enable or disable features based on authorization. + if (error) { + LOGD(@"Error while adding notification request :"); + LOGD(error.description); + } + }]; + } else { + if (securityDialog == nil) { + __block __strong StatusBarView *weakSelf = self; + securityDialog = [UIConfirmationDialog ShowWithMessage:message + cancelMessage:NSLocalizedString(@"DENY", nil) + confirmMessage:NSLocalizedString(@"ACCEPT", nil) + onCancelClick:^() { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, NO); + } + weakSelf->securityDialog = nil; + } + onConfirmationClick:^() { + if (linphone_core_get_current_call(LC) == call) { + linphone_call_set_authentication_token_verified(call, YES); + } + weakSelf->securityDialog = nil; + }]; + } } } } diff --git a/Classes/LinphoneUI/UIHangUpButton.m b/Classes/LinphoneUI/UIHangUpButton.m index b902c3c37..69d4c729d 100644 --- a/Classes/LinphoneUI/UIHangUpButton.m +++ b/Classes/LinphoneUI/UIHangUpButton.m @@ -94,8 +94,9 @@ if (linphone_core_is_in_conference(LC) || // In conference (linphone_core_get_conference_size(LC) > 0 && [UIHangUpButton callCount] == 0) // Only one conf ) { + LinphoneManager.instance.conf = TRUE; linphone_core_terminate_conference(LC); - } else if (currentcall != NULL) { // In a call + } else if (currentcall != NULL) { linphone_core_terminate_call(LC, currentcall); } else { const MSList *calls = linphone_core_get_calls(LC); diff --git a/Classes/LinphoneUI/UIPauseButton.m b/Classes/LinphoneUI/UIPauseButton.m index 154b2d287..9ca7b0689 100644 --- a/Classes/LinphoneUI/UIPauseButton.m +++ b/Classes/LinphoneUI/UIPauseButton.m @@ -82,7 +82,22 @@ switch (type) { case UIPauseButtonType_Call: { if (call != nil) { - linphone_core_pause_call(LC, call); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( + linphone_call_get_call_log(call))]]; + if (!uuid) { + linphone_core_pause_call(LC, call); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_pause_call(LC, call); + } } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } @@ -98,7 +113,22 @@ case UIPauseButtonType_CurrentCall: { LinphoneCall *currentCall = [UIPauseButton getCall]; if (currentCall != nil) { - linphone_core_pause_call(LC, currentCall); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( + linphone_call_get_call_log(currentCall))]]; + if (!uuid) { + linphone_core_pause_call(LC, currentCall); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_pause_call(LC, currentCall); + } } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } @@ -111,24 +141,65 @@ switch (type) { case UIPauseButtonType_Call: { if (call != nil) { - linphone_core_resume_call(LC, call); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( + linphone_call_get_call_log(call))]]; + if (!uuid) { + linphone_core_resume_call(LC, call); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_resume_call(LC, call); + } } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } break; } case UIPauseButtonType_Conference: { - linphone_core_enter_conference(LC); - // Fake event - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids allValues].firstObject; + if (!uuid) { + linphone_core_enter_conference(LC); + // Fake event + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_enter_conference(LC); + // Fake event + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; + } break; } case UIPauseButtonType_CurrentCall: { LinphoneCall *currentCall = [UIPauseButton getCall]; - if (currentCall != nil) { - linphone_core_resume_call(LC, currentCall); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( + linphone_call_get_call_log(currentCall))]]; + if (!uuid) { + linphone_core_resume_call(LC, currentCall); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; } else { - LOGW(@"Cannot toggle pause buttton, because no current call"); + linphone_core_resume_call(LC, currentCall); } break; } diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m index 8e124b932..c81c3eefb 100644 --- a/Classes/PhoneMainView.m +++ b/Classes/PhoneMainView.m @@ -323,7 +323,9 @@ static RootViewManager *rootViewManagerInstance = nil; switch (state) { case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: { - [self displayIncomingCall:call]; + if (linphone_core_get_calls_nb(LC) > 1) { + [self displayIncomingCall:call]; + } break; } case LinphoneCallOutgoingInit: { @@ -334,6 +336,21 @@ static RootViewManager *rootViewManagerInstance = nil; case LinphoneCallConnected: 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) { + 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: { @@ -357,7 +374,23 @@ static RootViewManager *rootViewManagerInstance = nil; [self popCurrentView]; } } else { - linphone_core_resume_call(LC, (LinphoneCall *)calls->data); + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString + stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log( + (LinphoneCall *)calls->data))]]; + if (!uuid) { + linphone_core_resume_call(LC, (LinphoneCall *)calls->data); + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + linphone_core_resume_call(LC, (LinphoneCall *)calls->data); + } [self changeCurrentView:CallView.compositeViewDescription]; } break; @@ -366,8 +399,45 @@ static RootViewManager *rootViewManagerInstance = nil; case LinphoneCallEarlyUpdating: case LinphoneCallIdle: case LinphoneCallOutgoingEarlyMedia: - case LinphoneCallOutgoingProgress: - case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingProgress: { + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call && + (linphone_core_get_calls_nb(LC) < 2)) { + // Create CallKit Call + NSString *callId = + [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))]; + NSUUID *uuid = [NSUUID UUID]; + [LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId]; + [LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid]; + NSString *address = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)]; + 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){ + }]; + } + } + case LinphoneCallOutgoingRinging: { + 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]; + [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]; + } + } + } case LinphoneCallPaused: case LinphoneCallPausing: case LinphoneCallRefered: diff --git a/Classes/ProviderDelegate.h b/Classes/ProviderDelegate.h new file mode 100644 index 000000000..9652be9d2 --- /dev/null +++ b/Classes/ProviderDelegate.h @@ -0,0 +1,26 @@ +// +// ProviderDelegate.h +// linphone +// +// Created by REIS Benjamin on 29/11/2016. +// +// + +#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; + +- (void)reportIncomingCallwithUUID:(NSUUID *)uuid handle:(NSString *)handle video:(BOOL)video; +- (void)config; +@end + +#endif /* ProviderDelegate_h */ diff --git a/Classes/ProviderDelegate.m b/Classes/ProviderDelegate.m new file mode 100644 index 000000000..97bdf5961 --- /dev/null +++ b/Classes/ProviderDelegate.m @@ -0,0 +1,227 @@ +// +// ProviderDelegate.m +// linphone +// +// Created by REIS Benjamin on 29/11/2016. +// +// + +#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]; + + CXCallController *callController = [[CXCallController alloc] initWithQueue:dispatch_get_main_queue()]; + [callController.callObserver setDelegate:self queue:dispatch_get_main_queue()]; + self.controller = callController; + + if (!self) { + LOGD(@"ProviderDelegate not initialized..."); + } + return self; +} + +- (void)config { + CXProviderConfiguration *config = [[CXProviderConfiguration alloc] initWithLocalizedName:@"Linphone"]; + config.ringtoneSound = @"shortring.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]; + self.provider = [[CXProvider alloc] initWithConfiguration:config]; + [self.provider setDelegate:self queue:dispatch_get_main_queue()]; +} + +- (void)reportIncomingCallwithUUID:(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 = video; + + LOGD(@"configuring audio session"); + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; + [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; + + double sampleRate = 44100.0; + [audioSession setPreferredSampleRate:sampleRate error:nil]; + + NSTimeInterval bufferDuration = .005; + [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; + LOGD(@"Activating audio session"); + [audioSession setActive:TRUE error:nil]; + + // Report incoming call to system + LOGD(@"CallKit: report new incoming call"); + [self.provider reportNewIncomingCallWithUUID:uuid + update:update + completion:^(NSError *error) { + LOGD(@"configuring audio session"); + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; + [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; + + double sampleRate = 44100.0; + [audioSession setPreferredSampleRate:sampleRate error:nil]; + + NSTimeInterval bufferDuration = .005; + [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; + LOGD(@"Activating audio session"); + [audioSession setActive:TRUE error:nil]; + }]; +} + +#pragma mark - CXProdiverDelegate Protocol + +- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action { + LOGD(@"CallKit : Answering Call"); + NSUUID *uuid = action.callUUID; + [action fulfill]; + + NSString *callID = [self.calls objectForKey:uuid]; // first, make sure this callid is not already involved in a call + LinphoneCall *call = [LinphoneManager.instance callByCallId:callID]; + if (call != NULL) { + BOOL video = (!([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) && + linphone_core_get_video_policy(LC)->automatically_accept && + linphone_call_params_video_enabled(linphone_call_get_remote_params((LinphoneCall *)call))); + [LinphoneManager.instance acceptCall:(LinphoneCall *)call evenWithVideo:video]; + return; + }; +} + +- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action { + LOGD(@"CallKit : Starting Call"); + // To restart Audio Unit + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; + [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; + double sampleRate = 44100.0; + [audioSession setPreferredSampleRate:sampleRate error:nil]; + NSTimeInterval bufferDuration = .005; + [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; + [audioSession setActive:TRUE error:nil]; + [action fulfill]; +} + +- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action { + LOGD(@"CallKit : Ending the Call"); + if (linphone_core_is_in_conference(LC)) { + LinphoneManager.instance.conf = TRUE; + linphone_core_terminate_conference(LC); + } else if (linphone_core_get_calls_nb(LC) > 1) { + LinphoneManager.instance.conf = TRUE; + linphone_core_terminate_all_calls(LC); + } else { + NSUUID *uuid = action.callUUID; + NSString *callID = [self.calls objectForKey:uuid]; + LinphoneCall *call = [LinphoneManager.instance callByCallId:callID]; + if (call) { + linphone_core_terminate_call(LC, (LinphoneCall *)call); + } + } + [action fulfill]; +} + +- (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 { + LOGD(@"CallKit : Call paused status changed"); + // To restart Audio Unit + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; + [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; + double sampleRate = 44100.0; + [audioSession setPreferredSampleRate:sampleRate error:nil]; + NSTimeInterval bufferDuration = .005; + [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; + [audioSession setActive:TRUE error:nil]; + [action fulfill]; + + if (linphone_core_is_in_conference(LC) && action.isOnHold) { + linphone_core_leave_conference(LC); + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; + return; + } + + if (linphone_core_get_calls_nb(LC) > 1 && action.isOnHold) { + linphone_core_pause_all_calls(LC); + return; + } + + NSUUID *uuid = action.callUUID; + NSString *callID = [self.calls objectForKey:uuid]; + if (!callID) { + return; + } + + LinphoneCall *call = [LinphoneManager.instance callByCallId:callID]; + if (call) { + if (action.isOnHold) { + linphone_core_pause_call(LC, (LinphoneCall *)call); + } else { + if (linphone_core_get_conference(LC)) { + linphone_core_enter_conference(LC); + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; + } else { + linphone_core_resume_call(LC, (LinphoneCall *)call); + } + } + } +} + +- (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action { + [action fulfill]; + LOGD(@"CallKit : playing DTMF"); + NSUUID *call_uuid = action.callUUID; + NSString *callID = [self.calls objectForKey:call_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"); +} + +- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(nonnull AVAudioSession *)audioSession { + LOGD(@"CallKit : Audio session deactivated"); +} + +- (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/Resources/images/callkit_logo.png b/Resources/images/callkit_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb444739fdc7c137a04ce5d3fbcaad4802fedfb GIT binary patch literal 1309 zcmV+&1>*XNP)TK zP(e_LRHS(kQlw-Q1YIPkIQ^(Wsi}kcSDI$(IHS!xU9=8-e4L+|oFNG}9L_m=uf6xX z*4lgRcP+Tm{^u=S4j|OQKs_)5*ppH^e>s4vRj*g)sqgwvd%I68sMZ04fm)yjsEVO( zpdDyWDV;4b_Znada2Iepa7PTC1e#My`?B&iy#O^e7H*19BhUa0$^g6w90v}m`+)<% zPr%Q>Xka!l510jv&uy_S53WZ5)%m~zU>YzctTq6sF5v-zt^nH!90uBek-$WtE~8Mk zt?aEEr>Wc2cD36}YnA4=mBLX|1lR!J8Q|42_`AbGUGclHR>88pJ?pRaD9pqGeiL}8 zi0r9w)luMJ{5}T!87?chuqndDwQ=rhd*HCfl+uYxE^ScX&zb%&^?UUd^+9!f50A$P zb&mQ(gphq@+}y4{rw*(Xla>ddLffw{RVS#`mk{r+R&R;*ca;Fy8Hx-mwa85MSZ2Z} z>TPOO9|qRNSZe&Rs&jf~*hIE(%Hirl9jECTYp`l4Es6>f}kpt`xI z=d2Gv&QW(40eM($$qKf_z!~+?zI1C3K(?qyi-7!49bOPx8J%0yiT?pi7uW{loWCdL8&G*If(DRR^MOj~lc&r`e{^>^F6dI=dgO*i^*L zd(?W=9bwW%nF))EOgO79>_?F6itITG^=tJ)4&dH05BvT71hBctUbCu#v(*FvMZF0a zR%Wdg{baEki(|UOb>{73bz7#ibZH2_=g8F-Gbg_)w8MQgL z@8_BEbD>~$F@Q;ZCj<|u)3fD?2&Nk@A-nrUosrvfRfa5ds~@Nhm2Tjwu)@ipV6|$> z-MAOjZ{qf!4hweYKJDspb*H+dkavV38r0Pp@LlSc>X@?Wpqf%js!M>UqXMuVSPg8C zvc`ps{8(T{WOSqBb_$#Y_5xc|N;?W?8W;rZKzeZnI zSkK^^bx|&v6#4S?Kq0-{348{86mj>`>VxVaU|ei81!w`LN5Y