From ccfc55e9b6a581849bf31c99f7d1f681ec5e4455 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 7 Apr 2020 18:50:39 +0200 Subject: [PATCH] show proper notification on incoming call --- src/main/AndroidManifest.xml | 3 +- .../services/NotificationService.java | 99 ++++++++++++++++--- .../services/XmppConnectionService.java | 6 ++ .../conversations/ui/RtpSessionActivity.java | 19 +++- .../xmpp/jingle/JingleRtpConnection.java | 12 +-- src/main/res/values/strings.xml | 4 + 6 files changed, 119 insertions(+), 24 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 66c4f5bcb..dac289003 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -10,7 +10,6 @@ - @@ -33,6 +32,8 @@ + + > notifications = new LinkedHashMap<>(); private final HashMap mBacklogMessageCounter = new HashMap<>(); @@ -100,6 +103,14 @@ public class NotificationService { return Pattern.compile("(?<=(^|\\s))" + Pattern.quote(nick) + "(?=\\s|$|\\p{Punct})"); } + private static boolean isImageMessage(Message message) { + return message.getType() != Message.TYPE_TEXT + && message.getTransferable() == null + && !message.isDeleted() + && message.getEncryption() != Message.ENCRYPTION_PGP + && message.getFileParams().height > 0; + } + @RequiresApi(api = Build.VERSION_CODES.O) void initializeChannels() { final Context c = mXmppConnectionService; @@ -112,6 +123,7 @@ public class NotificationService { notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("status", c.getString(R.string.notification_group_status_information))); notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("chats", c.getString(R.string.notification_group_messages))); + notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("calls", c.getString(R.string.notification_group_calls))); final NotificationChannel foregroundServiceChannel = new NotificationChannel("foreground", c.getString(R.string.foreground_service_channel_name), NotificationManager.IMPORTANCE_MIN); @@ -141,6 +153,20 @@ public class NotificationService { exportChannel.setGroup("status"); notificationManager.createNotificationChannel(exportChannel); + final NotificationChannel incomingCallsChannel = new NotificationChannel("incoming_calls", + c.getString(R.string.incoming_calls_channel_name), + NotificationManager.IMPORTANCE_HIGH); + incomingCallsChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build()); + incomingCallsChannel.setShowBadge(false); + incomingCallsChannel.setLightColor(LED_COLOR); + incomingCallsChannel.enableLights(true); + incomingCallsChannel.setGroup("calls"); + notificationManager.createNotificationChannel(incomingCallsChannel); + + final NotificationChannel messagesChannel = new NotificationChannel("messages", c.getString(R.string.messages_channel_name), NotificationManager.IMPORTANCE_HIGH); @@ -300,6 +326,53 @@ public class NotificationService { } } + public void showIncomingCallNotification(AbstractJingleConnection.Id id) { + final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString()); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString()); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); + fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + final PendingIntent pendingIntent = PendingIntent.getActivity(mXmppConnectionService, 101, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT); + final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "incoming_calls"); + builder.setSmallIcon(R.drawable.ic_call_white_24dp); + builder.setContentTitle(mXmppConnectionService.getString(R.string.rtp_state_incoming_call)); + builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName()); + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + builder.setPriority(NotificationCompat.PRIORITY_HIGH); + builder.setCategory(NotificationCompat.CATEGORY_CALL); + builder.setFullScreenIntent(createPendingRtpSession(id, Intent.ACTION_VIEW, 101), true); + builder.setOngoing(true); + builder.addAction(new NotificationCompat.Action.Builder( + R.drawable.ic_call_end_white_48dp, + mXmppConnectionService.getString(R.string.dismiss_call), + createDismissCall(id.sessionId, 102)) + .build()); + builder.addAction(new NotificationCompat.Action.Builder( + R.drawable.ic_call_white_24dp, + mXmppConnectionService.getString(R.string.answer_call), + createPendingRtpSession(id, RtpSessionActivity.ACTION_ACCEPT, 103)) + .build()); + final Notification notification = builder.build(); + notification.flags = notification.flags | Notification.FLAG_INSISTENT; + notify(INCOMING_CALL_NOTIFICATION_ID, builder.build()); + } + + private PendingIntent createPendingRtpSession(final AbstractJingleConnection.Id id, final String action, final int requestCode) { + final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class); + fullScreenIntent.setAction(action); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString()); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString()); + fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); + fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + return PendingIntent.getActivity(mXmppConnectionService, requestCode, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + public void cancelIncomingCallNotification() { + cancel(INCOMING_CALL_NOTIFICATION_ID); + } + private void pushNow(final Message message) { mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { @@ -467,7 +540,7 @@ public class NotificationService { private Builder buildMultipleConversation(final boolean notify, final boolean quietHours) { final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages")); final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - style.setBigContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations,notifications.size())); + style.setBigContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations, notifications.size())); final StringBuilder names = new StringBuilder(); Conversation conversation = null; for (final ArrayList messages : notifications.values()) { @@ -652,7 +725,7 @@ public class NotificationService { return builder.build(); } - private void modifyForTextOnly(final Builder builder, final ArrayList messages) { + private void modifyForTextOnly(final Builder builder, final ArrayList messages) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { final Conversation conversation = (Conversation) messages.get(0).getConversation(); final Person.Builder meBuilder = new Person.Builder().setName(mXmppConnectionService.getString(R.string.me)); @@ -668,7 +741,7 @@ public class NotificationService { for (Message message : messages) { final Person sender = message.getStatus() == Message.STATUS_RECEIVED ? getPerson(message) : null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isImageMessage(message)) { - final Uri dataUri = FileBackend.getMediaUri(mXmppConnectionService,mXmppConnectionService.getFileBackend().getFile(message)); + final Uri dataUri = FileBackend.getMediaUri(mXmppConnectionService, mXmppConnectionService.getFileBackend().getFile(message)); NotificationCompat.MessagingStyle.Message imageMessage = new NotificationCompat.MessagingStyle.Message(UIHelper.getMessagePreview(mXmppConnectionService, message).first, message.getTimeSent(), sender); if (dataUri != null) { imageMessage.setData(message.getMimeType(), dataUri); @@ -683,7 +756,7 @@ public class NotificationService { } else { if (messages.get(0).getConversation().getMode() == Conversation.MODE_SINGLE) { builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages))); - final CharSequence preview = UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size()-1)).first; + final CharSequence preview = UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size() - 1)).first; builder.setContentText(preview); builder.setTicker(preview); builder.setNumber(messages.size()); @@ -726,14 +799,6 @@ public class NotificationService { return image; } - private static boolean isImageMessage(Message message) { - return message.getType() != Message.TYPE_TEXT - && message.getTransferable() == null - && !message.isDeleted() - && message.getEncryption() != Message.ENCRYPTION_PGP - && message.getFileParams().height > 0; - } - private Message getFirstDownloadableMessage(final Iterable messages) { for (final Message message : messages) { if (message.getTransferable() != null || (message.getType() == Message.TYPE_TEXT && message.treatAsDownloadable())) { @@ -834,6 +899,14 @@ public class NotificationService { return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT); } + private PendingIntent createDismissCall(String sessionId, int requestCode) { + final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); + intent.setAction(XmppConnectionService.ACTION_DISMISS_CALL); + intent.setPackage(mXmppConnectionService.getPackageName()); + intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); + return PendingIntent.getService(mXmppConnectionService, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); + } + private PendingIntent createSnoozeIntent(Conversation conversation) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_SNOOZE); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5951b7116..1ac96354e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -107,6 +107,7 @@ import eu.siacs.conversations.parser.PresenceParser; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.ui.ChooseAccountForProfilePictureActivity; +import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; @@ -165,6 +166,7 @@ public class XmppConnectionService extends Service { public static final String ACTION_IDLE_PING = "idle_ping"; public static final String ACTION_FCM_TOKEN_REFRESH = "fcm_token_refresh"; public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received"; + public static final String ACTION_DISMISS_CALL = "dismiss_call"; private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE"; private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; @@ -638,6 +640,10 @@ public class XmppConnectionService extends Service { } }); break; + case ACTION_DISMISS_CALL: + final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID); + Log.d(Config.LOGTAG,"received intent to dismiss call with session id "+sessionId); + break; case ACTION_DISMISS_ERROR_NOTIFICATIONS: dismissErrorNotifications(); break; diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 32a0d03ef..2464f81b7 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -25,6 +25,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe public static final String EXTRA_WITH = "with"; public static final String EXTRA_SESSION_ID = "session_id"; + public static final String ACTION_ACCEPT = "accept"; + private WeakReference rtpConnectionReference; private ActivityRtpSessionBinding binding; @@ -32,6 +34,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) + ; + Log.d(Config.LOGTAG, "RtpSessionActivity.onCreate()"); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session); this.binding.rejectCall.setOnClickListener(this::rejectCall); this.binding.endCall.setOnClickListener(this::endCall); @@ -41,7 +49,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe @Override public void onStart() { super.onStart(); - Log.d(Config.LOGTAG,"RtpSessionActivity.onStart()"); + Log.d(Config.LOGTAG, "RtpSessionActivity.onStart()"); } private void endCall(View view) { @@ -78,8 +86,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe this.rtpConnectionReference = reference; binding.with.setText(getWith().getDisplayName()); final RtpEndUserState currentState = requireRtpConnection().getEndUserState(); + final String action = intent.getAction(); updateStateDisplay(currentState); updateButtonConfiguration(currentState); + if (ACTION_ACCEPT.equals(action)) { + Log.d(Config.LOGTAG,"intent action was accept"); + requireRtpConnection().acceptCall(); + } } } @@ -137,12 +150,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe public void onJingleRtpConnectionUpdate(Account account, Jid with, RtpEndUserState state) { final AbstractJingleConnection.Id id = requireRtpConnection().getId(); if (account == id.account && id.with.equals(with)) { - runOnUiThread(()->{ + runOnUiThread(() -> { updateStateDisplay(state); updateButtonConfiguration(state); }); } else { - Log.d(Config.LOGTAG,"received update for other rtp session"); + Log.d(Config.LOGTAG, "received update for other rtp session"); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 3d346dfd5..56991635f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -228,12 +228,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private void startRinging() { Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing"); - final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class); - intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - xmppConnectionService.startActivity(intent); + xmppConnectionService.getNotificationService().showIncomingCallNotification(id); } private void receiveProceed(final Jid from, final Element proceed) { @@ -342,6 +337,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web public void rejectCall() { Log.d(Config.LOGTAG, "todo rejecting call"); + xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); } public void endCall() { @@ -359,6 +355,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private void acceptCallFromProposed() { transitionOrThrow(State.PROCEED); + xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); final MessagePacket messagePacket = new MessagePacket(); messagePacket.setTo(id.with); //Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916 @@ -368,7 +365,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } private void acceptCallFromSessionInitialized() { - + xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); + throw new IllegalStateException("accepting from this state has not been implemented yet"); } private synchronized boolean isInState(State... state) { diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3d4c35661..e7d20d98f 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -748,7 +748,9 @@ Connectivity Problems This notification category is used to display a notification in case there is a problem connecting to an account. Messages + Calls Messages + Incoming calls Silent messages This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period). Notification Settings @@ -890,6 +892,8 @@ Connected Accepting call Ending call + Answer + Dismiss View %1$d Participant View %1$d Participants