Prevent audio control click handler feedback loops

Fixes #6356
Fixes #6406
Fixes #6169

Closes #6800
// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-07-12 16:18:54 -07:00
parent 9dd508b6f5
commit c36db03a3a
6 changed files with 104 additions and 75 deletions

View File

@ -7,20 +7,23 @@
android:orientation="horizontal" android:orientation="horizontal"
tools:background="@color/textsecure_primary"> tools:background="@color/textsecure_primary">
<ToggleButton android:id="@+id/speakerButton" <org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/speakerButton"
style="@style/WebRtcCallCompoundButton" style="@style/WebRtcCallCompoundButton"
android:background="@drawable/webrtc_speaker_button" android:background="@drawable/webrtc_speaker_button"
tools:checked="true" tools:checked="true"
android:layout_marginRight="15dp"/> android:layout_marginRight="15dp"/>
<ToggleButton android:id="@+id/bluetoothButton" <org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/bluetoothButton"
style="@style/WebRtcCallCompoundButton" style="@style/WebRtcCallCompoundButton"
android:background="@drawable/webrtc_bluetooth_button" android:background="@drawable/webrtc_bluetooth_button"
tools:checked="true" tools:checked="true"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:visibility="gone"/> android:visibility="gone"/>
<ToggleButton android:id="@+id/muteButton" <org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/muteButton"
style="@style/WebRtcCallCompoundButton" style="@style/WebRtcCallCompoundButton"
android:background="@drawable/webrtc_mute_button" android:background="@drawable/webrtc_mute_button"
android:contentDescription="@string/redphone_call_controls__mute" android:contentDescription="@string/redphone_call_controls__mute"
@ -28,7 +31,8 @@
tools:checked="false" tools:checked="false"
/> />
<ToggleButton android:id="@+id/video_mute_button" <org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/video_mute_button"
style="@style/WebRtcCallCompoundButton" style="@style/WebRtcCallCompoundButton"
android:background="@drawable/webrtc_video_mute_button"/> android:background="@drawable/webrtc_video_mute_button"/>

View File

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

View File

