Add button to flip camera (front vs rear). Fixes #6279

This commit is contained in:
Niklas Hambüchen 2018-03-23 20:31:03 +01:00 committed by Greyson Parrelli
parent 6c1a1fb9ad
commit f1c79eaebf
8 changed files with 140 additions and 15 deletions

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/webrtc_control_background"/>
<item android:top="5dp"
android:left="5dp"
android:right="5dp"
android:bottom="5dp"
android:drawable="@drawable/quick_camera_rear"/>
</layer-list>

View File

@ -34,6 +34,13 @@
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/video_mute_button"
style="@style/WebRtcCallCompoundButton"
android:layout_marginRight="15dp"
android:background="@drawable/webrtc_video_mute_button"/>
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/camera_flip_button"
style="@style/WebRtcCallCompoundButton"
android:contentDescription="@string/redphone_call_controls__flip_camera_rear"
android:background="@drawable/webrtc_camera_flip_button"/>
</merge>

View File

@ -995,6 +995,7 @@
<!--- redphone_call_controls -->
<string name="redphone_call_card__signal_call">Signal Call</string>
<string name="redphone_call_controls__mute">Mute</string>
<string name="redphone_call_controls__flip_camera_rear">Use rear camera</string>
<string name="redphone_call_controls__signal_call">Signal Call</string>
<!-- registration_activity -->

View File

@ -139,6 +139,7 @@ public class WebRtcCallActivity extends Activity {
callScreen.setIncomingCallActionListener(new IncomingCallActionListener());
callScreen.setAudioMuteButtonListener(new AudioMuteButtonListener());
callScreen.setVideoMuteButtonListener(new VideoMuteButtonListener());
callScreen.setCameraFlipButtonListener(new CameraFlipButtonListener());
callScreen.setSpeakerButtonListener(new SpeakerButtonListener());
callScreen.setBluetoothButtonListener(new BluetoothButtonListener());
@ -159,6 +160,13 @@ public class WebRtcCallActivity extends Activity {
startService(intent);
}
private void handleSetCameraFlip(boolean isRear) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_CAMERA_FLIP);
intent.putExtra(WebRtcCallService.EXTRA_CAMERA_FLIP_REAR, isRear);
startService(intent);
}
private void handleAnswerCall() {
WebRtcViewModel event = EventBus.getDefault().getStickyEvent(WebRtcViewModel.class);
@ -348,6 +356,11 @@ public class WebRtcCallActivity extends Activity {
}
}
private class CameraFlipButtonListener implements WebRtcCallControls.CameraFlipButtonListener {
@Override
public void onToggle(boolean isRear) { WebRtcCallActivity.this.handleSetCameraFlip(isRear); }
}
private class SpeakerButtonListener implements WebRtcCallControls.SpeakerButtonListener {
@Override
public void onSpeakerChange(boolean isSpeaker) {

View File

@ -27,6 +27,7 @@ public class WebRtcCallControls extends LinearLayout {
private AccessibleToggleButton audioMuteButton;
private AccessibleToggleButton videoMuteButton;
private AccessibleToggleButton cameraFlipButton;
private AccessibleToggleButton speakerButton;
private AccessibleToggleButton bluetoothButton;
@ -60,6 +61,8 @@ public class WebRtcCallControls extends LinearLayout {
this.bluetoothButton = ViewUtil.findById(this, R.id.bluetoothButton);
this.audioMuteButton = ViewUtil.findById(this, R.id.muteButton);
this.videoMuteButton = ViewUtil.findById(this, R.id.video_mute_button);
this.cameraFlipButton = ViewUtil.findById(this, R.id.camera_flip_button);
this.cameraFlipButton.setVisibility(View.INVISIBLE); // shown once video is enabled
}
public void setAudioMuteButtonListener(final MuteButtonListener listener) {
@ -75,7 +78,18 @@ public class WebRtcCallControls extends LinearLayout {
videoMuteButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
listener.onToggle(!isChecked);
boolean videoMuted = !isChecked;
listener.onToggle(videoMuted);
cameraFlipButton.setVisibility(videoMuted ? View.INVISIBLE : View.VISIBLE);
}
});
}
public void setCameraFlipButtonListener(final CameraFlipButtonListener listener) {
cameraFlipButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
listener.onToggle(isChecked);
}
});
}
@ -138,21 +152,25 @@ public class WebRtcCallControls extends LinearLayout {
speakerButton.setAlpha(1.0f);
bluetoothButton.setAlpha(1.0f);
videoMuteButton.setAlpha(1.0f);
cameraFlipButton.setAlpha(1.0f);
audioMuteButton.setAlpha(1.0f);
speakerButton.setEnabled(true);
bluetoothButton.setEnabled(true);
videoMuteButton.setEnabled(true);
cameraFlipButton.setEnabled(true);
audioMuteButton.setEnabled(true);
} else if (!enabled && Build.VERSION.SDK_INT >= 11) {
speakerButton.setAlpha(0.3f);
bluetoothButton.setAlpha(0.3f);
videoMuteButton.setAlpha(0.3f);
cameraFlipButton.setAlpha(0.3f);
audioMuteButton.setAlpha(0.3f);
speakerButton.setEnabled(false);
bluetoothButton.setEnabled(false);
videoMuteButton.setEnabled(false);
cameraFlipButton.setEnabled(false);
audioMuteButton.setEnabled(false);
}
}
@ -179,6 +197,10 @@ public class WebRtcCallControls extends LinearLayout {
public void onToggle(boolean isMuted);
}
public static interface CameraFlipButtonListener {
public void onToggle(boolean isRear);
}
public static interface SpeakerButtonListener {
public void onSpeakerChange(boolean isSpeaker);
}

