From bfe2aff7a158e767aad1a567f070a799690660b9 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 21 Jan 2024 10:07:35 +0100 Subject: [PATCH] show speaker selection during 'ringing' --- .../conversations/ui/RtpSessionActivity.java | 89 +++++++++++++++---- .../xmpp/jingle/AbstractJingleConnection.java | 8 +- .../xmpp/jingle/JingleConnectionManager.java | 32 +++++-- .../xmpp/jingle/JingleRtpConnection.java | 19 +++- .../xmpp/jingle/OngoingRtpSession.java | 9 ++ 5 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index ee0770023..429f2cfdb 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -63,6 +63,7 @@ import eu.siacs.conversations.xmpp.jingle.ContentAddition; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.Media; +import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession; import eu.siacs.conversations.xmpp.jingle.RtpCapability; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; @@ -117,6 +118,13 @@ public class RtpSessionActivity extends XmppActivity RtpEndUserState.ACCEPTING_CALL, RtpEndUserState.CONNECTING, RtpEndUserState.RECONNECTING); + private static final List STATES_SHOWING_SPEAKER_CONFIGURATION = + new ImmutableList.Builder() + .add(RtpEndUserState.FINDING_DEVICE) + .add(RtpEndUserState.RINGING) + .add(RtpEndUserState.CONNECTING) + .addAll(STATES_CONSIDERED_CONNECTED) + .build(); private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session"; private static final int REQUEST_ACCEPT_CALL = 0x1111; private static final int REQUEST_ACCEPT_CONTENT = 0x1112; @@ -139,8 +147,13 @@ public class RtpSessionActivity extends XmppActivity public static Set actionToMedia(final String action) { if (ACTION_MAKE_VIDEO_CALL.equals(action)) { return ImmutableSet.of(Media.AUDIO, Media.VIDEO); - } else { + } else if (ACTION_MAKE_VOICE_CALL.equals(action)) { return ImmutableSet.of(Media.AUDIO); + } else { + Log.w( + Config.LOGTAG, + "actionToMedia can not get media set from unknown action " + action); + return Collections.emptySet(); } } @@ -274,14 +287,15 @@ public class RtpSessionActivity extends XmppActivity private void retractSessionProposal() { final Intent intent = getIntent(); final String action = intent.getAction(); + final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION); final Account account = extractAccount(intent); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); final String state = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE); if (!Intent.ACTION_VIEW.equals(action) || state == null || !END_CARD.contains(RtpEndUserState.valueOf(state))) { - resetIntent( - account, with, RtpEndUserState.RETRACTED, actionToMedia(intent.getAction())); + final Set media = actionToMedia(lastAction == null ? action : lastAction); + resetIntent(account, with, RtpEndUserState.RETRACTED, media); } xmppConnectionService .getJingleConnectionManager() @@ -1049,6 +1063,14 @@ public class RtpSessionActivity extends XmppActivity } else { this.binding.inCallActionLeft.setVisibility(View.GONE); } + } else if (STATES_SHOWING_SPEAKER_CONFIGURATION.contains(state) + && !isPictureInPicture() + && Media.audioOnly(media)) { + final CallIntegration callIntegration = requireCallIntegration(); + updateInCallButtonConfigurationSpeaker( + callIntegration.getSelectedAudioDevice(), + callIntegration.getAudioDevices().size()); + this.binding.inCallActionFarRight.setVisibility(View.GONE); } else { this.binding.inCallActionLeft.setVisibility(View.GONE); this.binding.inCallActionRight.setVisibility(View.GONE); @@ -1297,17 +1319,13 @@ public class RtpSessionActivity extends XmppActivity } } - private void switchToEarpiece(View view) { - requireRtpConnection() - .getCallIntegration() - .setAudioDevice(CallIntegration.AudioDevice.EARPIECE); + private void switchToEarpiece(final View view) { + requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.EARPIECE); acquireProximityWakeLock(); } - private void switchToSpeaker(View view) { - requireRtpConnection() - .getCallIntegration() - .setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE); + private void switchToSpeaker(final View view) { + requireCallIntegration().setAudioDevice(CallIntegration.AudioDevice.SPEAKER_PHONE); releaseProximityWakeLock(); } @@ -1359,6 +1377,33 @@ public class RtpSessionActivity extends XmppActivity return connection; } + private CallIntegration requireCallIntegration() { + return requireOngoingRtpSession().getCallIntegration(); + } + + private OngoingRtpSession requireOngoingRtpSession() { + final JingleRtpConnection connection = + this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null; + if (connection != null) { + return connection; + } + final Intent currentIntent = getIntent(); + final String withExtra = + currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH); + final var account = extractAccount(currentIntent); + if (withExtra == null) { + throw new IllegalStateException("Current intent has no EXTRA_WITH"); + } + final var matching = + xmppConnectionService + .getJingleConnectionManager() + .matchingProposal(account, Jid.of(withExtra)); + if (matching.isPresent()) { + return matching.get(); + } + throw new IllegalStateException("No matching session proposal"); + } + @Override public void onJingleRtpConnectionUpdate( Account account, Jid with, final String sessionId, RtpEndUserState state) { @@ -1425,16 +1470,23 @@ public class RtpSessionActivity extends XmppActivity + ", available:" + availableAudioDevices); try { - final RtpEndUserState endUserState = requireRtpConnection().getEndUserState(); - final Set media = getMedia(); + final OngoingRtpSession ongoingRtpSession = requireOngoingRtpSession(); + final RtpEndUserState endUserState; + if (ongoingRtpSession instanceof JingleRtpConnection jingleRtpConnection) { + endUserState = jingleRtpConnection.getEndUserState(); + } else { + // for session proposals all end user states are functionally the same + endUserState = RtpEndUserState.RINGING; + } + final Set media = ongoingRtpSession.getMedia(); if (END_CARD.contains(endUserState)) { Log.d( Config.LOGTAG, "onAudioDeviceChanged() nothing to do because end card has been reached"); } else { - if (Media.audioOnly(media) && endUserState == RtpEndUserState.CONNECTED) { - final CallIntegration callIntegration = - requireRtpConnection().getCallIntegration(); + if (Media.audioOnly(media) + && STATES_SHOWING_SPEAKER_CONFIGURATION.contains(endUserState)) { + final CallIntegration callIntegration = requireCallIntegration(); updateInCallButtonConfigurationSpeaker( callIntegration.getSelectedAudioDevice(), callIntegration.getAudioDevices().size()); @@ -1457,16 +1509,17 @@ public class RtpSessionActivity extends XmppActivity if (withExtra == null) { return; } + final Set media = actionToMedia(currentIntent.getStringExtra(EXTRA_LAST_ACTION)); if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) { runOnUiThread( () -> { updateVerifiedShield(false); updateStateDisplay(state); - updateButtonConfiguration(state); + updateButtonConfiguration(state, media, null); updateIncomingCallScreen(state); invalidateOptionsMenu(); }); - resetIntent(account, with, state, actionToMedia(currentIntent.getAction())); + resetIntent(account, with, state, media); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java index efc32f5ff..6aeb348c1 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java @@ -219,8 +219,7 @@ public abstract class AbstractJingleConnection { if (isTerminated()) { this.jingleConnectionManager.finishConnectionOrThrow(this); } else { - throw new AssertionError( - String.format("Unable to call finish from %s", this.state)); + throw new AssertionError(String.format("Unable to call finish from %s", this.state)); } } @@ -348,7 +347,7 @@ public abstract class AbstractJingleConnection { return features != null && features.contains(feature); } - public static class Id implements OngoingRtpSession { + public static class Id { public final Account account; public final Jid with; public final String sessionId; @@ -400,17 +399,14 @@ public abstract class AbstractJingleConnection { return Objects.hashCode(account.getUuid(), with, sessionId); } - @Override public Account getAccount() { return account; } - @Override public Jid getWith() { return with; } - @Override public String getSessionId() { return sessionId; } 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 4356a0686..9dee8904d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -601,11 +601,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { public Optional getOngoingRtpConnection(final Contact contact) { for (final Map.Entry entry : this.connections.entrySet()) { - if (entry.getValue() instanceof JingleRtpConnection) { + if (entry.getValue() instanceof JingleRtpConnection jingleRtpConnection) { final AbstractJingleConnection.Id id = entry.getKey(); if (id.account == contact.getAccount() && id.with.asBareJid().equals(contact.getJid().asBareJid())) { - return Optional.of(id); + return Optional.of(jingleRtpConnection); } } } @@ -765,9 +765,22 @@ public class JingleConnectionManager extends AbstractConnectionManager { mXmppConnectionService.sendMessagePacket(account, messagePacket); } + public Optional matchingProposal(final Account account, final Jid with) { + synchronized (this.rtpSessionProposals) { + for (final Map.Entry entry : + this.rtpSessionProposals.entrySet()) { + final RtpSessionProposal proposal = entry.getKey(); + if (proposal.account == account && with.asBareJid().equals(proposal.with)) { + return Optional.of(proposal); + } + } + } + return Optional.absent(); + } + public boolean hasMatchingProposal(final Account account, final Jid with) { synchronized (this.rtpSessionProposals) { - for (Map.Entry entry : + for (final Map.Entry entry : this.rtpSessionProposals.entrySet()) { final var state = entry.getValue(); final RtpSessionProposal proposal = entry.getKey(); @@ -1102,9 +1115,15 @@ public class JingleConnectionManager extends AbstractConnectionManager { return sessionId; } + @Override public CallIntegration getCallIntegration() { return this.callIntegration; } + + @Override + public Set getMedia() { + return this.media; + } } public class ProposalStateCallback implements CallIntegration.Callback { @@ -1126,8 +1145,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { @Override public void onAudioDeviceChanged( - CallIntegration.AudioDevice selectedAudioDevice, - Set availableAudioDevices) {} + final CallIntegration.AudioDevice selectedAudioDevice, + final Set availableAudioDevices) { + mXmppConnectionService.notifyJingleRtpConnectionUpdate( + selectedAudioDevice, availableAudioDevices); + } @Override public void onCallIntegrationReject() {} 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 edb586565..df48ee8d7 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -30,6 +30,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.CryptoFailedException; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Message; @@ -68,7 +69,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class JingleRtpConnection extends AbstractJingleConnection - implements WebRTCWrapper.EventCallback, CallIntegration.Callback { + implements WebRTCWrapper.EventCallback, CallIntegration.Callback, OngoingRtpSession { public static final List STATES_SHOWING_ONGOING_CALL = Arrays.asList( @@ -2645,6 +2646,7 @@ public class JingleRtpConnection extends AbstractJingleConnection return this.sessionDuration.elapsed(TimeUnit.MILLISECONDS); } + @Override public CallIntegration getCallIntegration() { return this.callIntegration; } @@ -2870,6 +2872,21 @@ public class JingleRtpConnection extends AbstractJingleConnection return remoteHasFeature(Namespace.SDP_OFFER_ANSWER); } + @Override + public Account getAccount() { + return id.account; + } + + @Override + public Jid getWith() { + return id.with; + } + + @Override + public String getSessionId() { + return id.sessionId; + } + private interface OnIceServersDiscovered { void onIceServersDiscovered(List iceServers); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java index ebd2d8850..358411bcb 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/OngoingRtpSession.java @@ -1,10 +1,19 @@ package eu.siacs.conversations.xmpp.jingle; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.CallIntegration; import eu.siacs.conversations.xmpp.Jid; +import java.util.Set; + public interface OngoingRtpSession { Account getAccount(); + Jid getWith(); + String getSessionId(); + + CallIntegration getCallIntegration(); + + Set getMedia(); }