@@ -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<RtpEndUserState> STATES_SHOWING_SPEAKER_CONFIGURATION =
+ new ImmutableList.Builder<RtpEndUserState>()
+ .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<Media> 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> 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> 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> 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> 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);
}
}
@@ -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;
}
@@ -601,11 +601,11 @@ public class JingleConnectionManager extends AbstractConnectionManager {
public Optional<OngoingRtpSession> getOngoingRtpConnection(final Contact contact) {
for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> 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<RtpSessionProposal> matchingProposal(final Account account, final Jid with) {
+ synchronized (this.rtpSessionProposals) {
+ for (final Map.Entry<RtpSessionProposal, DeviceDiscoveryState> 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<RtpSessionProposal, DeviceDiscoveryState> entry :
+ for (final Map.Entry<RtpSessionProposal, DeviceDiscoveryState> 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<Media> 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<CallIntegration.AudioDevice> availableAudioDevices) {}
+ final CallIntegration.AudioDevice selectedAudioDevice,
+ final Set<CallIntegration.AudioDevice> availableAudioDevices) {
+ mXmppConnectionService.notifyJingleRtpConnectionUpdate(
+ selectedAudioDevice, availableAudioDevices);
+ }
@Override
public void onCallIntegrationReject() {}
@@ -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<State> 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<PeerConnection.IceServer> iceServers);
}