Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: react-native-webrtc/react-native-callkeep
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3.1.3
Choose a base ref
...
head repository: react-native-webrtc/react-native-callkeep
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Sep 24, 2020

  1. Copy the full SHA
    be5f3fb View commit details

Commits on Oct 13, 2020

  1. Copy the full SHA
    cb6dbb1 View commit details

Commits on Nov 11, 2020

  1. Copy the full SHA
    413c6a0 View commit details
  2. Merge pull request #314 from react-native-webrtc/fix_permission_version

    Fix android permissions depending on os version
    Sylvain Boily authored Nov 11, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    07d83e4 View commit details
  3. 3.1.4

    manuquentin committed Nov 11, 2020
    Copy the full SHA
    3bcd5ea View commit details

Commits on Nov 12, 2020

  1. Copy the full SHA
    ae3830d View commit details
  2. Copy the full SHA
    07afe95 View commit details
  3. Copy the full SHA
    f3e0146 View commit details
  4. Copy the full SHA
    130fac1 View commit details
  5. Copy the full SHA
    4654eb1 View commit details
  6. Copy the full SHA
    a0eb87a View commit details
  7. Copy the full SHA
    001c79b View commit details

Commits on Nov 13, 2020

  1. Merge pull request #238 from zxcpoiu/support_options

    ios: support additional options pass from js
    manuquentin authored Nov 13, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c3ade44 View commit details

Commits on Nov 22, 2020

  1. Copy the full SHA
    16410d5 View commit details
  2. Copy the full SHA
    6c81cab View commit details

Commits on Nov 23, 2020

  1. change permission requests logic

    fixes #288
    Shaun Parkison committed Nov 23, 2020
    Copy the full SHA
    f05f530 View commit details
  2. Copy the full SHA
    d7c65cc View commit details
  3. Copy the full SHA
    b8bc891 View commit details
  4. Merge pull request #321 from react-native-webrtc/android_11_foregroun…

    …d_service
    
    Start a foreground service to be able to get audio on Android 11 bg
    manuquentin authored Nov 23, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    619fb95 View commit details
  5. 3.1.5

    manuquentin committed Nov 23, 2020
    Copy the full SHA
    a8ca010 View commit details

Commits on Nov 24, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    bd49488 View commit details
  2. Merge pull request #326 from grit96/patch-1

    Fix `notiticationIcon` typo in README.md
    Sylvain Boily authored Nov 24, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    59add3e View commit details
  3. 4.0.0

    manuquentin committed Nov 24, 2020
    Copy the full SHA
    3f8aaed View commit details

Commits on Nov 30, 2020

  1. Copy the full SHA
    8d0ec78 View commit details
  2. Merge pull request #324 from sparkison/fix-android-permissions-dialog…

    …-crash
    
    [Android] fix app crash after permissions dialog closed
    manuquentin authored Nov 30, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    523f683 View commit details
  3. Merge pull request #315 from zxcpoiu/fix_initial_listener

    fix: not to add `didLoadWithEvents` in constructor
    manuquentin authored Nov 30, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    60b2620 View commit details

Commits on Dec 1, 2020

  1. Merge pull request #331 from react-native-webrtc/sdk_lower_30_compatible

    Make foreground service compatible with SDK version < 30
    manuquentin authored Dec 1, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    132eddf View commit details
  2. 4.0.1

    manuquentin committed Dec 1, 2020
    Copy the full SHA
    0bdd1e7 View commit details

Commits on Dec 4, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7040d6d View commit details
  2. Merge pull request #333 from react-native-webrtc/note_for_didLoadWith…

    …Events
    
    doc: add note for didLoadWithEvents in readme
    manuquentin authored Dec 4, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8bdddcc View commit details

Commits on Dec 8, 2020

  1. Update readme

    dtsolis committed Dec 8, 2020
    Copy the full SHA
    9ccc663 View commit details

