Skip to content

Handle issue with JS not initialized on start to fix #107 #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -55,6 +56,11 @@ const didResetProvider = handler =>
const checkReachability = handler =>
eventEmitter.addListener(RNCallKeepCheckReachability, handler);

const didLoadWithEvents = handler =>
eventEmitter.addListener(RNCallKeepDidLoadWithEvents, handler);

export const emit = (eventName, payload) => eventEmitter.emit(eventName, payload);

export const listeners = {
didReceiveStartCallAction,
answerCall,
Expand All @@ -67,4 +73,5 @@ export const listeners = {
didPerformDTMFAction,
didResetProvider,
checkReachability,
didLoadWithEvents,
};
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export type Events =
'didPerformDTMFAction' |
'didResetProvider' |
'checkReachability' |
'didPerformSetMutedCallAction';
'didPerformSetMutedCallAction' |
'didLoadWithEvents';

type HandleType = 'generic' | 'number' | 'email';

Expand Down
8 changes: 7 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,6 +22,12 @@ class RNCallKeep {

constructor() {
this._callkeepEventHandlers = new Map();

this.addEventListener('didLoadWithEvents', (events) => {
events.forEach(event => {
emit(event.name, event.data);
});
});
}

addEventListener = (type, handler) => {
Expand Down
72 changes: 42 additions & 30 deletions ios/RNCallKeep/RNCallKeep.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@
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;
Expand All @@ -52,6 +55,7 @@ - (instancetype)init
#endif
if (self = [super init]) {
_isStartCallActionEventListenerAdded = NO;
_delayedEvents = [NSMutableArray array];
}
return self;
}
Expand Down Expand Up @@ -92,10 +96,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"];
Expand Down Expand Up @@ -392,7 +422,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,
Expand Down Expand Up @@ -588,7 +618,7 @@ + (BOOL)application:(UIApplication *)application
};

RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil];
[callKeep handleStartCallNotification: userInfo];
[callKeep sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:userInfo];
return YES;
}
return NO;
Expand All @@ -599,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 sendEventWithName:RNCallKeepDidReceiveStartCallAction body:userInfo];
});
}

#pragma mark - CXProviderDelegate

- (void)providerDidReset:(CXProvider *)provider{
Expand All @@ -625,7 +637,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
Expand All @@ -637,7 +649,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];
}

Expand All @@ -662,7 +674,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];
}

Expand All @@ -672,7 +684,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];
}

Expand All @@ -682,15 +694,15 @@ -(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];
}

- (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action {
#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];
}

Expand All @@ -700,7 +712,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];
}

Expand All @@ -724,15 +736,15 @@ - (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
{
#ifdef DEBUG
NSLog(@"[RNCallKeep][CXProviderDelegate][provider:didDeactivateAudioSession]");
#endif
[self sendEventWithName:RNCallKeepDidDeactivateAudioSession body:nil];
[self sendEventWithNameWrapper:RNCallKeepDidDeactivateAudioSession body:nil];
}

@end