View File

@ -154,6 +154,10 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientModifiedLi
this.controls.setVideoMuteButtonListener(listener);
}
public void setCameraFlipButtonListener(WebRtcCallControls.CameraFlipButtonListener listener) {
this.controls.setCameraFlipButtonListener(listener);
}
public void setSpeakerButtonListener(WebRtcCallControls.SpeakerButtonListener listener) {
this.controls.setSpeakerButtonListener(listener);
}

View File

@ -116,6 +116,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
public static final String EXTRA_REMOTE_ADDRESS = "remote_address";
public static final String EXTRA_MUTE = "mute_value";
public static final String EXTRA_CAMERA_FLIP_REAR = "camera_flip_rear_value";
public static final String EXTRA_AVAILABLE = "enabled_value";
public static final String EXTRA_REMOTE_DESCRIPTION = "remote_description";
public static final String EXTRA_TIMESTAMP = "timestamp";
@ -132,6 +133,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP";
public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO";
public static final String ACTION_SET_MUTE_VIDEO = "SET_MUTE_VIDEO";
public static final String ACTION_SET_CAMERA_FLIP = "SET_CAMERA_FLIP";
public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE";
public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE";
public static final String ACTION_SCREEN_OFF = "SCREEN_OFF";
@ -208,6 +210,7 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
else if (intent.getAction().equals(ACTION_REMOTE_HANGUP)) handleRemoteHangup(intent);
else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent);
else if (intent.getAction().equals(ACTION_SET_MUTE_VIDEO)) handleSetMuteVideo(intent);
else if (intent.getAction().equals(ACTION_SET_CAMERA_FLIP)) handleSetCameraFlip(intent);
else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent);
else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent);
else if (intent.getAction().equals((ACTION_SCREEN_OFF))) handleScreenOffChange(intent);
@ -804,6 +807,17 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
sendMessage(viewModelStateFor(callState), this.recipient, localVideoEnabled, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled);
}
private void handleSetCameraFlip(Intent intent) {
boolean isRear = intent.getBooleanExtra(EXTRA_CAMERA_FLIP_REAR, false);
Log.w(TAG, "handleSetCameraFlip(isRear=" + isRear + ")...");
if (this.localVideoEnabled) {
if (this.peerConnection != null) {
this.peerConnection.flipCameras(isRear);
}
}
}
private void handleBluetoothChange(Intent intent) {
this.bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false);

View File