@ -6,6 +6,7 @@ import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -16,15 +17,18 @@ import com.tomergoldst.tooltips.ToolTip;
import com.tomergoldst.tooltips.ToolTipsManager; import com.tomergoldst.tooltips.ToolTipsManager;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AccessibleToggleButton;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
public class WebRtcCallControls extends LinearLayout { public class WebRtcCallControls extends LinearLayout {
private CompoundButton audioMuteButton; private static final String TAG = WebRtcCallControls.class.getSimpleName();
private CompoundButton videoMuteButton;
private CompoundButton speakerButton; private AccessibleToggleButton audioMuteButton;
private CompoundButton bluetoothButton; private AccessibleToggleButton videoMuteButton;
private AccessibleToggleButton speakerButton;
private AccessibleToggleButton bluetoothButton;
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public WebRtcCallControls(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@ -106,14 +110,14 @@ public class WebRtcCallControls extends LinearLayout {
} }
if (audioManager.isBluetoothScoOn()) { if (audioManager.isBluetoothScoOn()) {
bluetoothButton.setChecked(true); bluetoothButton.setChecked(true, false);
speakerButton.setChecked(false); speakerButton.setChecked(false, false);
} else if (audioManager.isSpeakerphoneOn()) { } else if (audioManager.isSpeakerphoneOn()) {
speakerButton.setChecked(true); speakerButton.setChecked(true, false);
bluetoothButton.setChecked(false); bluetoothButton.setChecked(false, false);
} else { } else {
speakerButton.setChecked(false); speakerButton.setChecked(false, false);
bluetoothButton.setChecked(false); bluetoothButton.setChecked(false, false);
} }
} }
@ -122,11 +126,11 @@ public class WebRtcCallControls extends LinearLayout {
} }
public void setVideoEnabled(boolean enabled) { public void setVideoEnabled(boolean enabled) {
videoMuteButton.setChecked(enabled); videoMuteButton.setChecked(enabled, false);
} }
public void setMicrophoneEnabled(boolean enabled) { public void setMicrophoneEnabled(boolean enabled) {
audioMuteButton.setChecked(!enabled); audioMuteButton.setChecked(!enabled, false);
} }
public void setControlsEnabled(boolean enabled) { public void setControlsEnabled(boolean enabled) {
@ -146,11 +150,6 @@ public class WebRtcCallControls extends LinearLayout {
videoMuteButton.setAlpha(0.3f); videoMuteButton.setAlpha(0.3f);
audioMuteButton.setAlpha(0.3f); audioMuteButton.setAlpha(0.3f);
speakerButton.setChecked(false);
bluetoothButton.setChecked(false);
videoMuteButton.setChecked(false);
audioMuteButton.setChecked(false);
speakerButton.setEnabled(false); speakerButton.setEnabled(false);
bluetoothButton.setEnabled(false); bluetoothButton.setEnabled(false);
videoMuteButton.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 static interface MuteButtonListener {
public void onToggle(boolean isMuted); public void onToggle(boolean isMuted);
} }

View File

@ -135,22 +135,6 @@ public class WebRtcCallScreen extends FrameLayout implements Recipient.Recipient
this.endCallButton.setVisibility(View.INVISIBLE); 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) { public void setIncomingCallActionListener(WebRtcIncomingCallOverlay.IncomingCallActionListener listener) {
incomingCallOverlay.setIncomingCallActionListener(listener); incomingCallOverlay.setIncomingCallActionListener(listener);
} }

View File

@ -32,7 +32,7 @@ public class IncomingRinger {
this.vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); this.vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
} }
public void start(boolean speakerphone) { public void start() {
AudioManager audioManager = ServiceUtil.getAudioManager(context); AudioManager audioManager = ServiceUtil.getAudioManager(context);
if (player != null) player.release(); if (player != null) player.release();
@ -61,10 +61,6 @@ public class IncomingRinger {
} else { } else {
Log.w(TAG, "Not ringing, mode: " + ringerMode); Log.w(TAG, "Not ringing, mode: " + ringerMode);
} }
if (speakerphone) {
audioManager.setSpeakerphoneOn(true);
}
} }
public void stop() { public void stop() {
@ -120,7 +116,7 @@ public class IncomingRinger {
mediaPlayer.setOnErrorListener(new MediaPlayerErrorListener()); mediaPlayer.setOnErrorListener(new MediaPlayerErrorListener());
mediaPlayer.setDataSource(context, ringtoneUri); mediaPlayer.setDataSource(context, ringtoneUri);
mediaPlayer.setLooping(true); mediaPlayer.setLooping(true);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
return mediaPlayer; return mediaPlayer;
} catch (IOException e) { } catch (IOException e) {

View File

@ -46,16 +46,11 @@ public class SignalAudioManager {
AudioManager audioManager = ServiceUtil.getAudioManager(context); AudioManager audioManager = ServiceUtil.getAudioManager(context);
boolean speaker = !audioManager.isWiredHeadsetOn() && !audioManager.isBluetoothScoOn(); boolean speaker = !audioManager.isWiredHeadsetOn() && !audioManager.isBluetoothScoOn();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { audioManager.setMode(AudioManager.MODE_RINGTONE);
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
} else {
audioManager.setMode(AudioManager.MODE_IN_CALL);
}
audioManager.setMicrophoneMute(false); audioManager.setMicrophoneMute(false);
audioManager.setSpeakerphoneOn(speaker); audioManager.setSpeakerphoneOn(speaker);
incomingRinger.start(speaker); incomingRinger.start();
} }
public void startOutgoingRinger(OutgoingRinger.Type type) { public void startOutgoingRinger(OutgoingRinger.Type type) {
@ -85,6 +80,12 @@ public class SignalAudioManager {
incomingRinger.stop(); incomingRinger.stop();
outgoingRinger.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) { if (!preserveSpeakerphone) {
audioManager.setSpeakerphoneOn(false); audioManager.setSpeakerphoneOn(false);
} }