make retract jingle messages work

This commit is contained in:
Daniel Gultsch 2020-04-08 09:42:06 +02:00
parent e2f1cec2e5
commit 7909a72d43
8 changed files with 187 additions and 40 deletions

View File

@ -244,4 +244,13 @@ public class MessageGenerator extends AbstractGenerator {
packet.addChild("request", "urn:xmpp:receipts");
return packet;
}
public MessagePacket sessionRetract(final JingleConnectionManager.RtpSessionProposal proposal) {
final MessagePacket packet = new MessagePacket();
packet.setTo(proposal.with);
final Element propose = packet.addChild("retract", Namespace.JINGLE_MESSAGE);
propose.setAttribute("id", proposal.sessionId);
propose.addChild("description", Namespace.JINGLE_APPS_RTP);
return packet;
}
}

View File

@ -351,7 +351,7 @@ public class NotificationService {
builder.addAction(new NotificationCompat.Action.Builder(
R.drawable.ic_call_white_24dp,
mXmppConnectionService.getString(R.string.answer_call),
createPendingRtpSession(id, RtpSessionActivity.ACTION_ACCEPT, 103))
createPendingRtpSession(id, RtpSessionActivity.ACTION_ACCEPT_CALL, 103))
.build());
final Notification notification = builder.build();
notification.flags = notification.flags | Notification.FLAG_INSISTENT;

View File

@ -3977,9 +3977,9 @@ public class XmppConnectionService extends Service {
}
}
public void notifyJingleRtpConnectionUpdate(final Account account, final Jid with, final RtpEndUserState state) {
public void notifyJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) {
for(OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) {
listener.onJingleRtpConnectionUpdate(account, with, state);
listener.onJingleRtpConnectionUpdate(account, with, sessionId, state);
}
}
@ -4661,7 +4661,7 @@ public class XmppConnectionService extends Service {
}
public interface OnJingleRtpConnectionUpdate {
void onJingleRtpConnectionUpdate(final Account account, final Jid with, final RtpEndUserState state);
void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state);
}
public interface OnAccountUpdate {

View File

@ -1243,7 +1243,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
private void triggerRtpSession() {
final Contact contact = conversation.getContact();
activity.xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(conversation.getAccount(), contact);
final Intent intent = new Intent(activity, RtpSessionActivity.class);
intent.setAction(RtpSessionActivity.ACTION_MAKE_VOICE_CALL);
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, contact.getAccount().getJid().toEscapedString());
intent.putExtra(RtpSessionActivity.EXTRA_WITH, contact.getJid().asBareJid().toEscapedString());
startActivity(intent);
}
private void handleAttachmentSelection(MenuItem item) {

View File

@ -8,6 +8,7 @@ import android.view.View;
import android.view.WindowManager;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@ -20,12 +21,16 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import rocks.xmpp.addr.Jid;
import static java.util.Arrays.asList;
public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
public static final String EXTRA_WITH = "with";
public static final String EXTRA_SESSION_ID = "session_id";
public static final String ACTION_ACCEPT = "accept";
public static final String ACTION_ACCEPT_CALL = "action_accept_call";
public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_call";
public static final String ACTION_MAKE_VIDEO_CALL = "action_make_video_call";
private WeakReference<JingleRtpConnection> rtpConnectionReference;
@ -53,8 +58,16 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void endCall(View view) {
if (this.rtpConnectionReference == null) {
final Intent intent = getIntent();
final Account account = extractAccount(intent);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
finish();
} else {
requireRtpConnection().endCall();
}
}
private void rejectCall(View view) {
requireRtpConnection().rejectCall();
@ -73,8 +86,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override
public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
if (ACTION_ACCEPT.equals(intent.getAction())) {
Log.d(Config.LOGTAG,"accepting through onNewIntent()");
if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
Log.d(Config.LOGTAG, "accepting through onNewIntent()");
requireRtpConnection().acceptCall();
}
}
@ -83,26 +96,48 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
void onBackendConnected() {
final Intent intent = getIntent();
final Account account = extractAccount(intent);
final String with = intent.getStringExtra(EXTRA_WITH);
final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
if (with != null && sessionId != null) {
if (sessionId != null) {
initializeActivityWithRunningRapSession(account, with, sessionId);
if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
Log.d(Config.LOGTAG, "intent action was accept");
requireRtpConnection().acceptCall();
}
} else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(intent.getAction())) {
xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with);
binding.with.setText(account.getRoster().getContact(with).getDisplayName());
}
}
private void initializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
.findJingleRtpConnection(account, Jid.ofEscaped(with), sessionId);
.findJingleRtpConnection(account, with, sessionId);
if (reference == null || reference.get() == null) {
finish();
return;
}
this.rtpConnectionReference = reference;
binding.with.setText(getWith().getDisplayName());
final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
final String action = intent.getAction();
if (currentState == RtpEndUserState.ENDED) {
finish();
return;
}
binding.with.setText(getWith().getDisplayName());
updateStateDisplay(currentState);
updateButtonConfiguration(currentState);
if (ACTION_ACCEPT.equals(action)) {
Log.d(Config.LOGTAG,"intent action was accept");
requireRtpConnection().acceptCall();
}
}
private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
runOnUiThread(() -> {
initializeActivityWithRunningRapSession(account, with, sessionId);
});
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
intent.putExtra(EXTRA_WITH, with.toEscapedString());
intent.putExtra(EXTRA_SESSION_ID, sessionId);
setIntent(intent);
}
private void updateStateDisplay(final RtpEndUserState state) {
@ -122,6 +157,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
case ENDING_CALL:
binding.status.setText(R.string.rtp_state_ending_call);
break;
case FINDING_DEVICE:
binding.status.setText(R.string.rtp_state_finding_device);
break;
case RINGING:
binding.status.setText(R.string.rtp_state_ringing);
}
}
@ -156,9 +196,19 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
@Override
public void onJingleRtpConnectionUpdate(Account account, Jid with, RtpEndUserState state) {
public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
Log.d(Config.LOGTAG,"onJingleRtpConnectionUpdate("+state+")");
if (with.isBareJid()) {
updateRtpSessionProposalState(with, state);
return;
}
if (this.rtpConnectionReference == null) {
//this happens when going from proposed session to actual session
reInitializeActivityWithRunningRapSession(account, with, sessionId);
return;
}
final AbstractJingleConnection.Id id = requireRtpConnection().getId();
if (account == id.account && id.with.equals(with)) {
if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
if (state == RtpEndUserState.ENDED) {
finish();
return;
@ -170,6 +220,19 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} else {
Log.d(Config.LOGTAG, "received update for other rtp session");
}
}
private void updateRtpSessionProposalState(Jid with, RtpEndUserState state) {
final Intent intent = getIntent();
final String intentExtraWith = intent == null ? null : intent.getStringExtra(EXTRA_WITH);
if (intentExtraWith == null) {
return;
}
if (Jid.ofEscaped(intentExtraWith).asBareJid().equals(with)) {
runOnUiThread(() -> {
updateStateDisplay(state);
updateButtonConfiguration(state);
});
}
}
}

