support mute via call integration

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/services/CallIntegration.java                           | 22 
src/main/java/eu/siacs/conversations/ui/fragment/settings/NotificationsSettingsFragment.java |  2 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java                | 10 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java                    |  8 
src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java                          | 31 
src/main/res/values/strings.xml                                                              |  2 
6 files changed, 57 insertions(+), 18 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/CallIntegration.java 🔗

@@ -39,7 +39,7 @@ public class CallIntegration extends Connection {
     private static final List<String> 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<CallEndpoint> 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<AudioDevice> getAudioDevices() {
         if (notSelfManaged(context)) {
             return getAudioDevicesFallback();
@@ -578,5 +596,7 @@ public class CallIntegration extends Connection {
         void onCallIntegrationAnswer();
 
         void onCallIntegrationSilence();
+
+        void onCallIntegrationMicrophoneEnabled(boolean enabled);
     }
 }

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

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,

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> 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> audioTrack =
+                TrackWrapper.get(peerConnection, this.localAudioTrack);
+        if (audioTrack.isPresent()) {
+            setEnabled(audioTrack.get(), enabled);
+        }
+    }
+
     boolean isVideoEnabled() {
         final Optional<VideoTrack> videoTrack =
                 TrackWrapper.get(peerConnection, this.localVideoTrack);

src/main/res/values/strings.xml 🔗

@@ -1064,5 +1064,5 @@
     <string name="pref_backup_recurring">Recurring backup</string>
     <string name="pref_fullscreen_notification">Full screen notifications</string>
     <string name="pref_fullscreen_notification_summary">Allow this app to show incoming call notifications that take up the full screen when the device is locked.</string>
-
+    <string name="unsupported_operation">Unsupported operation</string>
 </resources>