17
17
18
18
package io .wazo .callkeep ;
19
19
20
+ import android .Manifest ;
21
+ import android .app .Activity ;
22
+ import android .content .BroadcastReceiver ;
20
23
import android .content .ComponentName ;
21
24
import android .content .Context ;
22
- import android .content .BroadcastReceiver ;
25
+ import android .content .Intent ;
26
+ import android .content .IntentFilter ;
23
27
import android .content .pm .ApplicationInfo ;
24
28
import android .content .pm .PackageManager ;
25
- import android .content .IntentFilter ;
26
- import android .content .Intent ;
29
+ import android .net .Uri ;
30
+ import android .os .Build ;
31
+ import android .os .Bundle ;
32
+ import android .support .annotation .Nullable ;
27
33
import android .support .v4 .app .ActivityCompat ;
28
34
import android .support .v4 .content .ContextCompat ;
29
35
import android .support .v4 .content .LocalBroadcastManager ;
30
- import android .support .annotation .Nullable ;
31
-
32
- import android .accounts .AccountManager ;
33
- import android .accounts .Account ;
34
- import android .telecom .DisconnectCause ;
35
36
import android .telecom .Connection ;
36
- import android .telecom .PhoneAccountHandle ;
37
+ import android .telecom .DisconnectCause ;
37
38
import android .telecom .PhoneAccount ;
39
+ import android .telecom .PhoneAccountHandle ;
38
40
import android .telecom .TelecomManager ;
39
41
40
- import com .facebook .react .bridge .NativeModule ;
42
+ import com .facebook .react .bridge .Arguments ;
43
+ import com .facebook .react .bridge .Promise ;
41
44
import com .facebook .react .bridge .ReactApplicationContext ;
42
- import com .facebook .react .bridge .ReactContext ;
43
45
import com .facebook .react .bridge .ReactContextBaseJavaModule ;
44
46
import com .facebook .react .bridge .ReactMethod ;
45
47
import com .facebook .react .bridge .WritableMap ;
46
- import com .facebook .react .bridge .Arguments ;
47
- import com .facebook .react .bridge .Promise ;
48
48
import com .facebook .react .modules .core .DeviceEventManagerModule .RCTDeviceEventEmitter ;
49
49
50
- import android .os .Bundle ;
51
- import android .os .Build ;
52
- import android .net .Uri ;
53
- import android .app .Activity ;
54
- import android .Manifest ;
55
-
56
- import java .util .Map ;
57
- import java .util .HashMap ;
58
- import java .util .List ;
59
- import java .lang .SecurityException ;
60
-
61
50
// @see https://github.com/kbagchiGWC/voice-quickstart-android/blob/9a2aff7fbe0d0a5ae9457b48e9ad408740dfb968/exampleConnectionService/src/main/java/com/twilio/voice/examples/connectionservice/VoiceConnectionServiceActivity.java
62
51
public class RNCallKeepModule extends ReactContextBaseJavaModule {
63
52
public static final int REQUEST_READ_PHONE_STATE = 394858 ;
@@ -73,14 +62,15 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule {
73
62
public static final String ACTION_HOLD_CALL = "ACTION_HOLD_CALL" ;
74
63
public static final String ACTION_UNHOLD_CALL = "ACTION_UNHOLD_CALL" ;
75
64
public static final String ACTION_ONGOING_CALL = "ACTION_ONGOING_CALL" ;
65
+ public static final String ACTION_AUDIO_SESSION = "ACTION_AUDIO_SESSION" ;
76
66
77
67
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST" ;
78
68
private static final String REACT_NATIVE_MODULE_NAME = "RNCallKeep" ;
79
69
80
70
private static TelecomManager telecomManager ;
81
71
private static Promise hasPhoneAccountPromise ;
82
72
private ReactApplicationContext reactContext ;
83
- private PhoneAccountHandle pah ;
73
+ private static PhoneAccountHandle handle ;
84
74
private boolean isReceiverRegistered = false ;
85
75
private VoiceBroadcastReceiver voiceBroadcastReceiver ;
86
76
@@ -115,7 +105,7 @@ public String getName() {
115
105
116
106
@ ReactMethod
117
107
public void displayIncomingCall (String number , String callerName ) {
118
- if (!this . hasPhoneAccount ()) {
108
+ if (!isAvailable () || ! hasPhoneAccount ()) {
119
109
return ;
120
110
}
121
111
@@ -125,12 +115,30 @@ public void displayIncomingCall(String number, String callerName) {
125
115
extras .putParcelable (TelecomManager .EXTRA_INCOMING_CALL_ADDRESS , uri );
126
116
extras .putString (EXTRA_CALLER_NAME , callerName );
127
117
128
- telecomManager .addNewIncomingCall (this .pah , extras );
118
+ telecomManager .addNewIncomingCall (handle , extras );
119
+ }
120
+
121
+ @ ReactMethod
122
+ public void startCall (String number , String callerName ) {
123
+ if (!isAvailable () || !hasPhoneAccount ()) {
124
+ return ;
125
+ }
126
+
127
+ Bundle extras = new Bundle ();
128
+ Uri uri = Uri .fromParts (PhoneAccount .SCHEME_TEL , number , null );
129
+
130
+ Bundle callExtras = new Bundle ();
131
+ callExtras .putString (EXTRA_CALLER_NAME , callerName );
132
+
133
+ extras .putParcelable (TelecomManager .EXTRA_PHONE_ACCOUNT_HANDLE , handle );
134
+ extras .putParcelable (TelecomManager .EXTRA_OUTGOING_CALL_EXTRAS , callExtras );
135
+
136
+ telecomManager .placeCall (uri , extras );
129
137
}
130
138
131
139
@ ReactMethod
132
140
public void endCall () {
133
- if (!hasPhoneAccount ()) {
141
+ if (!isAvailable () || ! hasPhoneAccount ()) {
134
142
return ;
135
143
}
136
144
@@ -156,7 +164,8 @@ public void checkPhoneAccountPermission(Promise promise) {
156
164
}
157
165
158
166
hasPhoneAccountPromise = promise ;
159
- if (!this .checkPermission (Manifest .permission .READ_PHONE_STATE , REQUEST_READ_PHONE_STATE )) {
167
+ String [] permissions = { Manifest .permission .READ_PHONE_STATE , Manifest .permission .CALL_PHONE };
168
+ if (!this .checkPermissions (permissions , REQUEST_READ_PHONE_STATE )) {
160
169
return ;
161
170
}
162
171
@@ -201,17 +210,19 @@ public static Boolean isAvailable() {
201
210
}
202
211
203
212
private void registerPhoneAccount (Context appContext ) {
213
+ if (!isAvailable ()) {
214
+ return ;
215
+ }
216
+
204
217
ComponentName cName = new ComponentName (this .getAppContext (), VoiceConnectionService .class );
205
218
String appName = this .getApplicationName (appContext );
206
219
207
- this . pah = new PhoneAccountHandle (cName , appName );
220
+ handle = new PhoneAccountHandle (cName , appName );
208
221
209
- PhoneAccount account = new PhoneAccount .Builder (pah , appName )
222
+ PhoneAccount account = new PhoneAccount .Builder (handle , appName )
210
223
.setCapabilities (PhoneAccount .CAPABILITY_CALL_PROVIDER )
211
224
.build ();
212
225
213
- PhoneAccountHandle handle = new PhoneAccountHandle (cName , appName );
214
-
215
226
telecomManager = (TelecomManager ) this .getAppContext ().getSystemService (this .getAppContext ().TELECOM_SERVICE );
216
227
telecomManager .registerPhoneAccount (account );
217
228
}
@@ -227,32 +238,30 @@ private String getApplicationName(Context appContext) {
227
238
return stringId == 0 ? applicationInfo .nonLocalizedLabel .toString () : appContext .getString (stringId );
228
239
}
229
240
230
- private Boolean checkPermission (String name , int id ) {
241
+ private Boolean checkPermissions (String [] permissions , int id ) {
231
242
Activity currentActivity = this .getCurrentActivity ();
232
- int permissionCheck = ContextCompat .checkSelfPermission (currentActivity , name );
233
243
234
- if (permissionCheck != PackageManager .PERMISSION_GRANTED ) {
235
- ActivityCompat .requestPermissions (currentActivity , new String []{name }, id );
236
- return false ;
244
+ boolean hasPermissions = true ;
245
+ for (String permission : permissions ) {
246
+ int permissionCheck = ContextCompat .checkSelfPermission (currentActivity , permission );
247
+ if (permissionCheck != PackageManager .PERMISSION_GRANTED ) {
248
+ hasPermissions = false ;
249
+ }
237
250
}
238
251
239
- return true ;
252
+ if (!hasPermissions ) {
253
+ ActivityCompat .requestPermissions (currentActivity , permissions , id );
254
+ }
255
+
256
+ return hasPermissions ;
240
257
}
241
258
242
259
private static boolean hasPhoneAccount () {
243
260
if (!isAvailable ()) {
244
261
return false ;
245
262
}
246
263
247
- List <PhoneAccountHandle > enabledAccounts = telecomManager .getCallCapablePhoneAccounts ();
248
-
249
- for (PhoneAccountHandle account : enabledAccounts ) {
250
- if (account .getComponentName ().getClassName ().equals (VoiceConnectionService .class .getCanonicalName ())) {
251
- return true ;
252
- }
253
- }
254
-
255
- return false ;
264
+ return telecomManager .getPhoneAccount (handle ).isEnabled ();
256
265
}
257
266
258
267
private void registerReceiver () {
@@ -266,6 +275,7 @@ private void registerReceiver() {
266
275
intentFilter .addAction (ACTION_UNHOLD_CALL );
267
276
intentFilter .addAction (ACTION_HOLD_CALL );
268
277
intentFilter .addAction (ACTION_ONGOING_CALL );
278
+ intentFilter .addAction (ACTION_AUDIO_SESSION );
269
279
LocalBroadcastManager .getInstance (this .reactContext ).registerReceiver (voiceBroadcastReceiver , intentFilter );
270
280
isReceiverRegistered = true ;
271
281
}
@@ -313,6 +323,9 @@ public void onReceive(Context context, Intent intent) {
313
323
314
324
sendEventToJS ("RNCallKeepDidReceiveStartCallAction" , args );
315
325
break ;
326
+ case ACTION_AUDIO_SESSION :
327
+ sendEventToJS ("RNCallKeepDidActivateAudioSession" , null );
328
+ break ;
316
329
}
317
330
}
318
331
}
0 commit comments