show notification during ongoing call

This commit is contained in:
Daniel Gultsch 2020-04-10 15:19:56 +02:00
parent 2e8b91665b
commit d19b5e0634
6 changed files with 88 additions and 13 deletions

View file

@ -76,6 +76,7 @@ public class NotificationService {
private static final int NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 2; 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 ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6;
private static final int INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8; 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 XmppConnectionService mXmppConnectionService;
private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>(); private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>(); private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>();
@ -166,6 +167,13 @@ public class NotificationService {
incomingCallsChannel.setGroup("calls"); incomingCallsChannel.setGroup("calls");
notificationManager.createNotificationChannel(incomingCallsChannel); 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", final NotificationChannel messagesChannel = new NotificationChannel("messages",
c.getString(R.string.messages_channel_name), c.getString(R.string.messages_channel_name),
@ -333,7 +341,6 @@ public class NotificationService {
fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 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"); final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "incoming_calls");
builder.setSmallIcon(R.drawable.ic_call_white_24dp); builder.setSmallIcon(R.drawable.ic_call_white_24dp);
builder.setContentTitle(mXmppConnectionService.getString(R.string.rtp_state_incoming_call)); builder.setContentTitle(mXmppConnectionService.getString(R.string.rtp_state_incoming_call));
@ -346,7 +353,7 @@ public class NotificationService {
builder.addAction(new NotificationCompat.Action.Builder( builder.addAction(new NotificationCompat.Action.Builder(
R.drawable.ic_call_end_white_48dp, R.drawable.ic_call_end_white_48dp,
mXmppConnectionService.getString(R.string.dismiss_call), mXmppConnectionService.getString(R.string.dismiss_call),
createDismissCall(id.sessionId, 102)) createCallAction(id.sessionId, XmppConnectionService.ACTION_DISMISS_CALL, 102))
.build()); .build());
builder.addAction(new NotificationCompat.Action.Builder( builder.addAction(new NotificationCompat.Action.Builder(
R.drawable.ic_call_white_24dp, R.drawable.ic_call_white_24dp,
@ -358,6 +365,26 @@ public class NotificationService {
notify(INCOMING_CALL_NOTIFICATION_ID, builder.build()); 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) { private PendingIntent createPendingRtpSession(final AbstractJingleConnection.Id id, final String action, final int requestCode) {
final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class); final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class);
fullScreenIntent.setAction(action); fullScreenIntent.setAction(action);
@ -373,6 +400,10 @@ public class NotificationService {
cancel(INCOMING_CALL_NOTIFICATION_ID); cancel(INCOMING_CALL_NOTIFICATION_ID);
} }
public void cancelOngoingCallNotification() {
cancel(ONGOING_CALL_NOTIFICATION_ID);
}
private void pushNow(final Message message) { private void pushNow(final Message message) {
mXmppConnectionService.updateUnreadCountBadge(); mXmppConnectionService.updateUnreadCountBadge();
if (!notify(message)) { if (!notify(message)) {
@ -899,9 +930,9 @@ public class NotificationService {
return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT); 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); final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_DISMISS_CALL); intent.setAction(action);
intent.setPackage(mXmppConnectionService.getPackageName()); intent.setPackage(mXmppConnectionService.getPackageName());
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId);
return PendingIntent.getService(mXmppConnectionService, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getService(mXmppConnectionService, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

View file

@ -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_TOKEN_REFRESH = "fcm_token_refresh";
public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received"; 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_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 ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
@ -640,10 +641,17 @@ public class XmppConnectionService extends Service {
} }
}); });
break; break;
case ACTION_DISMISS_CALL: case ACTION_DISMISS_CALL: {
final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID); 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); 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; break;
case ACTION_DISMISS_ERROR_NOTIFICATIONS: case ACTION_DISMISS_ERROR_NOTIFICATIONS:
dismissErrorNotifications(); dismissErrorNotifications();

View file

@ -237,6 +237,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
finish(); finish();
return; return;
} }
if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(requireRtpConnection().getState())) {
putScreenInCallMode();
}
binding.with.setText(getWith().getDisplayName()); binding.with.setText(getWith().getDisplayName());
updateStateDisplay(currentState); updateStateDisplay(currentState);
updateButtonConfiguration(currentState); updateButtonConfiguration(currentState);

View file

@ -1,21 +1,19 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.security.SecureRandom;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.AbstractConnectionManager;
@ -271,7 +269,9 @@ public class JingleConnectionManager extends AbstractConnectionManager {
} }
static String nextRandomId() { 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) { 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) { public void failProceed(Account account, final Jid with, String sessionId) {
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId); final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId);
final AbstractJingleConnection existingJingleConnection = connections.get(id); final AbstractJingleConnection existingJingleConnection = connections.get(id);
@ -374,7 +384,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
} }
public static RtpSessionProposal of(Account account, Jid with) { public static RtpSessionProposal of(Account account, Jid with) {
return new RtpSessionProposal(account, with, UUID.randomUUID().toString()); return new RtpSessionProposal(account, with, nextRandomId());
} }
@Override @Override

View file

@ -74,6 +74,13 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
VALID_TRANSITIONS = transitionBuilder.build(); VALID_TRANSITIONS = transitionBuilder.build();
} }
public static final List<State> 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 WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>(); private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>();
private State state = State.NULL; private State state = State.NULL;
@ -727,6 +734,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.state = target; this.state = target;
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into " + target); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into " + target);
updateEndUserState(); updateEndUserState();
updateOngoingCallNotification();
return true; return true;
} else { } else {
return false; return false;
@ -759,6 +767,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState()); 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) { private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) {
if (id.account.getXmppConnection().getFeatures().extendedServiceDiscovery()) { if (id.account.getXmppConnection().getFeatures().extendedServiceDiscovery()) {
final IqPacket request = new IqPacket(IqPacket.TYPE.GET); 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 { private interface OnIceServersDiscovered {
void onIceServersDiscovered(List<PeerConnection.IceServer> iceServers); void onIceServersDiscovered(List<PeerConnection.IceServer> iceServers);
} }

View file

@ -751,6 +751,7 @@
<string name="notification_group_calls">Calls</string> <string name="notification_group_calls">Calls</string>
<string name="messages_channel_name">Messages</string> <string name="messages_channel_name">Messages</string>
<string name="incoming_calls_channel_name">Incoming calls</string> <string name="incoming_calls_channel_name">Incoming calls</string>
<string name="ongoing_calls_channel_name">Ongoing calls</string>
<string name="silent_messages_channel_name">Silent messages</string> <string name="silent_messages_channel_name">Silent messages</string>
<string name="silent_messages_channel_description">This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string> <string name="silent_messages_channel_description">This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string>
<string name="pref_more_notification_settings">Notification Settings</string> <string name="pref_more_notification_settings">Notification Settings</string>
@ -899,6 +900,8 @@
<string name="rtp_state_declined_or_busy">Busy</string> <string name="rtp_state_declined_or_busy">Busy</string>
<string name="rtp_state_connectivity_error">Unable to connect call</string> <string name="rtp_state_connectivity_error">Unable to connect call</string>
<string name="rtp_state_application_failure">Application failure</string> <string name="rtp_state_application_failure">Application failure</string>
<string name="hang_up">Hang up</string>
<string name="ongoing_call">Ongoing call</string>
<plurals name="view_users"> <plurals name="view_users">
<item quantity="one">View %1$d Participant</item> <item quantity="one">View %1$d Participant</item>
<item quantity="other">View %1$d Participants</item> <item quantity="other">View %1$d Participants</item>