Skip to content

[ios-13] add new class method to report incoming call when receiving voip push #86

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 2 commits into from
Sep 25, 2019
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
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ RNCallKeep.addEventListener('didActivateAudioSession', () => {
Callback for `RNCallKeep.displayIncomingCall`

```js
RNCallKeep.addEventListener('didDisplayIncomingCall', ({ error }) => {
RNCallKeep.addEventListener('didDisplayIncomingCall', ({ error, uuid, handle, localizedCallerName, fromPushKit }) => {
// you might want to do following things when receiving this event:
// - Start playing ringback if it is an outgoing call
});
Expand Down Expand Up @@ -567,7 +567,7 @@ class RNCallKeepExample extends React.Component {
onDTMFAction = (data) => {
let { digits, callUUID } = data;
// Called when the system or user performs a DTMF action
}
};

audioSessionActivated = (data) => {
// you might want to do following things when receiving this event:
Expand All @@ -587,6 +587,33 @@ class RNCallKeepExample extends React.Component {
}
```

## Receiving a call when the application is not reachable.

In some case your application can be unreachable :
- when the user kill the application
- when it's in background since a long time (eg: after ~5mn the os will kill all connections).

To be able to wake up your application to display the incoming call, you can use [https://github.com/ianlin/react-native-voip-push-notification](react-native-voip-push-notification) on iOS or BackgroundMessaging from [react-native-firebase](https://rnfirebase.io/docs/v5.x.x/messaging/receiving-messages#4)-(Optional)(Android-only)-Listen-for-FCM-messages-in-the-background).

You have to send a push to your application, like with Firebase for Android and with a library supporting PushKit pushes for iOS.

### PushKit

Since iOS 13, you'll have to report the incoming calls that wakes up your application, like in your `AppDelegate.m` :

```objective-c
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
// Process the received push
[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];

// Retrieve information like handle and callerName here

[RNCallKeep reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:false localizedCallerName:callerName fromPushKit: YES];

completion();
}
```

## Debug

### Android
Expand Down
6 changes: 6 additions & 0 deletions ios/RNCallKeep/RNCallKeep.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler;

+ (void)reportNewIncomingCall:(NSString *)uuidString
handle:(NSString *)handle
handleType:(NSString *)handleType
hasVideo:(BOOL)hasVideo
localizedCallerName:(NSString * _Nullable)localizedCallerName
fromPushKit:(BOOL)fromPushKit;
@end
143 changes: 85 additions & 58 deletions ios/RNCallKeep/RNCallKeep.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@

@implementation RNCallKeep
{
NSMutableDictionary *_settings;
NSOperatingSystemVersion _version;
BOOL _isStartCallActionEventListenerAdded;
}

static CXProvider* sharedProvider;

// should initialise in AppDelegate.m
RCT_EXPORT_MODULE()

Expand Down Expand Up @@ -64,23 +65,31 @@ - (void)dealloc
if (self.callKeepProvider != nil) {
[self.callKeepProvider invalidate];
}
sharedProvider = nil;
}

// Override method of RCTEventEmitter
- (NSArray<NSString *> *)supportedEvents
{
return @[
RNCallKeepDidReceiveStartCallAction,
RNCallKeepPerformAnswerCallAction,
RNCallKeepPerformEndCallAction,
RNCallKeepDidActivateAudioSession,
RNCallKeepDidDeactivateAudioSession,
RNCallKeepDidDisplayIncomingCall,
RNCallKeepDidPerformSetMutedCallAction,
RNCallKeepPerformPlayDTMFCallAction,
RNCallKeepDidToggleHoldAction,
RNCallKeepProviderReset
];
RNCallKeepDidReceiveStartCallAction,
RNCallKeepPerformAnswerCallAction,
RNCallKeepPerformEndCallAction,
RNCallKeepDidActivateAudioSession,
RNCallKeepDidDeactivateAudioSession,
RNCallKeepDidDisplayIncomingCall,
RNCallKeepDidPerformSetMutedCallAction,
RNCallKeepPerformPlayDTMFCallAction,
RNCallKeepDidToggleHoldAction,
RNCallKeepProviderReset
];
}

+ (void)initCallKitProvider {
if (sharedProvider == nil) {
NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"RNCallKeepSettings"];
sharedProvider = [[CXProvider alloc] initWithConfiguration:[RNCallKeep getProviderConfiguration:settings]];
}
}

