From 7e6d79406e065a04dba2d356999812e3eb9db200 Mon Sep 17 00:00:00 2001 From: Fabrizio Moscon Date: Thu, 12 Nov 2020 22:42:36 +0000 Subject: [PATCH] draft --- .../RNTwilioVoice/BackgroundCallReceiver.java | 105 ++++++++++++++++++ .../BackgroundCallTaskService.java | 28 +++++ .../CallNotificationManager.java | 19 ++-- .../RNTwilioVoice/TwilioVoiceModule.java | 9 +- .../fcm/VoiceFirebaseMessagingService.java | 94 +++++++++++----- 5 files changed, 214 insertions(+), 41 deletions(-) create mode 100644 android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallReceiver.java create mode 100644 android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallTaskService.java diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallReceiver.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallReceiver.java new file mode 100644 index 00000000..033a5fd9 --- /dev/null +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallReceiver.java @@ -0,0 +1,105 @@ +package com.hoxfon.react.RNTwilioVoice; + +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import com.facebook.react.HeadlessJsTaskService; + +import java.util.List; +//import java.util.Random; +// +//import androidx.localbroadcastmanager.content.LocalBroadcastManager; +// +//import com.facebook.react.ReactApplication; +//import com.facebook.react.ReactInstanceManager; +//import com.facebook.react.bridge.ReactApplicationContext; +//import com.facebook.react.bridge.ReactContext; +//import com.hoxfon.react.RNTwilioVoice.CallNotificationManager; +// +//import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_INCOMING_CALL; +//import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.INCOMING_CALL_INVITE; +//import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.INCOMING_CALL_NOTIFICATION_ID; +import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.TAG; + +public class BackgroundCallReceiver extends BroadcastReceiver { + + @Override + public void onReceive(final Context context, final Intent intent) { + Log.d(TAG, "### onReceive"); + /** + This part will be called every time network connection is changed + e.g. Connected -> Not Connected + **/ + if (!isAppOnForeground((context))) { +// ReactApplicationContext ctx = new ReactApplicationContext(context); +// +// Random randomNumberGenerator = new Random(System.currentTimeMillis()); +// final int notificationId = randomNumberGenerator.nextInt(); +// CallNotificationManager callNotificationManager = new CallNotificationManager(); +// +// int appImportance = callNotificationManager.getApplicationImportance(ctx); +// if (BuildConfig.DEBUG) { +// Log.d(TAG, "CONTEXT not present appImportance = " + appImportance); +// } +// Intent launchIntent = callNotificationManager.getLaunchIntent( +// ctx, +// notificationId, +// intent.getStringExtra("call_sid"), +// intent.getStringExtra("call_from"), +// intent.getStringExtra("call_to"), +// true, +// ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND +// ); +// context.startActivity(launchIntent); +//// Intent callInviteIntent = new Intent(ACTION_INCOMING_CALL); +//// callInviteIntent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); +//// LocalBroadcastManager.getInstance(context).sendBroadcast(callInviteIntent); +//// callNotificationManager.createIncomingCallNotification( +//// ctx, +//// intent.getStringExtra("call_sid"), +//// intent.getStringExtra("call_from"), +//// notificationId, +//// launchIntent +//// ); + + + + /** + We will start our service and send extra info about + network connections + **/ + Bundle extras = intent.getExtras(); + Intent serviceIntent = new Intent(context, BackgroundCallTaskService.class); + serviceIntent.putExtras(extras); + context.startService(serviceIntent); + HeadlessJsTaskService.acquireWakeLockNow(context); + } + } + + private boolean isAppOnForeground(Context context) { + /** + We need to check if app is in foreground otherwise the app will crash. + http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not + **/ + ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List appProcesses = + activityManager.getRunningAppProcesses(); + if (appProcesses == null) { + return false; + } + final String packageName = context.getPackageName(); + for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { + if (appProcess.importance == + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && + appProcess.processName.equals(packageName)) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallTaskService.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallTaskService.java new file mode 100644 index 00000000..699cc706 --- /dev/null +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/BackgroundCallTaskService.java @@ -0,0 +1,28 @@ +package com.hoxfon.react.RNTwilioVoice; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import com.facebook.react.HeadlessJsTaskService; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.jstasks.HeadlessJsTaskConfig; + +import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.TAG; + +public class BackgroundCallTaskService extends HeadlessJsTaskService { + @Override + protected HeadlessJsTaskConfig getTaskConfig(Intent intent) { + Bundle extras = intent.getExtras(); + Log.d(TAG, "###getTaskConfig"+ extras.toString()); + if (extras != null) { + return new HeadlessJsTaskConfig( + "BackgroundCallTaskService", + Arguments.fromBundle(extras), + 5000, // timeout for the task + false // optional: defines whether or not the task is allowed in foreground. Default is false + ); + } + return null; + } +} \ No newline at end of file diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java index af4e9175..ed9d929f 100644 --- a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java @@ -86,7 +86,9 @@ public Class getMainActivityClass(ReactApplicationContext context) { public Intent getLaunchIntent(ReactApplicationContext context, int notificationId, - CallInvite callInvite, + String callInviteSid, + String callInviteFrom, + String callInviteTo, Boolean shouldStartNewTask, int appImportance ) { @@ -107,14 +109,15 @@ public Intent getLaunchIntent(ReactApplicationContext context, WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON ); - if (callInvite != null) { - launchIntent.putExtra(INCOMING_CALL_INVITE, callInvite); - } + launchIntent.putExtra("callSid", callInviteSid); + launchIntent.putExtra("callFrom", callInviteFrom); + launchIntent.putExtra("callTo", callInviteTo); return launchIntent; } public void createIncomingCallNotification(ReactApplicationContext context, - CallInvite callInvite, + String callInviteSid, + String callInviteFrom, int notificationId, Intent launchIntent) { @@ -131,7 +134,7 @@ public void createIncomingCallNotification(ReactApplicationContext context, */ Bundle extras = new Bundle(); extras.putInt(INCOMING_CALL_NOTIFICATION_ID, notificationId); - extras.putString(CALL_SID_KEY, callInvite.getCallSid()); + extras.putString(CALL_SID_KEY, callInviteSid); extras.putString(NOTIFICATION_TYPE, ACTION_INCOMING_CALL); /* * Create the notification shown in the notification drawer @@ -145,7 +148,7 @@ public void createIncomingCallNotification(ReactApplicationContext context, .setCategory(NotificationCompat.CATEGORY_CALL) .setSmallIcon(R.drawable.ic_call_white_24dp) .setContentTitle("Incoming call") - .setContentText(callInvite.getFrom() + " is calling") + .setContentText(callInviteFrom + " is calling") .setOngoing(true) .setAutoCancel(true) .setExtras(extras) @@ -180,7 +183,7 @@ public void createIncomingCallNotification(ReactApplicationContext context, notificationBuilder.addAction(R.drawable.ic_call_white_24dp, "ANSWER", pendingAnswerIntent); notificationManager.notify(notificationId, notificationBuilder.build()); - TwilioVoiceModule.callNotificationMap.put(INCOMING_NOTIFICATION_PREFIX+callInvite.getCallSid(), notificationId); + TwilioVoiceModule.callNotificationMap.put(INCOMING_NOTIFICATION_PREFIX+callInviteSid, notificationId); } public void initCallNotificationsChannel(NotificationManager notificationManager) { diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java index fb68f49d..a0629c74 100644 --- a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java @@ -461,8 +461,7 @@ private void handleIncomingCallIntent(Intent intent) { if (BuildConfig.DEBUG) { Log.d(TAG, "handleIncomingCallIntent"); } - activeCallInvite = intent.getParcelableExtra(INCOMING_CALL_INVITE); - if (activeCallInvite != null) { + if (intent != null) { callAccepted = false; SoundPoolManager.getInstance(getReactApplicationContext()).playRinging(); @@ -479,9 +478,9 @@ private void handleIncomingCallIntent(Intent intent) { appImportance == RunningAppProcessInfo.IMPORTANCE_SERVICE) { WritableMap params = Arguments.createMap(); - params.putString("call_sid", activeCallInvite.getCallSid()); - params.putString("call_from", activeCallInvite.getFrom()); - params.putString("call_to", activeCallInvite.getTo()); // TODO check if needed + params.putString("call_sid", intent.getStringExtra("callSid")); + params.putString("call_from", intent.getStringExtra("callFrom")); + params.putString("call_to", intent.getStringExtra("callTo")); eventManager.sendEvent(EVENT_DEVICE_DID_RECEIVE_INCOMING, params); } } else { diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/fcm/VoiceFirebaseMessagingService.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/fcm/VoiceFirebaseMessagingService.java index 520b77f1..a8dd316f 100644 --- a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/fcm/VoiceFirebaseMessagingService.java +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/fcm/VoiceFirebaseMessagingService.java @@ -2,6 +2,7 @@ import android.app.ActivityManager; import android.content.Intent; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -60,7 +61,7 @@ public void onNewToken(String token) { @Override public void onMessageReceived(RemoteMessage remoteMessage) { if (BuildConfig.DEBUG) { - Log.d(TAG, "Bundle data: " + remoteMessage.getData()); + Log.d(TAG, "VoiceFirebaseMessagingService onMessageReceived: " + remoteMessage.getMessageType()); } // Check if message contains a data payload. @@ -74,7 +75,6 @@ public void onMessageReceived(RemoteMessage remoteMessage) { boolean valid = Voice.handleMessage(data, new MessageListener() { @Override public void onCallInvite(final CallInvite callInvite) { - // We need to run this on the main thread, as the React code assumes that is true. // Namely, DevServerHelper constructs a Handler() without a Looper, which triggers: // "Can't create handler inside thread that has not called Looper.prepare()" @@ -93,7 +93,9 @@ public void run() { Intent launchIntent = callNotificationManager.getLaunchIntent( (ReactApplicationContext)context, notificationId, - callInvite, + callInvite.getCallSid(), + callInvite.getFrom(), + callInvite.getTo(), false, appImportance ); @@ -106,28 +108,53 @@ public void run() { intent.putExtra(INCOMING_CALL_INVITE, callInvite); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } else { - // Otherwise wait for construction, then handle the incoming call - mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() { - public void onReactContextInitialized(ReactContext context) { - int appImportance = callNotificationManager.getApplicationImportance((ReactApplicationContext)context); - if (BuildConfig.DEBUG) { - Log.d(TAG, "CONTEXT not present appImportance = " + appImportance); - } - Intent launchIntent = callNotificationManager.getLaunchIntent((ReactApplicationContext)context, notificationId, callInvite, true, appImportance); - context.startActivity(launchIntent); - Intent intent = new Intent(ACTION_INCOMING_CALL); - intent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); - intent.putExtra(INCOMING_CALL_INVITE, callInvite); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - callNotificationManager.createIncomingCallNotification( - (ReactApplicationContext) context, callInvite, notificationId, - launchIntent); - } - }); - if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { - // Construct it in the background - mReactInstanceManager.createReactContextInBackground(); - } + Intent callKeepIntent = new Intent(); + callKeepIntent.setAction("com.hoxfon.HoxFon.DEV.debug.BACKGROUND_CALL"); + callKeepIntent.setPackage("com.hoxfon.HoxFon.DEV.debug"); + Bundle bundle = new Bundle(); + bundle.putString("call_state", "PENDING"); + bundle.putString("call_sid", callInvite.getCallSid()); + bundle.putString("call_from", callInvite.getFrom()); + bundle.putString("call_to", callInvite.getTo()); + callKeepIntent.putExtras(bundle); + Log.d(TAG, "BACKGROUND onCallInvite, callKeepIntent "+ callKeepIntent); + + sendBroadcast(callKeepIntent); + +// // Otherwise wait for construction, then handle the incoming call +// mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() { +// public void onReactContextInitialized(ReactContext context) { +// int appImportance = callNotificationManager.getApplicationImportance((ReactApplicationContext)context); +// if (BuildConfig.DEBUG) { +// Log.d(TAG, "CONTEXT not present appImportance = " + appImportance); +// } +// Intent launchIntent = callNotificationManager.getLaunchIntent( +// (ReactApplicationContext)context, +// notificationId, +// callInvite.getCallSid(), +// callInvite.getFrom(), +// callInvite.getTo(), +// true, +// appImportance +// ); +// context.startActivity(launchIntent); +// Intent intent = new Intent(ACTION_INCOMING_CALL); +// intent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); +// intent.putExtra(INCOMING_CALL_INVITE, callInvite); +// LocalBroadcastManager.getInstance(context).sendBroadcast(intent); +// callNotificationManager.createIncomingCallNotification( +// (ReactApplicationContext) context, +// callInvite.getCallSid(), +// callInvite.getFrom(), +// notificationId, +// launchIntent +// ); +// } +// }); +// if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { +// // Construct it in the background +// mReactInstanceManager.createReactContextInBackground(); +// } } } }); @@ -160,8 +187,19 @@ public void run() { */ private void sendCancelledCallInviteToActivity(CancelledCallInvite cancelledCallInvite) { SoundPoolManager.getInstance((this)).stopRinging(); - Intent intent = new Intent(ACTION_CANCEL_CALL_INVITE); - intent.putExtra(CANCELLED_CALL_INVITE, cancelledCallInvite); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); +// Intent intent = new Intent(ACTION_CANCEL_CALL_INVITE); +// intent.putExtra(CANCELLED_CALL_INVITE, cancelledCallInvite); +// LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + + Intent callKeepIntent = new Intent(); + callKeepIntent.setAction("com.hoxfon.react.RNTwilioVoice.BACKGROUND_CALL"); + callKeepIntent.setPackage("com.hoxfon.react.RNTwilioVoice"); + Bundle bundle = new Bundle(); + bundle.putString("call_state", "CANCELLED"); + bundle.putString("call_sid", cancelledCallInvite.getCallSid()); + bundle.putString("call_from", cancelledCallInvite.getFrom()); + bundle.putString("call_to", cancelledCallInvite.getTo()); + callKeepIntent.putExtras(bundle); + sendBroadcast(callKeepIntent); } }