diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index da7028f35..bd8e0dabb 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -76,6 +76,7 @@ public class NotificationService { private static final int NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 2; private static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6; private static final int INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8; + private static final int ONGOING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 10; private final XmppConnectionService mXmppConnectionService; private final LinkedHashMap> notifications = new LinkedHashMap<>(); private final HashMap mBacklogMessageCounter = new HashMap<>(); @@ -166,6 +167,13 @@ public class NotificationService { incomingCallsChannel.setGroup("calls"); notificationManager.createNotificationChannel(incomingCallsChannel); + final NotificationChannel ongoingCallsChannel = new NotificationChannel("ongoing_calls", + c.getString(R.string.ongoing_calls_channel_name), + NotificationManager.IMPORTANCE_LOW); + ongoingCallsChannel.setShowBadge(false); + ongoingCallsChannel.setGroup("calls"); + notificationManager.createNotificationChannel(ongoingCallsChannel); + final NotificationChannel messagesChannel = new NotificationChannel("messages", c.getString(R.string.messages_channel_name), @@ -333,7 +341,6 @@ public class NotificationService { 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)); @@ -346,7 +353,7 @@ public class NotificationService { builder.addAction(new NotificationCompat.Action.Builder( R.drawable.ic_call_end_white_48dp, mXmppConnectionService.getString(R.string.dismiss_call), - createDismissCall(id.sessionId, 102)) + createCallAction(id.sessionId, XmppConnectionService.ACTION_DISMISS_CALL, 102)) .build()); builder.addAction(new NotificationCompat.Action.Builder( R.drawable.ic_call_white_24dp, @@ -358,6 +365,26 @@ public class NotificationService { notify(INCOMING_CALL_NOTIFICATION_ID, builder.build()); } + public void showOngoingCallNotification(final AbstractJingleConnection.Id id) { + final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "ongoing_calls"); + builder.setSmallIcon(R.drawable.ic_call_white_24dp); + builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_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.setContentIntent(createPendingRtpSession(id, Intent.ACTION_VIEW, 101)); + builder.setOngoing(true); + builder.addAction(new NotificationCompat.Action.Builder( + R.drawable.ic_call_end_white_48dp, + mXmppConnectionService.getString(R.string.hang_up), + createCallAction(id.sessionId, XmppConnectionService.ACTION_END_CALL, 104)) + .build()); + final Notification notification = builder.build(); + notification.flags = notification.flags | Notification.FLAG_INSISTENT; + notify(ONGOING_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); @@ -373,6 +400,10 @@ public class NotificationService { cancel(INCOMING_CALL_NOTIFICATION_ID); } + public void cancelOngoingCallNotification() { + cancel(ONGOING_CALL_NOTIFICATION_ID); + } + private void pushNow(final Message message) { mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { @@ -899,9 +930,9 @@ public class NotificationService { return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT); } - private PendingIntent createDismissCall(String sessionId, int requestCode) { + private PendingIntent createCallAction(String sessionId, final String action, int requestCode) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); - intent.setAction(XmppConnectionService.ACTION_DISMISS_CALL); + intent.setAction(action); intent.setPackage(mXmppConnectionService.getPackageName()); intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); return PendingIntent.getService(mXmppConnectionService, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 14e94951f..ed2e5ba5a 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -167,6 +167,7 @@ public class XmppConnectionService extends Service { 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"; + public static final String ACTION_END_CALL = "end_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"; @@ -640,10 +641,17 @@ public class XmppConnectionService extends Service { } }); break; - case ACTION_DISMISS_CALL: + 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); + Log.d(Config.LOGTAG, "received intent to dismiss call with session id " + sessionId); mJingleConnectionManager.rejectRtpSession(sessionId); + } + break; + case ACTION_END_CALL: { + final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID); + Log.d(Config.LOGTAG, "received intent to end call with session id " + sessionId); + mJingleConnectionManager.endRtpSession(sessionId); + } break; case ACTION_DISMISS_ERROR_NOTIFICATIONS: dismissErrorNotifications(); @@ -3978,7 +3986,7 @@ public class XmppConnectionService extends Service { } public void notifyJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) { - for(OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) { + for (OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) { listener.onJingleRtpConnectionUpdate(account, with, sessionId, state); } } diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index ba3fae92e..0aaf1ef2a 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -237,6 +237,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe finish(); return; } + if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(requireRtpConnection().getState())) { + putScreenInCallMode(); + } binding.with.setText(getWith().getDisplayName()); updateStateDisplay(currentState); updateButtonConfiguration(currentState); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index f5fb9c1dc..f6f1abd91 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,21 +1,19 @@ package eu.siacs.conversations.xmpp.jingle; +import android.util.Base64; import android.util.Log; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import java.lang.ref.WeakReference; +import java.security.SecureRandom; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.services.AbstractConnectionManager; @@ -271,7 +269,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } static String nextRandomId() { - return UUID.randomUUID().toString(); + final byte[] id = new byte[16]; + new SecureRandom().nextBytes(id); + return Base64.encodeToString(id, Base64.NO_WRAP | Base64.NO_PADDING); } public void deliverIbbPacket(Account account, IqPacket packet) { @@ -354,6 +354,16 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } + public void endRtpSession(final String sessionId) { + for (final AbstractJingleConnection connection : this.connections.values()) { + if (connection.getId().sessionId.equals(sessionId)) { + if (connection instanceof JingleRtpConnection) { + ((JingleRtpConnection) connection).endCall(); + } + } + } + } + public void failProceed(Account account, final Jid with, String sessionId) { final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId); final AbstractJingleConnection existingJingleConnection = connections.get(id); @@ -374,7 +384,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public static RtpSessionProposal of(Account account, Jid with) { - return new RtpSessionProposal(account, with, UUID.randomUUID().toString()); + return new RtpSessionProposal(account, with, nextRandomId()); } @Override 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 e35a713c7..6cc3f69a7 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -74,6 +74,13 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web VALID_TRANSITIONS = transitionBuilder.build(); } + public static final List STATES_SHOWING_ONGOING_CALL = Arrays.asList( + State.PROCEED, + State.SESSION_INITIALIZED, + State.SESSION_INITIALIZED_PRE_APPROVED, + State.SESSION_ACCEPTED + ); + private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); private final ArrayDeque pendingIceCandidates = new ArrayDeque<>(); private State state = State.NULL; @@ -727,6 +734,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web this.state = target; Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into " + target); updateEndUserState(); + updateOngoingCallNotification(); return true; } else { return false; @@ -759,6 +767,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState()); } + private void updateOngoingCallNotification() { + if (STATES_SHOWING_ONGOING_CALL.contains(this.state)) { + xmppConnectionService.getNotificationService().showOngoingCallNotification(id); + } else { + xmppConnectionService.getNotificationService().cancelOngoingCallNotification(); + } + } + private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) { if (id.account.getXmppConnection().getFeatures().extendedServiceDiscovery()) { final IqPacket request = new IqPacket(IqPacket.TYPE.GET); @@ -815,6 +831,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } } + public State getState() { + return this.state; + } + private interface OnIceServersDiscovered { void onIceServersDiscovered(List iceServers); } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 39b9a3337..7bf491ed5 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -751,6 +751,7 @@ Calls Messages Incoming calls + Ongoing 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 @@ -899,6 +900,8 @@ Busy Unable to connect call Application failure + Hang up + Ongoing call View %1$d Participant View %1$d Participants