RCT_EXPORT_METHOD(setup:(NSDictionary *)options)
Expand All @@ -90,8 +99,14 @@ - (void)dealloc
#endif
_version = [[[NSProcessInfo alloc] init] operatingSystemVersion];
self.callKeepCallController = [[CXCallController alloc] init];
_settings = [[NSMutableDictionary alloc] initWithDictionary:options];
self.callKeepProvider = [[CXProvider alloc] initWithConfiguration:[self getProviderConfiguration]];
NSDictionary *settings = [[NSMutableDictionary alloc] initWithDictionary:options];
// Store settings in NSUserDefault
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:@"RNCallKeepSettings"];
[[NSUserDefaults standardUserDefaults] synchronize];

[RNCallKeep initCallKitProvider];

self.callKeepProvider = sharedProvider;
[self.callKeepProvider setDelegate:self queue:nil];
}

Expand Down Expand Up @@ -125,29 +140,7 @@ - (void)dealloc
hasVideo:(BOOL)hasVideo
localizedCallerName:(NSString * _Nullable)localizedCallerName)
{
#ifdef DEBUG
NSLog(@"[RNCallKeep][displayIncomingCall] uuidString = %@", uuidString);
#endif
int _handleType = [self getHandleType:handleType];
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = [[CXHandle alloc] initWithType:_handleType value:handle];
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = YES;
callUpdate.supportsUngrouping = YES;
callUpdate.hasVideo = hasVideo;
callUpdate.localizedCallerName = localizedCallerName;

[self.callKeepProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
[self sendEventWithName:RNCallKeepDidDisplayIncomingCall body:@{ @"error": error ? error.localizedDescription : @"" }];
if (error == nil) {
// Workaround per https://forums.developer.apple.com/message/169511
if ([self lessThanIos10_2]) {
[self configureAudioSession];
}
}
}];
[RNCallKeep reportNewIncomingCall: uuidString handle:handle handleType:handleType hasVideo:hasVideo localizedCallerName:localizedCallerName fromPushKit: NO];
}

RCT_EXPORT_METHOD(startCall:(NSString *)uuidString
Expand All @@ -159,7 +152,7 @@ - (void)dealloc
#ifdef DEBUG
NSLog(@"[RNCallKeep][startCall] uuidString = %@", uuidString);
#endif
int _handleType = [self getHandleType:handleType];
int _handleType = [RNCallKeep getHandleType:handleType];
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
CXHandle *callHandle = [[CXHandle alloc] initWithType:_handleType value:handle];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
Expand Down Expand Up @@ -316,6 +309,40 @@ - (void)requestTransaction:(CXTransaction *)transaction
}];
}

+ (void)reportNewIncomingCall:(NSString *)uuidString
handle:(NSString *)handle
handleType:(NSString *)handleType
hasVideo:(BOOL)hasVideo
localizedCallerName:(NSString * _Nullable)localizedCallerName
fromPushKit:(BOOL)fromPushKit
{
#ifdef DEBUG
NSLog(@"[RNCallKeep][reportNewIncomingCall] uuidString = %@", uuidString);
#endif
int _handleType = [RNCallKeep getHandleType:handleType];
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = [[CXHandle alloc] initWithType:_handleType value:handle];
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = YES;
callUpdate.supportsUngrouping = YES;
callUpdate.hasVideo = hasVideo;
callUpdate.localizedCallerName = localizedCallerName;

[RNCallKeep initCallKitProvider];
[sharedProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) {
RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil];
[callKeep sendEventWithName:RNCallKeepDidDisplayIncomingCall body:@{ @"error": error ? error.localizedDescription : @"", @"callUUID": uuidString, @"handle": handle, @"localizedCallerName": localizedCallerName, @"fromPushKit": fromPushKit }];
if (error == nil) {
// Workaround per https://forums.developer.apple.com/message/169511
if ([callKeep lessThanIos10_2]) {
[callKeep configureAudioSession];
}
}
}];
}

