Skip to content

Commit 81b82bb

Browse files
committed
Implemented self managed mode support for Android
1 parent 5a29880 commit 81b82bb

File tree

8 files changed

+75
-5
lines changed

8 files changed

+75
-5
lines changed

actions.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const RNCallKeepDidPerformDTMFAction = 'RNCallKeepDidPerformDTMFAction';
1515
const RNCallKeepProviderReset = 'RNCallKeepProviderReset';
1616
const RNCallKeepCheckReachability = 'RNCallKeepCheckReachability';
1717
const RNCallKeepDidLoadWithEvents = 'RNCallKeepDidLoadWithEvents';
18+
const RNCallKeepShowIncomingCallUi = 'RNCallKeepShowIncomingCallUi';
1819
const isIOS = Platform.OS === 'ios';
1920

2021
const didReceiveStartCallAction = handler => {
@@ -59,6 +60,9 @@ const checkReachability = handler =>
5960
const didLoadWithEvents = handler =>
6061
eventEmitter.addListener(RNCallKeepDidLoadWithEvents, handler);
6162

63+
const showIncomingCallUi = handler =>
64+
eventEmitter.addListener(RNCallKeepShowIncomingCallUi, (data) => handler(data));
65+
6266
export const emit = (eventName, payload) => eventEmitter.emit(eventName, payload);
6367

6468
export const listeners = {
@@ -74,4 +78,5 @@ export const listeners = {
7478
didResetProvider,
7579
checkReachability,
7680
didLoadWithEvents,
81+
showIncomingCallUi
7782
};

android/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
<uses-permission android:name="android.permission.READ_PHONE_STATE"
66
android:maxSdkVersion="29" />
77
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
8+
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
9+
<uses-permission android:name="android.permission.READ_CALL_LOG" />
810
</manifest>

android/src/main/java/io/wazo/callkeep/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class Constants {
1212
public static final String ACTION_UNHOLD_CALL = "ACTION_UNHOLD_CALL";
1313
public static final String ACTION_UNMUTE_CALL = "ACTION_UNMUTE_CALL";
1414
public static final String ACTION_WAKE_APP = "ACTION_WAKE_APP";
15+
public static final String ACTION_SHOW_INCOMING_CALL_UI = "ACTION_SHOW_INCOMING_CALL_UI";
1516

1617
public static final String EXTRA_CALL_NUMBER = "EXTRA_CALL_NUMBER";
1718
public static final String EXTRA_CALL_UUID = "EXTRA_CALL_UUID";

android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import static io.wazo.callkeep.Constants.ACTION_AUDIO_SESSION;
8686
import static io.wazo.callkeep.Constants.ACTION_CHECK_REACHABILITY;
8787
import static io.wazo.callkeep.Constants.ACTION_WAKE_APP;
88+
import static io.wazo.callkeep.Constants.ACTION_SHOW_INCOMING_CALL_UI;
8889

8990
// @see https://github.com/kbagchiGWC/voice-quickstart-android/blob/9a2aff7fbe0d0a5ae9457b48e9ad408740dfb968/exampleConnectionService/src/main/java/com/twilio/voice/examples/connectionservice/VoiceConnectionServiceActivity.java
9091
public class RNCallKeepModule extends ReactContextBaseJavaModule {
@@ -93,7 +94,7 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule {
9394

9495
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
9596
private static final String REACT_NATIVE_MODULE_NAME = "RNCallKeep";
96-
private static final String[] permissions = {
97+
private static String[] permissions = {
9798
Build.VERSION.SDK_INT < 30 ? Manifest.permission.READ_PHONE_STATE : Manifest.permission.READ_PHONE_NUMBERS,
9899
Manifest.permission.CALL_PHONE,
99100
Manifest.permission.RECORD_AUDIO
@@ -115,6 +116,10 @@ public RNCallKeepModule(ReactApplicationContext reactContext) {
115116
this.reactContext = reactContext;
116117
}
117118

119+
private boolean isSelfManaged() {
120+
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && _settings.hasKey("selfManaged") && _settings.getBoolean("selfManaged");
121+
}
122+
118123
@Override
119124
public String getName() {
120125
return REACT_NATIVE_MODULE_NAME;
@@ -127,6 +132,20 @@ public void setup(ReadableMap options) {
127132
VoiceConnectionService.setInitialized(true);
128133
this._settings = options;
129134

135+
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
136+
if(isSelfManaged()) {
137+
Log.d(TAG, "API Version supports self managed, and is enabled in setup");
138+
}
139+
else {
140+
Log.d(TAG, "API Version supports self managed, but it is not enabled in setup");
141+
}
142+
}
143+
144+
//If we're running in self managed mode we need fewer permissions.
145+
if(isSelfManaged()) {
146+
permissions = new String[]{ Manifest.permission.RECORD_AUDIO };
147+
}
148+
130149
if (isConnectionServiceAvailable()) {
131150
this.registerPhoneAccount();
132151
this.registerEvents();
@@ -196,6 +215,7 @@ public void startCall(String uuid, String number, String callerName) {
196215
Log.d(TAG, "startCall called, uuid: " + uuid + ", number: " + number + ", callerName: " + callerName);
197216

198217
if (!isConnectionServiceAvailable() || !hasPhoneAccount() || !hasPermissions() || number == null) {
218+
Log.d(TAG, "startCall ignored: " + isConnectionServiceAvailable() + ", " + hasPhoneAccount() + ", " + hasPermissions() + ", " + number);
199219
return;
200220
}
201221

@@ -341,7 +361,7 @@ public void reject(String message) {
341361
hasPhoneAccountPromise.resolve(false);
342362
}
343363
});
344-
return;
364+
return;
345365
}
346366

347367
promise.resolve(!hasPhoneAccount());
@@ -606,8 +626,13 @@ private void registerPhoneAccount(Context appContext) {
606626
this.initializeTelecomManager();
607627
String appName = this.getApplicationName(this.getAppContext());
608628

609-
PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, appName)
610-
.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
629+
PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, appName);
630+
if(isSelfManaged()) {
631+
builder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED);
632+
}
633+
else {
634+
builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
635+
}
611636

612637
if (_settings != null && _settings.hasKey("imageName")) {
613638
int identifier = appContext.getResources().getIdentifier(_settings.getString("imageName"), "drawable", appContext.getPackageName());
@@ -669,6 +694,7 @@ private void registerReceiver() {
669694
intentFilter.addAction(ACTION_ONGOING_CALL);
670695
intentFilter.addAction(ACTION_AUDIO_SESSION);
671696
intentFilter.addAction(ACTION_CHECK_REACHABILITY);
697+
intentFilter.addAction(ACTION_SHOW_INCOMING_CALL_UI);
672698
LocalBroadcastManager.getInstance(this.reactContext).registerReceiver(voiceBroadcastReceiver, intentFilter);
673699
isReceiverRegistered = true;
674700
}
@@ -743,6 +769,12 @@ public void onReceive(Context context, Intent intent) {
743769
case ACTION_CHECK_REACHABILITY:
744770
sendEventToJS("RNCallKeepCheckReachability", null);
745771
break;
772+
case ACTION_SHOW_INCOMING_CALL_UI:
773+
args.putString("handle", attributeMap.get(EXTRA_CALL_NUMBER));
774+
args.putString("callUUID", attributeMap.get(EXTRA_CALL_UUID));
775+
args.putString("name", attributeMap.get(EXTRA_CALLER_NAME));
776+
sendEventToJS("RNCallKeepShowIncomingCallUi", args);
777+
break;
746778
case ACTION_WAKE_APP:
747779
Intent headlessIntent = new Intent(reactContext, RNCallKeepBackgroundMessagingService.class);
748780
headlessIntent.putExtra("callUUID", attributeMap.get(EXTRA_CALL_UUID));

android/src/main/java/io/wazo/callkeep/VoiceConnection.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import static io.wazo.callkeep.Constants.EXTRA_CALLER_NAME;
4949
import static io.wazo.callkeep.Constants.EXTRA_CALL_NUMBER;
5050
import static io.wazo.callkeep.Constants.EXTRA_CALL_UUID;
51+
import static io.wazo.callkeep.Constants.ACTION_SHOW_INCOMING_CALL_UI;
5152

5253
@TargetApi(Build.VERSION_CODES.M)
5354
public class VoiceConnection extends Connection {
@@ -196,6 +197,12 @@ public void onReject() {
196197
destroy();
197198
}
198199

200+
@Override
201+
public void onShowIncomingCallUi() {
202+
Log.d(TAG, "onShowIncomingCallUi()");
203+
sendCallRequestToActivity(ACTION_SHOW_INCOMING_CALL_UI, handle);
204+
}
205+
199206
/*
200207
* Send call request to the RNCallKeepModule
201208
*/

android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import android.telecom.ConnectionRequest;
4141
import android.telecom.ConnectionService;
4242
import android.telecom.DisconnectCause;
43+
import android.telecom.PhoneAccount;
4344
import android.telecom.PhoneAccountHandle;
4445
import android.telecom.TelecomManager;
4546
import android.util.Log;
@@ -331,6 +332,22 @@ private Connection createConnection(ConnectionRequest request) {
331332
extrasMap.put(EXTRA_CALL_NUMBER, request.getAddress().toString());
332333
VoiceConnection connection = new VoiceConnection(this, extrasMap);
333334
connection.setConnectionCapabilities(Connection.CAPABILITY_MUTE | Connection.CAPABILITY_SUPPORT_HOLD);
335+
336+
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
337+
Context context = getApplicationContext();
338+
TelecomManager telecomManager = (TelecomManager) context.getSystemService(context.TELECOM_SERVICE);
339+
PhoneAccount phoneAccount = telecomManager.getPhoneAccount(request.getAccountHandle());
340+
341+
//If the phone account is self managed, then this connection must also be self managed.
342+
if((phoneAccount.getCapabilities() & PhoneAccount.CAPABILITY_SELF_MANAGED) == PhoneAccount.CAPABILITY_SELF_MANAGED) {
343+
Log.d(TAG, "PhoneAccount is SELF_MANAGED, so connection will be too");
344+
connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED);
345+
}
346+
else {
347+
Log.d(TAG, "PhoneAccount is not SELF_MANAGED, so connection won't be either");
348+
}
349+
}
350+
334351
connection.setInitializing();
335352
connection.setExtras(extras);
336353
currentConnections.put(extras.getString(EXTRA_CALL_UUID), connection);

index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ declare module 'react-native-callkeep' {
1111
'didResetProvider' |
1212
'checkReachability' |
1313
'didPerformSetMutedCallAction' |
14-
'didLoadWithEvents';
14+
'didLoadWithEvents' |
15+
'showIncomingCallUi';
1516

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

@@ -32,6 +33,7 @@ declare module 'react-native-callkeep' {
3233
okButton: string,
3334
imageName?: string,
3435
additionalPermissions: string[],
36+
selfManaged?: boolean,
3537
foregroundService?: {
3638
channelId: string,
3739
channelName: string,

index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ class RNCallKeep {
274274
_setupAndroid = async (options) => {
275275
RNCallKeepModule.setup(options);
276276

277+
if (options.selfManaged) {
278+
return false;
279+
}
280+
277281
const showAccountAlert = await RNCallKeepModule.checkPhoneAccountPermission(options.additionalPermissions || []);
278282
const shouldOpenAccounts = await this._alert(options, showAccountAlert);
279283

0 commit comments

Comments
 (0)