123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- /**
- * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- #import "CallKitManager.h"
- #import <CallKit/CXError.h>
- #import "CallConstants.h"
- #import "NCAudioController.h"
- #import "NCAPIController.h"
- #import "NCAppBranding.h"
- #import "NCDatabaseManager.h"
- #import "NCNotificationController.h"
- #import "NCRoomsManager.h"
- #import "NCSettingsController.h"
- #import "NCUserInterfaceController.h"
- #import "NextcloudTalk-Swift.h"
- NSString * const CallKitManagerDidAnswerCallNotification = @"CallKitManagerDidAnswerCallNotification";
- NSString * const CallKitManagerDidEndCallNotification = @"CallKitManagerDidEndCallNotification";
- NSString * const CallKitManagerDidStartCallNotification = @"CallKitManagerDidStartCallNotification";
- NSString * const CallKitManagerDidChangeAudioMuteNotification = @"CallKitManagerDidChangeAudioMuteNotification";
- NSString * const CallKitManagerWantsToUpgradeToVideoCallNotification = @"CallKitManagerWantsToUpgradeToVideoCall";
- NSString * const CallKitManagerDidFailRequestingCallTransactionNotification = @"CallKitManagerDidFailRequestingCallTransaction";
- NSTimeInterval const kCallKitManagerMaxRingingTimeSeconds = 45.0;
- NSTimeInterval const kCallKitManagerCheckCallStateEverySeconds = 5.0;
- @interface CallKitManager () <CXProviderDelegate>
- @property (nonatomic, strong) CXProvider *provider;
- @property (nonatomic, strong) CXCallController *callController;
- @property (nonatomic, strong) NSMutableDictionary *hangUpTimers; // uuid -> hangUpTimer
- @property (nonatomic, strong) NSMutableDictionary *callStateTimers; // uuid -> callStateTimer
- @property (nonatomic, assign) BOOL startCallRetried;
- @end
- @implementation CallKitCall
- @end
- @implementation CallKitManager
- + (CallKitManager *)sharedInstance
- {
- static CallKitManager *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[CallKitManager alloc] init];
- [sharedInstance provider];
- });
- return sharedInstance;
- }
- - (id)init
- {
- self = [super init];
- if (self) {
- self.calls = [[NSMutableDictionary alloc] init];
- self.hangUpTimers = [[NSMutableDictionary alloc] init];
- self.callStateTimers = [[NSMutableDictionary alloc] init];
- }
- return self;
- }
- + (BOOL)isCallKitAvailable
- {
- if ([NCUtils isiOSAppOnMac]) {
- // There's currently no support for CallKit when running on MacOS.
- // If this is enabled on MacOS, there's no audio, because we fail to retrieve
- // the streams from CallKit. Tested with MacOS 12 & 13.
- return NO;
- }
- // CallKit should be deactivated in China as requested by Apple
- return ![NSLocale.currentLocale.countryCode isEqual: @"CN"];
- }
- #pragma mark - Getters
- - (CXProvider *)provider
- {
- if (!_provider) {
- _provider = [[CXProvider alloc] initWithConfiguration:[self defaultProviderConfiguration]];
- [_provider setDelegate:self queue:nil];
- }
- return _provider;
- }
- - (CXCallController *)callController
- {
- if (!_callController) {
- _callController = [[CXCallController alloc] init];
- }
- return _callController;
- }
- #pragma mark - Utils
- - (CXCallUpdate *)defaultCallUpdate
- {
- CXCallUpdate *update = [[CXCallUpdate alloc] init];
- update.supportsHolding = NO;
- update.supportsGrouping = NO;
- update.supportsUngrouping = NO;
- update.supportsDTMF = NO;
- update.hasVideo = NO;
-
- return update;
- }
- - (CXProviderConfiguration *)defaultProviderConfiguration
- {
- CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] init];
- configuration.supportsVideo = YES;
- configuration.maximumCallGroups = 1;
- configuration.maximumCallsPerCallGroup = 1;
- configuration.includesCallsInRecents = [NCUserDefaults includeCallsInRecents];
- configuration.supportedHandleTypes = [NSSet setWithObjects:@(CXHandleTypePhoneNumber), @(CXHandleTypeEmailAddress), @(CXHandleTypeGeneric), nil];
- configuration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:@"app-logo-callkit"]);
- return configuration;
- }
- - (CallKitCall *)callForToken:(NSString *)token
- {
- for (CallKitCall *call in [_calls allValues]) {
- if ([call.token isEqualToString:token]) {
- return call;
- }
- }
-
- return nil;;
- }
- #pragma mark - Actions
- - (void)setDefaultProviderConfiguration
- {
- if (_provider) {
- [_provider setConfiguration:[self defaultProviderConfiguration]];
- }
- }
- - (void)reportIncomingCall:(NSString *)token withDisplayName:(NSString *)displayName forAccountId:(NSString *)accountId
- {
- NSString *protectedDataAvailable = @"available";
- if (!UIApplication.sharedApplication.isProtectedDataAvailable) {
- protectedDataAvailable = @"unavailable";
- }
- [NCUtils log:[NSString stringWithFormat:@"Report incoming call for token %@ for account %@. Protected data is %@", token, accountId, protectedDataAvailable]];
- BOOL ongoingCalls = _calls.count > 0;
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
-
- // If the app is not active (e.g. in background) and there is an open chat
- BOOL isAppActive = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
- ChatViewController *chatViewController = [[NCRoomsManager sharedInstance] chatViewController];
- if (!isAppActive && chatViewController) {
- // Leave the chat so it doesn't try to join the chat conversation when the app becomes active.
- [chatViewController leaveChat];
- [[NCUserInterfaceController sharedInstance] presentConversationsList];
- }
-
- // If the incoming call is from a different account
- if (![activeAccount.accountId isEqualToString:accountId]) {
- // If there is an ongoing call then show a local notification
- if (ongoingCalls) {
- [self reportAndCancelIncomingCall:token withDisplayName:displayName forAccountId:accountId];
- return;
- // Change accounts if there are no ongoing calls
- } else {
- [[NCSettingsController sharedInstance] setActiveAccountWithAccountId:accountId];
- }
- }
-
- CXCallUpdate *update = [self defaultCallUpdate];
- update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:token];
- update.localizedCallerName = displayName;
-
- NSUUID *callUUID = [NSUUID new];
- CallKitCall *call = [[CallKitCall alloc] init];
- call.uuid = callUUID;
- call.token = token;
- call.displayName = displayName;
- call.accountId = accountId;
- call.update = update;
- call.reportedWhileInCall = ongoingCalls;
- call.isRinging = YES;
-
- __weak CallKitManager *weakSelf = self;
- [self.provider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) {
- if (!error) {
- // Add call to calls array
- [weakSelf.calls setObject:call forKey:callUUID];
-
- // Add hangUpTimer to timers array
- NSTimer *hangUpTimer = [NSTimer scheduledTimerWithTimeInterval:kCallKitManagerMaxRingingTimeSeconds target:self selector:@selector(endCallWithMissedCallNotification:) userInfo:call repeats:NO];
- [weakSelf.hangUpTimers setObject:hangUpTimer forKey:callUUID];
-
- // Add callStateTimer to timers array
- NSTimer *callStateTimer = [NSTimer scheduledTimerWithTimeInterval:kCallKitManagerCheckCallStateEverySeconds target:self selector:@selector(checkCallStateForCall:) userInfo:call repeats:NO];
- [weakSelf.callStateTimers setObject:callStateTimer forKey:callUUID];
-
- // Get call info from server
- [weakSelf getCallInfoForCall:call];
- } else {
- NSLog(@"Provider could not present incoming call view.");
- }
- }];
- }
- - (void)reportAndCancelIncomingCall:(NSString *)token withDisplayName:(NSString *)displayName forAccountId:(NSString *)accountId
- {
- CXCallUpdate *update = [self defaultCallUpdate];
- NSUUID *callUUID = [NSUUID new];
- CallKitCall *call = [[CallKitCall alloc] init];
- call.uuid = callUUID;
- call.token = token;
- call.accountId = accountId;
- call.update = update;
- __weak CallKitManager *weakSelf = self;
- [self.provider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) {
- if (!error) {
- [weakSelf.calls setObject:call forKey:callUUID];
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:call.token forKey:@"roomToken"];
- [userInfo setValue:@(kNCLocalNotificationTypeCancelledCall) forKey:@"localNotificationType"];
- [userInfo setObject:call.accountId forKey:@"accountId"];
- [[NCNotificationController sharedInstance] showLocalNotification:kNCLocalNotificationTypeCancelledCall withUserInfo:userInfo];
- [weakSelf endCallWithUUID:callUUID];
- } else {
- NSLog(@"Provider could not present incoming call view.");
- }
- }];
- }
- - (void)reportIncomingCallForNonCallKitDevicesWithPushNotification:(NCPushNotification *)pushNotification
- {
- CXCallUpdate *update = [self defaultCallUpdate];
- NSUUID *callUUID = [NSUUID new];
- CallKitCall *call = [[CallKitCall alloc] init];
- call.uuid = callUUID;
- call.token = pushNotification.roomToken;
- call.accountId = pushNotification.accountId;
- call.update = update;
- __weak CallKitManager *weakSelf = self;
- [self.provider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) {
- if (!error) {
- [weakSelf.calls setObject:call forKey:callUUID];
- [[NCNotificationController sharedInstance] showLocalNotificationForIncomingCallWithPushNotificaion:pushNotification];
- [weakSelf endCallWithUUID:callUUID];
- } else {
- NSLog(@"Provider could not present incoming call view.");
- }
- }];
- }
- - (void)reportIncomingCallForOldAccount
- {
- CXCallUpdate *update = [self defaultCallUpdate];
- update.localizedCallerName = NSLocalizedString(@"Old account", @"Will be used as the caller name when a VoIP notification can't be decrypted");
- NSUUID *callUUID = [NSUUID new];
- CallKitCall *call = [[CallKitCall alloc] init];
- call.uuid = callUUID;
- call.update = update;
- __weak CallKitManager *weakSelf = self;
- [self.provider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) {
- if (!error) {
- [weakSelf.calls setObject:call forKey:callUUID];
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:@(kNCLocalNotificationTypeCallFromOldAccount) forKey:@"localNotificationType"];
- [[NCNotificationController sharedInstance] showLocalNotification:kNCLocalNotificationTypeCallFromOldAccount withUserInfo:userInfo];
- [weakSelf endCallWithUUID:callUUID];
- } else {
- NSLog(@"Provider could not present incoming call view.");
- }
- }];
- }
- - (void)getCallInfoForCall:(CallKitCall *)call
- {
- NCRoom *room = [[NCDatabaseManager sharedInstance] roomWithToken:call.token forAccountId:call.accountId];
- if (room) {
- [self updateCall:call withDisplayName:room.displayName];
- }
-
- TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:call.accountId];
- [[NCAPIController sharedInstance] getRoomForAccount:account withToken:call.token completionBlock:^(NSDictionary *roomDict, NSError *error) {
- if (!error) {
- NCRoom *room = [NCRoom roomWithDictionary:roomDict andAccountId:call.accountId];
- [self updateCall:call withDisplayName:room.displayName];
-
- if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityCallFlags forAccountId:call.accountId]) {
- NSInteger callFlag = [[roomDict objectForKey:@"callFlag"] integerValue];
- if (callFlag == CallFlagDisconnected) {
- [self presentMissedCallNotificationForCall:call];
- [self endCallWithUUID:call.uuid];
- } else if ((callFlag & CallFlagWithVideo) != 0) {
- [self updateCall:call hasVideo:YES];
- }
- }
- }
- }];
- }
- - (void)updateCall:(CallKitCall *)call withDisplayName:(NSString *)displayName
- {
- call.displayName = displayName;
- call.update.localizedCallerName = displayName;
-
- [self.provider reportCallWithUUID:call.uuid updated:call.update];
- }
- - (void)updateCall:(CallKitCall *)call hasVideo:(BOOL)hasVideo
- {
- call.update.hasVideo = hasVideo;
-
- [self.provider reportCallWithUUID:call.uuid updated:call.update];
- }
- - (void)stopHangUpTimerForCallUUID:(NSUUID *)uuid
- {
- NSTimer *hangUpTimer = [_hangUpTimers objectForKey:uuid];
- if (hangUpTimer) {
- [hangUpTimer invalidate];
- [_hangUpTimers removeObjectForKey:uuid];
- }
- }
- - (void)stopCallStateTimerForCallUUID:(NSUUID *)uuid
- {
- NSTimer *callStateTimer = [_callStateTimers objectForKey:uuid];
- if (callStateTimer) {
- [callStateTimer invalidate];
- [_callStateTimers removeObjectForKey:uuid];
- }
- }
- - (void)endCallWithMissedCallNotification:(NSTimer*)timer
- {
- CallKitCall *call = [timer userInfo];
- [self presentMissedCallNotificationForCall:call];
- [self endCallWithUUID:call.uuid];
- }
- - (void)presentMissedCallNotificationForCall:(CallKitCall *)call
- {
- if (call) {
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:call.token forKey:@"roomToken"];
- [userInfo setValue:call.displayName forKey:@"displayName"];
- [userInfo setValue:@(kNCLocalNotificationTypeMissedCall) forKey:@"localNotificationType"];
- [userInfo setObject:call.accountId forKey:@"accountId"];
- [[NCNotificationController sharedInstance] showLocalNotification:kNCLocalNotificationTypeMissedCall withUserInfo:userInfo];
- }
- }
- - (void)checkCallStateForCall:(NSTimer *)timer
- {
- CallKitCall *call = [timer userInfo];
- if (!call) {
- return;
- }
- __weak CallKitManager *weakSelf = self;
- TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:call.accountId];
- [[NCAPIController sharedInstance] getPeersForCall:call.token forAccount:account withCompletionBlock:^(NSMutableArray *peers, NSError *error, NSInteger statusCode) {
- // Make sure call is still ringing at this point to avoid a race-condition between answering the call on this device and the API callback
- if (!call.isRinging) {
- return;
- }
- if (statusCode == 404) {
- // The conversation was not found for this participant
- // Mostlikely the conversation was removed while an incoming call was ongoing
- [self endCallWithUUID:call.uuid];
- return;
- }
- if (!error && peers.count == 0) {
- // No one is in the call, we can hang up and show missed call notification
- [self presentMissedCallNotificationForCall:call];
- [self endCallWithUUID:call.uuid];
- return;
- }
-
- NSInteger callAPIVersion = [[NCAPIController sharedInstance] callAPIVersionForAccount:account];
- for (NSMutableDictionary *user in peers) {
- NSString *userId = [user objectForKey:@"userId"];
- BOOL isUserActorType = YES;
- if (callAPIVersion >= APIv3) {
- userId = [user objectForKey:@"actorId"];
- isUserActorType = [[user objectForKey:@"actorType"] isEqualToString:@"users"];
- }
- if ([account.userId isEqualToString:userId] && isUserActorType) {
- // Account is already in a call (answered the call on a different device) -> no need to keep ringing
- [self endCallWithUUID:call.uuid];
- return;
- }
- }
-
- // Reschedule next check
- NSTimer *callStateTimer = [NSTimer scheduledTimerWithTimeInterval:kCallKitManagerCheckCallStateEverySeconds target:self selector:@selector(checkCallStateForCall:) userInfo:call repeats:NO];
- [weakSelf.callStateTimers setObject:callStateTimer forKey:call.uuid];
- }];
- }
- - (void)startCall:(NSString *)token withVideoEnabled:(BOOL)videoEnabled andDisplayName:(NSString *)displayName asInitiator:(BOOL)initiator silently:(BOOL)silently recordingConsent:(BOOL)recordingConsent withAccountId:(NSString *)accountId
- {
- if (![CallKitManager isCallKitAvailable]) {
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:token forKey:@"roomToken"];
- [userInfo setValue:@(videoEnabled) forKey:@"isVideoEnabled"];
- [userInfo setValue:@(initiator) forKey:@"initiator"];
- [userInfo setValue:@(silently) forKey:@"silentCall"];
- [userInfo setValue:@(recordingConsent) forKey:@"recordingConsent"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidStartCallNotification
- object:self
- userInfo:userInfo];
- return;
- }
-
- // Start a new call
- if (_calls.count == 0) {
- CXCallUpdate *update = [self defaultCallUpdate];
- CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:token];
- update.remoteHandle = handle;
- update.localizedCallerName = displayName;
- update.hasVideo = videoEnabled;
-
- NSUUID *callUUID = [NSUUID new];
- CallKitCall *call = [[CallKitCall alloc] init];
- call.uuid = callUUID;
- call.token = token;
- call.displayName = displayName;
- call.accountId = accountId;
- call.update = update;
- call.initiator = initiator;
- call.silentCall = silently;
- call.recordingConsent = recordingConsent;
- CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:callUUID handle:handle];
- startCallAction.video = videoEnabled;
- startCallAction.contactIdentifier = displayName;
- CXTransaction *transaction = [[CXTransaction alloc] init];
- [transaction addAction:startCallAction];
-
- __weak CallKitManager *weakSelf = self;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
- [self.callController requestTransaction:transaction completion:^(NSError * _Nullable error) {
- if (!error) {
- self->_startCallRetried = NO;
- [weakSelf.calls setObject:call forKey:callUUID];
- } else {
- if (self->_startCallRetried) {
- NSLog(@"%@", error.localizedDescription);
- self->_startCallRetried = NO;
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:token forKey:@"roomToken"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidFailRequestingCallTransactionNotification
- object:self
- userInfo:userInfo];
- } else {
- self->_startCallRetried = YES;
- [self startCall:token withVideoEnabled:videoEnabled andDisplayName:displayName asInitiator:initiator silently:silently recordingConsent:recordingConsent withAccountId:accountId];
- }
- }
- }];
- });
- // Send notification for video call upgrade.
- // Since we send the token in the notification, it will only ask
- // for an upgrade if there is an ongoing (audioOnly) call in that room.
- } else if (videoEnabled) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:token forKey:@"roomToken"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerWantsToUpgradeToVideoCallNotification
- object:self
- userInfo:userInfo];
- }
- }
- - (void)presentRecordingConsentRequiredNotificationForCall:(CallKitCall *)call
- {
- if (call) {
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:call.token forKey:@"roomToken"];
- [userInfo setValue:call.displayName forKey:@"displayName"];
- [userInfo setValue:@(kNCLocalNotificationTypeRecordingConsentRequired) forKey:@"localNotificationType"];
- [userInfo setObject:call.accountId forKey:@"accountId"];
- [[NCNotificationController sharedInstance] showLocalNotification:kNCLocalNotificationTypeRecordingConsentRequired withUserInfo:userInfo];
- }
- }
- - (void)endCall:(NSString *)token withStatusCode:(NSInteger)statusCode
- {
- [NCUtils log:[NSString stringWithFormat:@"End call for token %@ with statusCode %ld", token, statusCode]];
- CallKitCall *call = [self callForToken:token];
- if (call) {
- // Check if recording consent is required
- if (statusCode == 400) {
- [self presentRecordingConsentRequiredNotificationForCall:call];
- }
- [self endCallWithUUID:call.uuid];
- }
- }
- - (void)endCallWithUUID:(NSUUID *)uuid
- {
- CallKitCall *call = [_calls objectForKey:uuid];
- if (call) {
- CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:call.uuid];
- CXTransaction *transaction = [[CXTransaction alloc] init];
- [transaction addAction:endCallAction];
- [self.callController requestTransaction:transaction completion:^(NSError * _Nullable error) {
- if (error) {
- NSLog(@"%@", error.localizedDescription);
- }
- }];
- }
- }
- - (void)changeAudioMuted:(BOOL)muted forCall:(NSString *)token
- {
- CallKitCall *call = [self callForToken:token];
- if (call) {
- CXSetMutedCallAction *muteAction = [[CXSetMutedCallAction alloc] initWithCallUUID:call.uuid muted:muted];
- CXTransaction *transaction = [[CXTransaction alloc] init];
- [transaction addAction:muteAction];
- [self.callController requestTransaction:transaction completion:^(NSError * _Nullable error) {
- if (error) {
- NSLog(@"%@", error.localizedDescription);
- }
- }];
- }
- }
- - (void)switchCallFrom:(NSString *)from toCall:(NSString *)to
- {
- CallKitCall *call = [self callForToken:from];
- if (call) {
- call.token = to;
- }
- }
- #pragma mark - CXProviderDelegate
- - (void)providerDidReset:(CXProvider *)provider
- {
- NSLog(@"Provider:didReset");
- }
- - (void)provider:(CXProvider *)provider performStartCallAction:(nonnull CXStartCallAction *)action
- {
- CallKitCall *call = [_calls objectForKey:action.callUUID];
- if (call) {
- // Seems to be needed to display the call name correctly
- [_provider reportCallWithUUID:call.uuid updated:call.update];
-
- // Report outgoing call
- [provider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate new]];
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:action.handle.value forKey:@"roomToken"];
- [userInfo setValue:@(action.isVideo) forKey:@"isVideoEnabled"];
- [userInfo setValue:@(call.initiator) forKey:@"initiator"];
- [userInfo setValue:@(call.silentCall) forKey:@"silentCall"];
- [userInfo setValue:@(call.recordingConsent) forKey:@"recordingConsent"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidStartCallNotification
- object:self
- userInfo:userInfo];
- }
-
- [action fulfill];
- }
- - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action
- {
- CallKitCall *call = [_calls objectForKey:action.callUUID];
- if (call) {
- [NCUtils log:[NSString stringWithFormat:@"CallKit provider answer call action for token %@", call.token]];
- call.isRinging = NO;
- [self stopCallStateTimerForCallUUID:call.uuid];
-
- [self stopHangUpTimerForCallUUID:call.uuid];
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:call.token forKey:@"roomToken"];
- [userInfo setValue:@(call.update.hasVideo) forKey:@"hasVideo"];
- [userInfo setValue:@(call.reportedWhileInCall) forKey:@"waitForCallEnd"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidAnswerCallNotification
- object:self
- userInfo:userInfo];
- }
-
- [action fulfill];
- }
- - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action
- {
- CallKitCall *call = [_calls objectForKey:action.callUUID];
- if (call) {
- [NCUtils log:[NSString stringWithFormat:@"CallKit provider end call action for token %@", call.token]];
- call.isRinging = NO;
- [self stopCallStateTimerForCallUUID:call.uuid];
-
- [self stopHangUpTimerForCallUUID:call.uuid];
- NSString *leaveCallToken = [call.token copy];
- [_calls removeObjectForKey:action.callUUID];
- if (leaveCallToken) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:leaveCallToken forKey:@"roomToken"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidEndCallNotification
- object:self
- userInfo:userInfo];
- }
- }
- [action fulfill];
- }
- - (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action
- {
- CallKitCall *call = [_calls objectForKey:action.callUUID];
- if (call) {
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:call.token forKey:@"roomToken"];
- [userInfo setValue:@(action.isMuted) forKey:@"isMuted"];
- [[NSNotificationCenter defaultCenter] postNotificationName:CallKitManagerDidChangeAudioMuteNotification
- object:self
- userInfo:userInfo];
- }
-
- [action fulfill];
- }
- - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession
- {
- NSLog(@"Provider:didActivateAudioSession - %@", audioSession);
- [[WebRTCCommon shared] dispatch:^{
- [[NCAudioController sharedInstance] providerDidActivateAudioSession:audioSession];
- }];
- }
- - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(nonnull AVAudioSession *)audioSession
- {
- NSLog(@"Provider:didDeactivateAudioSession - %@", audioSession);
- [[WebRTCCommon shared] dispatch:^{
- [[NCAudioController sharedInstance] providerDidDeactivateAudioSession:audioSession];
- }];
- }
- @end
|