diff --git a/app/CallManager.js b/app/CallManager.js index 904cb26..b318962 100644 --- a/app/CallManager.js +++ b/app/CallManager.js @@ -1,195 +1,198 @@ import events from 'events'; import Logger from '../Logger'; import uuid from 'react-native-uuid'; const logger = new Logger('CallManager'); export default class CallManager extends events.EventEmitter { constructor(RNCallKeep, answerFunc, rejectFunc, hangupFunc) { logger.debug('constructor()'); super(); this.setMaxListeners(Infinity); // Set of current SIP sessions. this._sessions = new Map(); this._callIDMap = new Map(); this._waitingCalls = new Map(); this._timeouts = new Map(); this._RNCallKeep = RNCallKeep; this._sylkAnswer = answerFunc; this._sylkReject = rejectFunc; this._sylkHangupFunc = hangupFunc; this._boundRnAnswer = this._rnAnswer.bind(this); this._boundRnEnd = this._rnEnd.bind(this); this._boundRnMute = this._rnMute.bind(this); this._boundRnActiveAudioSession = this._rnActiveAudioSession.bind(this); this._boundRnDeactiveAudioSession = this._rnDeactiveAudioSession.bind(this); this._boundRnDTMF = this._rnDTMF.bind(this); this._boundRnProviderReset = this._rnProviderReset.bind(this); this._RNCallKeep.addEventListener('answerCall', this._boundRnAnswer); this._RNCallKeep.addEventListener('endCall', this._boundRnEnd); this._RNCallKeep.addEventListener('didPerformSetMutedCallAction', this._boundRnMute); this._RNCallKeep.addEventListener('didActivateAudioSession', this._boundRnActiveAudioSession); this._RNCallKeep.addEventListener('didDeactivateAudioSession', this._boundRnDeactiveAudioSession.bind(this)); this._RNCallKeep.addEventListener('didPerformDTMFAction', this._boundRnDTMF); this._RNCallKeep.addEventListener('didResetProvider', this._boundRnProviderReset); } get callKeep() { return this._RNCallKeep; } get count() { return this._sessions.size; } get waitingCount() { return this._timeouts.size; } get sessions() { return [...this._sessions.values()]; } get activeSession() { for (let session of this.sessions) { if (session.active) { return session; } } } // there can only be one active one.... so just empty it for now remove() { this._sessions.clear(); } waitForInviteTimeout(callUUID, notificationContent) { let reason; if (this._waitingCalls.has(callUUID)) { reason = 1; } else { reason = 2; } this._callIDMap.set(notificationContent['call-id'], callUUID); this._timeouts.set(callUUID, setTimeout(() => { this._RNCallKeep.reportEndCallWithUUID(callUUID, reason); this._timeouts.delete(callUUID); }, 10000)); } _rnActiveAudioSession() { logger.debug('CallKeep activated audio session call'); } _rnDeactiveAudioSession() { logger.debug('CallKeep deactivated audio session call'); } _rnAnswer(data) { logger.debug('answering call event from callkeep', data) //get the uuid, find the session with that uuid and answer it if (this._sessions.has(data.callUUID.toLowerCase())) { let session = this._sessions.get(data.callUUID.toLowerCase()); logger.debug('answering call', session, data.callUUID) this._sylkAnswer(); } else { this._waitingCalls.set(data.callUUID.toLowerCase(), '_sylkAnswer'); } } _rnEnd(data) { logger.debug('hanging up call event from callkeep', data) //get the uuid, find the session with that uuid and answer it if (this._sessions.has(data.callUUID.toLowerCase())) { let session = this._sessions.get(data.callUUID.toLowerCase()); logger.debug('terminating call', data.callUUID) this._sylkHangupFunc(); } else { this._waitingCalls.set(data.callUUID.toLowerCase(), '_sylkHangupFunc'); } } _rnMute(data) { //get the uuid, find the session with that uuid and mute/unmute it logger.debug('mute event from callkeep', data) if (this._sessions.has(data.callUUID.toLowerCase())) { let session = this._sessions.get(data.callUUID.toLowerCase()); const localStream = session.getLocalStreams()[0]; localStream.getAudioTracks()[0].enabled = !data.muted; } } _rnDTMF(data) { logger.debug(data, 'got dtmf event') if (this._sessions.has(data.callUUID.toLowerCase())) { let session = this._sessions.get(data.callUUID.toLowerCase()); logger.debug('sending webrtc dtmf', data.digits) session.sendDtmf(data.digits); } } _rnProviderReset() { logger.debug('got a provider reset, clearing down all sessions'); this._sessions.forEach((session) => { session.terminate(); }) } handleSession(session, sessionUUID) { + logger.debug('Handling session, id is ', session.id); + logger.debug('Handling Session, call map is', this._callIDMap); + let incomingCallUUID = this._callIDMap.has(session.id) && this._callIDMap.get(session.id) session._callkeepUUID = sessionUUID || incomingCallUUID || uuid.v4(); logger.debug('Handling Session', session._callkeepUUID); if (this._timeouts.has(session._callkeepUUID)) { clearTimeout(this._timeouts.get(session._callkeepUUID)); this._timeouts.delete(session._callkeepUUID); } session.on('close', () => { // Remove from the set. this._sessions.delete(session._callkeepUUID); }); //if the call is in waiting then answer it (or decline it) if (this._waitingCalls.get(session._callkeepUUID)) { let action = this._waitingCalls.get(session._callkeepUUID); this[action](); this._waitingCalls.delete(session._callkeepUUID); } this._callIDMap.delete(session._callkeepUUID); // Add to the set. this._sessions.set(session._callkeepUUID, session); // Emit event. this._emitSessionsChange(true); } _emitSessionsChange(countChanged) { this.emit('sessionschange', countChanged); } destroy() { this._RNCallKeep.removeEventListener('answerCall', this._boundRnAnswer); this._RNCallKeep.removeEventListener('endCall', this._boundRnEnd); this._RNCallKeep.removeEventListener('didPerformSetMutedCallAction', this._boundRnMute); this._RNCallKeep.removeEventListener('didActivateAudioSession', this._boundRnActiveAudioSession); this._RNCallKeep.removeEventListener('didDeactivateAudioSession', this._boundRnDeactiveAudioSession); this._RNCallKeep.removeEventListener('didPerformDTMFAction', this._boundRnDTMF); this._RNCallKeep.removeEventListener('didResetProvider', this._boundRnProviderReset); } } diff --git a/ios/sylk/AppDelegate.m b/ios/sylk/AppDelegate.m index cdf423b..27cf8e0 100644 --- a/ios/sylk/AppDelegate.m +++ b/ios/sylk/AppDelegate.m @@ -1,179 +1,186 @@ /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #import "AppDelegate.h" #import #import #import #import #import #import #import @import Firebase; #import "RNCallKeep.h" #import "RNVoipPushNotificationManager.h" #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // RCTSetLogThreshold(RCTLogLevelInfo - 1); // RTCSetMinDebugLogLevel(RTCLoggingSeverityInfo); if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } // Define UNUserNotificationCenter UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Sylk" initialProperties:nil]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES; } //Called when a notification is delivered to a foreground app. -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge); } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler { BOOL handled = [RNCallKeep application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; if (!handled) { handled = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; } return handled; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } // Required to register for notifications - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings]; } // Required for the register event. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } // Required for the notification event. You must call the completion handler after handling the remote notification. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"Got a PUSH NOTIFICATION"); NSString *eventType = userInfo[@"event"]; NSLog(@"Value of eventType = %@", eventType); if ([eventType isEqualToString:@"cancel"]) { NSString *calluuid = userInfo[@"session-id"]; BOOL active = [RNCallKeep isCallActive:calluuid]; if (active) { [RNCallKeep endCallWithUUID:calluuid reason:2]; } return completionHandler(UIBackgroundFetchResultNoData); } [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } // Required for the registrationError event. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error]; } // IOS 10+ Required for localNotification event - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { [RNCPushNotificationIOS didReceiveNotificationResponse:response]; completionHandler(); } // IOS 4-10 Required for the localNotification event. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [RNCPushNotificationIOS didReceiveLocalNotification:notification]; } // Handle updated push credentials - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type { // Register VoIP push token (a property of PKPushCredentials) with server [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type]; } - (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type { // --- The system calls this method when a previously provided push token is no longer valid for use. No action is necessary on your part to reregister the push type. Instead, use this method to notify your server not to send push notifications using the matching push token. } // --- Handle incoming pushes (for ios <= 10) - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type { [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type]; } // Handle incoming pushes - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type withCompletionHandler:(void (^)(void))completion{ NSLog(@"Got a PUSHKIT NOTIFICATION"); [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type]; + + + + // Retrieve information like handle and callerName here NSString *eventType = [payload.dictionaryPayload valueForKey:@"event"]; NSLog(@"Value of eventType = %@", eventType); // if ([eventType isEqualToString:@"incoming_session"]) // { NSString *calluuid = [payload.dictionaryPayload valueForKey:@"session-id"]; NSString *mediaType = [payload.dictionaryPayload valueForKey:@"media-type"]; NSString *callerName = [payload.dictionaryPayload valueForKey:@"from_display_name"]; NSString *handle = [payload.dictionaryPayload valueForKey:@"from_uri"]; [RNVoipPushNotificationManager addCompletionHandler:calluuid completionHandler:completion]; - [RNCallKeep reportNewIncomingCall:calluuid handle:handle handleType:@"generic" hasVideo:[mediaType isEqualToString:@"video"] localizedCallerName:callerName fromPushKit: YES payload:payload.dictionaryPayload withCompletionHandler:nil]; + + if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) { + [RNCallKeep reportNewIncomingCall:calluuid handle:handle handleType:@"generic" hasVideo:[mediaType isEqualToString:@"video"] localizedCallerName:callerName fromPushKit: YES payload:payload.dictionaryPayload withCompletionHandler:nil]; + } // } // else { //completion(); // } } @end