diff --git a/README.md b/README.md index a0b7e2b4..07d3fd67 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is a React-Native wrapper for [Twilio Programmable Voice SDK](https://www.t ## Twilio Programmable Voice SDK -- Android 4.5.0 (bundled within the module) +- Android 5.0.0 (bundled within the module) - iOS 5.1.0 (specified by the app's own podfile) ## Breaking changes in v4.0.0 diff --git a/android/build.gradle b/android/build.gradle index fe752fa4..bc1a9e31 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -55,9 +55,10 @@ dependencies { def supportLibVersion = rootProject.hasProperty('supportLibVersion') ? rootProject.supportLibVersion : DEFAULT_SUPPORT_LIB_VERSION implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.twilio:voice-android:4.5.0' + implementation 'com.twilio:voice-android:5.0.1' implementation "com.android.support:appcompat-v7:$supportLibVersion" implementation 'com.facebook.react:react-native:+' implementation 'com.google.firebase:firebase-messaging:17.6.+' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' testImplementation 'junit:junit:4.12' } diff --git a/android/gradle.properties b/android/gradle.properties index af6dcbe4..a066d344 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -16,4 +16,4 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true 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..eaaba9c1 100644 --- a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/CallNotificationManager.java @@ -26,25 +26,23 @@ import java.util.List; import static android.content.Context.ACTIVITY_SERVICE; - +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_ANSWER_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_REJECT_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_HANGUP_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_INCOMING_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_MISSED_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_NOTIFICATION_ID; +import static com.hoxfon.react.RNTwilioVoice.Constants.NOTIFICATION_TYPE; +import static com.hoxfon.react.RNTwilioVoice.Constants.CALL_SID_KEY; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_NOTIFICATION_PREFIX; +import static com.hoxfon.react.RNTwilioVoice.Constants.MISSED_CALLS_GROUP; +import static com.hoxfon.react.RNTwilioVoice.Constants.MISSED_CALLS_NOTIFICATION_ID; +import static com.hoxfon.react.RNTwilioVoice.Constants.HANGUP_NOTIFICATION_ID; +import static com.hoxfon.react.RNTwilioVoice.Constants.PREFERENCE_KEY; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_CLEAR_MISSED_CALLS_COUNT; +import static com.hoxfon.react.RNTwilioVoice.Constants.CLEAR_MISSED_CALLS_NOTIFICATION_ID; import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.TAG; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_ANSWER_CALL; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_REJECT_CALL; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_HANGUP_CALL; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_INCOMING_CALL; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_MISSED_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.NOTIFICATION_TYPE; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.CALL_SID_KEY; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.INCOMING_NOTIFICATION_PREFIX; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.MISSED_CALLS_GROUP; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.MISSED_CALLS_NOTIFICATION_ID; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.HANGUP_NOTIFICATION_ID; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.PREFERENCE_KEY; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_CLEAR_MISSED_CALLS_COUNT; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.CLEAR_MISSED_CALLS_NOTIFICATION_ID; - public class CallNotificationManager { @@ -72,7 +70,7 @@ public int getApplicationImportance(ReactApplicationContext context) { return 0; } - public Class getMainActivityClass(ReactApplicationContext context) { + public static Class getMainActivityClass(Context context) { String packageName = context.getPackageName(); Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName); String className = launchIntent.getComponent().getClassName(); diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/Constants.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/Constants.java new file mode 100644 index 00000000..dde58f95 --- /dev/null +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/Constants.java @@ -0,0 +1,33 @@ +package com.hoxfon.react.RNTwilioVoice; + +public class Constants { + public static final String INCOMING_NOTIFICATION_PREFIX = "Incoming_"; + public static final String MISSED_CALLS_GROUP = "MISSED_CALLS"; + public static final int MISSED_CALLS_NOTIFICATION_ID = 1; + public static final int HANGUP_NOTIFICATION_ID = 11; + public static final int CLEAR_MISSED_CALLS_NOTIFICATION_ID = 21; + public static final String PREFERENCE_KEY = "com.hoxfon.react.TwilioVoice.PREFERENCE_FILE_KEY"; + + public static final String CALL_SID_KEY = "CALL_SID"; + public static final String VOICE_CHANNEL_LOW_IMPORTANCE = "notification-channel-low-importance"; + public static final String VOICE_CHANNEL_HIGH_IMPORTANCE = "notification-channel-high-importance"; + public static final String INCOMING_CALL_INVITE = "INCOMING_CALL_INVITE"; + public static final String CANCELLED_CALL_INVITE = "CANCELLED_CALL_INVITE"; + public static final String INCOMING_CALL_NOTIFICATION_ID = "INCOMING_CALL_NOTIFICATION_ID"; + public static final String ACTION_ACCEPT = "ACTION_ACCEPT"; + public static final String ACTION_REJECT = "ACTION_REJECT"; + public static final String ACTION_MISSED_CALL = "MISSED_CALL"; + public static final String ACTION_ANSWER_CALL = "ANSWER_CALL"; + public static final String ACTION_REJECT_CALL = "REJECT_CALL"; + public static final String ACTION_HANGUP_CALL = "HANGUP_CALL"; + public static final String ACTION_INCOMING_CALL_NOTIFICATION = "ACTION_INCOMING_CALL_NOTIFICATION"; + public static final String ACTION_INCOMING_CALL = "ACTION_INCOMING_CALL"; + public static final String ACTION_CANCEL_CALL = "ACTION_CANCEL_CALL"; + public static final String ACTION_FCM_TOKEN = "ACTION_FCM_TOKEN"; + public static final String ACTION_CANCEL_CALL_INVITE = "CANCEL_CALL_INVITE"; + public static final String ACTION_CLEAR_MISSED_CALLS_COUNT = "CLEAR_MISSED_CALLS_COUNT"; + + public static final String NOTIFICATION_TYPE = "NOTIFICATION_TYPE"; + public static final String CANCELLED_CALL_INVITE_ERR = "CANCELLED_CALL_INVITE_EXCEPTION"; + +} diff --git a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/IncomingCallNotificationService.java b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/IncomingCallNotificationService.java new file mode 100644 index 00000000..a807a4f7 --- /dev/null +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/IncomingCallNotificationService.java @@ -0,0 +1,229 @@ +package com.hoxfon.react.RNTwilioVoice; + +import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; + +import androidx.core.app.NotificationCompat; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.ProcessLifecycleOwner; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.twilio.voice.CallInvite; + +import static com.hoxfon.react.RNTwilioVoice.CallNotificationManager.getMainActivityClass; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_ACCEPT; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_CANCEL_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_INCOMING_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_INCOMING_CALL_NOTIFICATION; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_REJECT; +import static com.hoxfon.react.RNTwilioVoice.Constants.CALL_SID_KEY; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_NOTIFICATION_ID; + +public class IncomingCallNotificationService extends Service { + + private static final String TAG = IncomingCallNotificationService.class.getSimpleName(); + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + String action = intent.getAction(); + + CallInvite callInvite = intent.getParcelableExtra(INCOMING_CALL_INVITE); + int notificationId = intent.getIntExtra(INCOMING_CALL_NOTIFICATION_ID, 0); + + switch (action) { + case ACTION_INCOMING_CALL: + handleIncomingCall(callInvite, notificationId); + break; + case ACTION_ACCEPT: + accept(callInvite, notificationId); + break; + case ACTION_REJECT: + reject(callInvite); + break; + case ACTION_CANCEL_CALL: + handleCancelledCall(intent); + break; + default: + break; + } + return START_NOT_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private Notification createNotification(CallInvite callInvite, int notificationId, int channelImportance) { + Intent intent = new Intent(this, getMainActivityClass(this)); + intent.setAction(ACTION_INCOMING_CALL_NOTIFICATION); + intent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); + intent.putExtra(INCOMING_CALL_INVITE, callInvite); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pendingIntent = + PendingIntent.getActivity(this, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT); + /* + * Pass the notification id and call sid to use as an identifier to cancel the + * notification later + */ + Bundle extras = new Bundle(); + extras.putString(CALL_SID_KEY, callInvite.getCallSid()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return buildNotification(callInvite.getFrom() + " is calling.", + pendingIntent, + extras, + callInvite, + notificationId, + createChannel(channelImportance)); + } else { + return new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_call_white_24dp) + .setContentTitle("Incoming call") + .setContentText(callInvite.getFrom() + " is calling.") + .setAutoCancel(true) + .setExtras(extras) + .setContentIntent(pendingIntent) + .setGroup("test_app_notification") + .setColor(Color.rgb(214, 10, 37)).build(); + } + } + + /** + * Build a notification. + * + * @param text the text of the notification + * @param pendingIntent the body, pending intent for the notification + * @param extras extras passed with the notification + * @return the builder + */ + @TargetApi(Build.VERSION_CODES.O) + private Notification buildNotification(String text, PendingIntent pendingIntent, Bundle extras, + final CallInvite callInvite, + int notificationId, + String channelId) { + Intent rejectIntent = new Intent(getApplicationContext(), IncomingCallNotificationService.class); + rejectIntent.setAction(ACTION_REJECT); + rejectIntent.putExtra(INCOMING_CALL_INVITE, callInvite); + rejectIntent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); + PendingIntent piRejectIntent = PendingIntent.getService(getApplicationContext(), 0, rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + Intent acceptIntent = new Intent(getApplicationContext(), IncomingCallNotificationService.class); + acceptIntent.setAction(ACTION_ACCEPT); + acceptIntent.putExtra(INCOMING_CALL_INVITE, callInvite); + acceptIntent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); + PendingIntent piAcceptIntent = PendingIntent.getService(getApplicationContext(), 0, acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + Notification.Builder builder = + new Notification.Builder(getApplicationContext(), channelId) + .setSmallIcon(R.drawable.ic_call_white_24dp) + .setContentTitle("Incoming call") + .setContentText(text) + .setCategory(Notification.CATEGORY_CALL) + .setFullScreenIntent(pendingIntent, true) + .setExtras(extras) + .setAutoCancel(true) + .addAction(android.R.drawable.ic_menu_delete, getString(R.string.decline), piRejectIntent) + .addAction(android.R.drawable.ic_menu_call, getString(R.string.answer), piAcceptIntent) + .setFullScreenIntent(pendingIntent, true); + + return builder.build(); + } + + @TargetApi(Build.VERSION_CODES.O) + private String createChannel(int channelImportance) { + NotificationChannel callInviteChannel = new NotificationChannel(Constants.VOICE_CHANNEL_HIGH_IMPORTANCE, + "Primary Voice Channel", NotificationManager.IMPORTANCE_HIGH);; + String channelId = Constants.VOICE_CHANNEL_HIGH_IMPORTANCE; + + if (channelImportance == NotificationManager.IMPORTANCE_LOW) { + callInviteChannel = new NotificationChannel(Constants.VOICE_CHANNEL_LOW_IMPORTANCE, + "Primary Voice Channel", NotificationManager.IMPORTANCE_LOW);; + channelId = Constants.VOICE_CHANNEL_LOW_IMPORTANCE; + } + callInviteChannel.setLightColor(Color.GREEN); + callInviteChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.createNotificationChannel(callInviteChannel); + + return channelId; + } + + private void accept(CallInvite callInvite, int notificationId) { + endForeground(); + Intent activeCallIntent = new Intent(this, getMainActivityClass(this)); + activeCallIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + activeCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + activeCallIntent.putExtra(INCOMING_CALL_INVITE, callInvite); + activeCallIntent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); + activeCallIntent.setAction(ACTION_ACCEPT); + startActivity(activeCallIntent); + } + + private void reject(CallInvite callInvite) { + endForeground(); + callInvite.reject(getApplicationContext()); + } + + private void handleCancelledCall(Intent intent) { + endForeground(); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } + + private void handleIncomingCall(CallInvite callInvite, int notificationId) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setCallInProgressNotification(callInvite, notificationId); + } + sendCallInviteToActivity(callInvite, notificationId); + } + + private void endForeground() { + stopForeground(true); + } + + private void setCallInProgressNotification(CallInvite callInvite, int notificationId) { + if (isAppVisible()) { + Log.i(TAG, "setCallInProgressNotification - app is visible."); + startForeground(notificationId, createNotification(callInvite, notificationId, NotificationManager.IMPORTANCE_LOW)); + } else { + Log.i(TAG, "setCallInProgressNotification - app is NOT visible."); + startForeground(notificationId, createNotification(callInvite, notificationId, NotificationManager.IMPORTANCE_HIGH)); + } + } + + /* + * Send the CallInvite to the VoiceActivity. Start the activity if it is not running already. + */ + private void sendCallInviteToActivity(CallInvite callInvite, int notificationId) { + if (Build.VERSION.SDK_INT >= 29 && !isAppVisible()) { + return; + } + Intent intent = new Intent(this, getMainActivityClass(this)); + intent.setAction(ACTION_INCOMING_CALL); + intent.putExtra(INCOMING_CALL_NOTIFICATION_ID, notificationId); + intent.putExtra(INCOMING_CALL_INVITE, callInvite); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + this.startActivity(intent); + } + + private boolean isAppVisible() { + return ProcessLifecycleOwner + .get() + .getLifecycle() + .getCurrentState() + .isAtLeast(Lifecycle.State.STARTED); + } +} \ No newline at end of file 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..36d8fde4 100644 --- a/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java +++ b/android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java @@ -39,10 +39,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; import com.google.firebase.iid.FirebaseInstanceId; -import com.google.firebase.iid.InstanceIdResult; import com.twilio.voice.AcceptOptions; import com.twilio.voice.Call; import com.twilio.voice.CallException; @@ -57,6 +54,21 @@ import java.util.HashMap; import java.util.Map; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_ANSWER_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_CANCEL_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_CLEAR_MISSED_CALLS_COUNT; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_FCM_TOKEN; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_HANGUP_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_INCOMING_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_MISSED_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_REJECT_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.CANCELLED_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.CANCELLED_CALL_INVITE_ERR; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_NOTIFICATION_ID; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_NOTIFICATION_PREFIX; +import static com.hoxfon.react.RNTwilioVoice.Constants.MISSED_CALLS_GROUP; +import static com.hoxfon.react.RNTwilioVoice.Constants.PREFERENCE_KEY; import static com.hoxfon.react.RNTwilioVoice.EventManager.EVENT_CONNECTION_DID_CONNECT; import static com.hoxfon.react.RNTwilioVoice.EventManager.EVENT_CONNECTION_DID_DISCONNECT; import static com.hoxfon.react.RNTwilioVoice.EventManager.EVENT_DEVICE_DID_RECEIVE_INCOMING; @@ -82,30 +94,6 @@ public class TwilioVoiceModule extends ReactContextBaseJavaModule implements Act // Empty HashMap, contains parameters for the Outbound call private HashMap twiMLParams = new HashMap<>(); - public static final String INCOMING_CALL_INVITE = "INCOMING_CALL_INVITE"; - public static final String INCOMING_CALL_NOTIFICATION_ID = "INCOMING_CALL_NOTIFICATION_ID"; - public static final String NOTIFICATION_TYPE = "NOTIFICATION_TYPE"; - public static final String CANCELLED_CALL_INVITE = "CANCELLED_CALL_INVITE"; - - - public static final String ACTION_INCOMING_CALL = "com.hoxfon.react.TwilioVoice.INCOMING_CALL"; - public static final String ACTION_FCM_TOKEN = "com.hoxfon.react.TwilioVoice.ACTION_FCM_TOKEN"; - public static final String ACTION_MISSED_CALL = "com.hoxfon.react.TwilioVoice.MISSED_CALL"; - public static final String ACTION_ANSWER_CALL = "com.hoxfon.react.TwilioVoice.ANSWER_CALL"; - public static final String ACTION_REJECT_CALL = "com.hoxfon.react.TwilioVoice.REJECT_CALL"; - public static final String ACTION_HANGUP_CALL = "com.hoxfon.react.TwilioVoice.HANGUP_CALL"; - public static final String ACTION_CANCEL_CALL_INVITE = "com.hoxfon.react.TwilioVoice.CANCEL_CALL_INVITE"; - public static final String ACTION_CLEAR_MISSED_CALLS_COUNT = "com.hoxfon.react.TwilioVoice.CLEAR_MISSED_CALLS_COUNT"; - - public static final String CALL_SID_KEY = "CALL_SID"; - public static final String INCOMING_NOTIFICATION_PREFIX = "Incoming_"; - public static final String MISSED_CALLS_GROUP = "MISSED_CALLS"; - public static final int MISSED_CALLS_NOTIFICATION_ID = 1; - public static final int HANGUP_NOTIFICATION_ID = 11; - public static final int CLEAR_MISSED_CALLS_NOTIFICATION_ID = 21; - - public static final String PREFERENCE_KEY = "com.hoxfon.react.TwilioVoice.PREFERENCE_FILE_KEY"; - private NotificationManager notificationManager; private CallNotificationManager callNotificationManager; private ProximityManager proximityManager; @@ -529,12 +517,16 @@ public void onReceive(Context context, Intent intent) { handleIncomingCallIntent(intent); } else if (action.equals(ACTION_CANCEL_CALL_INVITE)) { CancelledCallInvite cancelledCallInvite = intent.getParcelableExtra(CANCELLED_CALL_INVITE); + String cancelledCallInviteErr = intent.getParcelableExtra(CANCELLED_CALL_INVITE_ERR); clearIncomingNotification(cancelledCallInvite.getCallSid()); WritableMap params = Arguments.createMap(); if (cancelledCallInvite != null) { params.putString("call_sid", cancelledCallInvite.getCallSid()); params.putString("call_from", cancelledCallInvite.getFrom()); params.putString("call_to", cancelledCallInvite.getTo()); + if (cancelledCallInviteErr != "") { + params.putString("err", cancelledCallInviteErr); + } } eventManager.sendEvent(EVENT_CALL_INVITE_CANCELLED, params); } else if (action.equals(ACTION_MISSED_CALL)) { @@ -593,25 +585,14 @@ private void clearIncomingNotification(String callSid) { * */ private void registerForCallInvites() { - FirebaseInstanceId.getInstance().getInstanceId() - .addOnCompleteListener(new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "getInstanceId failed", task.getException()); - return; - } - - // Get new Instance ID token - String fcmToken = task.getResult().getToken(); - if (fcmToken != null) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Registering with FCM"); - } - Voice.register(accessToken, Voice.RegistrationChannel.FCM, fcmToken, registrationListener); - } - } - }); + final String fcmToken = FirebaseInstanceId.getInstance().getToken(); + if (fcmToken == null) { + return; + } + if (BuildConfig.DEBUG) { + Log.i(TAG, "Registering with FCM"); + } + Voice.register(accessToken, Voice.RegistrationChannel.FCM, fcmToken, registrationListener); } @ReactMethod 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..aca5b13c 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 @@ -4,6 +4,8 @@ import android.content.Intent; import android.os.Handler; import android.os.Looper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.util.Log; @@ -14,6 +16,7 @@ import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; +import com.twilio.voice.CallException; import com.hoxfon.react.RNTwilioVoice.BuildConfig; import com.hoxfon.react.RNTwilioVoice.CallNotificationManager; import com.twilio.voice.CallInvite; @@ -24,13 +27,15 @@ import java.util.Map; import java.util.Random; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_FCM_TOKEN; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_INCOMING_CALL; +import static com.hoxfon.react.RNTwilioVoice.Constants.ACTION_CANCEL_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.CANCELLED_CALL_INVITE; +import static com.hoxfon.react.RNTwilioVoice.Constants.CANCELLED_CALL_INVITE_ERR; +import static com.hoxfon.react.RNTwilioVoice.Constants.INCOMING_CALL_NOTIFICATION_ID; import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.TAG; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_FCM_TOKEN; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_INCOMING_CALL; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.ACTION_CANCEL_CALL_INVITE; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.INCOMING_CALL_INVITE; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.CANCELLED_CALL_INVITE; -import static com.hoxfon.react.RNTwilioVoice.TwilioVoiceModule.INCOMING_CALL_NOTIFICATION_ID; + import com.hoxfon.react.RNTwilioVoice.SoundPoolManager; public class VoiceFirebaseMessagingService extends FirebaseMessagingService { @@ -71,7 +76,7 @@ public void onMessageReceived(RemoteMessage remoteMessage) { Random randomNumberGenerator = new Random(System.currentTimeMillis()); final int notificationId = randomNumberGenerator.nextInt(); - boolean valid = Voice.handleMessage(data, new MessageListener() { + boolean valid = Voice.handleMessage(this, data, new MessageListener() { @Override public void onCallInvite(final CallInvite callInvite) { @@ -134,11 +139,11 @@ public void onReactContextInitialized(ReactContext context) { } @Override - public void onCancelledCallInvite(final CancelledCallInvite cancelledCallInvite) { + public void onCancelledCallInvite(@NonNull CancelledCallInvite cancelledCallInvite, @Nullable CallException callException) { Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { public void run() { - VoiceFirebaseMessagingService.this.sendCancelledCallInviteToActivity(cancelledCallInvite); + VoiceFirebaseMessagingService.this.sendCancelledCallInviteToActivity(cancelledCallInvite, callException); } }); } @@ -158,10 +163,13 @@ public void run() { /* * Send the CancelledCallInvite to the TwilioVoiceModule */ - private void sendCancelledCallInviteToActivity(CancelledCallInvite cancelledCallInvite) { + private void sendCancelledCallInviteToActivity(@NonNull CancelledCallInvite cancelledCallInvite, @Nullable CallException callException) { SoundPoolManager.getInstance((this)).stopRinging(); Intent intent = new Intent(ACTION_CANCEL_CALL_INVITE); intent.putExtra(CANCELLED_CALL_INVITE, cancelledCallInvite); + if (callException != null) { + intent.putExtra(CANCELLED_CALL_INVITE_ERR, callException.toString()); + } LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } } diff --git a/android/src/main/res/values/values.xml b/android/src/main/res/values/values.xml new file mode 100644 index 00000000..1ac58f4b --- /dev/null +++ b/android/src/main/res/values/values.xml @@ -0,0 +1,5 @@ + + + Answer + Decline + \ No newline at end of file