-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- notification for incoming call when the app is in the background
- Loading branch information
1 parent
64c59fa
commit 98c7b0f
Showing
9 changed files
with
333 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
android/src/main/java/com/hoxfon/react/RNTwilioVoice/Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"; | ||
|
||
} |
229 changes: 229 additions & 0 deletions
229
android/src/main/java/com/hoxfon/react/RNTwilioVoice/IncomingCallNotificationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
Oops, something went wrong.