Commits on Dec 11, 2020

  1. Bump ini from 1.3.5 to 1.3.7 in /example

    Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
    - [Release notes](https://github.com/isaacs/ini/releases)
    - [Commits](npm/ini@v1.3.5...v1.3.7)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Dec 11, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d0394b5 View commit details

Commits on Jan 5, 2021

  1. Copy the full SHA
    adc6bba View commit details
  2. review suggestion changes updated

    1. Indentation issue
    2. platform specific-check
    vtn-dev-prithipal committed Jan 5, 2021
    Copy the full SHA
    49d984f View commit details

Commits on Feb 3, 2021

  1. Copy the full SHA
    c053d4b View commit details
  2. Copy the full SHA
    37e1285 View commit details

Commits on Feb 8, 2021

  1. Fix return type on setup

    Setup returns a boolean
    martin-juul authored Feb 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    be723d9 View commit details

Commits on Feb 17, 2021

  1. Merge pull request #335 from react-native-webrtc/dependabot/npm_and_y…

    …arn/example/ini-1.3.7
    
    Bump ini from 1.3.5 to 1.3.7 in /example
    Sylvain Boily authored Feb 17, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f3fe188 View commit details
  2. feat: add getCalls method for ios only

    Jerome91410 committed Feb 17, 2021
    Copy the full SHA
    dbb8a1a View commit details
  3. fix: avoid isCallActive returning undefined

    Jerome91410 committed Feb 17, 2021
    Copy the full SHA
    af160e7 View commit details

Commits on Feb 19, 2021

  1. Merge pull request #356 from markgibaud-vtail/includeCallsInRecent-types

    Include calls in recent types
    Sylvain Boily authored Feb 19, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cacd8c6 View commit details
  2. Merge pull request #362 from martin-juul/patch-1

    Fix return type on setup
    Sylvain Boily authored Feb 19, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c64ec3e View commit details
  3. Merge pull request #364 from Jerome91410/fix-isCallActive-returns

    fix: avoid isCallActive returning undefined
    Sylvain Boily authored Feb 19, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2da7e8b View commit details
  4. Merge pull request #366 from Jerome91410/ios-get-current-calls

    feat: add getCalls method for ios only
    Sylvain Boily authored Feb 19, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ba4197a View commit details
  5. Merge pull request #300 from dtsolis/answerIncoming

    [iOS] Add support for answering incoming call
    Sylvain Boily authored Feb 19, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8e56b0a View commit details

Commits on Feb 23, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    efb99e4 View commit details
  2. Merge pull request #370 from dayze/patch-1

    docs: update readme for react-native-voip-push-notification new link
    Sylvain Boily authored Feb 23, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8afacec View commit details
  3. androidx import

    Kelt committed Feb 23, 2021
    Copy the full SHA
    226e7e6 View commit details

Commits on Feb 26, 2021

  1. Fixing types

    maludwig committed Feb 26, 2021
    Copy the full SHA
    aaf68a6 View commit details
  2. Merge pull request #373 from maludwig/fixing-constant-types

    Fixing types
    Sylvain Boily authored Feb 26, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    51c4b07 View commit details
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -10,3 +10,4 @@ Since react-native-callkit only targets iOS devices, we created react-native-cal
Linus Unnebäck <linus@folkdatorn.se>
Geraint White <mail@geraintwhite.co.uk>
Samuel Bégin
Jonas Swiatek <jswiatek@telzio.com>
20 changes: 20 additions & 0 deletions MIGRATION_v3_v4.md
Original file line number Diff line number Diff line change
@@ -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];
```
526 changes: 469 additions & 57 deletions README.md

Large diffs are not rendered by default.

38 changes: 36 additions & 2 deletions actions.js
Original file line number Diff line number Diff line change
@@ -15,6 +15,11 @@ const RNCallKeepDidPerformDTMFAction = 'RNCallKeepDidPerformDTMFAction';
const RNCallKeepProviderReset = 'RNCallKeepProviderReset';
const RNCallKeepCheckReachability = 'RNCallKeepCheckReachability';
const RNCallKeepDidLoadWithEvents = 'RNCallKeepDidLoadWithEvents';
const RNCallKeepShowIncomingCallUi = 'RNCallKeepShowIncomingCallUi';
const RNCallKeepOnSilenceIncomingCall = 'RNCallKeepOnSilenceIncomingCall';
const RNCallKeepOnIncomingConnectionFailed = 'RNCallKeepOnIncomingConnectionFailed';
const RNCallKeepDidChangeAudioRoute = 'RNCallKeepDidChangeAudioRoute';
const RNCallKeepHasActiveCall = 'RNCallKeepHasActiveCall';
const isIOS = Platform.OS === 'ios';

const didReceiveStartCallAction = handler => {
@@ -32,18 +37,33 @@ const answerCall = handler =>
const endCall = handler =>
eventEmitter.addListener(RNCallKeepPerformEndCallAction, (data) => handler(data));

const didChangeAudioRoute = handler =>
eventEmitter.addListener(RNCallKeepDidChangeAudioRoute, handler);

const didActivateAudioSession = handler =>
eventEmitter.addListener(RNCallKeepDidActivateAudioSession, handler);

const didDeactivateAudioSession = handler =>
eventEmitter.addListener(RNCallKeepDidDeactivateAudioSession, handler);

const didDisplayIncomingCall = handler =>
eventEmitter.addListener(RNCallKeepDidDisplayIncomingCall, (data) => handler(data));
const didDisplayIncomingCall = handler => eventEmitter.addListener(RNCallKeepDidDisplayIncomingCall, data => {
// On Android the payload parameter is sent a String
// As it requires too much code on Android to convert it to WritableMap, let's do it here.
if (data.payload && typeof data.payload === 'string') {
try {
data.payload = JSON.parse(data.payload);
} catch (_) {
}
}
handler(data);
});

const didPerformSetMutedCallAction = handler =>
eventEmitter.addListener(RNCallKeepDidPerformSetMutedCallAction, (data) => handler(data));

const onHasActiveCall = handler =>
eventEmitter.addListener(RNCallKeepHasActiveCall, handler);

const didToggleHoldCallAction = handler =>
eventEmitter.addListener(RNCallKeepDidToggleHoldAction, handler);

@@ -59,6 +79,15 @@ const checkReachability = handler =>
const didLoadWithEvents = handler =>
eventEmitter.addListener(RNCallKeepDidLoadWithEvents, handler);

const showIncomingCallUi = handler =>
eventEmitter.addListener(RNCallKeepShowIncomingCallUi, (data) => handler(data));

const silenceIncomingCall = handler =>
eventEmitter.addListener(RNCallKeepOnSilenceIncomingCall, (data) => handler(data));

const createIncomingConnectionFailed = handler =>
eventEmitter.addListener(RNCallKeepOnIncomingConnectionFailed, (data) => handler(data));

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

export const listeners = {
@@ -74,4 +103,9 @@ export const listeners = {
didResetProvider,
checkReachability,
didLoadWithEvents,
showIncomingCallUi,
silenceIncomingCall,
createIncomingConnectionFailed,
didChangeAudioRoute,
onHasActiveCall
};
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
buildscript {
repositories {
mavenCentral()
google()
jcenter()
}

dependencies {
@@ -32,4 +32,5 @@ repositories {

dependencies {
implementation 'com.facebook.react:react-native:+'
implementation "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0"
}
1 change: 1 addition & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -5,4 +5,5 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
</manifest>
10 changes: 10 additions & 0 deletions android/src/main/java/io/wazo/callkeep/Constants.java
Original file line number Diff line number Diff line change
@@ -12,10 +12,20 @@ public class Constants {
public static final String ACTION_UNHOLD_CALL = "ACTION_UNHOLD_CALL";
public static final String ACTION_UNMUTE_CALL = "ACTION_UNMUTE_CALL";
public static final String ACTION_WAKE_APP = "ACTION_WAKE_APP";
public static final String ACTION_SHOW_INCOMING_CALL_UI = "ACTION_SHOW_INCOMING_CALL_UI";
public static final String ACTION_ON_SILENCE_INCOMING_CALL = "ACTION_ON_SILENCE_INCOMING_CALL";
public static final String ACTION_ON_CREATE_CONNECTION_FAILED = "ACTION_ON_CREATE_CONNECTION_FAILED";
public static final String ACTION_DID_CHANGE_AUDIO_ROUTE = "ACTION_DID_CHANGE_AUDIO_ROUTE";


public static final String EXTRA_CALL_NUMBER = "EXTRA_CALL_NUMBER";
public static final String EXTRA_CALL_NUMBER_SCHEMA = "EXTRA_CALL_NUMBER_SCHEMA";
public static final String EXTRA_CALL_UUID = "EXTRA_CALL_UUID";
public static final String EXTRA_CALLER_NAME = "EXTRA_CALLER_NAME";
public static final String EXTRA_HAS_VIDEO = "EXTRA_HAS_VIDEO";
public static final String EXTRA_PAYLOAD = "EXTRA_PAYLOAD";
// 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;
}
76 changes: 76 additions & 0 deletions android/src/main/java/io/wazo/callkeep/MapUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.wazo.callkeep;

import java.util.Iterator;
import java.util.Map;

import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;

import org.json.JSONObject;
import org.json.JSONException;

public class MapUtils {
// @see https://gist.github.com/viperwarp/2beb6bbefcc268dee7ad
public static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException {
WritableMap map = new WritableNativeMap();

Iterator<String> iterator = jsonObject.keys();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = jsonObject.get(key);
if (value instanceof JSONObject) {
map.putMap(key, convertJsonToMap((JSONObject) value));
} else if (value instanceof Boolean) {
map.putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
map.putInt(key, (Integer) value);
} else if (value instanceof Double) {
map.putDouble(key, (Double) value);
} else if (value instanceof String) {
map.putString(key, (String) value);
} else {
map.putString(key, value.toString());
}
}
return map;
}

public static JSONObject convertMapToJson(ReadableMap readableMap) throws JSONException {
JSONObject object = new JSONObject();
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
switch (readableMap.getType(key)) {
case Null:
object.put(key, JSONObject.NULL);
break;
case Boolean:
object.put(key, readableMap.getBoolean(key));
break;
case Number:
object.put(key, readableMap.getDouble(key));
break;
case String:
object.put(key, readableMap.getString(key));
break;
case Map:
object.put(key, convertMapToJson(readableMap.getMap(key)));
break;
}
}
return object;
}

public static WritableMap readableToWritableMap(ReadableMap readableMap) {
try {
JSONObject json = convertMapToJson(readableMap);

return convertJsonToMap(json);
} catch (JSONException e) {
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -25,6 +25,8 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
import com.facebook.react.jstasks.HeadlessJsTaskRetryPolicy;
import com.facebook.react.jstasks.LinearCountingRetryPolicy;

import static io.wazo.callkeep.Constants.EXTRA_CALLER_NAME;
import static io.wazo.callkeep.Constants.EXTRA_CALL_NUMBER;
@@ -38,11 +40,17 @@ public class RNCallKeepBackgroundMessagingService extends HeadlessJsTaskService
HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();

HeadlessJsTaskRetryPolicy retryPolicy = new LinearCountingRetryPolicy(
5, // Max number of retry attempts
500 // Delay between each retry attempt
);

return new HeadlessJsTaskConfig(
"RNCallKeepBackgroundMessage",
Arguments.fromBundle(extras),
60000,
false
false,
retryPolicy
);
}
}
876 changes: 803 additions & 73 deletions android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ public class RNCallKeepPackage implements ReactPackage {

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.<NativeModule>singletonList(new RNCallKeepModule(reactContext));
return Collections.<NativeModule>singletonList(RNCallKeepModule.getInstance(reactContext, true));
}

// Deprecated RN 0.47
171 changes: 156 additions & 15 deletions android/src/main/java/io/wazo/callkeep/VoiceConnection.java
Original file line number Diff line number Diff line change
@@ -48,13 +48,18 @@
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.ACTION_SHOW_INCOMING_CALL_UI;
import static io.wazo.callkeep.Constants.ACTION_ON_SILENCE_INCOMING_CALL;
import static io.wazo.callkeep.Constants.ACTION_DID_CHANGE_AUDIO_ROUTE;

@TargetApi(Build.VERSION_CODES.M)
public class VoiceConnection extends Connection {
private boolean isMuted = false;
private boolean answered = false;
private boolean rejected = false;
private HashMap<String, String> handle;
private Context context;
private static final String TAG = "RNCK:VoiceConnection";
private static final String TAG = "RNCallKeep";

VoiceConnection(Context context, HashMap<String, String> handle) {
super();
@@ -83,6 +88,11 @@ public void onExtrasChanged(Bundle extras) {

@Override
public void onCallAudioStateChanged(CallAudioState state) {
Log.d(TAG, "[VoiceConnection] onCallAudioStateChanged muted :" + (state.isMuted() ? "true" : "false"));

handle.put("output", CallAudioState.audioRouteToString(state.getRoute()));
sendCallRequestToActivity(ACTION_DID_CHANGE_AUDIO_ROUTE, handle);

if (state.isMuted() == this.isMuted) {
return;
}
@@ -91,25 +101,29 @@ public void onCallAudioStateChanged(CallAudioState state) {
sendCallRequestToActivity(isMuted ? ACTION_MUTE_CALL : ACTION_UNMUTE_CALL, handle);
}

@Override
public void onAnswer(int videoState) {
super.onAnswer(videoState);
Log.d(TAG, "[VoiceConnection] onAnswer(int) executed");

this._onAnswer(videoState);
}

@Override
public void onAnswer() {
super.onAnswer();
Log.d(TAG, "onAnswer called");
Log.d(TAG, "[VoiceConnection] onAnswer() executed");

setConnectionCapabilities(getConnectionCapabilities() | Connection.CAPABILITY_HOLD);
setAudioModeIsVoip(true);

sendCallRequestToActivity(ACTION_ANSWER_CALL, handle);
sendCallRequestToActivity(ACTION_AUDIO_SESSION, handle);
Log.d(TAG, "onAnswer executed");
this._onAnswer(0);
}

@Override
public void onPlayDtmfTone(char dtmf) {
Log.d(TAG, "[VoiceConnection] Playing DTMF : " + dtmf);
try {
handle.put("DTMF", Character.toString(dtmf));
} catch (Throwable exception) {
Log.e(TAG, "Handle map error", exception);
Log.e(TAG, "[VoiceConnection] Handle map error", exception);
}
sendCallRequestToActivity(ACTION_DTMF_TONE, handle);
}
@@ -119,11 +133,11 @@ public void onDisconnect() {
super.onDisconnect();
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
sendCallRequestToActivity(ACTION_END_CALL, handle);
Log.d(TAG, "onDisconnect executed");
Log.d(TAG, "[VoiceConnection] onDisconnect executed");
try {
((VoiceConnectionService) context).deinitConnection(handle.get(EXTRA_CALL_UUID));
} catch(Throwable exception) {
Log.e(TAG, "Handle map error", exception);
Log.e(TAG, "[VoiceConnection] onDisconnect handle map error", exception);
}
destroy();
}
@@ -159,43 +173,170 @@ public void onAbort() {
super.onAbort();
setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
sendCallRequestToActivity(ACTION_END_CALL, handle);
Log.d(TAG, "onAbort executed");
Log.d(TAG, "[VoiceConnection] onAbort executed");
try {
((VoiceConnectionService) context).deinitConnection(handle.get(EXTRA_CALL_UUID));
} catch(Throwable exception) {
Log.e(TAG, "Handle map error", exception);
Log.e(TAG, "[VoiceConnection] onAbort handle map error", exception);
}
destroy();
}

@Override
public void onHold() {
Log.d(TAG, "[VoiceConnection] onHold");
super.onHold();
this.setOnHold();
sendCallRequestToActivity(ACTION_HOLD_CALL, handle);
}

@Override
public void onUnhold() {
Log.d(TAG, "[VoiceConnection] onUnhold");
super.onUnhold();
sendCallRequestToActivity(ACTION_UNHOLD_CALL, handle);
setActive();
}

public void onReject(int rejectReason) {
Log.d(TAG, "[VoiceConnection] onReject(int) executed");

this._onReject(rejectReason, null);
}

@Override
public void onReject() {
super.onReject();
Log.d(TAG, "[VoiceConnection] onReject() executed");

this._onReject(0, null);
}

@Override
public void onReject(String replyMessage) {
super.onReject(replyMessage);
Log.d(TAG, "[VoiceConnection] onReject(String) executed");

this._onReject(0, replyMessage);
}

@Override
public void onCallEvent(String event, Bundle extras) {
super.onCallEvent(event, extras);

Log.d(TAG, "[VoiceConnection] onCallEvent called, event: " + event);
}

@Override
public void onDeflect(Uri address) {
super.onDeflect(address);

Log.d(TAG, "[VoiceConnection] onDeflect called, address: " + address);
}

@Override
public void onHandoverComplete() {
super.onHandoverComplete();

Log.d(TAG, "[VoiceConnection] onHandoverComplete called");
}

@Override
public void onPostDialContinue(boolean proceed) {
super.onPostDialContinue(proceed);

Log.d(TAG, "[VoiceConnection] onPostDialContinue called, proceed: " + proceed);
}

@Override
public void onPullExternalCall() {
super.onPullExternalCall();

Log.d(TAG, "[VoiceConnection] onPullExternalCall called");
}

@Override
public void onSeparate() {
super.onSeparate();

Log.d(TAG, "[VoiceConnection] onSeparate called");
}

@Override
public void onStateChanged(int state) {
super.onStateChanged(state);

Log.d(TAG, "[VoiceConnection] onStateChanged called, state : " + state);
}

@Override
public void onSilence() {
// onSilence was added on API level 29
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
return;
}

super.onSilence();

sendCallRequestToActivity(ACTION_ON_SILENCE_INCOMING_CALL, handle);
Log.d(TAG, "[VoiceConnection] onSilence called");
}

@Override
public void onStopDtmfTone() {
super.onStopDtmfTone();

Log.d(TAG, "[VoiceConnection] onStopDtmfTone called");
}

@Override
public void onStopRtt() {
super.onStopRtt();

Log.d(TAG, "[VoiceConnection] onStopRtt called");
}

private void _onAnswer(int videoState) {
Log.d(TAG, "[VoiceConnection] onAnswer called, videoState: " + videoState + ", answered: " + answered);
// On some device (like Huawei P30 lite), both onAnswer() and onAnswer(int) are called
// we have to trigger the callback only once
if (answered) {
return;
}
answered = true;

setConnectionCapabilities(getConnectionCapabilities() | Connection.CAPABILITY_HOLD);
setAudioModeIsVoip(true);

sendCallRequestToActivity(ACTION_ANSWER_CALL, handle);
sendCallRequestToActivity(ACTION_AUDIO_SESSION, handle);
Log.d(TAG, "[VoiceConnection] onAnswer executed");
}

private void _onReject(int rejectReason, String replyMessage) {
Log.d(TAG, "[VoiceConnection] onReject executed, rejectReason: " + rejectReason + ", replyMessage: " + replyMessage + ", rejected:" + rejected);
if (rejected) {
return;
}
rejected = true;

setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
sendCallRequestToActivity(ACTION_END_CALL, handle);
Log.d(TAG, "onReject executed");
Log.d(TAG, "[VoiceConnection] onReject executed");
try {
((VoiceConnectionService) context).deinitConnection(handle.get(EXTRA_CALL_UUID));
} catch(Throwable exception) {
Log.e(TAG, "Handle map error", exception);
Log.e(TAG, "[VoiceConnection] onReject, handle map error", exception);
}
destroy();
}

@Override
public void onShowIncomingCallUi() {
Log.d(TAG, "[VoiceConnection] onShowIncomingCallUi");
sendCallRequestToActivity(ACTION_SHOW_INCOMING_CALL_UI, handle);
}

/*
* Send call request to the RNCallKeepModule
*/
392 changes: 345 additions & 47 deletions android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions docs/android-installation.md
Original file line number Diff line number Diff line change
@@ -60,18 +60,27 @@ public class MainActivity extends ReactActivity {

1. In `android/app/src/main/AndroidManifest.xml` add these permissions:


```xml
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
// Use this to target android >= 14
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />

<application>
// ...
<service android:name="io.wazo.callkeep.VoiceConnectionService"
android:label="Wazo"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
// Use this to target android >= 11
android:foregroundServiceType="camera|microphone"
// For android < 11
android:foregroundServiceType="phoneCall"
>

<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
@@ -80,6 +89,8 @@ public class MainActivity extends ReactActivity {
</application>
```

Beware to choose the right `foregroundServiceType` depending on the version of Android you want to target.

2. To be able to wake up your killed application when making an outgoing call form the native Phone application:

Add this in the `application` node of `android/app/src/main/AndroidManifest.xml` :
2 changes: 1 addition & 1 deletion docs/ios-installation.md
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ Add it before the `@end` tag.
```diff
+ - (BOOL)application:(UIApplication *)application
+ continueUserActivity:(NSUserActivity *)userActivity
+ restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler
+ restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler
+ {
+ return [RNCallKeep application:application
+ continueUserActivity:userActivity
2 changes: 1 addition & 1 deletion example/App.js
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ export default function App() {
const { [callUUID]: __, ...updatedHeldCalls } = heldCalls;

setCalls(updated);
setCalls(updatedHeldCalls);
setHeldCalls(updatedHeldCalls);
};

const setCallHeld = (callUUID, held) => {
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -9,12 +9,12 @@
"expo": "^34.0.1",
"react": "16.8.3",
"react-dom": "^16.8.6",
"react-native": "0.59.8",
"react-native": "0.62.3",
"react-native-background-timer": "^2.1.1",
"react-native-callkeep": "3.0.7",
"react-native-device-info": "^2.3.2",
"react-native-gesture-handler": "~1.3.0",
"react-native-reanimated": "~1.1.0",
"react-native-reanimated": "~2.10.0",
"react-native-unimodules": "~0.5.2",
"react-native-web": "^0.11.4",
"uuid": "^3.3.2"
2,997 changes: 1,767 additions & 1,230 deletions example/yarn.lock

Large diffs are not rendered by default.

183 changes: 161 additions & 22 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,94 @@
declare module 'react-native-callkeep' {
export type Events =
'didReceiveStartCallAction' |
'answerCall' |
'endCall' |
'didActivateAudioSession' |
'didDeactivateAudioSession' |
'didDisplayIncomingCall' |
'didToggleHoldCallAction' |
'didPerformDTMFAction' |
'didResetProvider' |
'checkReachability' |
'didPerformSetMutedCallAction' |
'didLoadWithEvents';
export type NativeEvents = {
didReceiveStartCallAction: 'RNCallKeepDidReceiveStartCallAction';
answerCall: 'RNCallKeepPerformAnswerCallAction';
endCall: 'RNCallKeepPerformEndCallAction';
didActivateAudioSession: 'RNCallKeepDidActivateAudioSession';
didDeactivateAudioSession: 'RNCallKeepDidDeactivateAudioSession';
didDisplayIncomingCall: 'RNCallKeepDidDisplayIncomingCall';
didPerformSetMutedCallAction: 'RNCallKeepDidPerformSetMutedCallAction';
didToggleHoldCallAction: 'RNCallKeepDidToggleHoldAction';
didChangeAudioRoute: 'RNCallKeepDidChangeAudioRoute';
didPerformDTMFAction: 'RNCallKeepDidPerformDTMFAction';
showIncomingCallUi: 'RNCallKeepShowIncomingCallUi';
silenceIncomingCall: 'RNCallKeepOnSilenceIncomingCall';
createIncomingConnectionFailed: 'RNCallKeepOnIncomingConnectionFailed';
checkReachability: 'RNCallKeepCheckReachability';
didResetProvider: 'RNCallKeepProviderReset';
didLoadWithEvents: 'RNCallKeepDidLoadWithEvents';
onHasActiveCall : 'onHasActiveCall';
}

export type InitialEvents = Array<{
[Event in Events]: { name: NativeEvents[Event], data: EventsPayload[Event] }
}[Events]>

export type Events = keyof NativeEvents;
export type EventsPayload = {
didReceiveStartCallAction: { handle: string, callUUID?: string, name?: string };
answerCall: { callUUID: string };
endCall: { callUUID: string };
didActivateAudioSession: undefined;
didDeactivateAudioSession: undefined;
didDisplayIncomingCall: {
error?: string,
errorCode?: 'Unentitled' | 'CallUUIDAlreadyExists' | 'FilteredByDoNotDisturb' | 'FilteredByBlockList' | 'Unknown',
callUUID: string,
handle: string,
localizedCallerName: string,
hasVideo: '1' | '0',
fromPushKit: '1' | '0',
payload: object,
};
didPerformSetMutedCallAction: { muted: boolean, callUUID: string };
didToggleHoldCallAction: { hold: boolean, callUUID: string };
didChangeAudioRoute: {
output: string,
reason?: number,
handle?: string,
callUUID?: string,
};
didPerformDTMFAction: { digits: string, callUUID: string };
showIncomingCallUi: { handle: string, callUUID: string, name: string };
silenceIncomingCall: { handle: string, callUUID: string, name: string };
createIncomingConnectionFailed: { handle: string, callUUID: string, name: string };
checkReachability: undefined;
didResetProvider: undefined;
didLoadWithEvents: InitialEvents;
onHasActiveCall : undefined;
}

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

export type AudioRoute = {
name: string,
type: string,
selected?: boolean
}

export enum AudioSessionCategoryOption {
mixWithOthers = 0x1,
duckOthers = 0x2,
interruptSpokenAudioAndMixWithOthers = 0x11,
allowBluetooth = 0x4,
allowBluetoothA2DP = 0x20,
allowAirPlay = 0x40,
defaultToSpeaker = 0x8,
overrideMutedMicrophoneInterruption = 0x80,
}

export enum AudioSessionMode {
default = 'AVAudioSessionModeDefault',
gameChat = 'AVAudioSessionModeGameChat',
measurement = 'AVAudioSessionModeMeasurement',
moviePlayback = 'AVAudioSessionModeMoviePlayback',
spokenAudio = 'AVAudioSessionModeSpokenAudio',
videoChat = 'AVAudioSessionModeVideoChat',
videoRecording = 'AVAudioSessionModeVideoRecording',
voiceChat = 'AVAudioSessionModeVoiceChat',
voicePrompt = 'AVAudioSessionModeVoicePrompt',
}

interface IOptions {
ios: {
appName: string,
@@ -23,6 +97,11 @@ declare module 'react-native-callkeep' {
maximumCallGroups?: string,
maximumCallsPerCallGroup?: string,
ringtoneSound?: string,
includesCallsInRecents?: boolean
audioSession?: {
categoryOptions?: AudioSessionCategoryOption | number,
mode?: AudioSessionMode | string,
}
},
android: {
alertTitle: string,
@@ -31,36 +110,62 @@ declare module 'react-native-callkeep' {
okButton: string,
imageName?: string,
additionalPermissions: string[],
},
selfManaged?: boolean,
foregroundService?: {
channelId: string,
channelName: string,
notificationTitle: string,
notificationIcon?: string
}
}
}

export type DidReceiveStartCallActionPayload = { handle: string };
export type AnswerCallPayload = { callUUID: string };
export type EndCallPayload = AnswerCallPayload;
export type DidDisplayIncomingCallPayload = string | undefined;
export type DidPerformSetMutedCallActionPayload = boolean;
export const CONSTANTS: {
END_CALL_REASONS: {
FAILED: 1,
REMOTE_ENDED: 2,
UNANSWERED: 3,
ANSWERED_ELSEWHERE: 4,
DECLINED_ELSEWHERE: 5 | 2,
MISSED: 2 | 6
}
};

export class EventListener {
remove(): void
}

export default class RNCallKeep {
static addEventListener(type: Events, handler: (args: any) => void): void
static getInitialEvents(): Promise<InitialEvents>

static clearInitialEvents(): void

static addEventListener<Event extends Events>(
type: Event,
handler: (args: EventsPayload[Event]) => void,
): EventListener

static removeEventListener(type: Events): void

static setup(options: IOptions): Promise<void>
static setup(options: IOptions): Promise<boolean>

static hasDefaultPhoneAccount(): boolean

static answerIncomingCall(uuid: string): void

static registerPhoneAccount(): void
static registerPhoneAccount(options: IOptions): void

static registerAndroidEvents(): void

static unregisterAndroidEvents(): void

static displayIncomingCall(
uuid: string,
handle: string,
localizedCallerName?: string,
handleType?: HandleType,
hasVideo?: boolean,
options?: object,
): void

static startCall(
@@ -75,6 +180,7 @@ declare module 'react-native-callkeep' {
uuid: string,
displayName: string,
handle: string,
options?: object,
): void

static checkPhoneAccountEnabled(): Promise<boolean>;
@@ -101,8 +207,25 @@ declare module 'react-native-callkeep' {

static setReachable(): void

static setSettings(settings: IOptions): void;

/**
* @description isCallActive method is available only on iOS.
*/
static isCallActive(uuid: string): Promise<boolean>

static getCalls(): Promise<{
callUUID: string,
hasConnected: boolean,
hasEnded: boolean,
onHold: boolean,
outgoing: boolean
}[] | void>

static getAudioRoutes(): Promise<void>

static setAudioRoute: (uuid: string, inputName: string) => Promise<void>

/**
* @description supportConnectionService method is available only on Android.
*/
@@ -120,8 +243,17 @@ declare module 'react-native-callkeep' {
*/
static setMutedCall(uuid: string, muted: boolean): void

/**
* @description toggleAudioRouteSpeaker method is available only on Android.
* @param uuid
* @param routeSpeaker
*/
static toggleAudioRouteSpeaker(uuid: string, routeSpeaker: boolean): void

static setOnHold(uuid: string, held: boolean): void

static setConnectionState(uuid: string, state: number): void

/**
* @descriptions sendDTMF is used to send DTMF tones to the PBX.
*/
@@ -136,10 +268,17 @@ declare module 'react-native-callkeep' {
*/
static setAvailable(active: boolean): void

static setForegroundServiceSettings(settings: NonNullable<IOptions['android']['foregroundService']>): void

static canMakeMultipleCalls(allow: boolean): void

static setCurrentCallActive(callUUID: string): void

static backToForeground(): void

/**
* @descriptions Android Only, Check if there is active native call
*/
static checkIsInManagedCall(): Promise<boolean>
}
}
262 changes: 191 additions & 71 deletions index.js

Large diffs are not rendered by default.

16 changes: 7 additions & 9 deletions ios/RNCallKeep/RNCallKeep.h
Original file line number Diff line number Diff line change
@@ -32,14 +32,10 @@ continueUserActivity:(NSUserActivity *)userActivity
handleType:(NSString *)handleType
hasVideo:(BOOL)hasVideo
localizedCallerName:(NSString * _Nullable)localizedCallerName
fromPushKit:(BOOL)fromPushKit
payload:(NSDictionary * _Nullable)payload;

+ (void)reportNewIncomingCall:(NSString *)uuidString
handle:(NSString *)handle
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
withCompletionHandler:(void (^_Nullable)(void))completion;
@@ -49,4 +45,6 @@ continueUserActivity:(NSUserActivity *)userActivity

+ (BOOL)isCallActive:(NSString *)uuidString;

@end
+ (void)setup:(NSDictionary *)options;

@end
545 changes: 476 additions & 69 deletions ios/RNCallKeep/RNCallKeep.m

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-callkeep",
"version": "3.1.3",
"version": "4.3.16",
"description": "iOS 10 CallKit and Android ConnectionService Framework For React Native",
"main": "index.js",
"scripts": {},