@ -40,8 +40,16 @@ public class PeerConnectionWrapper {
@NonNull private final AudioSource audioSource;
@Nullable private final VideoCapturer videoCapturer;
@Nullable private final VideoCapturer videoCapturerRear;
@Nullable private final VideoSource videoSource;
@Nullable private final VideoSource videoSourceRear;
@Nullable private final VideoTrack videoTrack;
@Nullable private final VideoTrack videoTrackRear;
@Nullable private VideoCapturer videoCapturerActive;
@Nullable private VideoTrack videoTrackActive;
@Nullable private final MediaStream mediaStream;
public PeerConnectionWrapper(@NonNull Context context,
@NonNull PeerConnectionFactory factory,
@ -72,44 +80,71 @@ public class PeerConnectionWrapper {
this.peerConnection.setAudioPlayout(false);
this.peerConnection.setAudioRecording(false);
this.videoCapturer = createVideoCapturer(context);
this.videoCapturer = createVideoCapturer(context, false);
this.videoCapturerRear = createVideoCapturer(context, true);
MediaStream mediaStream = factory.createLocalMediaStream("ARDAMS");
this.videoCapturerActive = videoCapturer;
this.mediaStream = factory.createLocalMediaStream("ARDAMS");
this.audioSource = factory.createAudioSource(audioConstraints);
this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
this.audioTrack.setEnabled(false);
mediaStream.addTrack(audioTrack);
this.mediaStream.addTrack(audioTrack);
if (videoCapturer != null) {
this.videoSource = factory.createVideoSource(videoCapturer);
this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
this.videoTrackActive = videoTrack;
this.videoTrack.addRenderer(new VideoRenderer(localRenderer));
this.videoTrack.setEnabled(false);
mediaStream.addTrack(videoTrack);
this.mediaStream.addTrack(videoTrack);
} else {
this.videoSource = null;
this.videoTrack = null;
this.videoTrackActive = null;
}
if (videoCapturerRear != null) {
this.videoSourceRear = factory.createVideoSource(videoCapturerRear);
this.videoTrackRear = factory.createVideoTrack("ARDAMSv0", videoSourceRear);
this.videoTrackRear.addRenderer(new VideoRenderer(localRenderer));
this.videoTrackRear.setEnabled(false);
} else {
this.videoSourceRear = null;
this.videoTrackRear = null;
}
this.peerConnection.addStream(mediaStream);
}
public void setVideoEnabled(boolean enabled) {
if (this.videoTrack != null) {
this.videoTrack.setEnabled(enabled);
if (this.videoTrackActive != null) {
this.videoTrackActive.setEnabled(enabled);
}
if (this.videoCapturer != null) {
if (this.videoCapturerActive != null) {
try {
if (enabled) this.videoCapturer.startCapture(1280, 720, 30);
else this.videoCapturer.stopCapture();
if (enabled) this.videoCapturerActive.startCapture(1280, 720, 30);
else this.videoCapturerActive.stopCapture();
} catch (InterruptedException e) {
Log.w(TAG, e);
}
}
}
public void flipCameras(boolean isRear) {
if (videoCapturerRear != null) {
setVideoEnabled(false);
mediaStream.removeTrack(videoTrackActive);
this.videoTrackActive = isRear ? videoTrackRear : videoTrack;
this.videoCapturerActive = isRear ? videoCapturerRear : videoCapturer;
mediaStream.addTrack(videoTrackActive);
setVideoEnabled(true);
}
}
public void setCommunicationMode() {
this.peerConnection.setAudioPlayout(true);
this.peerConnection.setAudioRecording(true);
@ -268,10 +303,23 @@ public class PeerConnectionWrapper {
this.videoCapturer.dispose();
}
if (this.videoCapturerRear != null) {
try {
this.videoCapturerRear.stopCapture();
} catch (InterruptedException e) {
Log.w(TAG, e);
}
this.videoCapturerRear.dispose();
}
if (this.videoSource != null) {
this.videoSource.dispose();
}
if (this.videoSourceRear != null) {
this.videoSourceRear.dispose();
}
this.audioSource.dispose();
this.peerConnection.close();
this.peerConnection.dispose();
@ -281,7 +329,7 @@ public class PeerConnectionWrapper {
return this.peerConnection.addIceCandidate(candidate);
}
private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull Context context) {
private @Nullable CameraVideoCapturer createVideoCapturer(@NonNull Context context, boolean rear) {
boolean camera2EnumeratorIsSupported = false;
try {
camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
@ -298,12 +346,16 @@ public class PeerConnectionWrapper {
String[] deviceNames = enumerator.getDeviceNames();
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
Log.w(TAG, "Creating front facing camera capturer.");
boolean isDesiredDirection =
rear ? enumerator.isBackFacing(deviceName)
: enumerator.isFrontFacing(deviceName);
if (isDesiredDirection) {
String direction = rear ? "rear" : "front";
Log.w(TAG, "Creating " + direction + " facing camera capturer.");
final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
Log.w(TAG, "Found front facing capturer: " + deviceName);
Log.w(TAG, "Found " + direction + " facing capturer: " + deviceName);
return videoCapturer;
}
@ -311,7 +363,10 @@ public class PeerConnectionWrapper {
}
for (String deviceName : deviceNames) {
if (!enumerator.isFrontFacing(deviceName)) {
boolean isDesiredDirection =
rear ? enumerator.isBackFacing(deviceName)
: enumerator.isFrontFacing(deviceName);
if (!isDesiredDirection) {
Log.w(TAG, "Creating other camera capturer.");
final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);