Skip to content

Commit 99b7bb9

Browse files
committed
Delay events sent to RNCallKeepModule in VoiceConnectionService
1 parent 5f85a4e commit 99b7bb9

File tree

2 files changed

+108
-30
lines changed

2 files changed

+108
-30
lines changed

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

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,13 @@ public class RNCallKeepModule extends ReactContextBaseJavaModule {
121121
public static PhoneAccountHandle handle;
122122
private boolean isReceiverRegistered = false;
123123
private VoiceBroadcastReceiver voiceBroadcastReceiver;
124-
private WritableMap _settings;
124+
private static WritableMap _settings;
125125
private WritableNativeArray delayedEvents;
126126
private boolean hasListeners = false;
127127

128128
public static RNCallKeepModule getInstance(ReactApplicationContext reactContext, boolean realContext) {
129129
if (instance == null) {
130+
Log.d(TAG, "[RNCallKeepModule] getInstance : " + (reactContext == null ? "null" : "ok"));
130131
instance = new RNCallKeepModule(reactContext);
131132
}
132133
if (realContext) {
@@ -135,8 +136,12 @@ public static RNCallKeepModule getInstance(ReactApplicationContext reactContext,
135136
return instance;
136137
}
137138

138-
public static WritableMap getInstanceSettings() {
139-
return getInstance(null, false).getSettings();
139+
public static WritableMap getSettings() {
140+
if (_settings == null) {
141+
fetchStoredSettings();
142+
}
143+
144+
return _settings;
140145
}
141146

142147
private RNCallKeepModule(ReactApplicationContext reactContext) {
@@ -198,6 +203,10 @@ public void startObserving() {
198203

199204
public void initializeTelecomManager() {
200205
Context context = this.getAppContext();
206+
if (context == null) {
207+
Log.w(TAG, "[RNCallKeepModule][initializeTelecomManager] no react context found.");
208+
return;
209+
}
201210
ComponentName cName = new ComponentName(context, VoiceConnectionService.class);
202211
String appName = this.getApplicationName(context);
203212

@@ -213,15 +222,7 @@ public void setSettings(ReadableMap options) {
213222
Log.d(TAG, "[RNCallKeepModule] setSettings: " + options);
214223
storeSettings(options);
215224

216-
this._settings = getSettings();
217-
}
218-
219-
public WritableMap getSettings() {
220-
if (_settings == null) {
221-
fetchStoredSettings();
222-
}
223-
224-
return _settings;
225+
_settings = getSettings();
225226
}
226227

227228
@ReactMethod
@@ -275,8 +276,13 @@ public void registerPhoneAccount(ReadableMap options) {
275276
}
276277

277278
Log.d(TAG, "[RNCallKeepModule] registerPhoneAccount");
279+
Context context = this.getAppContext();
280+
if (context == null) {
281+
Log.w(TAG, "[RNCallKeepModule][registerPhoneAccount] no react context found.");
282+
return;
283+
}
278284

279-
this.registerPhoneAccount(this.getAppContext());
285+
this.registerPhoneAccount(context);
280286
}
281287

282288
@ReactMethod
@@ -666,6 +672,11 @@ public void setAudioRoute(String uuid, String audioRoute, Promise promise){
666672
public void getAudioRoutes(Promise promise){
667673
try {
668674
Context context = this.getAppContext();
675+
if (context == null) {
676+
Log.w(TAG, "[RNCallKeepModule][getAudioRoutes] no react context found.");
677+
promise.reject("No react context found to list audio routes");
678+
return;
679+
}
669680
AudioManager audioManager = (AudioManager) context.getSystemService(context.AUDIO_SERVICE);
670681
WritableArray devices = Arguments.createArray();
671682
ArrayList<String> typeChecker = new ArrayList<>();
@@ -804,7 +815,13 @@ public void openPhoneAccounts() {
804815
intent.setComponent(new ComponentName("com.android.server.telecom",
805816
"com.android.server.telecom.settings.EnableAccountPreferenceActivity"));
806817

807-
this.getAppContext().startActivity(intent);
818+
Context context = this.getAppContext();
819+
if (context == null) {
820+
Log.w(TAG, "[RNCallKeepModule][openPhoneAccounts] no react context found.");
821+
return;
822+
}
823+
824+
context.startActivity(intent);
808825
return;
809826
}
810827

@@ -821,7 +838,12 @@ public void openPhoneAccountSettings() {
821838

822839
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
823840
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
824-
this.getAppContext().startActivity(intent);
841+
Context context = this.getAppContext();
842+
if (context == null) {
843+
Log.w(TAG, "[RNCallKeepModule][openPhoneAccountSettings] no react context found.");
844+
return;
845+
}
846+
context.startActivity(intent);
825847
}
826848

827849
public static Boolean isConnectionServiceAvailable() {
@@ -842,6 +864,10 @@ public void checkPhoneAccountEnabled(Promise promise) {
842864
@ReactMethod
843865
public void backToForeground() {
844866
Context context = getAppContext();
867+
if (context == null) {
868+
Log.w(TAG, "[RNCallKeepModule][backToForeground] no react context found.");
869+
return;
870+
}
845871
String packageName = context.getApplicationContext().getPackageName();
846872
Intent focusIntent = context.getPackageManager().getLaunchIntentForPackage(packageName).cloneFilter();
847873
Activity activity = getCurrentActivity();
@@ -881,7 +907,12 @@ private void registerPhoneAccount(Context appContext) {
881907
}
882908

883909
this.initializeTelecomManager();
884-
String appName = this.getApplicationName(this.getAppContext());
910+
Context context = this.getAppContext();
911+
if (context == null) {
912+
Log.w(TAG, "[RNCallKeepModule][registerPhoneAccount] no react context found.");
913+
return;
914+
}
915+
String appName = this.getApplicationName(context);
885916

886917
PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, appName);
887918
if (isSelfManaged()) {
@@ -899,7 +930,7 @@ private void registerPhoneAccount(Context appContext) {
899930

900931
PhoneAccount account = builder.build();
901932

902-
telephonyManager = (TelephonyManager) this.getAppContext().getSystemService(Context.TELEPHONY_SERVICE);
933+
telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
903934

904935
telecomManager.registerPhoneAccount(account);
905936
}
@@ -971,19 +1002,27 @@ private void registerReceiver() {
9711002
intentFilter.addAction(ACTION_ON_CREATE_CONNECTION_FAILED);
9721003
intentFilter.addAction(ACTION_DID_CHANGE_AUDIO_ROUTE);
9731004

974-
LocalBroadcastManager.getInstance(this.reactContext).registerReceiver(voiceBroadcastReceiver, intentFilter);
975-
isReceiverRegistered = true;
1005+
Log.w(TAG, "[RNCallKeepModule][registerReceiver] reactContext:" + (this.reactContext == null ? "NOPE" : "OK"));
1006+
1007+
1008+
if (this.reactContext != null) {
1009+
LocalBroadcastManager.getInstance(this.reactContext).registerReceiver(voiceBroadcastReceiver, intentFilter);
1010+
isReceiverRegistered = true;
1011+
1012+
VoiceConnectionService.startObserving();
1013+
}
9761014
}
9771015
}
9781016

9791017
private Context getAppContext() {
980-
return this.reactContext.getApplicationContext();
1018+
return this.reactContext != null ? this.reactContext.getApplicationContext() : null;
9811019
}
9821020

9831021
// Store all callkeep settings in JSON
9841022
private void storeSettings(ReadableMap options) {
9851023
Context context = getInstance(null, false).getAppContext();
9861024
if (context == null) {
1025+
Log.w(TAG, "[RNCallKeepModule][storeSettings] no react context found.");
9871026
return;
9881027
}
9891028

@@ -996,10 +1035,14 @@ private void storeSettings(ReadableMap options) {
9961035
}
9971036
}
9981037

999-
private void fetchStoredSettings() {
1000-
Context context = getInstance(null, false).getAppContext();
1038+
private static void fetchStoredSettings() {
1039+
if (instance == null) {
1040+
return;
1041+
}
1042+
Context context = instance.getAppContext();
10011043
_settings = new WritableNativeMap();
10021044
if (context == null) {
1045+
Log.w(TAG, "[RNCallKeepModule][fetchStoredSettings] no react context found.");
10031046
return;
10041047
}
10051048

@@ -1021,6 +1064,8 @@ public void onReceive(Context context, Intent intent) {
10211064
WritableMap args = Arguments.createMap();
10221065
HashMap<String, String> attributeMap = (HashMap<String, String>)intent.getSerializableExtra("attributeMap");
10231066

1067+
Log.d(TAG, "[RNCallKeepModule][onReceive] :" + intent.getAction());
1068+
10241069
switch (intent.getAction()) {
10251070
case ACTION_END_CALL:
10261071
args.putString("callUUID", attributeMap.get(EXTRA_CALL_UUID));

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

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import android.os.Build;
3232
import android.os.Bundle;
3333
import android.os.Handler;
34+
import android.os.Looper;
3435
import android.speech.tts.Voice;
3536
import androidx.annotation.Nullable;
3637
import androidx.core.app.NotificationCompat;
@@ -81,6 +82,10 @@ public class VoiceConnectionService extends ConnectionService {
8182
private static ConnectionRequest currentConnectionRequest;
8283
private static PhoneAccountHandle phoneAccountHandle;
8384
private static String TAG = "RNCallKeep";
85+
86+
// Delay events sent to RNCallKeepModule when there is no listener available
87+
private static List<Bundle> delayedEvents = new ArrayList<Bundle>();
88+
8489
public static Map<String, VoiceConnection> currentConnections = new HashMap<>();
8590
public static Boolean hasOutgoingCall = false;
8691
public static VoiceConnectionService currentConnectionService = null;
@@ -94,7 +99,7 @@ public static Connection getConnection(String connectionId) {
9499

95100
public VoiceConnectionService() {
96101
super();
97-
Log.e(TAG, "[VoiceConnectionService] Constructor");
102+
Log.d(TAG, "[VoiceConnectionService] Constructor");
98103
currentConnectionRequest = null;
99104
currentConnectionService = this;
100105
}
@@ -113,7 +118,7 @@ public static void setAvailable(Boolean value) {
113118
}
114119

115120
public static WritableMap getSettings() {
116-
WritableMap settings = RNCallKeepModule.getInstanceSettings();
121+
WritableMap settings = RNCallKeepModule.getSettings();
117122
return settings;
118123
}
119124

@@ -270,8 +275,8 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool
270275

271276
HashMap<String, String> extrasMap = this.bundleToMap(extras);
272277

273-
sendCallRequestToActivity(ACTION_ONGOING_CALL, extrasMap);
274-
sendCallRequestToActivity(ACTION_AUDIO_SESSION, extrasMap);
278+
sendCallRequestToActivity(ACTION_ONGOING_CALL, extrasMap, true);
279+
sendCallRequestToActivity(ACTION_AUDIO_SESSION, extrasMap, true);
275280

276281
Log.d(TAG, "[VoiceConnectionService] onCreateOutgoingConnection: done");
277282

@@ -368,7 +373,7 @@ private void checkReachability() {
368373
Log.d(TAG, "[VoiceConnectionService] checkReachability");
369374

370375
final VoiceConnectionService instance = this;
371-
sendCallRequestToActivity(ACTION_CHECK_REACHABILITY, null);
376+
sendCallRequestToActivity(ACTION_CHECK_REACHABILITY, null, true);
372377

373378
new android.os.Handler().postDelayed(
374379
new Runnable() {
@@ -474,10 +479,31 @@ public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManage
474479
sendCallRequestToActivity(ACTION_ON_CREATE_CONNECTION_FAILED, extrasMap);
475480
}
476481

482+
// When a listener is available for `sendCallRequestToActivity`, send delayed events.
483+
public static void startObserving() {
484+
new Handler(Looper.getMainLooper()).post(new Runnable() {
485+
@Override
486+
public void run() {
487+
// Run this in a Looper to avoid : java.lang.RuntimeException: Can't create handler inside thread Thread
488+
int count = delayedEvents.size();
489+
Log.d(TAG, "[VoiceConnectionService] startObserving, event count: " + count);
490+
491+
for (Bundle event : delayedEvents) {
492+
String action = event.getString("action");
493+
HashMap attributeMap = (HashMap) event.getSerializable("attributeMap");
494+
495+
currentConnectionService.sendCallRequestToActivity(action, attributeMap, false);
496+
}
497+
498+
delayedEvents = new ArrayList<Bundle>();
499+
}
500+
});
501+
}
502+
477503
/*
478504
* Send call request to the RNCallKeepModule
479505
*/
480-
private void sendCallRequestToActivity(final String action, @Nullable final HashMap attributeMap) {
506+
private void sendCallRequestToActivity(final String action, @Nullable final HashMap attributeMap, final boolean retry) {
481507
final VoiceConnectionService instance = this;
482508
final Handler handler = new Handler();
483509

@@ -487,12 +513,19 @@ private void sendCallRequestToActivity(final String action, @Nullable final Hash
487513
@Override
488514
public void run() {
489515
Intent intent = new Intent(action);
516+
Bundle extras = new Bundle();
517+
extras.putString("action", action);
518+
490519
if (attributeMap != null) {
491-
Bundle extras = new Bundle();
492520
extras.putSerializable("attributeMap", attributeMap);
493521
intent.putExtras(extras);
494522
}
495-
LocalBroadcastManager.getInstance(instance).sendBroadcast(intent);
523+
524+
boolean result = LocalBroadcastManager.getInstance(instance).sendBroadcast(intent);
525+
if (!result && retry) {
526+
// Event will be sent later when a listener will be available.
527+
delayedEvents.add(extras);
528+
}
496529
}
497530
});
498531
}

0 commit comments

Comments
 (0)