View File

@ -188,12 +188,52 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
public void proposeJingleRtpSession(final Account account, final Contact contact) {
final RtpSessionProposal proposal = RtpSessionProposal.of(account, contact.getJid().asBareJid());
public void retractSessionProposal(final Account account, final Jid with) {
synchronized (this.rtpSessionProposals) {
RtpSessionProposal matchingProposal = null;
for (RtpSessionProposal proposal : this.rtpSessionProposals.keySet()) {
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
matchingProposal = proposal;
break;
}
}
if (matchingProposal != null) {
this.rtpSessionProposals.remove(matchingProposal);
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(matchingProposal);
Log.d(Config.LOGTAG, messagePacket.toString());
mXmppConnectionService.sendMessagePacket(account, messagePacket);
}
}
}
public void proposeJingleRtpSession(final Account account, final Jid with) {
synchronized (this.rtpSessionProposals) {
for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {
RtpSessionProposal proposal = entry.getKey();
if (proposal.account == account && with.asBareJid().equals(proposal.with)) {
final DeviceDiscoveryState preexistingState = entry.getValue();
if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
account,
with,
proposal.sessionId,
preexistingState.toEndUserState()
);
return;
}
}
}
final RtpSessionProposal proposal = RtpSessionProposal.of(account, with.asBareJid());
this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
account,
proposal.with,
proposal.sessionId,
RtpEndUserState.FINDING_DEVICE
);
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
Log.d(Config.LOGTAG,messagePacket.toString());
Log.d(Config.LOGTAG, messagePacket.toString());
mXmppConnectionService.sendMessagePacket(account, messagePacket);
}
}
@ -255,24 +295,25 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public void updateProposedSessionDiscovered(Account account, Jid from, String sessionId, final DeviceDiscoveryState target) {
final RtpSessionProposal sessionProposal = new RtpSessionProposal(account,from.asBareJid(),sessionId);
final RtpSessionProposal sessionProposal = new RtpSessionProposal(account, from.asBareJid(), sessionId);
synchronized (this.rtpSessionProposals) {
final DeviceDiscoveryState currentState = rtpSessionProposals.get(sessionProposal);
if (currentState == null) {
Log.d(Config.LOGTAG,"unable to find session proposal for session id "+sessionId);
Log.d(Config.LOGTAG, "unable to find session proposal for session id " + sessionId);
return;
}
if (currentState == DeviceDiscoveryState.DISCOVERED) {
Log.d(Config.LOGTAG,"session proposal already at discovered. not going to fall back");
Log.d(Config.LOGTAG, "session proposal already at discovered. not going to fall back");
return;
}
this.rtpSessionProposals.put(sessionProposal, target);
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": flagging session "+sessionId+" as "+target);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, sessionProposal.with, sessionProposal.sessionId, target.toEndUserState());
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": flagging session " + sessionId + " as " + target);
}
}
public void rejectRtpSession(final String sessionId) {
for(final AbstractJingleConnection connection : this.connections.values()) {
for (final AbstractJingleConnection connection : this.connections.values()) {
if (connection.getId().sessionId.equals(sessionId)) {
if (connection instanceof JingleRtpConnection) {
((JingleRtpConnection) connection).rejectCall();
@ -313,6 +354,17 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public enum DeviceDiscoveryState {
SEARCHING, DISCOVERED, FAILED
SEARCHING, DISCOVERED, FAILED;
public RtpEndUserState toEndUserState() {
switch (this) {
case SEARCHING:
return RtpEndUserState.FINDING_DEVICE;
case DISCOVERED:
return RtpEndUserState.RINGING;
default:
return RtpEndUserState.CONNECTIVITY_ERROR;
}
}
}
}

View File

@ -1,6 +1,5 @@
package eu.siacs.conversations.xmpp.jingle;
import android.content.Intent;
import android.util.Log;
import com.google.common.collect.ImmutableList;
@ -17,7 +16,6 @@ import java.util.List;
import java.util.Map;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
@ -34,7 +32,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
static {
final ImmutableMap.Builder<State, Collection<State>> transitionBuilder = new ImmutableMap.Builder<>();
transitionBuilder.put(State.NULL, ImmutableList.of(State.PROPOSED, State.SESSION_INITIALIZED));
transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED, State.REJECTED));
transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED, State.REJECTED, State.RETRACTED));
transitionBuilder.put(State.PROCEED, ImmutableList.of(State.SESSION_INITIALIZED));
transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(State.SESSION_ACCEPTED));
VALID_TRANSITIONS = transitionBuilder.build();
@ -234,6 +232,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
break;
case "proceed":
receiveProceed(from, message);
break;
case "retract":
receiveRetract(from, message);
break;
default:
break;
}
@ -272,6 +274,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
private void receiveRetract(final Jid from, final Element retract) {
if (from.equals(id.with)) {
if (transition(State.RETRACTED)) {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted");
//TODO create missed call notification/message
jingleConnectionManager.finishConnection(this);
} else {
Log.d(Config.LOGTAG, "ignoring retract because already in " + this.state);
}
} else {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received retract from " + from + ". expected retract from" + id.with + ". ignoring");
}
}
private void sendSessionInitiate() {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
setupWebRTC();
@ -472,6 +489,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private void updateEndUserState() {
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, getEndUserState());
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState());
}
}

View File

@ -894,6 +894,8 @@
<string name="rtp_state_ending_call">Ending call</string>
<string name="answer_call">Answer</string>
<string name="dismiss_call">Dismiss</string>
<string name="rtp_state_finding_device">Locating devices</string>
<string name="rtp_state_ringing">Ringing</string>
<plurals name="view_users">
<item quantity="one">View %1$d Participant</item>
<item quantity="other">View %1$d Participants</item>