diff --git a/actions.js b/actions.js index 4e936360..82a61507 100644 --- a/actions.js +++ b/actions.js @@ -11,6 +11,7 @@ const RNCallKeepDidDeactivateAudioSession = 'RNCallKeepDidDeactivateAudioSession const RNCallKeepDidDisplayIncomingCall = 'RNCallKeepDidDisplayIncomingCall'; const RNCallKeepDidPerformSetMutedCallAction = 'RNCallKeepDidPerformSetMutedCallAction'; const RNCallKeepDidToggleHoldAction = 'RNCallKeepDidToggleHoldAction'; +const RNCallKeepPerformGroupCallAction = 'RNCallKeepPerformGroupCallAction'; const RNCallKeepDidPerformDTMFAction = 'RNCallKeepDidPerformDTMFAction'; const RNCallKeepProviderReset = 'RNCallKeepProviderReset'; const RNCallKeepCheckReachability = 'RNCallKeepCheckReachability'; @@ -63,6 +64,9 @@ const didPerformSetMutedCallAction = handler => const didToggleHoldCallAction = handler => eventEmitter.addListener(RNCallKeepDidToggleHoldAction, handler); +const performGroupCallAction = handler => + eventEmitter.addListener(RNCallKeepPerformGroupCallAction, handler); + const didPerformDTMFAction = handler => eventEmitter.addListener(RNCallKeepDidPerformDTMFAction, (data) => handler(data)); @@ -95,6 +99,7 @@ export const listeners = { didDisplayIncomingCall, didPerformSetMutedCallAction, didToggleHoldCallAction, + performGroupCallAction, didPerformDTMFAction, didResetProvider, checkReachability, diff --git a/android/build.gradle b/android/build.gradle index 3accf1c6..2e7b48a7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -31,5 +31,5 @@ repositories { } dependencies { - implementation 'com.facebook.react:react-native:+' + implementation 'com.facebook.react:react-native:0.66.3' } diff --git a/index.d.ts b/index.d.ts index cd948ed8..f06767db 100644 --- a/index.d.ts +++ b/index.d.ts @@ -7,6 +7,7 @@ declare module 'react-native-callkeep' { 'didDeactivateAudioSession' | 'didDisplayIncomingCall' | 'didToggleHoldCallAction' | + 'performGroupCallAction' | 'didPerformDTMFAction' | 'didResetProvider' | 'checkReachability' | @@ -144,6 +145,8 @@ declare module 'react-native-callkeep' { */ static isCallActive(uuid: string): Promise + static isAudioSessionActive(): Promise + static getCalls(): Promise static getAudioRoutes(): Promise @@ -175,6 +178,9 @@ declare module 'react-native-callkeep' { static toggleAudioRouteSpeaker(uuid: string, routeSpeaker: boolean): void static setOnHold(uuid: string, held: boolean): void + + static setGroupCall(activeUuid: string, heldUuid: string): void + static setGroupCallFulfilled(): void static setConnectionState(uuid: string, state: number): void @@ -199,5 +205,9 @@ declare module 'react-native-callkeep' { static setCurrentCallActive(callUUID: string): void static backToForeground(): void + + static configureVoiceAudioSession(): void + + static configureVideoAudioSession(): void } } diff --git a/index.js b/index.js index 3e1df0f0..23d159d4 100644 --- a/index.js +++ b/index.js @@ -27,6 +27,17 @@ class RNCallKeep { addEventListener = (type, handler) => { const listener = listeners[type](handler); + /** + * xxx https://idtjira.atlassian.net/browse/MOB-5329 + */ + if (type === 'didActivateAudioSession') { + this.isAudioSessionActive().then(isActive => { + if (isActive) { + handler() + } + }) + } + this._callkeepEventHandlers.set(type, listener); }; @@ -170,6 +181,8 @@ class RNCallKeep { isCallActive = async (uuid) => await RNCallKeepModule.isCallActive(uuid); + isAudioSessionActive = RNCallKeepModule.isAudioSessionActive; + getCalls = () => { if (isIOS) { return RNCallKeepModule.getCalls(); @@ -258,6 +271,9 @@ class RNCallKeep { }; setOnHold = (uuid, shouldHold) => RNCallKeepModule.setOnHold(uuid, shouldHold); + + setGroupCall = (activeUuid, heldUuid) => RNCallKeepModule.setGroupCall(activeUuid, heldUuid); + setGroupCallFulfilled = () => RNCallKeepModule.setGroupCallFulfilled(); setConnectionState = (uuid, state) => isIOS ? null : RNCallKeepModule.setConnectionState(uuid, state); @@ -347,6 +363,14 @@ class RNCallKeep { clearInitialEvents() { return RNCallKeepModule.clearInitialEvents(); } + + configureVideoAudioSession() { + return RNCallKeepModule.configureVideoAudioSession(); + } + + configureVoiceAudioSession() { + return RNCallKeepModule.configureVoiceAudioSession(); + } } export default new RNCallKeep(); diff --git a/ios/RNCallKeep/RNCallKeep.h b/ios/RNCallKeep/RNCallKeep.h index da9fbb56..2efb133d 100644 --- a/ios/RNCallKeep/RNCallKeep.h +++ b/ios/RNCallKeep/RNCallKeep.h @@ -21,6 +21,7 @@ @property (nonatomic, strong) CXCallController *callKeepCallController; @property (nonatomic, strong) CXProvider *callKeepProvider; +@property (nonatomic, strong) CXSetGroupCallAction * callKeepGroupCallAction; + (BOOL)application:(UIApplication *)application openURL:(NSURL *)url @@ -47,7 +48,11 @@ continueUserActivity:(NSUserActivity *)userActivity reason:(int)reason; + (BOOL)isCallActive:(NSString *)uuidString; ++ (BOOL)isAudioSessionActive; + (void)setup:(NSDictionary *)options; ++ (void)configureVoiceAudioSession; ++ (void)configureVideoAudioSession; + @end diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index c3e8e35a..98dee1a6 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -33,6 +33,7 @@ static NSString *const RNCallKeepDidPerformSetMutedCallAction = @"RNCallKeepDidPerformSetMutedCallAction"; static NSString *const RNCallKeepPerformPlayDTMFCallAction = @"RNCallKeepDidPerformDTMFAction"; static NSString *const RNCallKeepDidToggleHoldAction = @"RNCallKeepDidToggleHoldAction"; +static NSString *const RNCallKeepPerformGroupCallAction = @"RNCallKeepPerformGroupCallAction"; static NSString *const RNCallKeepProviderReset = @"RNCallKeepProviderReset"; static NSString *const RNCallKeepCheckReachability = @"RNCallKeepCheckReachability"; static NSString *const RNCallKeepDidChangeAudioRoute = @"RNCallKeepDidChangeAudioRoute"; @@ -43,6 +44,7 @@ @implementation RNCallKeep NSOperatingSystemVersion _version; BOOL _isStartCallActionEventListenerAdded; bool _hasListeners; + bool _isAudioSessionActive; bool _isReachable; NSMutableArray *_delayedEvents; } @@ -113,6 +115,7 @@ - (void)dealloc RNCallKeepDidPerformSetMutedCallAction, RNCallKeepPerformPlayDTMFCallAction, RNCallKeepDidToggleHoldAction, + RNCallKeepPerformGroupCallAction, RNCallKeepProviderReset, RNCallKeepCheckReachability, RNCallKeepDidLoadWithEvents, @@ -168,6 +171,15 @@ - (void)sendEventWithNameWrapper:(NSString *)name body:(id)body { nil ]; [_delayedEvents addObject:dictionary]; + + } + /** + * xxx https://idtjira.atlassian.net/browse/MOB-5329 + */ + if ([name isEqualToString:@"RNCallKeepDidReceiveStartCallAction"]) { + [_delayedEvents addObject: [NSDictionary dictionaryWithObjectsAndKeys: + @"RNCallKeepDidActivateAudioSession", @"name", nil]]; + _isAudioSessionActive = YES; } } @@ -365,7 +377,7 @@ + (void)setup:(NSDictionary *)options { NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid]; CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction]; - + _isAudioSessionActive = NO; [self requestTransaction:transaction]; } @@ -377,6 +389,7 @@ + (void)setup:(NSDictionary *)options { for (CXCall *call in self.callKeepCallController.callObserver.calls) { CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:call.UUID]; CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction]; + _isAudioSessionActive = NO; [self requestTransaction:transaction]; } @@ -397,6 +410,30 @@ + (void)setup:(NSDictionary *)options { [self requestTransaction:transaction]; } +RCT_EXPORT_METHOD(setGroupCall:(NSString *)activeUuidString :(NSString *)heldUuidString) +{ +#ifdef DEBUG + NSLog(@"[RNCallKeep][setGroupCall] activeUuidString = %@, heldUuidString = %d", activeUuidString, heldUuidString); +#endif + NSUUID *activeUuid = [[NSUUID alloc] initWithUUIDString:activeUuidString]; + NSUUID *heldUuid = [[NSUUID alloc] initWithUUIDString:heldUuidString]; + CXSetGroupCallAction *setGroupCallAction = [[CXSetGroupCallAction alloc] initWithCallUUID:heldUuid callUUIDToGroupWith:activeUuid]; + CXTransaction *transaction = [[CXTransaction alloc] init]; + [transaction addAction:setGroupCallAction]; + + [self requestTransaction:transaction]; +} + +RCT_EXPORT_METHOD(setGroupCallFulfilled) +{ +#ifdef DEBUG + NSLog(@"[RNCallKeep][setGroupCallFulfilled]"); +#endif + if (self.callKeepGroupCallAction != nil) { + [self.callKeepGroupCallAction fulfill]; + } +} + RCT_EXPORT_METHOD(_startCallActionEventListenerAdded) { _isStartCallActionEventListenerAdded = YES; @@ -475,6 +512,12 @@ + (void)setup:(NSDictionary *)options { [self requestTransaction:transaction]; } +RCT_EXPORT_METHOD(isAudioSessionActive:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + resolve([NSNumber numberWithBool:_isAudioSessionActive]); +} + RCT_EXPORT_METHOD(isCallActive:(NSString *)uuidString isCallActiveResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @@ -526,7 +569,7 @@ + (void)setup:(NSDictionary *)options { NSArray *ports = [RNCallKeep getAudioInputs]; - BOOL isCategorySetted = [myAudioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&err]; + BOOL isCategorySetted = [myAudioSession setCategory:AVAudioSessionCategoryPlayAndRecord mode:AVAudioSessionModeVoiceChat options: AVAudioSessionCategoryOptionAllowAirPlay | AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowBluetoothA2DP error:&err]; if (!isCategorySetted) { NSLog(@"[RNCallKeep][setAudioRoute] setCategory failed"); @@ -786,7 +829,7 @@ + (void)reportNewIncomingCall:(NSString *)uuidString if (error == nil) { // Workaround per https://forums.developer.apple.com/message/169511 if ([callKeep lessThanIos10_2]) { - [callKeep configureAudioSession]; + [callKeep configureAudioSession:AVAudioSessionModeVoiceChat]; } } if (completion != nil) { @@ -874,23 +917,45 @@ + (CXProviderConfiguration *)getProviderConfiguration:(NSDictionary*)settings return providerConfiguration; } -- (void)configureAudioSession +- (void)configureAudioSession:(AVAudioSessionMode)mode { #ifdef DEBUG NSLog(@"[RNCallKeep][configureAudioSession] Activating audio session"); #endif - - AVAudioSession* audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil]; - - [audioSession setMode:AVAudioSessionModeDefault error:nil]; - - double sampleRate = 44100.0; - [audioSession setPreferredSampleRate:sampleRate error:nil]; - - NSTimeInterval bufferDuration = .005; - [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; - [audioSession setActive:TRUE error:nil]; + @try{ + NSError* err = nil; + + AVAudioSession* audioSession = [AVAudioSession sharedInstance]; + BOOL isConfigured = [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord mode:mode options: AVAudioSessionCategoryOptionAllowAirPlay | AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionAllowBluetoothA2DP error:&err]; + if(!isConfigured){ + NSLog(@"[RNCallKeep][configureAudioSession][setCategory] failed"); + [NSException raise:@"audioSession#setCategory failed" format:@"error: %@", err]; + } + + double sampleRate = 44100.0; + BOOL sampleRateSetted = [audioSession setPreferredSampleRate:sampleRate error:&err]; + if(!sampleRateSetted){ + NSLog(@"[RNCallKeep][configureAudioSession][setPreferredSampleRate] failed"); + [NSException raise:@"audioSession#setPreferredSampleRate failed" format:@"error: %@", err]; + } + + NSTimeInterval bufferDuration = .005; + BOOL bufferSetted = [audioSession setPreferredIOBufferDuration:bufferDuration error:&err]; + if(!bufferSetted){ + NSLog(@"[RNCallKeep][configureAudioSession][setPreferredIOBufferDuration] failed"); + [NSException raise:@"audioSession#setPreferredIOBufferDuration failed" format:@"error: %@", err]; + } + + BOOL isActivated = [audioSession setActive:TRUE error:&err]; + if(!isActivated){ + NSLog(@"[RNCallKeep][configureAudioSession][setActive] failed"); + [NSException raise:@"audioSession#setActive failed" format:@"error: %@", err]; + } + } + @catch ( NSException *e ){ + NSLog(@"[RNCallKeep][configureAudioSession] exception: %@",e); + } + } - (void) forceBluetoothPreferredInput:(id)sender @@ -1002,6 +1067,7 @@ + (BOOL)application:(UIApplication *)application }; RNCallKeep *callKeep = [RNCallKeep allocWithZone: nil]; + [callKeep sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:userInfo]; return YES; } @@ -1031,7 +1097,7 @@ - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallActio NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performStartCallAction]"); #endif //do this first, audio sessions are flakey - [self configureAudioSession]; + //[self configureAudioSession]; //tell the JS to actually make the call [self sendEventWithNameWrapper:RNCallKeepDidReceiveStartCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString], @"handle": action.handle.value }]; [action fulfill]; @@ -1051,13 +1117,24 @@ - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallActio [self.callKeepProvider reportCallWithUUID:uuid updated:callUpdate]; } +RCT_EXPORT_METHOD(configureVoiceAudioSession) +{ + [self configureAudioSession:AVAudioSessionModeVoiceChat]; +} + + +RCT_EXPORT_METHOD(configureVideoAudioSession) +{ + [self configureAudioSession:AVAudioSessionModeVideoChat]; +} + // Answering incoming call - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action { #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performAnswerCallAction]"); #endif - [self configureAudioSession]; + //[self configureAudioSession]; [self sendEventWithNameWrapper:RNCallKeepPerformAnswerCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -1068,6 +1145,7 @@ - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *) #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performEndCallAction]"); #endif + _isAudioSessionActive = NO; [self sendEventWithNameWrapper:RNCallKeepPerformEndCallAction body:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] }]; [action fulfill]; } @@ -1082,6 +1160,17 @@ -(void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAc [action fulfill]; } +-(void)provider:(CXProvider *)provider performSetGroupCallAction:(CXSetGroupCallAction *)action +{ +#ifdef DEBUG + NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performSetGroupCallAction]"); +#endif + + [self sendEventWithNameWrapper:RNCallKeepPerformGroupCallAction body:@{ @"activeCallUUID": [action.callUUID.UUIDString lowercaseString], @"heldCallUUID": [action.callUUIDToGroupWith.UUIDString lowercaseString] }]; + + self.callKeepGroupCallAction = action; +} + - (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action { #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:performPlayDTMFCallAction]"); @@ -1119,7 +1208,9 @@ - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession }; [[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo]; - [self configureAudioSession]; + //[self configureAudioSession]; + _isAudioSessionActive = YES; + [self sendEventWithNameWrapper:RNCallKeepDidActivateAudioSession body:nil]; } @@ -1128,6 +1219,7 @@ - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSessio #ifdef DEBUG NSLog(@"[RNCallKeep][CXProviderDelegate][provider:didDeactivateAudioSession]"); #endif + _isAudioSessionActive = NO; [self sendEventWithNameWrapper:RNCallKeepDidDeactivateAudioSession body:nil]; }