From 3ad2c6b580e893fdcf32017135b08338b7af537f Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 8 Oct 2020 14:33:31 -0400 Subject: [PATCH 01/29] Introduce checkPhoneAccountEnabled and isConnectionServiceAvailable --- .../java/io/wazo/callkeep/RNCallKeepModule.java | 11 ++++++++++- index.d.ts | 4 ++++ index.js | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 54ff44ca..7f9397c5 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -432,12 +432,21 @@ public void openPhoneAccountSettings() { this.getAppContext().startActivity(intent); } - @ReactMethod public static Boolean isConnectionServiceAvailable() { // PhoneAccount is available since api level 23 return Build.VERSION.SDK_INT >= 23; } + @ReactMethod + public void isConnectionServiceAvailable(Promise promise) { + promise.resolve(isConnectionServiceAvailable()); + } + + @ReactMethod + public void checkPhoneAccountEnabled(Promise promise) { + promise.resolve(hasPhoneAccount()); + } + @ReactMethod public void backToForeground() { Context context = getAppContext(); diff --git a/index.d.ts b/index.d.ts index f8133789..b7e2670f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -77,6 +77,10 @@ declare module 'react-native-callkeep' { handle: string, ): void + static checkPhoneAccountEnabled(): Promise; + + static isConnectionServiceAvailable(): Promise; + /** * @description reportConnectedOutgoingCallWithUUID method is available only on iOS. */ diff --git a/index.js b/index.js index f5e223fb..39df5f61 100644 --- a/index.js +++ b/index.js @@ -101,6 +101,22 @@ class RNCallKeep { RNCallKeepModule.startCall(uuid, handle, contactIdentifier, handleType, hasVideo); }; + checkPhoneAccountEnabled = async () => { + if (isIOS) { + return; + } + + return RNCallKeepModule.checkPhoneAccountEnabled(); + } + + isConnectionServiceAvailable = async () => { + if (isIOS) { + return true; + } + + return RNCallKeepModule.isConnectionServiceAvailable(); + } + reportConnectingOutgoingCallWithUUID = (uuid) => { //only available on iOS if (isIOS) { From c257086fa09e9022878f5925fed3ada59c8db14a Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 8 Oct 2020 14:38:46 -0400 Subject: [PATCH 02/29] Document checkPhoneAccountEnabled and checkPhoneAccountEnabled --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index ac892673..3b1de862 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,29 @@ const options = { RNCallKeep.hasDefaultPhoneAccount(options); ``` +### checkPhoneAccountEnabled + +Checks if the user has set a default [phone account](https://developer.android.com/reference/android/telecom/PhoneAccount) and it's enabled. + +It's useful for custom permission prompts. It should be used in pair with `registerPhoneAccount` +Similar to `hasDefaultPhoneAccount` but without trigering a prompt if the user doesn't have a phone account. + +_This feature is available only on Android._ + +```js +RNCallKeep.checkPhoneAccountEnabled(); +``` + +### isConnectionServiceAvailable + +Check if the device support ConnectionService. + +_This feature is available only on Android._ + +```js +RNCallKeep.checkPhoneAccountEnabled(); +``` + ### backToForeground _This feature is available only on Android._ From 5677bc0ee1f6b0bb8c27a185ab0c9cb6ced9b118 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Thu, 8 Oct 2020 15:40:29 -0400 Subject: [PATCH 03/29] Add new canMakeMultipleCalls method --- README.md | 9 +++++++++ android/src/main/java/io/wazo/callkeep/Constants.java | 2 ++ .../main/java/io/wazo/callkeep/RNCallKeepModule.java | 5 +++++ .../java/io/wazo/callkeep/VoiceConnectionService.java | 10 ++++++++++ index.d.ts | 2 ++ index.js | 8 ++++++++ 6 files changed, 36 insertions(+) diff --git a/README.md b/README.md index 3b1de862..73aaecdc 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,15 @@ Eg: When your used log out (or the connection to your server is broken, etc..), RNCallKeep.setAvailable(true); ``` +### canMakeMultipleCalls +_This feature is available only on Android._ + +Disable the "Add call" button in ConnectionService UI. + +```js +RNCallKeep.canMakeMultipleCalls(false); // Enabled by default +``` + - `active`: boolean - Tell whether the app is ready or not diff --git a/android/src/main/java/io/wazo/callkeep/Constants.java b/android/src/main/java/io/wazo/callkeep/Constants.java index 4310de56..f10e109c 100644 --- a/android/src/main/java/io/wazo/callkeep/Constants.java +++ b/android/src/main/java/io/wazo/callkeep/Constants.java @@ -16,4 +16,6 @@ public class Constants { public static final String EXTRA_CALL_NUMBER = "EXTRA_CALL_NUMBER"; public static final String EXTRA_CALL_UUID = "EXTRA_CALL_UUID"; public static final String EXTRA_CALLER_NAME = "EXTRA_CALLER_NAME"; + // Can't use telecom.EXTRA_DISABLE_ADD_CALL ... + public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL"; } diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 7f9397c5..b75467f2 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -386,6 +386,11 @@ public void setAvailable(Boolean active) { VoiceConnectionService.setAvailable(active); } + @ReactMethod + public void canMakeMultipleCalls(Boolean allow) { + VoiceConnectionService.setCanMakeMultipleCalls(allow); + } + @ReactMethod public void setReachable() { VoiceConnectionService.setReachable(); diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index bbe8c3df..4419cf57 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -58,6 +58,7 @@ import static io.wazo.callkeep.Constants.EXTRA_CALLER_NAME; import static io.wazo.callkeep.Constants.EXTRA_CALL_NUMBER; import static io.wazo.callkeep.Constants.EXTRA_CALL_UUID; +import static io.wazo.callkeep.Constants.EXTRA_DISABLE_ADD_CALL; // @see https://github.com/kbagchiGWC/voice-quickstart-android/blob/9a2aff7fbe0d0a5ae9457b48e9ad408740dfb968/exampleConnectionService/src/main/java/com/twilio/voice/examples/connectionservice/VoiceConnectionService.java @TargetApi(Build.VERSION_CODES.M) @@ -65,6 +66,7 @@ public class VoiceConnectionService extends ConnectionService { private static Boolean isAvailable; private static Boolean isInitialized; private static Boolean isReachable; + private static Boolean canMakeMultipleCalls = true; private static String notReachableCallUuid; private static ConnectionRequest currentConnectionRequest; private static PhoneAccountHandle phoneAccountHandle; @@ -103,6 +105,10 @@ public static void setAvailable(Boolean value) { isAvailable = value; } + public static void setCanMakeMultipleCalls(Boolean allow) { + VoiceConnectionService.canMakeMultipleCalls = allow; + } + public static void setReachable() { Log.d(TAG, "setReachable"); isReachable = true; @@ -170,6 +176,10 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool extras.putString(EXTRA_CALL_NUMBER, number); } + if (!canMakeMultipleCalls) { + extras.putBoolean(EXTRA_DISABLE_ADD_CALL, true); + } + outgoingCallConnection = createConnection(request); outgoingCallConnection.setDialing(); outgoingCallConnection.setAudioModeIsVoip(true); diff --git a/index.d.ts b/index.d.ts index b7e2670f..025593ab 100644 --- a/index.d.ts +++ b/index.d.ts @@ -136,6 +136,8 @@ declare module 'react-native-callkeep' { */ static setAvailable(active: boolean): void + static canMakeMultipleCalls(allow: boolean): void + static setCurrentCallActive(callUUID: string): void static backToForeground(): void diff --git a/index.js b/index.js index 39df5f61..0ef37db0 100644 --- a/index.js +++ b/index.js @@ -184,6 +184,14 @@ class RNCallKeep { RNCallKeepModule.setAvailable(state); }; + canMakeMultipleCalls = (state) => { + if (isIOS) { + return; + } + + RNCallKeepModule.canMakeMultipleCalls(state); + }; + setCurrentCallActive = (callUUID) => { if (isIOS) { return; From 4950657d4d4feb2c3e4aa67a1a22579f9b76f71b Mon Sep 17 00:00:00 2001 From: Geraint White Date: Thu, 22 Oct 2020 16:02:25 +0100 Subject: [PATCH 04/29] Fix potential NullPointerException #305 --- android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 54ff44ca..2be6c772 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -507,6 +507,10 @@ private String getApplicationName(Context appContext) { private Boolean hasPermissions() { Activity currentActivity = this.getCurrentActivity(); + + if (currentActivity == null) { + return false; + } boolean hasPermissions = true; for (String permission : permissions) { From 98b6afc04c9a303cabf427817bee2f3a80042ca8 Mon Sep 17 00:00:00 2001 From: Rinor Dreshaj <10086015+RinorDreshaj@users.noreply.github.com> Date: Fri, 30 Oct 2020 15:34:18 +0100 Subject: [PATCH 05/29] Update README.md FIX: react native firevase url in documentation, as the package is updates now --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ac892673..a92226cf 100644 --- a/README.md +++ b/README.md @@ -724,7 +724,8 @@ 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). +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- +](https://rnfirebase.io/messaging/usage#receiving-messages)-(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. From 879371860b70c859a084cfec9da8adf97ab3b4d0 Mon Sep 17 00:00:00 2001 From: Rinor Dreshaj <10086015+RinorDreshaj@users.noreply.github.com> Date: Fri, 30 Oct 2020 15:35:50 +0100 Subject: [PATCH 06/29] Update README.md FIX: correct name of the link --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a92226cf..459a7a42 100644 --- a/README.md +++ b/README.md @@ -724,8 +724,7 @@ 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- -](https://rnfirebase.io/messaging/usage#receiving-messages)-(Optional)(Android-only)-Listen-for-FCM-messages-in-the-background). +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/messaging/usage#receiving-messages)-(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. From 95d866258aab00bd1005ad3d9e9efb60d337ded5 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Wed, 11 Nov 2020 08:50:16 -0500 Subject: [PATCH 07/29] 3.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 529a422b..08d52a0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "3.1.1", + "version": "3.1.2", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From 8f2489efabaff7e00d196f6c6906582961b95aef Mon Sep 17 00:00:00 2001 From: Sylvain Boily Date: Wed, 11 Nov 2020 15:37:28 -0500 Subject: [PATCH 08/29] add android 11 permission support --- android/src/main/AndroidManifest.xml | 4 +++- android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 9eb5933f..9b2100f7 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -2,5 +2,7 @@ - + + diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 54ff44ca..dc4c44c2 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -92,7 +92,7 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule { private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"; private static final String REACT_NATIVE_MODULE_NAME = "RNCallKeep"; private static final String[] permissions = { Manifest.permission.READ_PHONE_STATE, - Manifest.permission.CALL_PHONE, Manifest.permission.RECORD_AUDIO }; + Manifest.permission.CALL_PHONE, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_NUMBERS }; private static final String TAG = "RNCK:RNCallKeepModule"; private static TelecomManager telecomManager; From 3a27fb16b9557506c617b54030de15ea42247c4d Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Wed, 11 Nov 2020 15:41:08 -0500 Subject: [PATCH 09/29] 3.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08d52a0e..6b29b036 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "3.1.2", + "version": "3.1.3", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From 413c6a069732777cfb2fa00b8dc4412c02f64ef4 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Wed, 11 Nov 2020 17:42:35 -0500 Subject: [PATCH 10/29] Fix android permissions depending on os version --- .../src/main/java/io/wazo/callkeep/RNCallKeepModule.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 85e2f401..ad01429f 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -91,8 +91,11 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule { private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"; private static final String REACT_NATIVE_MODULE_NAME = "RNCallKeep"; - private static final String[] permissions = { Manifest.permission.READ_PHONE_STATE, - Manifest.permission.CALL_PHONE, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_NUMBERS }; + private static final String[] permissions = { + Build.VERSION.SDK_INT < 30 ? Manifest.permission.READ_PHONE_STATE : Manifest.permission.READ_PHONE_NUMBERS, + Manifest.permission.CALL_PHONE, + Manifest.permission.RECORD_AUDIO + }; private static final String TAG = "RNCK:RNCallKeepModule"; private static TelecomManager telecomManager; @@ -521,7 +524,7 @@ private String getApplicationName(Context appContext) { private Boolean hasPermissions() { Activity currentActivity = this.getCurrentActivity(); - + if (currentActivity == null) { return false; } From 3bcd5ea7c43a54eddd1644e887f9fbe7388720cf Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Wed, 11 Nov 2020 17:51:17 -0500 Subject: [PATCH 11/29] 3.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b29b036..9e463cdb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "3.1.3", + "version": "3.1.4", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From ae3830dae0d99496adab7316ec250d365df6a0c4 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Sat, 28 Mar 2020 20:04:42 +0800 Subject: [PATCH 12/29] ios: implement support options for disaply incoming call --- index.d.ts | 1 + index.js | 16 ++++++++++-- ios/RNCallKeep/RNCallKeep.h | 6 ++++- ios/RNCallKeep/RNCallKeep.m | 51 +++++++++++++++++++++++++++++++------ 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 025593ab..97c0f810 100644 --- a/index.d.ts +++ b/index.d.ts @@ -61,6 +61,7 @@ declare module 'react-native-callkeep' { localizedCallerName?: string, handleType?: HandleType, hasVideo?: boolean, + options?: object, ): void static startCall( diff --git a/index.js b/index.js index 0ef37db0..fe8235c4 100644 --- a/index.js +++ b/index.js @@ -77,13 +77,25 @@ class RNCallKeep { return; }; - displayIncomingCall = (uuid, handle, localizedCallerName = '', handleType = 'number', hasVideo = false) => { + displayIncomingCall = (uuid, handle, localizedCallerName = '', handleType = 'number', hasVideo = false, options = null) => { if (!isIOS) { RNCallKeepModule.displayIncomingCall(uuid, handle, localizedCallerName); return; } - RNCallKeepModule.displayIncomingCall(uuid, handle, handleType, hasVideo, localizedCallerName); + let supportsHolding = true, + supportsDTMF = true, + supportsGrouping = true, + supportsUngrouping = true; + + if (options) { + if (typeof options.supportsHolding === 'boolean') supportsHolding = options.supportsHolding; + if (typeof options.supportsDTMF === 'boolean') supportsDTMF = options.supportsDTMF; + if (typeof options.supportsGrouping === 'boolean') supportsGrouping = options.supportsGrouping; + if (typeof options.supportsUngrouping === 'boolean') supportsUngrouping = options.supportsUngrouping; + } + + RNCallKeepModule.displayIncomingCall(uuid, handle, handleType, hasVideo, localizedCallerName, supportsHolding, supportsDTMF, supportsGrouping, supportsUngrouping); }; answerIncomingCall = (uuid) => { diff --git a/ios/RNCallKeep/RNCallKeep.h b/ios/RNCallKeep/RNCallKeep.h index a91dfacd..c7b4ef09 100644 --- a/ios/RNCallKeep/RNCallKeep.h +++ b/ios/RNCallKeep/RNCallKeep.h @@ -32,6 +32,10 @@ continueUserActivity:(NSUserActivity *)userActivity handleType:(NSString *)handleType hasVideo:(BOOL)hasVideo localizedCallerName:(NSString * _Nullable)localizedCallerName + supportsHolding:(BOOL)supportsHolding + supportsDTMF:(BOOL)supportsDTMF + supportsGrouping:(BOOL)supportsGrouping + supportsUngrouping:(BOOL)supportsUngrouping fromPushKit:(BOOL)fromPushKit payload:(NSDictionary * _Nullable)payload; @@ -49,4 +53,4 @@ continueUserActivity:(NSUserActivity *)userActivity + (BOOL)isCallActive:(NSString *)uuidString; -@end \ No newline at end of file +@end diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 8179e168..162f102a 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -179,9 +179,24 @@ + (void)initCallKitProvider { handle:(NSString *)handle handleType:(NSString *)handleType hasVideo:(BOOL)hasVideo - localizedCallerName:(NSString * _Nullable)localizedCallerName) -{ - [RNCallKeep reportNewIncomingCall: uuidString handle:handle handleType:handleType hasVideo:hasVideo localizedCallerName:localizedCallerName fromPushKit: NO payload:nil withCompletionHandler:nil]; + localizedCallerName:(NSString * _Nullable)localizedCallerName + supportsHolding:(BOOL)supportsHolding + supportsDTMF:(BOOL)supportsDTMF + supportsGrouping:(BOOL)supportsGrouping + supportsUngrouping:(BOOL)supportsUngrouping) +{ + [RNCallKeep reportNewIncomingCall: uuidString + handle: handle + handleType: handleType + hasVideo: hasVideo + localizedCallerName: localizedCallerName + supportsHolding: supportsHolding + supportsDTMF: supportsDTMF + supportsGrouping: supportsGrouping + supportsUngrouping: supportsUngrouping + fromPushKit: NO + payload: nil + withCompletionHandler: nil]; } RCT_EXPORT_METHOD(startCall:(NSString *)uuidString @@ -390,6 +405,10 @@ + (void)reportNewIncomingCall:(NSString *)uuidString handleType:(NSString *)handleType hasVideo:(BOOL)hasVideo localizedCallerName:(NSString * _Nullable)localizedCallerName + supportsHolding:(BOOL)supportsHolding + supportsDTMF:(BOOL)supportsDTMF + supportsGrouping:(BOOL)supportsGrouping + supportsUngrouping:(BOOL)supportsUngrouping fromPushKit:(BOOL)fromPushKit payload:(NSDictionary * _Nullable)payload { @@ -412,10 +431,10 @@ + (void)reportNewIncomingCall:(NSString *)uuidString 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.supportsHolding = supportsHolding; + callUpdate.supportsDTMF = supportsDTMF; + callUpdate.supportsGrouping = supportsGrouping; + callUpdate.supportsUngrouping = supportsUngrouping; callUpdate.hasVideo = hasVideo; callUpdate.localizedCallerName = localizedCallerName; @@ -428,6 +447,10 @@ + (void)reportNewIncomingCall:(NSString *)uuidString @"handle": handle, @"localizedCallerName": localizedCallerName ? localizedCallerName : @"", @"hasVideo": hasVideo ? @"1" : @"0", + @"supportsHolding": supportsHolding ? @"1" : @"0", + @"supportsDTMF": supportsDTMF ? @"1" : @"0", + @"supportsGrouping": supportsGrouping ? @"1" : @"0", + @"supportsUngrouping": supportsUngrouping ? @"1" : @"0", @"fromPushKit": fromPushKit ? @"1" : @"0", @"payload": payload ? payload : @"", }]; @@ -443,6 +466,7 @@ + (void)reportNewIncomingCall:(NSString *)uuidString }]; } +// --- overloading functions for backward compatibility and simple api + (void)reportNewIncomingCall:(NSString *)uuidString handle:(NSString *)handle handleType:(NSString *)handleType @@ -450,7 +474,18 @@ + (void)reportNewIncomingCall:(NSString *)uuidString localizedCallerName:(NSString * _Nullable)localizedCallerName fromPushKit:(BOOL)fromPushKit { - [RNCallKeep reportNewIncomingCall: uuidString handle:handle handleType:handleType hasVideo:hasVideo localizedCallerName:localizedCallerName fromPushKit: fromPushKit payload:nil withCompletionHandler:nil]; + [RNCallKeep reportNewIncomingCall: uuidString + handle: handle + handleType: handleType + hasVideo: hasVideo + localizedCallerName: localizedCallerName + supportsHolding: YES + supportsDTMF: YES + supportsGrouping: YES + supportsUngrouping: YES + fromPushKit: fromPushKit + payload: nil + withCompletionHandler: nil]; } - (BOOL)lessThanIos10_2 From 07afe9598e8af5b62e9d437e08a4a09ad7d8eb3d Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Sat, 28 Mar 2020 20:41:59 +0800 Subject: [PATCH 13/29] ios: implemet support options for updateDisplay --- index.d.ts | 1 + index.js | 12 +++++++++++- ios/RNCallKeep/RNCallKeep.m | 19 ++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index 97c0f810..2090fe29 100644 --- a/index.d.ts +++ b/index.d.ts @@ -76,6 +76,7 @@ declare module 'react-native-callkeep' { uuid: string, displayName: string, handle: string, + options?: object, ): void static checkPhoneAccountEnabled(): Promise; diff --git a/index.js b/index.js index fe8235c4..7a48d174 100644 --- a/index.js +++ b/index.js @@ -212,7 +212,17 @@ class RNCallKeep { RNCallKeepModule.setCurrentCallActive(callUUID); }; - updateDisplay = (uuid, displayName, handle) => RNCallKeepModule.updateDisplay(uuid, displayName, handle); + updateDisplay = (uuid, displayName, handle, options = null) => { + if (!isIOS) { + RNCallKeepModule.updateDisplay(uuid, displayName, handle); + return; + } + + if (!options) { + options = {}; + } + RNCallKeepModule.updateDisplay(uuid, displayName, handle, options); + }; setOnHold = (uuid, shouldHold) => RNCallKeepModule.setOnHold(uuid, shouldHold); diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index 162f102a..c1b042a6 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -279,7 +279,7 @@ + (void)initCallKitProvider { [RNCallKeep endCallWithUUID: uuidString reason:reason]; } -RCT_EXPORT_METHOD(updateDisplay:(NSString *)uuidString :(NSString *)displayName :(NSString *)uri) +RCT_EXPORT_METHOD(updateDisplay:(NSString *)uuidString :(NSString *)displayName :(NSString *)uri :(NSDictionary *)options) { #ifdef DEBUG NSLog(@"[RNCallKeep][updateDisplay] uuidString = %@ displayName = %@ uri = %@", uuidString, displayName, uri); @@ -289,6 +289,23 @@ + (void)initCallKitProvider { CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init]; callUpdate.localizedCallerName = displayName; callUpdate.remoteHandle = callHandle; + + if ([options valueForKey:@"hasVideo"] != nil) { + callUpdate.hasVideo = [RCTConvert BOOL:options[@"hasVideo"]]; + } + if ([options valueForKey:@"supportsHolding"] != nil) { + callUpdate.supportsHolding = [RCTConvert BOOL:options[@"supportsHolding"]]; + } + if ([options valueForKey:@"supportsDTMF"] != nil) { + callUpdate.supportsDTMF = [RCTConvert BOOL:options[@"supportsDTMF"]]; + } + if ([options valueForKey:@"supportsGrouping"] != nil) { + callUpdate.supportsGrouping = [RCTConvert BOOL:options[@"supportsGrouping"]]; + } + if ([options valueForKey:@"supportsUngrouping"] != nil) { + callUpdate.supportsUngrouping = [RCTConvert BOOL:options[@"supportsUngrouping"]]; + } + [self.callKeepProvider reportCallWithUUID:uuid updated:callUpdate]; } From f3e014667000327a2af6b31f51528091a00b4886 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Thu, 30 Apr 2020 16:18:02 +0800 Subject: [PATCH 14/29] ios: platform specific options for displayIncomingCall and updateDisplay --- index.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 7a48d174..88a9d37c 100644 --- a/index.js +++ b/index.js @@ -83,17 +83,11 @@ class RNCallKeep { return; } - let supportsHolding = true, - supportsDTMF = true, - supportsGrouping = true, - supportsUngrouping = true; - - if (options) { - if (typeof options.supportsHolding === 'boolean') supportsHolding = options.supportsHolding; - if (typeof options.supportsDTMF === 'boolean') supportsDTMF = options.supportsDTMF; - if (typeof options.supportsGrouping === 'boolean') supportsGrouping = options.supportsGrouping; - if (typeof options.supportsUngrouping === 'boolean') supportsUngrouping = options.supportsUngrouping; - } + // should be boolean type value + let supportsHolding = !!(options?.ios?.supportsHolding ?? true); + let supportsDTMF = !!(options?.ios?.supportsDTMF ?? true); + let supportsGrouping = !!(options?.ios?.supportsGrouping ?? true); + let supportsUngrouping = !!(options?.ios?.supportsUngrouping ?? true); RNCallKeepModule.displayIncomingCall(uuid, handle, handleType, hasVideo, localizedCallerName, supportsHolding, supportsDTMF, supportsGrouping, supportsUngrouping); }; @@ -218,10 +212,13 @@ class RNCallKeep { return; } - if (!options) { - options = {}; + let iosOptions = {}; + if (options && options.ios) { + iosOptions = { + ...options.ios, + } } - RNCallKeepModule.updateDisplay(uuid, displayName, handle, options); + RNCallKeepModule.updateDisplay(uuid, displayName, handle, iosOptions); }; setOnHold = (uuid, shouldHold) => RNCallKeepModule.setOnHold(uuid, shouldHold); From 130fac17d4dcb06dff33d90a2638657117b1bfc7 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Thu, 30 Apr 2020 16:29:19 +0800 Subject: [PATCH 15/29] readme: add options arg for displayIncomingCall and updateDisplay --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 90bcb20c..d486374a 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,13 @@ RNCallKeep.displayIncomingCall(uuid, handle, localizedCallerName); - `hasVideo`: boolean (optional, iOS only) - `false` (default) - `true` (you know... when not false) +- `options`: object (optional) + - `ios`: object + - `supportsHolding`: boolean (optional, default true) + - `supportsDTMF`: boolean (optional, default true) + - `supportsGrouping`: boolean (optional, default true) + - `supportsUngrouping`: boolean (optional, default true) + - `android`: object (currently no-op) ### answerIncomingCall _This feature is available only on Android._ @@ -241,6 +248,14 @@ RNCallKeep.updateDisplay(uuid, displayName, handle) - Name of the caller to be displayed on the native UI - `handle`: string - Phone number of the caller +- `options`: object (optional) + - `ios`: object + - `hasVideo`: boolean (optional) + - `supportsHolding`: boolean (optional) + - `supportsDTMF`: boolean (optional) + - `supportsGrouping`: boolean (optional) + - `supportsUngrouping`: boolean (optional) + - `android`: object (currently no-op) ### endCall From 4654eb1f596554462c7ac6a0294e71d163c13bb2 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Tue, 14 Jul 2020 17:17:33 +0800 Subject: [PATCH 16/29] ios: clean/remove reportNewIncomingCall overloading api --- ios/RNCallKeep/RNCallKeep.h | 8 -------- ios/RNCallKeep/RNCallKeep.m | 33 --------------------------------- 2 files changed, 41 deletions(-) diff --git a/ios/RNCallKeep/RNCallKeep.h b/ios/RNCallKeep/RNCallKeep.h index c7b4ef09..47ce98b3 100644 --- a/ios/RNCallKeep/RNCallKeep.h +++ b/ios/RNCallKeep/RNCallKeep.h @@ -36,14 +36,6 @@ continueUserActivity:(NSUserActivity *)userActivity supportsDTMF:(BOOL)supportsDTMF supportsGrouping:(BOOL)supportsGrouping supportsUngrouping:(BOOL)supportsUngrouping - fromPushKit:(BOOL)fromPushKit - payload:(NSDictionary * _Nullable)payload; - -+ (void)reportNewIncomingCall:(NSString *)uuidString - handle:(NSString *)handle - handleType:(NSString *)handleType - hasVideo:(BOOL)hasVideo - localizedCallerName:(NSString * _Nullable)localizedCallerName fromPushKit:(BOOL)fromPushKit payload:(NSDictionary * _Nullable)payload withCompletionHandler:(void (^_Nullable)(void))completion; diff --git a/ios/RNCallKeep/RNCallKeep.m b/ios/RNCallKeep/RNCallKeep.m index c1b042a6..04af4039 100644 --- a/ios/RNCallKeep/RNCallKeep.m +++ b/ios/RNCallKeep/RNCallKeep.m @@ -428,17 +428,6 @@ + (void)reportNewIncomingCall:(NSString *)uuidString supportsUngrouping:(BOOL)supportsUngrouping fromPushKit:(BOOL)fromPushKit payload:(NSDictionary * _Nullable)payload -{ - [RNCallKeep reportNewIncomingCall:uuidString handle:handle handleType:handleType hasVideo:hasVideo localizedCallerName:localizedCallerName fromPushKit:fromPushKit payload:payload withCompletionHandler:nil]; -} - -+ (void)reportNewIncomingCall:(NSString *)uuidString - handle:(NSString *)handle - handleType:(NSString *)handleType - hasVideo:(BOOL)hasVideo - localizedCallerName:(NSString * _Nullable)localizedCallerName - fromPushKit:(BOOL)fromPushKit - payload:(NSDictionary * _Nullable)payload withCompletionHandler:(void (^_Nullable)(void))completion { #ifdef DEBUG @@ -483,28 +472,6 @@ + (void)reportNewIncomingCall:(NSString *)uuidString }]; } -// --- overloading functions for backward compatibility and simple api -+ (void)reportNewIncomingCall:(NSString *)uuidString - handle:(NSString *)handle - handleType:(NSString *)handleType - hasVideo:(BOOL)hasVideo - localizedCallerName:(NSString * _Nullable)localizedCallerName - fromPushKit:(BOOL)fromPushKit -{ - [RNCallKeep reportNewIncomingCall: uuidString - handle: handle - handleType: handleType - hasVideo: hasVideo - localizedCallerName: localizedCallerName - supportsHolding: YES - supportsDTMF: YES - supportsGrouping: YES - supportsUngrouping: YES - fromPushKit: fromPushKit - payload: nil - withCompletionHandler: nil]; -} - - (BOOL)lessThanIos10_2 { if (_version.majorVersion < 10) { From a0eb87a2e8a343eea9b6ae5e32e84bcd31f78492 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Tue, 14 Jul 2020 18:10:52 +0800 Subject: [PATCH 17/29] readme: update `reportNewIncomingCall` example in `AppDelegate.m` --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d486374a..4c51ebde 100644 --- a/README.md +++ b/README.md @@ -790,7 +790,18 @@ Since iOS 13, you'll have to report the incoming calls that wakes up your applic // NSString *handle = @"caller number here"; // NSDictionary *extra = [payload.dictionaryPayload valueForKeyPath:@"custom.path.to.data"]; /* use this to pass any special data (ie. from your notification) down to RN. Can also be `nil` */ - [RNCallKeep reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:false localizedCallerName:callerName fromPushKit: YES payload:extra withCompletionHandler:completion]; + [RNCallKeep reportNewIncomingCall: uuid + handle: handle + handleType: @"generic" + hasVideo: NO + localizedCallerName: callerName + supportsHolding: YES + supportsDTMF: YES + supportsGrouping: YES + supportsUngrouping: YES + fromPushKit: YES + payload: extra + withCompletionHandler: completion]; } ``` From 001c79ba419f6adea7f6144e640d28d0af15c262 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Thu, 12 Nov 2020 18:37:15 +0800 Subject: [PATCH 18/29] fix: not to add `didLoadWithEvents` in constructor --- README.md | 5 ++++- index.js | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 90bcb20c..1e6f11c4 100644 --- a/README.md +++ b/README.md @@ -571,8 +571,11 @@ Called as soon as JS context initializes if there were some actions performed by 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 +// register `didLoadWithEvents` somewhere early in your app when it is ready to handle callkeep events. + RNCallKeep.addEventListener('didLoadWithEvents', (events) => { - // see example usage in https://github.com/react-native-webrtc/react-native-callkeep/pull/169 + // `events` is passed as an Array chronologically, handle or ignore events based on the app's logic + // see example usage in https://github.com/react-native-webrtc/react-native-callkeep/pull/169 or https://github.com/react-native-webrtc/react-native-callkeep/pull/205 }); ``` diff --git a/index.js b/index.js index 0ef37db0..2b694966 100644 --- a/index.js +++ b/index.js @@ -22,12 +22,6 @@ class RNCallKeep { constructor() { this._callkeepEventHandlers = new Map(); - - this.addEventListener('didLoadWithEvents', (events) => { - events.forEach(event => { - emit(event.name, event.data); - }); - }); } addEventListener = (type, handler) => { From 16410d5e2b9158a970617f3c855c64bd4addf77a Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Sun, 22 Nov 2020 14:00:15 -0500 Subject: [PATCH 19/29] Start a foreground service to ba able to get audio on Android 11 bg --- README.md | 14 ++++++- .../io/wazo/callkeep/RNCallKeepModule.java | 2 + .../wazo/callkeep/VoiceConnectionService.java | 42 +++++++++++++++++-- docs/android-installation.md | 3 +- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4c51ebde..448edd8a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,13 @@ const options = { cancelButton: 'Cancel', okButton: 'ok', imageName: 'phone_account_icon', - additionalPermissions: [PermissionsAndroid.PERMISSIONS.example] + additionalPermissions: [PermissionsAndroid.PERMISSIONS.example], + // Required to get audio in background when using Android 11 + foregroundService: { + channelId: 'com.company.my', + channelName: 'Foreground service for my app', + notificationTitle: 'My app is running on background', + }, } }; @@ -805,6 +811,12 @@ Since iOS 13, you'll have to report the incoming calls that wakes up your applic } ``` +## Android 11 + +Since Android 11, your application [requires to start a foregroundService](https://developer.android.com/about/versions/11/privacy/foreground-services) in order to access the microphone in background. + +You have to set the `foregroundService` key in the [`setup()`](#setup) method and add a `foregroundServiceType` in the [`AndroidManifest` file](docs/android-installation.md#android-common-step-installation). + ## Debug ### Android diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index ad01429f..9941bd82 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -128,6 +128,8 @@ public void setup(ReadableMap options) { this.registerEvents(); VoiceConnectionService.setAvailable(true); } + + VoiceConnectionService.setSettings(options); } @ReactMethod diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index 4419cf57..07175f07 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -18,6 +18,11 @@ package io.wazo.callkeep; import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Intent; import android.content.Context; import android.content.ComponentName; @@ -27,6 +32,7 @@ import android.os.Handler; import android.speech.tts.Voice; import androidx.annotation.Nullable; +import android.support.v4.app.NotificationCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.telecom.CallAudioState; import android.telecom.Connection; @@ -37,10 +43,8 @@ import android.telecom.TelecomManager; import android.util.Log; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningTaskInfo; - import com.facebook.react.HeadlessJsTaskService; +import com.facebook.react.bridge.ReadableMap; import java.util.ArrayList; import java.util.HashMap; @@ -51,6 +55,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; import static io.wazo.callkeep.Constants.ACTION_AUDIO_SESSION; import static io.wazo.callkeep.Constants.ACTION_ONGOING_CALL; import static io.wazo.callkeep.Constants.ACTION_CHECK_REACHABILITY; @@ -70,6 +75,7 @@ public class VoiceConnectionService extends ConnectionService { private static String notReachableCallUuid; private static ConnectionRequest currentConnectionRequest; private static PhoneAccountHandle phoneAccountHandle; + private static ReadableMap _settings; private static String TAG = "RNCK:VoiceConnectionService"; public static Map currentConnections = new HashMap<>(); public static Boolean hasOutgoingCall = false; @@ -105,6 +111,10 @@ public static void setAvailable(Boolean value) { isAvailable = value; } + public static void setSettings(ReadableMap settings) { + _settings = settings; + } + public static void setCanMakeMultipleCalls(Boolean allow) { VoiceConnectionService.canMakeMultipleCalls = allow; } @@ -133,6 +143,8 @@ public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManage incomingCallConnection.setRinging(); incomingCallConnection.setInitialized(); + startForegroundService(); + return incomingCallConnection; } @@ -185,6 +197,8 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool outgoingCallConnection.setAudioModeIsVoip(true); outgoingCallConnection.setCallerDisplayName(displayName, TelecomManager.PRESENTATION_ALLOWED); + startForegroundService(); + // ‍️Weirdly on some Samsung phones (A50, S9...) using `setInitialized` will not display the native UI ... // when making a call from the native Phone application. The call will still be displayed correctly without it. if (!Build.MANUFACTURER.equalsIgnoreCase("Samsung")) { @@ -201,6 +215,28 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool return outgoingCallConnection; } + private void startForegroundService() { + if (_settings == null || !_settings.hasKey("foregroundService")) { + return; + } + ReadableMap foregroundSettings = _settings.getMap("foregroundService"); + String NOTIFICATION_CHANNEL_ID = foregroundSettings.getString("channelId"); + String channelName = foregroundSettings.getString("channelName"); + NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); + chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + assert manager != null; + manager.createNotificationChannel(chan); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); + Notification notification = notificationBuilder.setOngoing(true) + .setContentTitle(foregroundSettings.getString("notificationTitle")) + .setPriority(NotificationManager.IMPORTANCE_MIN) + .setCategory(Notification.CATEGORY_SERVICE) + .build(); + startForeground(FOREGROUND_SERVICE_TYPE_MICROPHONE, notification); + } + private void wakeUpApplication(String uuid, String number, String displayName) { Intent headlessIntent = new Intent( this.getApplicationContext(), diff --git a/docs/android-installation.md b/docs/android-installation.md index 9238ab9d..71c638d8 100644 --- a/docs/android-installation.md +++ b/docs/android-installation.md @@ -71,7 +71,8 @@ public class MainActivity extends ReactActivity { // ... + android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" + android:foregroundServiceType="camera|microphone">> From 6c81cab1c9cffd6b639ebc696e5000a33ab95dd6 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Sun, 22 Nov 2020 14:21:22 -0500 Subject: [PATCH 20/29] Add setForegroundServiceSettings method to avoid calling setup --- README.md | 14 ++++++++++++++ .../java/io/wazo/callkeep/RNCallKeepModule.java | 5 +++++ .../io/wazo/callkeep/VoiceConnectionService.java | 1 + index.d.ts | 2 ++ index.js | 10 ++++++++++ 5 files changed, 32 insertions(+) diff --git a/README.md b/README.md index 448edd8a..2c692d1e 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,20 @@ Eg: When your used log out (or the connection to your server is broken, etc..), RNCallKeep.setAvailable(true); ``` +### setForegroundServiceSettings +_This feature is available only on Android._ + +Configures the [Foreground Service](https://developer.android.com/about/versions/11/privacy/foreground-services) used for Android 11 to get microphone access on background. +Similar to set the `foregroundService` key in the `setup()` method. + +```js +RNCallKeep.setForegroundServiceSettings({ + channelId: 'com.company.my', + channelName: 'Foreground service for my app', + notificationTitle: 'My app is running on background', +}); +``` + ### canMakeMultipleCalls _This feature is available only on Android._ diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index 9941bd82..4deb5cbd 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -391,6 +391,11 @@ public void setAvailable(Boolean active) { VoiceConnectionService.setAvailable(active); } + @ReactMethod + public void setForegroundServiceSettings(ReadableMap settings) { + VoiceConnectionService.setSettings(settings); + } + @ReactMethod public void canMakeMultipleCalls(Boolean allow) { VoiceConnectionService.setCanMakeMultipleCalls(allow); diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index 07175f07..d5c914db 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -217,6 +217,7 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool private void startForegroundService() { if (_settings == null || !_settings.hasKey("foregroundService")) { + Log.d(TAG, "Not creating foregroundService because not configured"); return; } ReadableMap foregroundSettings = _settings.getMap("foregroundService"); diff --git a/index.d.ts b/index.d.ts index 2090fe29..fa75eb98 100644 --- a/index.d.ts +++ b/index.d.ts @@ -138,6 +138,8 @@ declare module 'react-native-callkeep' { */ static setAvailable(active: boolean): void + static setForegroundServiceSettings(settings: Object): void + static canMakeMultipleCalls(allow: boolean): void static setCurrentCallActive(callUUID: string): void diff --git a/index.js b/index.js index 88a9d37c..72185e2a 100644 --- a/index.js +++ b/index.js @@ -190,6 +190,16 @@ class RNCallKeep { RNCallKeepModule.setAvailable(state); }; + setForegroundServiceSettings = (settings) => { + if (isIOS) { + return; + } + + RNCallKeepModule.setForegroundServiceSettings({ + foregroundService: settings, + }); + }; + canMakeMultipleCalls = (state) => { if (isIOS) { return; From f05f5304544e2d5b041c5ce59b95343754ce4b73 Mon Sep 17 00:00:00 2001 From: Shaun Parkison Date: Mon, 23 Nov 2020 09:59:50 -0700 Subject: [PATCH 21/29] change permission requests logic fixes #288 --- .../io/wazo/callkeep/RNCallKeepModule.java | 76 ++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java index ad01429f..a57b4627 100644 --- a/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java +++ b/android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java @@ -55,9 +55,11 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.HeadlessJsTaskService; import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter; +import com.facebook.react.modules.permissions.PermissionsModule; import java.lang.reflect.Array; import java.util.ArrayList; @@ -253,13 +255,83 @@ public void checkPhoneAccountPermission(ReadableArray optionalPermissions, Promi optionalPermsArr[i] = optionalPermissions.getString(i); } - String[] allPermissions = Arrays.copyOf(permissions, permissions.length + optionalPermsArr.length); + final String[] allPermissions = Arrays.copyOf(permissions, permissions.length + optionalPermsArr.length); System.arraycopy(optionalPermsArr, 0, allPermissions, permissions.length, optionalPermsArr.length); hasPhoneAccountPromise = promise; if (!this.hasPermissions()) { - requestPermissions(currentActivity, allPermissions, REQUEST_READ_PHONE_STATE); + WritableArray allPermissionaw = Arguments.createArray(); + for (String allPermission : allPermissions) { + allPermissionaw.pushString(allPermission); + } + + getReactApplicationContext() + .getNativeModule(PermissionsModule.class) + .requestMultiplePermissions(allPermissionaw, new Promise() { + @Override + public void resolve(@Nullable Object value) { + WritableMap grantedPermission = (WritableMap) value; + int[] grantedResult = new int[allPermissions.length]; + for (int i=0; i Date: Mon, 23 Nov 2020 12:16:48 -0500 Subject: [PATCH 22/29] Improve check on foreground service and add icon in settings --- .../wazo/callkeep/VoiceConnectionService.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index d5c914db..c8b96a47 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -23,6 +23,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.content.res.Resources; import android.content.Intent; import android.content.Context; import android.content.ComponentName; @@ -216,6 +217,10 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool } private void startForegroundService() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + // Foreground services not required before SDK 28 + return; + } if (_settings == null || !_settings.hasKey("foregroundService")) { Log.d(TAG, "Not creating foregroundService because not configured"); return; @@ -230,11 +235,19 @@ private void startForegroundService() { manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); - Notification notification = notificationBuilder.setOngoing(true) - .setContentTitle(foregroundSettings.getString("notificationTitle")) - .setPriority(NotificationManager.IMPORTANCE_MIN) - .setCategory(Notification.CATEGORY_SERVICE) - .build(); + notificationBuilder.setOngoing(true) + .setContentTitle(foregroundSettings.getString("notificationTitle")) + .setPriority(NotificationManager.IMPORTANCE_MIN) + .setCategory(Notification.CATEGORY_SERVICE); + + if (foregroundSettings.hasKey("icon")) { + Context context = this.getApplicationContext(); + Resources res = context.getResources(); + String smallIcon = foregroundSettings.getString("icon"); + notificationBuilder.setSmallIcon(res.getIdentifier(smallIcon, "mipmap", context.getPackageName())); + } + + Notification notification = notificationBuilder.build(); startForeground(FOREGROUND_SERVICE_TYPE_MICROPHONE, notification); } From b8bc8913e62d5678634a5ffc123b39e652983bd8 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Mon, 23 Nov 2020 14:22:57 -0500 Subject: [PATCH 23/29] Rename icon to notiticationIcon and add documentation --- README.md | 2 ++ .../main/java/io/wazo/callkeep/VoiceConnectionService.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c692d1e..a96d778a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ const options = { channelId: 'com.company.my', channelName: 'Foreground service for my app', notificationTitle: 'My app is running on background', + notiticationIcon: 'Path to the resource icon of the notification', }, } }; @@ -141,6 +142,7 @@ RNCallKeep.setForegroundServiceSettings({ channelId: 'com.company.my', channelName: 'Foreground service for my app', notificationTitle: 'My app is running on background', + notiticationIcon: 'Path to the resource icon of the notification', }); ``` diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index c8b96a47..1d0eb5b9 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -240,10 +240,10 @@ private void startForegroundService() { .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE); - if (foregroundSettings.hasKey("icon")) { + if (foregroundSettings.hasKey("notificationIcon")) { Context context = this.getApplicationContext(); Resources res = context.getResources(); - String smallIcon = foregroundSettings.getString("icon"); + String smallIcon = foregroundSettings.getString("notificationIcon"); notificationBuilder.setSmallIcon(res.getIdentifier(smallIcon, "mipmap", context.getPackageName())); } From a8ca010b52ffe31e3a7cf221e142712ab451b129 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Mon, 23 Nov 2020 14:24:16 -0500 Subject: [PATCH 24/29] 3.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e463cdb..42e77002 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "3.1.4", + "version": "3.1.5", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From bd4948815e382881416fa2f8fdadf1781b0510ca Mon Sep 17 00:00:00 2001 From: Geraint White Date: Tue, 24 Nov 2020 10:28:32 +0000 Subject: [PATCH 25/29] Fix `notiticationIcon` typo in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a96d778a..da50852b 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ const options = { channelId: 'com.company.my', channelName: 'Foreground service for my app', notificationTitle: 'My app is running on background', - notiticationIcon: 'Path to the resource icon of the notification', + notificationIcon: 'Path to the resource icon of the notification', }, } }; @@ -142,7 +142,7 @@ RNCallKeep.setForegroundServiceSettings({ channelId: 'com.company.my', channelName: 'Foreground service for my app', notificationTitle: 'My app is running on background', - notiticationIcon: 'Path to the resource icon of the notification', + notificationIcon: 'Path to the resource icon of the notification', }); ``` From 3f8aaed3e835de863320489406561755dd2a05b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Tue, 24 Nov 2020 09:27:36 -0500 Subject: [PATCH 26/29] 4.0.0 --- MIGRATION_v3_v4.md | 20 ++++++++++++++++++++ package.json | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 MIGRATION_v3_v4.md diff --git a/MIGRATION_v3_v4.md b/MIGRATION_v3_v4.md new file mode 100644 index 00000000..dec23113 --- /dev/null +++ b/MIGRATION_v3_v4.md @@ -0,0 +1,20 @@ +# Migration from CallKeep v3 to v4 + +The `reportNewIncomingCall` method on iOS is now more consistent. + +Please update your `AppDelegate.m` file with this new signature: + +```objc +[RNCallKeep reportNewIncomingCall: uuidString + handle: handle + handleType: handleType + hasVideo: YES + localizedCallerName: localizedCallerName + supportsHolding: YES + supportsDTMF: YES + supportsGrouping: YES + supportsUngrouping: YES + fromPushKit: YES + payload: nil + withCompletionHandler: nil]; +``` diff --git a/package.json b/package.json index 42e77002..d4e84c60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "3.1.5", + "version": "4.0.0", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From 8d0ec78c54810283e392d2e9bc664647a4f8fa39 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Mon, 30 Nov 2020 15:01:15 -0500 Subject: [PATCH 27/29] Make foreground service compatible with SDK version < 30 --- README.md | 1 + android/src/main/java/io/wazo/callkeep/Constants.java | 2 ++ .../src/main/java/io/wazo/callkeep/VoiceConnectionService.java | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da50852b..c14edc3b 100644 --- a/README.md +++ b/README.md @@ -830,6 +830,7 @@ Since iOS 13, you'll have to report the incoming calls that wakes up your applic ## Android 11 Since Android 11, your application [requires to start a foregroundService](https://developer.android.com/about/versions/11/privacy/foreground-services) in order to access the microphone in background. +You'll need to upgrade your `compileSdkVersion` to `30` to be able to use this feature. You have to set the `foregroundService` key in the [`setup()`](#setup) method and add a `foregroundServiceType` in the [`AndroidManifest` file](docs/android-installation.md#android-common-step-installation). diff --git a/android/src/main/java/io/wazo/callkeep/Constants.java b/android/src/main/java/io/wazo/callkeep/Constants.java index f10e109c..e0b8bc57 100644 --- a/android/src/main/java/io/wazo/callkeep/Constants.java +++ b/android/src/main/java/io/wazo/callkeep/Constants.java @@ -18,4 +18,6 @@ public class Constants { public static final String EXTRA_CALLER_NAME = "EXTRA_CALLER_NAME"; // Can't use telecom.EXTRA_DISABLE_ADD_CALL ... public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL"; + + public static final int FOREGROUND_SERVICE_TYPE_MICROPHONE = 128; } diff --git a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java index 1d0eb5b9..aa83b532 100644 --- a/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java +++ b/android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java @@ -56,7 +56,6 @@ import java.util.UUID; import java.util.stream.Collectors; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; import static io.wazo.callkeep.Constants.ACTION_AUDIO_SESSION; import static io.wazo.callkeep.Constants.ACTION_ONGOING_CALL; import static io.wazo.callkeep.Constants.ACTION_CHECK_REACHABILITY; @@ -65,6 +64,7 @@ import static io.wazo.callkeep.Constants.EXTRA_CALL_NUMBER; import static io.wazo.callkeep.Constants.EXTRA_CALL_UUID; import static io.wazo.callkeep.Constants.EXTRA_DISABLE_ADD_CALL; +import static io.wazo.callkeep.Constants.FOREGROUND_SERVICE_TYPE_MICROPHONE; // @see https://github.com/kbagchiGWC/voice-quickstart-android/blob/9a2aff7fbe0d0a5ae9457b48e9ad408740dfb968/exampleConnectionService/src/main/java/com/twilio/voice/examples/connectionservice/VoiceConnectionService.java @TargetApi(Build.VERSION_CODES.M) From 0bdd1e7dbb52c81d6f5e8a39c13726f6c7451223 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Tue, 1 Dec 2020 09:06:25 -0500 Subject: [PATCH 28/29] 4.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4e84c60..5c15958e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-callkeep", - "version": "4.0.0", + "version": "4.0.1", "description": "iOS 10 CallKit and Android ConnectionService Framework For React Native", "main": "index.js", "scripts": {}, From 7040d6d03f2a60a69fdaeac6e8b0b18be03823b9 Mon Sep 17 00:00:00 2001 From: zxcpoiu Date: Fri, 4 Dec 2020 17:43:26 +0800 Subject: [PATCH 29/29] doc: add note for didLoadWithEvents in readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9c754287..35ed1d30 100644 --- a/README.md +++ b/README.md @@ -607,6 +607,8 @@ Called as soon as JS context initializes if there were some actions performed by 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. +**NOTE: You still need to subscribe / handle the rest events as usuall. This is just a helper whcih cache and propagate early fired events if and only if for "the native events which DID fire BEFORE js bridge is initialed", it does NOT mean this will have events each time when the app reopened.** + ```js // register `didLoadWithEvents` somewhere early in your app when it is ready to handle callkeep events.