59
59
import com .facebook .react .bridge .ReadableMap ;
60
60
import com .facebook .react .bridge .WritableArray ;
61
61
import com .facebook .react .bridge .WritableMap ;
62
+ import com .facebook .react .bridge .WritableNativeArray ;
62
63
import com .facebook .react .HeadlessJsTaskService ;
63
64
import com .facebook .react .modules .core .DeviceEventManagerModule .RCTDeviceEventEmitter ;
64
65
import com .facebook .react .modules .permissions .PermissionsModule ;
@@ -95,6 +96,8 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule {
95
96
public static final int REQUEST_READ_PHONE_STATE = 1337 ;
96
97
public static final int REQUEST_REGISTER_CALL_PROVIDER = 394859 ;
97
98
99
+ public static RNCallKeepModule instance = null ;
100
+
98
101
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST" ;
99
102
private static final String REACT_NATIVE_MODULE_NAME = "RNCallKeep" ;
100
103
private static String [] permissions = {
@@ -112,18 +115,32 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule {
112
115
private boolean isReceiverRegistered = false ;
113
116
private VoiceBroadcastReceiver voiceBroadcastReceiver ;
114
117
private ReadableMap _settings ;
118
+ private WritableNativeArray delayedEvents ;
119
+ private boolean hasListeners = false ;
120
+
121
+ public static RNCallKeepModule getInstance (ReactApplicationContext reactContext , boolean realContext ) {
122
+ if (instance == null ) {
123
+ instance = new RNCallKeepModule (reactContext );
124
+ }
125
+ if (realContext ) {
126
+ instance .setContext (reactContext );
127
+ }
128
+ return instance ;
129
+ }
115
130
116
- public RNCallKeepModule (ReactApplicationContext reactContext ) {
131
+ private RNCallKeepModule (ReactApplicationContext reactContext ) {
117
132
super (reactContext );
118
133
Log .d (TAG , "[VoiceConnection] constructor" );
119
134
120
135
this .reactContext = reactContext ;
136
+ delayedEvents = new WritableNativeArray ();
137
+ this .registerReceiver ();
121
138
}
122
139
123
140
private boolean isSelfManaged () {
124
- try {
141
+ try {
125
142
return Build .VERSION .SDK_INT >= Build .VERSION_CODES .O && _settings .hasKey ("selfManaged" ) && _settings .getBoolean ("selfManaged" );
126
- } catch (Exception e ) {
143
+ } catch (Exception e ) {
127
144
return false ;
128
145
}
129
146
}
@@ -133,12 +150,56 @@ public String getName() {
133
150
return REACT_NATIVE_MODULE_NAME ;
134
151
}
135
152
153
+ public void setContext (ReactApplicationContext reactContext ) {
154
+ Log .d (TAG , "[VoiceConnection] updating react context" );
155
+ this .reactContext = reactContext ;
156
+ }
157
+
158
+ public void reportNewIncomingCall (String uuid , String number , String callerName , boolean hasVideo , String payload ) {
159
+ Log .d (TAG , "[VoiceConnection] reportNewIncomingCall, uuid: " + uuid + ", number: " + number + ", callerName: " + callerName );
160
+ // @TODO: handle video
161
+
162
+ this .displayIncomingCall (uuid , number , callerName );
163
+
164
+ // Send event to JS
165
+ WritableMap args = Arguments .createMap ();
166
+ args .putString ("handle" , number );
167
+ args .putString ("callUUID" , uuid );
168
+ args .putString ("name" , callerName );
169
+ if (payload != null ) {
170
+ args .putString ("payload" , payload );
171
+ }
172
+ sendEventToJS ("RNCallKeepDidDisplayIncomingCall" , args );
173
+ }
174
+
175
+ public void startObserving () {
176
+ int count = delayedEvents .size ();
177
+ Log .d (TAG , "[VoiceConnection] startObserving, event count: " + count );
178
+ if (count > 0 ) {
179
+ this .reactContext .getJSModule (RCTDeviceEventEmitter .class ).emit ("RNCallKeepDidLoadWithEvents" , delayedEvents );
180
+ delayedEvents = new WritableNativeArray ();
181
+ }
182
+ }
183
+
184
+ public void initializeTelecomManager () {
185
+ Context context = this .getAppContext ();
186
+ ComponentName cName = new ComponentName (context , VoiceConnectionService .class );
187
+ String appName = this .getApplicationName (context );
188
+
189
+ handle = new PhoneAccountHandle (cName , appName );
190
+ telecomManager = (TelecomManager ) context .getSystemService (Context .TELECOM_SERVICE );
191
+ }
192
+
193
+ public void setSettings (ReadableMap options ) {
194
+ this ._settings = options ;
195
+ }
196
+
136
197
@ ReactMethod
137
198
public void setup (ReadableMap options ) {
138
199
Log .d (TAG , "[VoiceConnection] setup" );
139
200
VoiceConnectionService .setAvailable (false );
140
201
VoiceConnectionService .setInitialized (true );
141
- this ._settings = options ;
202
+ this .setSettings ( options ) ;
142
203
143
204
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
144
205
if (isSelfManaged ()) {
@@ -158,6 +219,7 @@ public void setup(ReadableMap options) {
158
219
if (isConnectionServiceAvailable ()) {
159
220
this .registerPhoneAccount (options );
160
221
this .registerEvents ();
222
+ this .startObserving ();
161
223
VoiceConnectionService .setAvailable (true );
162
224
}
163
225
@@ -187,11 +249,18 @@ public void registerEvents() {
187
249
188
250
Log .d (TAG , "[VoiceConnection] registerEvents" );
189
251
190
- voiceBroadcastReceiver = new VoiceBroadcastReceiver () ;
191
- registerReceiver ();
252
+ this . hasListeners = true ;
253
+ this . startObserving ();
192
254
VoiceConnectionService .setPhoneAccountHandle (handle );
193
255
}
194
256
257
+ @ ReactMethod
258
+ public void unregisterEvents () {
259
+ Log .d (TAG , "[RNCallKeepModule] unregisterEvents" );
260
+
261
+ this .hasListeners = false ;
262
+ }
263
+
195
264
@ ReactMethod
196
265
public void displayIncomingCall (String uuid , String number , String callerName ) {
197
266
if (!isConnectionServiceAvailable () || !hasPhoneAccount ()) {
@@ -410,6 +479,11 @@ public void checkDefaultPhoneAccount(Promise promise) {
410
479
promise .resolve (!hasSim || hasDefaultAccount );
411
480
}
412
481
482
+ @ ReactMethod
483
+ public void getInitialEvents (Promise promise ) {
484
+ promise .resolve (delayedEvents );
485
+ }
486
+
413
487
@ ReactMethod
414
488
public void setOnHold (String uuid , boolean shouldHold ) {
415
489
Log .d (TAG , "[VoiceConnection] setOnHold, uuid: " + uuid + ", shouldHold: " + (shouldHold ? "true" : "false" ));
@@ -721,13 +795,21 @@ public void backToForeground() {
721
795
}
722
796
}
723
797
724
- private void initializeTelecomManager () {
725
- Context context = this .getAppContext ();
726
- ComponentName cName = new ComponentName (context , VoiceConnectionService .class );
727
- String appName = this .getApplicationName (context );
798
+ public static void onRequestPermissionsResult (int requestCode , String [] grantedPermissions , int [] grantResults ) {
799
+ int permissionsIndex = 0 ;
800
+ List <String > permsList = Arrays .asList (permissions );
801
+ for (int result : grantResults ) {
802
+ if (permsList .contains (grantedPermissions [permissionsIndex ]) && result != PackageManager .PERMISSION_GRANTED ) {
803
+ hasPhoneAccountPromise .resolve (false );
804
+ return ;
805
+ }
806
+ permissionsIndex ++;
807
+ }
808
+ hasPhoneAccountPromise .resolve (true );
809
+ }
728
810
729
- handle = new PhoneAccountHandle ( cName , appName );
730
- telecomManager = ( TelecomManager ) context . getSystemService ( Context . TELECOM_SERVICE );
811
+ private boolean isSelfManaged () {
812
+ return Build . VERSION . SDK_INT >= Build . VERSION_CODES . O && _settings . hasKey ( "selfManaged" ) && _settings . getBoolean ( "selfManaged" );
731
813
}
732
814
733
815
private void registerPhoneAccount (Context appContext ) {
@@ -761,8 +843,18 @@ private void registerPhoneAccount(Context appContext) {
761
843
}
762
844
763
845
private void sendEventToJS (String eventName , @ Nullable WritableMap params ) {
764
- Log .v (TAG , "[VoiceConnection] sendEventToJS, eventName :" + eventName + ", args : " + (params != null ? params .toString () : "null" ));
765
- this .reactContext .getJSModule (RCTDeviceEventEmitter .class ).emit (eventName , params );
846
+ boolean isBoundToJS = this .reactContext .hasActiveCatalystInstance ();
847
+ Log .v (TAG , "[VoiceConnection] sendEventToJS, eventName: " + eventName + ", bound: " + isBoundToJS + ", hasListeners: " + hasListeners + " args : " + (params != null ? params .toString () : "null" ));
848
+
849
+ if (isBoundToJS && hasListeners ) {
850
+ this .reactContext .getJSModule (RCTDeviceEventEmitter .class ).emit (eventName , params );
851
+ } else {
852
+ if (params == null ) {
853
+ params = Arguments .createMap ();
854
+ }
855
+ params .putString ("name" , eventName );
856
+ delayedEvents .pushMap (params );
857
+ }
766
858
}
767
859
768
860
private String getApplicationName (Context appContext ) {
@@ -797,6 +889,7 @@ private static boolean hasPhoneAccount() {
797
889
798
890
private void registerReceiver () {
799
891
if (!isReceiverRegistered ) {
892
+ voiceBroadcastReceiver = new VoiceBroadcastReceiver ();
800
893
IntentFilter intentFilter = new IntentFilter ();
801
894
intentFilter .addAction (ACTION_END_CALL );
802
895
intentFilter .addAction (ACTION_ANSWER_CALL );
@@ -820,19 +913,6 @@ private Context getAppContext() {
820
913
return this .reactContext .getApplicationContext ();
821
914
}
822
915
823
- public static void onRequestPermissionsResult (int requestCode , String [] grantedPermissions , int [] grantResults ) {
824
- int permissionsIndex = 0 ;
825
- List <String > permsList = Arrays .asList (permissions );
826
- for (int result : grantResults ) {
827
- if (permsList .contains (grantedPermissions [permissionsIndex ]) && result != PackageManager .PERMISSION_GRANTED ) {
828
- hasPhoneAccountPromise .resolve (false );
829
- return ;
830
- }
831
- permissionsIndex ++;
832
- }
833
- hasPhoneAccountPromise .resolve (true );
834
- }
835
-
836
916
private class VoiceBroadcastReceiver extends BroadcastReceiver {
837
917
@ Override
838
918
public void onReceive (Context context , Intent intent ) {
0 commit comments