show speaker selection during 'ringing'

This commit is contained in:
Daniel Gultsch 2024-01-21 10:07:35 +01:00
parent bff1ac5ebc
commit bfe2aff7a1
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
5 changed files with 127 additions and 30 deletions

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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() {}

View file

@ -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);
}

View file

@ -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<Media> getMedia();
}