- (BOOL)lessThanIos10_2
{
if (_version.majorVersion < 10) {
Expand All @@ -327,7 +354,7 @@ - (BOOL)lessThanIos10_2
}
}

- (int)getHandleType:(NSString *)handleType
+ (int)getHandleType:(NSString *)handleType
{
int _handleType;
if ([handleType isEqualToString:@"generic"]) {
Expand All @@ -342,30 +369,30 @@ - (int)getHandleType:(NSString *)handleType
return _handleType;
}

- (CXProviderConfiguration *)getProviderConfiguration
+ (CXProviderConfiguration *)getProviderConfiguration:(NSDictionary*)settings
{
#ifdef DEBUG
NSLog(@"[RNCallKeep][getProviderConfiguration]");
#endif
CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:_settings[@"appName"]];
CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:settings[@"appName"]];
providerConfiguration.supportsVideo = YES;
providerConfiguration.maximumCallGroups = 3;
providerConfiguration.maximumCallsPerCallGroup = 1;
providerConfiguration.supportedHandleTypes = [NSSet setWithObjects:[NSNumber numberWithInteger:CXHandleTypePhoneNumber], nil];
if (_settings[@"supportsVideo"]) {
providerConfiguration.supportsVideo = _settings[@"supportsVideo"];
if (settings[@"supportsVideo"]) {
providerConfiguration.supportsVideo = settings[@"supportsVideo"];
}
if (_settings[@"maximumCallGroups"]) {
providerConfiguration.maximumCallGroups = [_settings[@"maximumCallGroups"] integerValue];
if (settings[@"maximumCallGroups"]) {
providerConfiguration.maximumCallGroups = [settings[@"maximumCallGroups"] integerValue];
}
if (_settings[@"maximumCallsPerCallGroup"]) {
providerConfiguration.maximumCallsPerCallGroup = [_settings[@"maximumCallsPerCallGroup"] integerValue];
if (settings[@"maximumCallsPerCallGroup"]) {
providerConfiguration.maximumCallsPerCallGroup = [settings[@"maximumCallsPerCallGroup"] integerValue];
}
if (_settings[@"imageName"]) {
providerConfiguration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:_settings[@"imageName"]]);
if (settings[@"imageName"]) {
providerConfiguration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:settings[@"imageName"]]);
}
if (_settings[@"ringtoneSound"]) {
providerConfiguration.ringtoneSound = _settings[@"ringtoneSound"];
if (settings[@"ringtoneSound"]) {
providerConfiguration.ringtoneSound = settings[@"ringtoneSound"];
}
return providerConfiguration;
}
Expand Down Expand Up @@ -440,9 +467,9 @@ + (BOOL)application:(UIApplication *)application

if (handle != nil && handle.length > 0 ){
NSDictionary *userInfo = @{
@"handle": handle,
@"video": @(isVideoCall)
};
@"handle": handle,
@"video": @(isVideoCall)
};

[[NSNotificationCenter defaultCenter] postNotificationName:RNCallKeepHandleStartCallNotification
object:self
Expand Down Expand Up @@ -574,10 +601,10 @@ - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession
NSLog(@"[RNCallKeep][CXProviderDelegate][provider:didActivateAudioSession]");
#endif
NSDictionary *userInfo
= @{
AVAudioSessionInterruptionTypeKey: [NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded],
AVAudioSessionInterruptionOptionKey: [NSNumber numberWithInt:AVAudioSessionInterruptionOptionShouldResume]
};
= @{
AVAudioSessionInterruptionTypeKey: [NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded],
AVAudioSessionInterruptionOptionKey: [NSNumber numberWithInt:AVAudioSessionInterruptionOptionShouldResume]
};
[[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo];

[self configureAudioSession];
Expand Down