From c36db03a3afef94eb69acee56bcd71d4765e2af4 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Wed, 12 Jul 2017 16:18:54 -0700 Subject: [PATCH] Prevent audio control click handler feedback loops Fixes #6356 Fixes #6406 Fixes #6169 Closes #6800 // FREEBIE --- res/layout/webrtc_call_controls.xml | 44 ++++++++------- .../components/AccessibleToggleButton.java | 53 +++++++++++++++++++ .../components/webrtc/WebRtcCallControls.java | 43 ++++++--------- .../components/webrtc/WebRtcCallScreen.java | 16 ------ .../webrtc/audio/IncomingRinger.java | 8 +-- .../webrtc/audio/SignalAudioManager.java | 15 +++--- 6 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/components/AccessibleToggleButton.java diff --git a/res/layout/webrtc_call_controls.xml b/res/layout/webrtc_call_controls.xml index a4289063a1..9e513c370b 100644 --- a/res/layout/webrtc_call_controls.xml +++ b/res/layout/webrtc_call_controls.xml @@ -7,29 +7,33 @@ android:orientation="horizontal" tools:background="@color/textsecure_primary"> - + - + - - + diff --git a/src/org/thoughtcrime/securesms/components/AccessibleToggleButton.java b/src/org/thoughtcrime/securesms/components/AccessibleToggleButton.java new file mode 100644 index 0000000000..1acc918e96 --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/AccessibleToggleButton.java @@ -0,0 +1,53 @@ +package org.thoughtcrime.securesms.components; + + +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.util.AttributeSet; +import android.widget.ToggleButton; + +public class AccessibleToggleButton extends ToggleButton { + + private OnCheckedChangeListener listener; + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public AccessibleToggleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public AccessibleToggleButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public AccessibleToggleButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AccessibleToggleButton(Context context) { + super(context); + } + + @Override + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + super.setOnCheckedChangeListener(listener); + this.listener = listener; + } + + public void setChecked(boolean checked, boolean notifyListener) { + if (!notifyListener) { + super.setOnCheckedChangeListener(null); + } + + super.setChecked(checked); + + if (!notifyListener) { + super.setOnCheckedChangeListener(listener); + } + } + + public OnCheckedChangeListener getOnCheckedChangeListener() { + return this.listener; + } + +} diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java index 93bd6200ea..6c5d07c8b7 100644 --- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java +++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallControls.java @@ -6,6 +6,7 @@ import android.content.Context; import android.media.AudioManager; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,15 +17,18 @@ import com.tomergoldst.tooltips.ToolTip; import com.tomergoldst.tooltips.ToolTipsManager; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.components.AccessibleToggleButton; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ViewUtil; public class WebRtcCallControls extends LinearLayout { - private CompoundButton audioMuteButton; - private CompoundButton videoMuteButton; - private CompoundButton speakerButton; - private CompoundButton bluetoothButton; + private static final String TAG = WebRtcCallControls.class.getSimpleName(); + + private AccessibleToggleButton audioMuteButton; + private AccessibleToggleButton videoMuteButton; + private AccessibleToggleButton speakerButton; + private AccessibleToggleButton bluetoothButton; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -106,14 +110,14 @@ public class WebRtcCallControls extends LinearLayout { } if (audioManager.isBluetoothScoOn()) { - bluetoothButton.setChecked(true); - speakerButton.setChecked(false); + bluetoothButton.setChecked(true, false); + speakerButton.setChecked(false, false); } else if (audioManager.isSpeakerphoneOn()) { - speakerButton.setChecked(true); - bluetoothButton.setChecked(false); + speakerButton.setChecked(true, false); + bluetoothButton.setChecked(false, false); } else { - speakerButton.setChecked(false); - bluetoothButton.setChecked(false); + speakerButton.setChecked(false, false); + bluetoothButton.setChecked(false, false); } } @@ -122,11 +126,11 @@ public class WebRtcCallControls extends LinearLayout { } public void setVideoEnabled(boolean enabled) { - videoMuteButton.setChecked(enabled); + videoMuteButton.setChecked(enabled, false); } public void setMicrophoneEnabled(boolean enabled) { - audioMuteButton.setChecked(!enabled); + audioMuteButton.setChecked(!enabled, false); } public void setControlsEnabled(boolean enabled) { @@ -145,12 +149,7 @@ public class WebRtcCallControls extends LinearLayout { bluetoothButton.setAlpha(0.3f); videoMuteButton.setAlpha(0.3f); audioMuteButton.setAlpha(0.3f); - - speakerButton.setChecked(false); - bluetoothButton.setChecked(false); - videoMuteButton.setChecked(false); - audioMuteButton.setChecked(false); - + speakerButton.setEnabled(false); bluetoothButton.setEnabled(false); videoMuteButton.setEnabled(false); @@ -176,14 +175,6 @@ public class WebRtcCallControls extends LinearLayout { } } - public void reset() { - audioMuteButton.setChecked(false); - videoMuteButton.setChecked(false); - speakerButton.setChecked(false); - bluetoothButton.setChecked(false); - bluetoothButton.setVisibility(View.GONE); - } - public static interface MuteButtonListener { public void onToggle(boolean isMuted); } diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java index 14cf94b175..35db803b2b 100644 --- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java +++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java @@ -135,22 +135,6 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient this.endCallButton.setVisibility(View.INVISIBLE); } - - public void reset() { - setPersonInfo(Recipient.getUnknownRecipient()); - setMinimized(false); - this.status.setText(""); - this.recipient = null; - - this.controls.reset(); - this.untrustedIdentityExplanation.setText(""); - this.untrustedIdentityContainer.setVisibility(View.GONE); - this.localRenderLayout.removeAllViews(); - this.remoteRenderLayout.removeAllViews(); - - incomingCallOverlay.reset(); - } - public void setIncomingCallActionListener(WebRtcIncomingCallOverlay.IncomingCallActionListener listener) { incomingCallOverlay.setIncomingCallActionListener(listener); } diff --git a/src/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java b/src/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java index 48eca28085..d8afa93c05 100644 --- a/src/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java +++ b/src/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.java @@ -32,7 +32,7 @@ public class IncomingRinger { this.vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); } - public void start(boolean speakerphone) { + public void start() { AudioManager audioManager = ServiceUtil.getAudioManager(context); if (player != null) player.release(); @@ -61,10 +61,6 @@ public class IncomingRinger { } else { Log.w(TAG, "Not ringing, mode: " + ringerMode); } - - if (speakerphone) { - audioManager.setSpeakerphoneOn(true); - } } public void stop() { @@ -120,7 +116,7 @@ public class IncomingRinger { mediaPlayer.setOnErrorListener(new MediaPlayerErrorListener()); mediaPlayer.setDataSource(context, ringtoneUri); mediaPlayer.setLooping(true); - mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING); return mediaPlayer; } catch (IOException e) { diff --git a/src/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java b/src/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java index f09a45337c..e7390effcc 100644 --- a/src/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java +++ b/src/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.java @@ -46,16 +46,11 @@ public class SignalAudioManager { AudioManager audioManager = ServiceUtil.getAudioManager(context); boolean speaker = !audioManager.isWiredHeadsetOn() && !audioManager.isBluetoothScoOn(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - } else { - audioManager.setMode(AudioManager.MODE_IN_CALL); - } - + audioManager.setMode(AudioManager.MODE_RINGTONE); audioManager.setMicrophoneMute(false); audioManager.setSpeakerphoneOn(speaker); - incomingRinger.start(speaker); + incomingRinger.start(); } public void startOutgoingRinger(OutgoingRinger.Type type) { @@ -85,6 +80,12 @@ public class SignalAudioManager { incomingRinger.stop(); outgoingRinger.stop(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); + } else { + audioManager.setMode(AudioManager.MODE_IN_CALL); + } + if (!preserveSpeakerphone) { audioManager.setSpeakerphoneOn(false); }