From d04fecf3ea865bd678335eda8e03a5401dde89cd Mon Sep 17 00:00:00 2001 From: Konrad Kierus Date: Mon, 3 Feb 2020 14:10:17 +0100 Subject: [PATCH 1/6] Proof of concept to handle issue with JS not initialized on start --- actions.js | 5 ++++ ios/RNCallKeep/RNCallKeep.m | 55 +++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/actions.js b/actions.js index 2e5ff59e..e3374b50 100644 --- a/actions.js +++ b/actions.js @@ -14,6 +14,7 @@ const RNCallKeepDidToggleHoldAction = 'RNCallKeepDidToggleHoldAction'; const RNCallKeepDidPerformDTMFAction = 'RNCallKeepDidPerformDTMFAction'; const RNCallKeepProviderReset = 'RNCallKeepProviderReset'; const RNCallKeepCheckReachability = 'RNCallKeepCheckReachability'; +const RNCallKeepDidLoadWithEvents = 'RNCallKeepDidLoadWithEvents'; const isIOS = Platform.OS === 'ios'; const didReceiveStartCallAction = handler => { @@ -55,6 +56,9 @@ const didResetProvider = handler => const checkReachability = handler => eventEmitter.addListener(RNCallKeepCheckReachability, handler); +const didLoadWithEvents = handler => + eventEmitter.addListener(RNCallKeepDidLoadWithEvents, handler); + export const listeners = { didReceiveStartCallAction, answerCall, @@ -67,4 +71,5 @@ export const listeners = { didPerformDTMFAction, didResetProvider, checkReachability, + didLoadWithEvents }; diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 67499f40..9a4eb40d 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -33,11 +33,15 @@ static NSString *const RNCallKeepDidToggleHoldAction = @"RNCallKeepDidToggleHoldAction"; static NSString *const RNCallKeepProviderReset = @"RNCallKeepProviderReset"; static NSString *const RNCallKeepCheckReachability = @"RNCallKeepCheckReachability"; +static NSString *const RNCallKeepDidLoadWithEvents = @"RNCallKeepDidLoadWithEvents"; @implementation RNCallKeep { NSOperatingSystemVersion _version; BOOL _isStartCallActionEventListenerAdded; + bool _hasListeners; + NSMutableArray *_delayedEvents; + } static CXProvider* sharedProvider; @@ -52,6 +56,7 @@ - (instancetype)init #endif if (self = [super init]) { _isStartCallActionEventListenerAdded = NO; + _delayedEvents = [NSMutableArray array]; } return self; } @@ -92,10 +97,36 @@ - (void)dealloc RNCallKeepPerformPlayDTMFCallAction, RNCallKeepDidToggleHoldAction, RNCallKeepProviderReset, - RNCallKeepCheckReachability + RNCallKeepCheckReachability, + RNCallKeepDidLoadWithEvents ]; } +- (void)startObserving +{ + _hasListeners = YES; + if ([_delayedEvents count] > 0) { + [self sendEventWithName:RNCallKeepDidLoadWithEvents body:_delayedEvents]; + } +} + +- (void)stopObserving +{ + _hasListeners = FALSE; +} + +- (void)sendEventWithNameWrapper:(NSString *)name body:(id)body { + if (_hasListeners) { + [self sendEventWithName:name body:body]; + } else { + NSDictionary *dictionary = @{ + @"name": name, + @"data": body + }; + [_delayedEvents addObject:dictionary]; + } +} + + (void)initCallKitProvider { if (sharedProvider == nil) { NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"RNCallKeepSettings"]; @@ -392,7 +423,7 @@ + (void)reportNewIncomingCall:(NSString *)uuidString [RNCallKeep initCallKitProvider]; [sharedProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) { RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil]; - [callKeep sendEventWithName:RNCallKeepDidDisplayIncomingCall body:@{ + [callKeep sendEventWithNameWrapper:RNCallKeepDidDisplayIncomingCall body:@{ @"error": error && error.localizedDescription ? error.localizedDescription : @"", @"callUUID": uuidString, @"handle": handle, @@ -613,7 +644,7 @@ - (void)handleStartCallNotification:(NSDictionary *)userInfo } dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^{ - [self sendEventWithName:RNCallKeepDidReceiveStartCallAction body:userInfo]; + [self sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:userInfo]; }); } @@ -625,7 +656,7 @@ - (void)providerDidReset:(CXProvider *)provider{ #endif //this means something big changed, so tell the JS. The JS should //probably respond by hanging up all calls. - [self sendEventWithName:RNCallKeepProviderReset body:nil]; + [self sendEventWithNameWrapper:RNCallKeepProviderReset body:nil]; } // Starting outgoing call @@ -637,7 +668,7 @@ - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallActio //do this first, audio sessions are flakey [self configureAudioSession]; //tell the JS to actually make the call - [self sendEventWithName:RNCallKeepDidReceiveStartCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString], @"handle": action.handle.value }]; + [self sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString], @"handle": action.handle.value }]; [action fulfill]; } @@ -662,7 +693,7 @@ - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAct NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performAnswerCallAction]"); #endif [self configureAudioSession]; - [self sendEventWithName:RNCallKeepPerformAnswerCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; + [self sendEventWithNameWrapper:RNCallKeepPerformAnswerCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -672,7 +703,7 @@ - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *) #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performEndCallAction]"); #endif - [self sendEventWithName:RNCallKeepPerformEndCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; + [self sendEventWithNameWrapper:RNCallKeepPerformEndCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -682,7 +713,7 @@ -(void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAc NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performSetHeldCallAction]"); #endif - [self sendEventWithName:RNCallKeepDidToggleHoldAction body:@{ @"hold": @(action.onHold), @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; + [self sendEventWithNameWrapper:RNCallKeepDidToggleHoldAction body:@{ @"hold": @(action.onHold), @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -690,7 +721,7 @@ - (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCal #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performPlayDTMFCallAction]"); #endif - [self sendEventWithName:RNCallKeepPerformPlayDTMFCallAction body:@{ @"digits": action.digits, @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; + [self sendEventWithNameWrapper:RNCallKeepPerformPlayDTMFCallAction body:@{ @"digits": action.digits, @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -700,7 +731,7 @@ -(void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCall NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performSetMutedCallAction]"); #endif - [self sendEventWithName:RNCallKeepDidPerformSetMutedCallAction body:@{ @"muted": @(action.muted), @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; + [self sendEventWithNameWrapper:RNCallKeepDidPerformSetMutedCallAction body:@{ @"muted": @(action.muted), @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -724,7 +755,7 @@ - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession [[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo]; [self configureAudioSession]; - [self sendEventWithName:RNCallKeepDidActivateAudioSession body:nil]; + [self sendEventWithNameWrapper:RNCallKeepDidActivateAudioSession body:nil]; } - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession @@ -732,7 +763,7 @@ - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSessio #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:didDeactivateAudioSession]"); #endif - [self sendEventWithName:RNCallKeepDidDeactivateAudioSession body:nil]; + [self sendEventWithNameWrapper:RNCallKeepDidDeactivateAudioSession body:nil]; } @end From 1877abfaf8ad223a6f86ef94c6610f4db55ff10d Mon Sep 17 00:00:00 2001 From: Konrad Kierus Date: Thu, 12 Mar 2020 21:34:43 +0100 Subject: [PATCH 2/6] . --- ios/RNCallKeep/RNCallKeep.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 9a4eb40d..a4890a4c 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -619,7 +619,7 @@ + (BOOL)application:(UIApplication *)application }; RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil]; - [callKeep handleStartCallNotification: userInfo]; + [callKeep sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:userInfo]; return YES; } return NO; From 48c6b8875f2b9289dd2741f13d1e988563be1f9f Mon Sep 17 00:00:00 2001 From: Konrad Kierus Date: Thu, 12 Mar 2020 22:44:54 +0100 Subject: [PATCH 3/6] Add documentation for didLoadWithEvents event --- README.md | 20 ++++++++++++++++++++ index.d.ts | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f2305377..f121cd5b 100644 --- a/README.md +++ b/README.md @@ -519,6 +519,26 @@ RNCallKeep.addEventListener('didPerformDTMFAction', ({ digits, callUUID }) => { - `callUUID` (string) - The UUID of the call. +### - didLoadWithEvents + +iOS only. + +Called as soon as JS context initializes if there were some actions performed by user before JS context has been created. + +Since iOS 13, you must display incoming call on receiving PushKit push notification. But if app was killed, it takes some time to create JS context. If user answers the call (or ends it) before JS context has been initialized, user actions will be passed as events array of this event. Similar situation can happen if user would like to start a call from Recents or similar iOS app, assuming that your app was in killed state. + +```js +RNCallKeep.addEventListener('didLoadWithEvents', (events) => { + // see example usage in https://github.com/react-native-webrtc/react-native-callkeep/pull/169 +}); +``` + +- `events` Array + - `name`: string + Native event name like: `RNCallKeepPerformAnswerCallAction` + - `data`: object + Object with data passed together with specific event so it can be handled in the same way like original event, for example `({ callUUID })` for `answerCall` event if `name` is `RNCallKeepPerformAnswerCallAction` + ### - checkReachability On Android when the application is in background, after a certain delay the OS will close every connection with informing about it. diff --git a/index.d.ts b/index.d.ts index 449db32d..2726f92d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,7 +9,8 @@ export type Events = 'didPerformDTMFAction' | 'didResetProvider' | 'checkReachability' | - 'didPerformSetMutedCallAction'; + 'didPerformSetMutedCallAction' | + 'didLoadWithEvents'; type HandleType = 'generic' | 'number' | 'email'; From 875cd6e3f3609ca210ba70b3066ee6d64e7262e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 28 May 2020 15:54:41 -0400 Subject: [PATCH 4/6] Trigger non-fired events when JS bridge is up --- actions.js | 4 +++- index.js | 8 +++++++- ios/RNCallKeep/RNCallKeep.m | 1 - 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/actions.js b/actions.js index e3374b50..480c139e 100644 --- a/actions.js +++ b/actions.js @@ -59,6 +59,8 @@ const checkReachability = handler => const didLoadWithEvents = handler => eventEmitter.addListener(RNCallKeepDidLoadWithEvents, handler); +export const emit = (eventName, payload) => eventEmitter.emit(eventName, payload); + export const listeners = { didReceiveStartCallAction, answerCall, @@ -71,5 +73,5 @@ export const listeners = { didPerformDTMFAction, didResetProvider, checkReachability, - didLoadWithEvents + didLoadWithEvents, }; diff --git a/index.js b/index.js index b09c0477..f1b15148 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ import { NativeModules, Platform, Alert } from 'react-native'; -import { listeners } from './actions' +import { listeners, emit } from './actions' const RNCallKeepModule = NativeModules.RNCallKeep; const isIOS = Platform.OS === 'ios'; @@ -22,6 +22,12 @@ class RNCallKeep { constructor() { this._callkeepEventHandlers = new Map(); + + this.addEventListener('didLoadWithEvents', (events) => { + events.forEach(event => { + emit(evemt.name, event.data); + }); + }); } addEventListener = (type, handler) => { diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index a4890a4c..5cc6a616 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -41,7 +41,6 @@ @implementation RNCallKeep BOOL _isStartCallActionEventListenerAdded; bool _hasListeners; NSMutableArray *_delayedEvents; - } static CXProvider* sharedProvider; From 1455d42795b0e0e03e23568a3c8d444905430261 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 28 May 2020 15:57:00 -0400 Subject: [PATCH 5/6] Remove useless handleStartCallNotification --- ios/RNCallKeep/RNCallKeep.m | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 5cc6a616..fb7b35fb 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -629,24 +629,6 @@ + (BOOL)requiresMainQueueSetup return YES; } -- (void)handleStartCallNotification:(NSDictionary *)userInfo -{ -#ifdef DEBUG - NSLog(@"[RNCallKeep][handleStartCallNotification] userInfo = %@", userInfo); -#endif - int delayInSeconds; - if (!_isStartCallActionEventListenerAdded) { - // Workaround for when app is just launched and JS side hasn't registered to the event properly - delayInSeconds = OUTGOING_CALL_WAKEUP_DELAY; - } else { - delayInSeconds = 0; - } - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, dispatch_get_main_queue(), ^{ - [self sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:userInfo]; - }); -} - #pragma mark - CXProviderDelegate - (void)providerDidReset:(CXProvider *)provider{ From b28e0ec392b1723336409443907a0b76b2f02cd8 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 4 Jun 2020 09:56:05 -0400 Subject: [PATCH 6/6] Fix typo in didLoadWithEvents --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f1b15148..b9967b6c 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ class RNCallKeep { this.addEventListener('didLoadWithEvents', (events) => { events.forEach(event => { - emit(evemt.name, event.data); + emit(event.name, event.data); }); }); }