Skip to content

Commit a58a3ee

Browse files
fix: handle call invites when the phone is locked
1 parent 2ef17df commit a58a3ee

File tree

5 files changed

+65
-31
lines changed

5 files changed

+65
-31
lines changed

README.md

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ To allow the library to show heads up notifications you must add the following l
7575
>
7676
<service
7777
android:enabled="true"
78-
android:name="com.hoxfon.react.RNTwilioVoice.IncomingCallNotificationService">
78+
android:name="com.hoxfon.react.RNTwilioVoice.IncomingCallNotificationService"
79+
android:foregroundServiceType="phoneCall">
7980
<intent-filter>
8081
<action android:name="com.hoxfon.react.RNTwilioVoice.ACTION_ACCEPT" />
8182
<action android:name="com.hoxfon.react.RNTwilioVoice.ACTION_REJECT" />
@@ -85,8 +86,20 @@ To allow the library to show heads up notifications you must add the following l
8586
</application>
8687
```
8788

88-
Launch your app with `callInvite` or `call` initial properties.
89-
Add the following lines to your app `MainActivity`:
89+
Previously, in order to launch the app when receiving a call, the flow was:
90+
91+
1. the module would launch the app
92+
2. after the React app is initialised, it would always ask to the native module whether there were incoming call invites
93+
3. if there where any incoming call invites the module would send an event to the React app with the incoming call invite parameters
94+
4. the Reach app would listen to the event and launch the view with the appropriate incoming call answer/reject controls
95+
96+
This loop was long and prone to race conditions. In case the event was sent before the React main view was completely initialised, it would not be handled at all.
97+
98+
Version 5.0.0 replaces the previous flow by using `getLaunchOptions()` to pass initial properties from native to React when receiving a call invite as explained here: https://reactnative.dev/docs/communication-android.
99+
100+
The React app will be launched with the initial properties `callInvite` or `call`.
101+
102+
Add the following blocks to your app's `MainActivity`:
90103
91104
```java
92105
@@ -131,7 +144,23 @@ public class MainActivity extends ReactActivity {
131144
}
132145
};
133146
}
134-
...
147+
148+
// ...
149+
150+
@Override
151+
protected void onCreate(Bundle savedInstanceState) {
152+
super.onCreate(savedInstanceState);
153+
154+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
155+
setShowWhenLocked(true);
156+
setTurnScreenOn(true);
157+
}
158+
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
159+
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
160+
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
161+
}
162+
163+
// ...
135164
}
136165
```
137166

android/src/main/java/com/hoxfon/react/RNTwilioVoice/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class Constants {
2121
public static final String ACTION_MISSED_CALL = "MISSED_CALL";
2222
public static final String ACTION_HANGUP_CALL = "HANGUP_CALL";
2323
public static final String ACTION_INCOMING_CALL = "ACTION_INCOMING_CALL";
24-
public static final String ACTION_INCOMING_CALL_NOTIFICATION = "com.hoxfon.react.RNTwilioVoice.ACTION_INCOMING_CALL";
24+
public static final String ACTION_INCOMING_CALL_NOTIFICATION = "ACTION_INCOMING_CALL_NOTIFICATION";
2525
public static final String ACTION_CANCEL_CALL = "ACTION_CANCEL_CALL";
2626
public static final String ACTION_FCM_TOKEN = "ACTION_FCM_TOKEN";
2727
public static final String ACTION_CLEAR_MISSED_CALLS_COUNT = "CLEAR_MISSED_CALLS_COUNT";

android/src/main/java/com/hoxfon/react/RNTwilioVoice/IncomingCallNotificationService.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class IncomingCallNotificationService extends Service {
4040
@Override
4141
public int onStartCommand(Intent intent, int flags, int startId) {
4242
if (BuildConfig.DEBUG) {
43-
Log.d(TAG, "onStartCommand() intent: " + intent + ", flags: " + flags);
43+
Log.d(TAG, "IncomingCallNotificationService onStartCommand() intent: " + intent + ", flags: " + flags);
4444
}
4545
String action = intent.getAction();
4646

@@ -87,6 +87,7 @@ private Notification createNotification(CallInvite callInvite, int notificationI
8787
intent.putExtra(Constants.CALL_FROM, callInvite.getFrom());
8888
intent.putExtra(Constants.CALL_TO, callInvite.getTo());
8989
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
90+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
9091

9192
PendingIntent pendingIntent =
9293
PendingIntent.getActivity(this, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -152,7 +153,9 @@ private PendingIntent createActionPendingIntent(Context context, Intent intent)
152153
* @return the builder
153154
*/
154155
@TargetApi(Build.VERSION_CODES.O)
155-
private Notification buildNotification(String text, PendingIntent pendingIntent, Bundle extras,
156+
private Notification buildNotification(String text,
157+
PendingIntent pendingIntent,
158+
Bundle extras,
156159
final CallInvite callInvite,
157160
int notificationId,
158161
String channelId) {
@@ -183,15 +186,15 @@ private Notification buildNotification(String text, PendingIntent pendingIntent,
183186
.setSmallIcon(R.drawable.ic_call_white_24dp)
184187
.setContentTitle(getString(R.string.call_incoming_title))
185188
.setContentText(text)
186-
.setCategory(Notification.CATEGORY_CALL)
187189
.setExtras(extras)
188190
.setAutoCancel(true)
189191
.addAction(rejectAction)
190192
.addAction(answerAction)
191193
.setFullScreenIntent(pendingIntent, true)
192-
.setPriority(NotificationCompat.PRIORITY_MAX)
194+
.setPriority(NotificationCompat.PRIORITY_HIGH)
195+
.setCategory(Notification.CATEGORY_CALL)
193196
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
194-
.setContentIntent(pendingIntent);
197+
;
195198

196199
// build notification large icon
197200
Resources res = context.getResources();

android/src/main/java/com/hoxfon/react/RNTwilioVoice/TwilioVoiceModule.java

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ public void onHostResume() {
163163
if (action.equals(Constants.ACTION_ACCEPT) && currentCallInviteIntent == existingCallInviteIntent) {
164164
return;
165165
}
166+
167+
if (action.equals(Constants.ACTION_INCOMING_CALL_NOTIFICATION) && currentCallInviteIntent == existingCallInviteIntent) {
168+
return;
169+
}
170+
166171
existingCallInviteIntent = currentCallInviteIntent;
167172
handleStartActivityIntent(intent);
168173
}
@@ -469,16 +474,27 @@ private void handleStartActivityIntent(Intent intent) {
469474
activeCallInvite = intent.getParcelableExtra(Constants.INCOMING_CALL_INVITE);
470475

471476
switch (action) {
477+
case Constants.ACTION_INCOMING_CALL_NOTIFICATION:
478+
if (BuildConfig.DEBUG) {
479+
Log.d(TAG, "ACTION_INCOMING_CALL_NOTIFICATION handleStartActivityIntent");
480+
}
481+
WritableMap params = Arguments.createMap();
482+
params.putString(Constants.CALL_SID, activeCallInvite.getCallSid());
483+
params.putString(Constants.CALL_FROM, activeCallInvite.getFrom());
484+
params.putString(Constants.CALL_TO, activeCallInvite.getTo());
485+
eventManager.sendEvent(EVENT_DEVICE_DID_RECEIVE_INCOMING, params);
486+
break;
487+
472488
case Constants.ACTION_MISSED_CALL:
473489
if (BuildConfig.DEBUG) {
474-
Log.d(TAG, "ACTION_MISSED_CALL handleCallInviteIntent");
490+
Log.d(TAG, "ACTION_MISSED_CALL handleStartActivityIntent");
475491
}
476492
removeMissedCalls();
477493
break;
478494

479495
case Constants.ACTION_CLEAR_MISSED_CALLS_COUNT:
480496
if (BuildConfig.DEBUG) {
481-
Log.d(TAG, "ACTION_CLEAR_MISSED_CALLS_COUNT handleCallInviteIntent");
497+
Log.d(TAG, "ACTION_CLEAR_MISSED_CALLS_COUNT handleStartActivityIntent");
482498
}
483499
removeMissedCalls();
484500
break;
@@ -511,24 +527,11 @@ private void handleCallInviteNotification() {
511527
}
512528
SoundPoolManager.getInstance(getReactApplicationContext()).playRinging();
513529

514-
// TODO refactor this old block as it doesn't work in Android SDK 29
515-
// not called when phone is locked
516-
// the window flags can only be changed by the main View that, on creation of activity?
517-
// if (getReactApplicationContext().getCurrentActivity() != null) {
518-
// Window window = getReactApplicationContext().getCurrentActivity().getWindow();
519-
// window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
520-
// | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
521-
// );
522-
// }
523-
// if the app is VISIBLE, send a call received event
524-
int appImportance = callNotificationManager.getApplicationImportance(getReactApplicationContext());
525-
if (appImportance <= RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
526-
WritableMap params = Arguments.createMap();
527-
params.putString(Constants.CALL_SID, activeCallInvite.getCallSid());
528-
params.putString(Constants.CALL_FROM, activeCallInvite.getFrom());
529-
params.putString(Constants.CALL_TO, activeCallInvite.getTo());
530-
eventManager.sendEvent(EVENT_DEVICE_DID_RECEIVE_INCOMING, params);
531-
}
530+
WritableMap params = Arguments.createMap();
531+
params.putString(Constants.CALL_SID, activeCallInvite.getCallSid());
532+
params.putString(Constants.CALL_FROM, activeCallInvite.getFrom());
533+
params.putString(Constants.CALL_TO, activeCallInvite.getTo());
534+
eventManager.sendEvent(EVENT_DEVICE_DID_RECEIVE_INCOMING, params);
532535
}
533536

534537
private void handleCancelCall(Intent intent) {

android/src/main/java/com/hoxfon/react/RNTwilioVoice/fcm/VoiceFirebaseMessagingService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ private void handleInvite(CallInvite callInvite, int notificationId) {
137137
}
138138

139139
private void handleCancelledCallInvite(CancelledCallInvite cancelledCallInvite, CallException callException) {
140-
Log.e(TAG, "handleCancelledCallInvite exception: " + callException.getMessage());
141140
Intent intent = new Intent(this, IncomingCallNotificationService.class);
142141
intent.setAction(Constants.ACTION_CANCEL_CALL);
143142
intent.putExtra(Constants.CANCELLED_CALL_INVITE, cancelledCallInvite);

0 commit comments

Comments
 (0)