react-native-twilio-programmable-voice
react-native-twilio-programmable-voice copied to clipboard
feat: Android Twilio SDK 5.4.2
Ported Twilio quickstart changes up to this point https://github.com/twilio/voice-quickstart-android/commit/e3db03fac386ae18d3790a52dbd423ead7234ab3
-
[x] notification for incoming call when the app is in the background
-
[x] implement Android v5
-
[x] test making a call
-
[x] test incoming call, when app in foreground
-
[x] test incoming call, when app in background
-
[x] test incoming call, when app is closed
-
[x] test after a call invite is cancelled, opening the JS app doesn't trigger the incoming call screen
-
[x] cleanup XML resources
-
[x] incoming notifications when the device is locked
-
[x] correctly cleanup a CALL_INVITE after answering or missed, currently the second time the app is stuck with this log:
-
[x] Error when receiving a call and the app is closed
2020-11-15 12:53:28.746 32167-32167/? W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10000000 pkg=com.hoxfon.HoxFon.DEV.debug (has extras) }
-
[x] Ensure that issue #77 is resolved: In call dialpad not accessible when the call is answered from the lock screen
-
[x] add intent to call in progress notification to bring the app to the foreground
-
[x] write migration instruction in the README
Known issues:
- [x] Call invite heads-up notification are not auto cancelled. This issue happens when a call is received and the screen is locked or the screen in unlocked, but the app is in the background. The heads-up notification service is started. If the user taps on the notification full screen content (not reject nor answer button), and then accepts the call from JS, at the end of the call the heads-up notification won't be removed, because there is no push notification for end call, just for call invite cancelled by the caller. I don't know how to solve this issue, because
endForeground()
can only be called by the service and all the user interactions from JS end are handled by the VoiceModule. At the same time full screen content is needed to show the JS incoming call screen when the phone is locked, therefore it can't be removed.
Reported bugs:
- [x] getLaunchOptions() crashes upon the first app launch: https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/164#issuecomment-757965249 (not able to reproduce)
- [ ] check microphone permissions: https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/164#issuecomment-753997031
- [x] check fullscreen intent https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/164#issuecomment-753997031
- [x] If I invoke call screen on app kill with device locked then accept call using TwilioVoice.accept() still the notification is showing after the call connect - Android version (8 , 9, 10 , 11): https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/164#issuecomment-753994599
@fabriziomoscon is there any reason why you are upgrading to 5.0.1 and not to 5.6.0? I'm asking since #39 depends on Android SDK being 5.3.0 or newer.
@fmonsalvo I am going through all commits here: https://github.com/twilio/voice-quickstart-android/commits one by one and port them
Perfect, just wanted to know the rationale. I appreciate all your hard work!
@fabriziomoscon Hi. I have updated the Android Twilio SDK 5.0.2 code from this branch (458b0d4) and still facing same issue for incoming call.
@Samsritha1596 please refer to the above checklist to see the completion stage of this PR. Currently test incoming call, when app is closed
is unchecked. Note that this use case is one of the known issue with the current master branch, which PR aims to solve.
@Samsritha1596 please try to use this commit: 88f4f7301aebc36c89c07ba85decd5b7b6ee0a1c and let me know if you can receive calls when the app is killed.
@fabriziomoscon Incoming call is not receiving when app is killed and also no events were triggered
Here is the version details:
"react-native": "0.61.5", "react-native-twilio-programmable-voice": "^4.3.0", "react-native-push-notification": "^6.1.3",
Please find the attached screenshot for app is killed
@fabriziomoscon Any update ?
You are not using this PR branch
"react-native-twilio-programmable-voice": "^4.3.0"
Please use:
"react-native-twilio-programmable-voice": "https://github.com/hoxfon/react-native-twilio-programmable-voice#feat/twilio-android-sdk-5",
@fabriziomoscon Receives heads-up notification when app is killed and background without ringing and does not trigger any event
@Samsritha1596
Receives heads-up notification when app is killed and background
This is the correct behaviour
without ringing
I don't know if the limitation for background services in Android 10 allow to ring, because they rely on the sound the user sets for notifications
does not trigger any event
This is right, because the JS app is not started, it can't deliver any event. Only tapping on ACCEPT will start the app and deliver the event to JS
@fabriziomoscon Behaves the same way after adding USE_FULL_SCREEN_INTENT
@Samsritha1596 Please retry now, note that I pushed forced new commits.
@fabriziomoscon Screen does not invoke automatically when device is locked
I found an issue with receiving call in the background when the app is GONE (appImportance 1000). It seems that on my device (OnePlus 6), an activity is started on background whilst the device is locked. Whereas is not starting when the device is not locked.
19:39:24.417 18688-18769/<MY_APP> D/RNTwilioVoice: Bundle data: {twi_account_sid=xxx, twi_to=client:xxx, twi_bridge_token=xxx, twi_message_type=twilio.voice.call, twi_call_sid=xxx, twi_message_id=xxx, twi_from=xxx}
19:39:24.731 18688-18688/<MY_APP> D/RNTwilioVoice: context: null. appImportance = 1000
19:39:24.731 18688-18688/<MY_APP> D/RNTwilioVoice: Background
19:39:24.752 18688-18688/<MY_APP> D/RNTwilioVoice: onStartCommand() intent: Intent { act=ACTION_INCOMING_CALL cmp=<MY_APP>/com.hoxfon.react.RNTwilioVoice.IncomingCallNotificationService (has extras) }, flags: 0
19:39:24.753 18688-18688/<MY_APP> D/RNTwilioVoice: setCallInProgressNotification()
19:39:24.753 18688-18688/<MY_APP> I/RNTwilioVoice: app is NOT visible.
19:39:24.818 18688-18688/<MY_APP> D/RNTwilioVoice: sendCallInviteToActivity(). Android SDK: 29 app visible: false
19:39:24.885 18688-18688/<MY_APP> D/RNTwilioVoice: sendCallInviteToActivity(). DO NOTHING
19:39:24.939 1322-5062/? D/OemSceneCallBlock: isCallBlockedWithUidIntent { act=com.hoxfon.react.RNTwilioVoice.ACTION_INCOMING_CALL flg=0x4000000 cmp=<MY_APP>/com.hoxfon.MainActivity (has extras) }, ResolveInfo{1c0c12d <MY_APP>/com.hoxfon.MainActivity m=0x0}, false
19:39:24.940 1322-5062/? I/ActivityTaskManager: START u0 {act=com.hoxfon.react.RNTwilioVoice.ACTION_INCOMING_CALL flg=0x4000000 cmp=<MY_APP>/com.hoxfon.MainActivity (has extras)} from uid 10469 pid -1
19:39:24.941 1322-5062/? W/ActivityTaskManager: startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: Intent { act=com.hoxfon.react.RNTwilioVoice.ACTION_INCOMING_CALL flg=0x4000000 cmp=<MY_APP>/com.hoxfon.MainActivity (has extras) }
19:39:29.619 18688-18883/<MY_APP> D/RNTwilioVoice: initWithAccessToken()
19:39:29.620 18688-18883/<MY_APP> I/RNTwilioVoice: Registering with FCM
Could anybody try this scenario and verify whether this is a device specific bug?
@fabriziomoscon Yes.. TwilioVoice.getCallInvite() gets called when the device is locked but it does not unlock the device automatically...No activity invoke when app is in background and app closed.. only the connectionDidConnect invoked on answering call. I have tested in samsung galaxy m11 device
If I invoke call screen on app kill with device locked then accept call using TwilioVoice.accept() still the notification is showing after the call connect - Android version (8 , 9, 10 , 11)
@fabriziomoscon How to handle microphone permission on app background and closed if we denied permission and no events were triggered. Here I need to ask permission
Notification full screen intent also not working
TwilioVoice.accept() is not working when I receive call from background without device lock. But it's working background with lock.
Error log - 2021-01-08 12:44:52.265 26753-27024/com.ternster D/RNTwilioVoice: sendEvent callInviteCancelled params null
@fabriziomoscon I can also confirm that when the device is locked no activity is launched and the device just starts ringing on a blank screen.
@fabriziomoscon Any solution to clear heads up notification after call end ?
After the fresh installation on the android device. App crashes the first time but after that, if we reopen the app, It starts functioning normally and doesn't crash. After debugging I'm able to get these logs
Android Studio Logs
2021-01-11 18:38:05.343 20282-20282/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.free.myapp, PID: 20282
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.free.popup_automessenger/com.free.myapp.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at com.free.popup_automessenger.MainActivity$1.getLaunchOptions(MainActivity.java:37)
at com.facebook.react.ReactActivityDelegate.onCreate(ReactActivityDelegate.java:78)
at com.facebook.react.ReactActivity.onCreate(ReactActivity.java:45)
at android.app.Activity.performCreate(Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Code which causing this issue ( at MainActivity.java:37 - getLaunchOptions )
package com.free.myapp;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.hoxfon.react.RNTwilioVoice.Constants;
import com.facebook.react.ReactActivity;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "popup_automessenger";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
Intent intent = this.getPlainActivity().getIntent();
if (intent == null) {
return initialProperties;
}
switch (intent.getAction()) {. // This is line 37
case Constants.ACTION_INCOMING_CALL_NOTIFICATION:
Bundle callInviteBundle = new Bundle();
callInviteBundle.putString(Constants.CALL_SID, intent.getStringExtra(Constants.CALL_SID));
callInviteBundle.putString(Constants.CALL_FROM, intent.getStringExtra(Constants.CALL_FROM));
callInviteBundle.putString(Constants.CALL_TO, intent.getStringExtra(Constants.CALL_TO));
initialProperties.putBundle(Constants.CALL_INVITE_KEY, callInviteBundle);
break;
case Constants.ACTION_ACCEPT:
Bundle callBundle = new Bundle();
callBundle.putString(Constants.CALL_SID, intent.getStringExtra(Constants.CALL_SID));
callBundle.putString(Constants.CALL_FROM, intent.getStringExtra(Constants.CALL_FROM));
callBundle.putString(Constants.CALL_TO, intent.getStringExtra(Constants.CALL_TO));
callBundle.putString(Constants.CALL_STATE, Constants.CALL_STATE_CONNECTED);
initialProperties.putBundle(Constants.CALL_KEY, callBundle);
break;
}
return initialProperties;
}
};
}
}
@Samsritha1596 Are you also experiencing this issue?
@fabriziomoscon If you could take a look at this that will be really helpful.
@salman-pixarsart No, i didn't face this issue
@fabriziomoscon Any updates to clear heads up notification after call end ?
Thank you guys for testing. For all reported bugs that don't require further clarification, I have created a checkbox in the PR description. I suggest that I provide fixes for those before we start discussing the issues I didn't list.
@salman-pixarsart Previously, in order to launch the app when receiving a call, the flow was:
- this module would launch the app normally
- the JS would always request the module whether there were incoming calls
- if there where any incoming call request the module would use an event to communicate with JS the incoming call parameters
- the JS would listen to the event and launch the view with the appropriate incoming call answer/reject controls.
This loop was long and prone to race conditions in case the event was fired before the root view in JS were completely initialised.
This version replaces the previous flow by using getLaunchOptions()
to pass initial properties from native to JS when receiving a call as explained here: https://reactnative.dev/docs/communication-android
To check for properties I use an Android's intent
.
The crash you reported here https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/164#issuecomment-757965249 is caused by hashCode()
called on null
:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
at com.free.popup_automessenger.MainActivity$1.getLaunchOptions(MainActivity.java:37)
However, this module has checks for intent
not null:
Intent intent = getCurrentActivity().getIntent();
if (intent == null) {
return;
}
int currentCallInviteIntent = intent.hashCode();
I am not able to reproduce your error. And I am not really able to understand which hashCode()
generated this FATAL ERROR. The issue might be with a specific implementation of your app `com.free.popup_automessenger.
My suggest would be:
- check whether your app MainActivity class needs you to override
protected void onCreate(Bundle savedInstanceState)
- look for
hashCode()
in your app's code - to receive more help you could: open an issue (to allow me to focus on that), with the RN version you are using, a video for the crash in the simulator and the logs. And include the logs line before and after the crash.
Thank you for testing.
@Samsritha1596 @salman-pixarsart @jdegger
I have added a commit to handle call invites when the phone is locked. Please test and tell me if it fixes a few issues reported above. Note that I changed the PR description to add know issues
.
@salman-pixarsart Previously, in order to launch the app when receiving a call, the flow was:
- this module would launch the app normally
- the JS would always request the module whether there were incoming calls
- if there where any incoming call request the module would use an event to communicate with JS the incoming call parameters
- the JS would listen to the event and launch the view with the appropriate incoming call answer/reject controls.
This loop was long and prone to race conditions in case the event was fired before the root view in JS were completely initialised.
This version replaces the previous flow by using
getLaunchOptions()
to pass initial properties from native to JS when receiving a call as explained here: https://reactnative.dev/docs/communication-android To check for properties I use an Android'sintent
.The crash you reported here #164 (comment) is caused by
hashCode()
called onnull
:Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference at com.free.popup_automessenger.MainActivity$1.getLaunchOptions(MainActivity.java:37)
However, this module has checks for
intent
not null:Intent intent = getCurrentActivity().getIntent(); if (intent == null) { return; } int currentCallInviteIntent = intent.hashCode();
I am not able to reproduce your error. And I am not really able to understand which
hashCode()
generated this FATAL ERROR. The issue might be with a specific implementation of your app `com.free.popup_automessenger. My suggest would be:
- check whether your app MainActivity class needs you to override
protected void onCreate(Bundle savedInstanceState)
- look for
hashCode()
in your app's code- to receive more help you could: open an issue (to allow me to focus on that), with the RN version you are using, a video for the crash in the simulator, and the logs. And include the logs line before and after the crash.
Thank you for testing.
@fabriziomoscon I'm able to solve this by adding a couple of lines in your code in the main activity of my app. What I'm doing is just verifying if the intent's action is null or not. If null just return the initial properties.
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
Intent intent = this.getPlainActivity().getIntent();
if (intent == null || intent.getAction() == null) {
return initialProperties;
}
switch (intent.getAction()) {
case Constants.ACTION_INCOMING_CALL_NOTIFICATION:
Bundle callInviteBundle = new Bundle();
callInviteBundle.putString(Constants.CALL_SID, intent.getStringExtra(Constants.CALL_SID));
callInviteBundle.putString(Constants.CALL_FROM, intent.getStringExtra(Constants.CALL_FROM));
callInviteBundle.putString(Constants.CALL_TO, intent.getStringExtra(Constants.CALL_TO));
initialProperties.putBundle(Constants.CALL_INVITE_KEY, callInviteBundle);
break;
case Constants.ACTION_ACCEPT:
Bundle callBundle = new Bundle();
callBundle.putString(Constants.CALL_SID, intent.getStringExtra(Constants.CALL_SID));
callBundle.putString(Constants.CALL_FROM, intent.getStringExtra(Constants.CALL_FROM));
callBundle.putString(Constants.CALL_TO, intent.getStringExtra(Constants.CALL_TO));
callBundle.putString(Constants.CALL_STATE, Constants.CALL_STATE_CONNECTED);
initialProperties.putBundle(Constants.CALL_KEY, callBundle);
break;
}
return initialProperties;
}
};
Also replacing below code in onHostResume() in TwilioVoiceModule.java
if (intent == null) {
return;
}
with
if (intent == null || intent.getAction() == null) {
return
}
Thank you for your response.
@salman-pixarsart I see. That means that sometimes even if intent is not null the action can be null? Is your fix related to this other PR: https://github.com/hoxfon/react-native-twilio-programmable-voice/pull/186/files ?
@fabriziomoscon Yes it is related to that PR. On initial install an intent exist to invoke the app but the intent's action is null. That's why it was causing app crash on initial install.
Hi @fabriziomoscon! I've prepared updates to this branch incl. unregistartion for Android/IOS, and I also solved an issue with "Call invite heads-up notification are not auto cancelled". Can we contact somehow to include those changes to #164 PR?
Hello, thanks for make this library update. I've found some issues on getAudioDevices and getSelectedAudioDevice, it returning undefined, apparently the result is not returned on method in index.js, so i make changes like this:
async getAudioDevices() {
if (Platform.OS === IOS) {
return
}
return await TwilioVoice.getAudioDevices()
},
async getSelectedAudioDevice() {
if (Platform.OS === IOS) {
return
}
return await TwilioVoice.getSelectedAudioDevice()
},
I also make change on method selectAudioDevice in TwilioVoiceModule.java, so when user send null param, audioSwitch will select a device automatically based on the following priority: BluetoothHeadset -> WiredHeadset -> Earpiece -> Speakerphone
@ReactMethod
public void selectAudioDevice(String name) {
if (name != null) {
AudioDevice selected = availableAudioDevices.get(name);
if (selected == null) {
return;
}
audioSwitch.selectDevice(selected);
} else {
audioSwitch.selectDevice(null);
}
}
Also please consider to bring back method setSpeakerPhone on android, with audioSwitch we can make something like this:
@ReactMethod
public void setSpeakerPhone(Boolean value) {
if (value) {
AudioDevice speakerphoneDevice = availableAudioDevices.get("Speakerphone");
if (speakerphoneDevice != null) {
audioSwitch.selectDevice(speakerphoneDevice);
}
} else {
audioSwitch.selectDevice(null);
}
}
Android 12 support + setSpeaker functionality: https://github.com/hoxfon/react-native-twilio-programmable-voice/issues/211#issuecomment-1165048758