diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java index 80fd42021..28cdb36e5 100644 --- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java +++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java @@ -39,7 +39,7 @@ public class CallIntegration extends Connection { private static final List BROKEN_DEVICE_MODELS = Arrays.asList( "OnePlus6" // OnePlus 6 (Android 8.1-11) Device is buggy and always starts the - // OS call screen even though we want to be self managed + // OS call screen even though we want to be self managed ); public static final int DEFAULT_TONE_VOLUME = 60; @@ -56,6 +56,7 @@ public class CallIntegration extends Connection { private final AtomicBoolean isDestroyed = new AtomicBoolean(false); private List availableEndpoints = Collections.emptyList(); + private boolean isMicrophoneEnabled = true; private Callback callback = null; @@ -74,6 +75,7 @@ public class CallIntegration extends Connection { this.appRTCAudioManager.setAudioManagerEvents(this::onAudioDeviceChanged); } setRingbackRequested(true); + setConnectionCapabilities(CAPABILITY_MUTE | CAPABILITY_RESPOND_VIA_TEXT); } public void setCallback(final Callback callback) { @@ -139,10 +141,26 @@ public class CallIntegration extends Connection { Log.d(Config.LOGTAG, "ignoring onCallAudioStateChange() on Upside Down Cake"); return; } + setMicrophoneEnabled(!state.isMuted()); Log.d(Config.LOGTAG, "onCallAudioStateChange(" + state + ")"); this.onAudioDeviceChanged(getAudioDeviceOreo(state), getAudioDevicesOreo(state)); } + @Override + public void onMuteStateChanged(final boolean isMuted) { + Log.d(Config.LOGTAG, "onMuteStateChanged(" + isMuted + ")"); + setMicrophoneEnabled(!isMuted); + } + + private void setMicrophoneEnabled(final boolean enabled) { + this.isMicrophoneEnabled = enabled; + this.callback.onCallIntegrationMicrophoneEnabled(enabled); + } + + public boolean isMicrophoneEnabled() { + return this.isMicrophoneEnabled; + } + public Set getAudioDevices() { if (notSelfManaged(context)) { return getAudioDevicesFallback(); @@ -578,5 +596,7 @@ public class CallIntegration extends Connection { void onCallIntegrationAnswer(); void onCallIntegrationSilence(); + + void onCallIntegrationMicrophoneEnabled(boolean enabled); } } diff --git a/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java b/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java index 5d02c8d21..b354ebaff 100644 --- a/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java @@ -117,7 +117,7 @@ public class NotificationsSettingsFragment extends XmppPreferenceFragment { try { startActivity(intent); } catch (final ActivityNotFoundException e) { - Toast.makeText(requireContext(), R.string.no_application_found, Toast.LENGTH_SHORT) + Toast.makeText(requireContext(), R.string.unsupported_operation, Toast.LENGTH_SHORT) .show(); return false; } 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 5f5a523e1..ba6ccf19d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -649,7 +649,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public JingleRtpConnection getOngoingRtpConnection() { - for(final AbstractJingleConnection jingleConnection : this.connections.values()) { + for (final AbstractJingleConnection jingleConnection : this.connections.values()) { if (jingleConnection instanceof JingleRtpConnection jingleRtpConnection) { if (jingleRtpConnection.isTerminated()) { continue; @@ -986,10 +986,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.rtpSessionProposals.remove(sessionProposal); sessionProposal.getCallIntegration().error(); mXmppConnectionService.notifyJingleRtpConnectionUpdate( - account, - sessionProposal.with, - sessionProposal.sessionId, - endUserState); + account, sessionProposal.with, sessionProposal.sessionId, endUserState); return; } @@ -1226,5 +1223,8 @@ public class JingleConnectionManager extends AbstractConnectionManager { @Override public void onCallIntegrationSilence() {} + + @Override + public void onCallIntegrationMicrophoneEnabled(boolean enabled) {} } } 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 234c429cb..695ce20b4 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -2324,6 +2324,7 @@ public class JingleRtpConnection extends AbstractJingleConnection this.jingleConnectionManager.ensureConnectionIsRegistered(this); this.webRTCWrapper.setup(this.xmppConnectionService); this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle); + this.webRTCWrapper.setMicrophoneEnabledOrThrow(callIntegration.isMicrophoneEnabled()); } private void acceptCallFromProposed() { @@ -2686,7 +2687,7 @@ public class JingleRtpConnection extends AbstractJingleConnection } public boolean setMicrophoneEnabled(final boolean enabled) { - return webRTCWrapper.setMicrophoneEnabled(enabled); + return webRTCWrapper.setMicrophoneEnabledOrThrow(enabled); } public boolean isVideoEnabled() { @@ -2762,6 +2763,11 @@ public class JingleRtpConnection extends AbstractJingleConnection xmppConnectionService.getNotificationService().stopSoundAndVibration(); } + @Override + public void onCallIntegrationMicrophoneEnabled(final boolean enabled) { + this.webRTCWrapper.setMicrophoneEnabled(enabled); + } + @Override public void onAudioDeviceChanged( final CallIntegration.AudioDevice selectedAudioDevice, diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index 8bc7c6f6f..36433790f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -506,23 +506,36 @@ public class WebRTCWrapper { } } - boolean setMicrophoneEnabled(final boolean enabled) { + boolean setMicrophoneEnabledOrThrow(final boolean enabled) { final Optional audioTrack = TrackWrapper.get(peerConnection, this.localAudioTrack); if (audioTrack.isPresent()) { - try { - audioTrack.get().setEnabled(enabled); - return true; - } catch (final IllegalStateException e) { - Log.d(Config.LOGTAG, "unable to toggle microphone", e); - // ignoring race condition in case MediaStreamTrack has been disposed - return false; - } + return setEnabled(audioTrack.get(), enabled); + } else { throw new IllegalStateException("Local audio track does not exist (yet)"); } } + private static boolean setEnabled(final AudioTrack audioTrack, final boolean enabled) { + try { + audioTrack.setEnabled(enabled); + return true; + } catch (final IllegalStateException e) { + Log.d(Config.LOGTAG, "unable to toggle audio track", e); + // ignoring race condition in case MediaStreamTrack has been disposed + return false; + } + } + + void setMicrophoneEnabled(final boolean enabled) { + final Optional audioTrack = + TrackWrapper.get(peerConnection, this.localAudioTrack); + if (audioTrack.isPresent()) { + setEnabled(audioTrack.get(), enabled); + } + } + boolean isVideoEnabled() { final Optional videoTrack = TrackWrapper.get(peerConnection, this.localVideoTrack); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index e5c3d376f..0310425e6 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1064,5 +1064,5 @@ Recurring backup Full screen notifications Allow this app to show incoming call notifications that take up the full screen when the device is locked. - + Unsupported operation