mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-01-31 11:24:58 +01:00
Support toggle front/back camera
This commit is contained in:
parent
77a01f0cd4
commit
b27eead016
@ -19,6 +19,7 @@ package im.vector.riotx.features.call
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
@ -41,6 +42,11 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
callViewModel.handle(VectorCallViewActions.SwitchSoundDevice)
|
callViewModel.handle(VectorCallViewActions.SwitchSoundDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callControlsSwitchCamera.clickableView.debouncedClicks {
|
||||||
|
callViewModel.handle(VectorCallViewActions.ToggleCamera)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
callViewModel.observeViewEvents {
|
callViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is VectorCallViewEvents.ShowSoundDeviceChooser -> {
|
is VectorCallViewEvents.ShowSoundDeviceChooser -> {
|
||||||
@ -55,19 +61,19 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
private fun showSoundDeviceChooser(available: List<CallAudioManager.SoundDevice>, current: CallAudioManager.SoundDevice) {
|
private fun showSoundDeviceChooser(available: List<CallAudioManager.SoundDevice>, current: CallAudioManager.SoundDevice) {
|
||||||
val soundDevices = available.map {
|
val soundDevices = available.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
CallAudioManager.SoundDevice.WIRELESS_HEADSET -> span {
|
CallAudioManager.SoundDevice.WIRELESS_HEADSET -> span {
|
||||||
text = getString(R.string.sound_device_wireless_headset)
|
text = getString(R.string.sound_device_wireless_headset)
|
||||||
textStyle = if (current == it) "bold" else "normal"
|
textStyle = if (current == it) "bold" else "normal"
|
||||||
}
|
}
|
||||||
CallAudioManager.SoundDevice.PHONE -> span {
|
CallAudioManager.SoundDevice.PHONE -> span {
|
||||||
text = getString(R.string.sound_device_phone)
|
text = getString(R.string.sound_device_phone)
|
||||||
textStyle = if (current == it) "bold" else "normal"
|
textStyle = if (current == it) "bold" else "normal"
|
||||||
}
|
}
|
||||||
CallAudioManager.SoundDevice.SPEAKER -> span {
|
CallAudioManager.SoundDevice.SPEAKER -> span {
|
||||||
text = getString(R.string.sound_device_speaker)
|
text = getString(R.string.sound_device_speaker)
|
||||||
textStyle = if (current == it) "bold" else "normal"
|
textStyle = if (current == it) "bold" else "normal"
|
||||||
}
|
}
|
||||||
CallAudioManager.SoundDevice.HEADSET -> span {
|
CallAudioManager.SoundDevice.HEADSET -> span {
|
||||||
text = getString(R.string.sound_device_headset)
|
text = getString(R.string.sound_device_headset)
|
||||||
textStyle = if (current == it) "bold" else "normal"
|
textStyle = if (current == it) "bold" else "normal"
|
||||||
}
|
}
|
||||||
@ -77,13 +83,13 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
.setItems(soundDevices.toTypedArray()) { d, n ->
|
.setItems(soundDevices.toTypedArray()) { d, n ->
|
||||||
d.cancel()
|
d.cancel()
|
||||||
when (soundDevices[n].toString()) {
|
when (soundDevices[n].toString()) {
|
||||||
getString(R.string.sound_device_phone) -> {
|
getString(R.string.sound_device_phone) -> {
|
||||||
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.PHONE))
|
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.PHONE))
|
||||||
}
|
}
|
||||||
getString(R.string.sound_device_speaker) -> {
|
getString(R.string.sound_device_speaker) -> {
|
||||||
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.SPEAKER))
|
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.SPEAKER))
|
||||||
}
|
}
|
||||||
getString(R.string.sound_device_headset) -> {
|
getString(R.string.sound_device_headset) -> {
|
||||||
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.HEADSET))
|
callViewModel.handle(VectorCallViewActions.ChangeAudioDevice(CallAudioManager.SoundDevice.HEADSET))
|
||||||
}
|
}
|
||||||
getString(R.string.sound_device_wireless_headset) -> {
|
getString(R.string.sound_device_wireless_headset) -> {
|
||||||
@ -95,23 +101,16 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
// return super.onCreateDialog(savedInstanceState).apply {
|
|
||||||
// window?.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
|
||||||
// window?.decorView?.systemUiVisibility =
|
|
||||||
// View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
|
||||||
// View.SYSTEM_UI_FLAG_FULLSCREEN or
|
|
||||||
// View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private fun renderState(state: VectorCallViewState) {
|
private fun renderState(state: VectorCallViewState) {
|
||||||
callControlsSoundDevice.title = getString(R.string.call_select_sound_device)
|
callControlsSoundDevice.title = getString(R.string.call_select_sound_device)
|
||||||
callControlsSoundDevice.subTitle = when (state.soundDevice) {
|
callControlsSoundDevice.subTitle = when (state.soundDevice) {
|
||||||
CallAudioManager.SoundDevice.PHONE -> getString(R.string.sound_device_phone)
|
CallAudioManager.SoundDevice.PHONE -> getString(R.string.sound_device_phone)
|
||||||
CallAudioManager.SoundDevice.SPEAKER -> getString(R.string.sound_device_speaker)
|
CallAudioManager.SoundDevice.SPEAKER -> getString(R.string.sound_device_speaker)
|
||||||
CallAudioManager.SoundDevice.HEADSET -> getString(R.string.sound_device_headset)
|
CallAudioManager.SoundDevice.HEADSET -> getString(R.string.sound_device_headset)
|
||||||
CallAudioManager.SoundDevice.WIRELESS_HEADSET -> getString(R.string.sound_device_wireless_headset)
|
CallAudioManager.SoundDevice.WIRELESS_HEADSET -> getString(R.string.sound_device_wireless_headset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callControlsSwitchCamera.isVisible = state.canSwitchCamera
|
||||||
|
callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.riotx.features.call
|
||||||
|
|
||||||
|
enum class CameraType {
|
||||||
|
FRONT,
|
||||||
|
BACK
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CameraProxy(
|
||||||
|
val name: String,
|
||||||
|
val type: CameraType
|
||||||
|
)
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.riotx.features.call
|
package im.vector.riotx.features.call
|
||||||
|
|
||||||
// import im.vector.riotx.features.call.service.CallHeadsUpService
|
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -161,13 +160,8 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
|||||||
v.updatePadding(bottom = if (systemUiVisibility) insets.systemWindowInsetBottom else 0)
|
v.updatePadding(bottom = if (systemUiVisibility) insets.systemWindowInsetBottom else 0)
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
// // window.navigationBarColor = ContextCompat.getColor(this, R.color.riotx_background_light)
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// for content intent when screen is locked
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
// window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
|
||||||
// window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
|
|
||||||
if (intent.hasExtra(MvRx.KEY_ARG)) {
|
if (intent.hasExtra(MvRx.KEY_ARG)) {
|
||||||
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
|
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
|
||||||
@ -323,6 +317,10 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
|||||||
|
|
||||||
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer,
|
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer,
|
||||||
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
|
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
|
||||||
|
|
||||||
|
pipRenderer.setOnClickListener {
|
||||||
|
callViewModel.handle(VectorCallViewActions.ToggleCamera)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
|
@ -48,6 +48,8 @@ data class VectorCallViewState(
|
|||||||
val isAudioMuted: Boolean = false,
|
val isAudioMuted: Boolean = false,
|
||||||
val isVideoEnabled: Boolean = true,
|
val isVideoEnabled: Boolean = true,
|
||||||
val isVideoCaptureInError: Boolean = false,
|
val isVideoCaptureInError: Boolean = false,
|
||||||
|
val isFrontCamera: Boolean = true,
|
||||||
|
val canSwitchCamera: Boolean = true,
|
||||||
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
|
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
|
||||||
val availableSoundDevices: List<CallAudioManager.SoundDevice> = emptyList(),
|
val availableSoundDevices: List<CallAudioManager.SoundDevice> = emptyList(),
|
||||||
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
|
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
|
||||||
@ -62,7 +64,8 @@ sealed class VectorCallViewActions : VectorViewModelAction {
|
|||||||
object ToggleVideo : VectorCallViewActions()
|
object ToggleVideo : VectorCallViewActions()
|
||||||
data class ChangeAudioDevice(val device: CallAudioManager.SoundDevice) : VectorCallViewActions()
|
data class ChangeAudioDevice(val device: CallAudioManager.SoundDevice) : VectorCallViewActions()
|
||||||
object SwitchSoundDevice : VectorCallViewActions()
|
object SwitchSoundDevice : VectorCallViewActions()
|
||||||
object HeadSetButtonPressed: VectorCallViewActions()
|
object HeadSetButtonPressed : VectorCallViewActions()
|
||||||
|
object ToggleCamera : VectorCallViewActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class VectorCallViewEvents : VectorViewEvents {
|
sealed class VectorCallViewEvents : VectorViewEvents {
|
||||||
@ -140,6 +143,15 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCameraChange(mgr: WebRtcPeerConnectionManager) {
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
canSwitchCamera = mgr.canSwitchCamera(),
|
||||||
|
isFrontCamera = mgr.currentCameraType() == CameraType.FRONT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -160,7 +172,9 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
callState = Success(mxCall.state),
|
callState = Success(mxCall.state),
|
||||||
otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized,
|
otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized,
|
||||||
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice(),
|
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice(),
|
||||||
availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices()
|
availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices(),
|
||||||
|
isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT,
|
||||||
|
canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
@ -236,6 +250,9 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
Unit
|
Unit
|
||||||
}
|
}
|
||||||
|
VectorCallViewActions.ToggleCamera -> {
|
||||||
|
webRtcPeerConnectionManager.switchCamera()
|
||||||
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ import org.webrtc.AudioSource
|
|||||||
import org.webrtc.AudioTrack
|
import org.webrtc.AudioTrack
|
||||||
import org.webrtc.Camera1Enumerator
|
import org.webrtc.Camera1Enumerator
|
||||||
import org.webrtc.Camera2Enumerator
|
import org.webrtc.Camera2Enumerator
|
||||||
|
import org.webrtc.CameraVideoCapturer
|
||||||
import org.webrtc.DataChannel
|
import org.webrtc.DataChannel
|
||||||
import org.webrtc.DefaultVideoDecoderFactory
|
import org.webrtc.DefaultVideoDecoderFactory
|
||||||
import org.webrtc.DefaultVideoEncoderFactory
|
import org.webrtc.DefaultVideoEncoderFactory
|
||||||
@ -54,7 +55,6 @@ import org.webrtc.RtpReceiver
|
|||||||
import org.webrtc.SessionDescription
|
import org.webrtc.SessionDescription
|
||||||
import org.webrtc.SurfaceTextureHelper
|
import org.webrtc.SurfaceTextureHelper
|
||||||
import org.webrtc.SurfaceViewRenderer
|
import org.webrtc.SurfaceViewRenderer
|
||||||
import org.webrtc.VideoCapturer
|
|
||||||
import org.webrtc.VideoSource
|
import org.webrtc.VideoSource
|
||||||
import org.webrtc.VideoTrack
|
import org.webrtc.VideoTrack
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -78,6 +78,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||||||
fun onCurrentCallChange(call: MxCall?)
|
fun onCurrentCallChange(call: MxCall?)
|
||||||
fun onCaptureStateChanged(captureInError: Boolean)
|
fun onCaptureStateChanged(captureInError: Boolean)
|
||||||
fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {}
|
fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {}
|
||||||
|
fun onCameraChange(mgr: WebRtcPeerConnectionManager) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallsListeners = emptyList<CurrentCallListener>().toMutableList()
|
private val currentCallsListeners = emptyList<CurrentCallListener>().toMutableList()
|
||||||
@ -159,9 +160,10 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||||||
|
|
||||||
private var peerConnectionFactory: PeerConnectionFactory? = null
|
private var peerConnectionFactory: PeerConnectionFactory? = null
|
||||||
|
|
||||||
// private var localSdp: SessionDescription? = null
|
private var videoCapturer: CameraVideoCapturer? = null
|
||||||
|
|
||||||
private var videoCapturer: VideoCapturer? = null
|
private val availableCamera = ArrayList<CameraProxy>()
|
||||||
|
private var cameraInUse: CameraProxy? = null
|
||||||
|
|
||||||
var capturerIsInError = false
|
var capturerIsInError = false
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -413,51 +415,66 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||||||
|
|
||||||
// add video track if needed
|
// add video track if needed
|
||||||
if (callContext.mxCall.isVideoCall) {
|
if (callContext.mxCall.isVideoCall) {
|
||||||
|
availableCamera.clear()
|
||||||
|
|
||||||
val cameraIterator = if (Camera2Enumerator.isSupported(context)) Camera2Enumerator(context) else Camera1Enumerator(false)
|
val cameraIterator = if (Camera2Enumerator.isSupported(context)) Camera2Enumerator(context) else Camera1Enumerator(false)
|
||||||
|
|
||||||
|
// I don't realy know how that works if there are 2 front or 2 back cameras
|
||||||
val frontCamera = cameraIterator.deviceNames
|
val frontCamera = cameraIterator.deviceNames
|
||||||
?.firstOrNull { cameraIterator.isFrontFacing(it) }
|
?.firstOrNull { cameraIterator.isFrontFacing(it) }
|
||||||
?: cameraIterator.deviceNames?.first()
|
?.let {
|
||||||
|
CameraProxy(it, CameraType.FRONT).also { availableCamera.add(it) }
|
||||||
// TODO detect when no camera or no front camera
|
|
||||||
|
|
||||||
val videoCapturer = cameraIterator.createCapturer(frontCamera, object : CameraEventsHandlerAdapter() {
|
|
||||||
override fun onFirstFrameAvailable() {
|
|
||||||
super.onFirstFrameAvailable()
|
|
||||||
capturerIsInError = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCameraClosed() {
|
|
||||||
// This could happen if you open the camera app in chat
|
|
||||||
// We then register in order to restart capture as soon as the camera is available again
|
|
||||||
Timber.v("## VOIP onCameraClosed")
|
|
||||||
this@WebRtcPeerConnectionManager.capturerIsInError = true
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
val restarter = CameraRestarter(frontCamera ?: "", callContext.mxCall.callId)
|
|
||||||
callContext.cameraAvailabilityCallback = restarter
|
|
||||||
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
|
||||||
cameraManager.registerAvailabilityCallback(restarter, null)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
val videoSource = peerConnectionFactory!!.createVideoSource(videoCapturer.isScreencast)
|
val backCamera = cameraIterator.deviceNames
|
||||||
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase!!.eglBaseContext)
|
?.firstOrNull { cameraIterator.isBackFacing(it) }
|
||||||
Timber.v("## VOIP Local video source created")
|
?.let {
|
||||||
|
CameraProxy(it, CameraType.BACK).also { availableCamera.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
videoCapturer.initialize(surfaceTextureHelper, context.applicationContext, videoSource!!.capturerObserver)
|
val camera = frontCamera?.also { cameraInUse = frontCamera }
|
||||||
// HD
|
?: backCamera?.also { cameraInUse = backCamera }
|
||||||
videoCapturer.startCapture(1280, 720, 30)
|
?: null.also { cameraInUse = null }
|
||||||
|
|
||||||
this.videoCapturer = videoCapturer
|
if (camera != null) {
|
||||||
|
val videoCapturer = cameraIterator.createCapturer(camera.name, object : CameraEventsHandlerAdapter() {
|
||||||
|
override fun onFirstFrameAvailable() {
|
||||||
|
super.onFirstFrameAvailable()
|
||||||
|
capturerIsInError = false
|
||||||
|
}
|
||||||
|
|
||||||
val localVideoTrack = peerConnectionFactory!!.createVideoTrack("ARDAMSv0", videoSource)
|
override fun onCameraClosed() {
|
||||||
Timber.v("## VOIP Local video track created")
|
// This could happen if you open the camera app in chat
|
||||||
localVideoTrack?.setEnabled(true)
|
// We then register in order to restart capture as soon as the camera is available again
|
||||||
|
Timber.v("## VOIP onCameraClosed")
|
||||||
|
this@WebRtcPeerConnectionManager.capturerIsInError = true
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
val restarter = CameraRestarter(cameraInUse?.name ?: "", callContext.mxCall.callId)
|
||||||
|
callContext.cameraAvailabilityCallback = restarter
|
||||||
|
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||||
|
cameraManager.registerAvailabilityCallback(restarter, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
callContext.localVideoSource = videoSource
|
val videoSource = peerConnectionFactory!!.createVideoSource(videoCapturer.isScreencast)
|
||||||
callContext.localVideoTrack = localVideoTrack
|
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase!!.eglBaseContext)
|
||||||
|
Timber.v("## VOIP Local video source created")
|
||||||
|
|
||||||
localMediaStream?.addTrack(localVideoTrack)
|
videoCapturer.initialize(surfaceTextureHelper, context.applicationContext, videoSource!!.capturerObserver)
|
||||||
|
// HD
|
||||||
|
videoCapturer.startCapture(1280, 720, 30)
|
||||||
|
this.videoCapturer = videoCapturer
|
||||||
|
|
||||||
|
val localVideoTrack = peerConnectionFactory!!.createVideoTrack("ARDAMSv0", videoSource)
|
||||||
|
Timber.v("## VOIP Local video track created")
|
||||||
|
localVideoTrack?.setEnabled(true)
|
||||||
|
|
||||||
|
callContext.localVideoSource = videoSource
|
||||||
|
callContext.localVideoTrack = localVideoTrack
|
||||||
|
|
||||||
|
localMediaStream?.addTrack(localVideoTrack)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,6 +678,34 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||||||
currentCall?.localVideoTrack?.setEnabled(enabled)
|
currentCall?.localVideoTrack?.setEnabled(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun switchCamera() {
|
||||||
|
Timber.v("## VOIP switchCamera")
|
||||||
|
if (currentCall != null && currentCall?.mxCall?.state is CallState.Connected && currentCall?.mxCall?.isVideoCall == true) {
|
||||||
|
videoCapturer?.switchCamera(object : CameraVideoCapturer.CameraSwitchHandler {
|
||||||
|
// Invoked on success. |isFrontCamera| is true if the new camera is front facing.
|
||||||
|
override fun onCameraSwitchDone(isFrontCamera: Boolean) {
|
||||||
|
Timber.v("## VOIP onCameraSwitchDone isFront $isFrontCamera")
|
||||||
|
cameraInUse = availableCamera.first { if (isFrontCamera) it.type == CameraType.FRONT else it.type == CameraType.BACK }
|
||||||
|
currentCallsListeners.forEach {
|
||||||
|
tryThis { it.onCameraChange(this@WebRtcPeerConnectionManager) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraSwitchError(errorDescription: String?) {
|
||||||
|
Timber.v("## VOIP onCameraSwitchError isFront $errorDescription")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canSwitchCamera(): Boolean {
|
||||||
|
return availableCamera.size > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun currentCameraType(): CameraType? {
|
||||||
|
return cameraInUse?.type
|
||||||
|
}
|
||||||
|
|
||||||
fun endCall() {
|
fun endCall() {
|
||||||
// Update service state
|
// Update service state
|
||||||
CallService.onNoActiveCall(context)
|
CallService.onNoActiveCall(context)
|
||||||
|
42
vector/src/main/res/drawable/ic_video_flip.xml
Normal file
42
vector/src/main/res/drawable/ic_video_flip.xml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M4,9L1,12L4,15"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10,15L13,12L10,9"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M13,12H2"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23,7L16,12L23,17V7Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M1,7.5V7C1,5.8954 1.8954,5 3,5H14C15.1046,5 16,5.8954 16,7V17C16,18.1046 15.1046,19 14,19H3C1.8954,19 1,18.1046 1,17V16.5"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
@ -11,9 +11,18 @@
|
|||||||
android:id="@+id/callControlsSoundDevice"
|
android:id="@+id/callControlsSoundDevice"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
tools:actionTitle="Select sound device"
|
app:actionTitle="@string/call_select_sound_device"
|
||||||
tools:actionDescription="Speaker"
|
tools:actionDescription="Speaker"
|
||||||
app:leftIcon="@drawable/ic_call_speaker_default"
|
app:leftIcon="@drawable/ic_call_speaker_default"
|
||||||
app:tint="?attr/riotx_text_primary" />
|
app:tint="?attr/riotx_text_primary" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.BottomSheetActionButton
|
||||||
|
android:id="@+id/callControlsSwitchCamera"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:actionTitle="@string/call_switch_camera"
|
||||||
|
tools:actionDescription="Front"
|
||||||
|
app:leftIcon="@drawable/ic_video_flip"
|
||||||
|
app:tint="?attr/riotx_text_primary" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -219,6 +219,9 @@
|
|||||||
<string name="sound_device_speaker">Speaker</string>
|
<string name="sound_device_speaker">Speaker</string>
|
||||||
<string name="sound_device_headset">Headset</string>
|
<string name="sound_device_headset">Headset</string>
|
||||||
<string name="sound_device_wireless_headset">Wireless Headset</string>
|
<string name="sound_device_wireless_headset">Wireless Headset</string>
|
||||||
|
<string name="call_switch_camera">Switch Camera</string>
|
||||||
|
<string name="call_camera_front">Front</string>
|
||||||
|
<string name="call_camera_back">Back</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="option_send_files">Send files</string>
|
<string name="option_send_files">Send files</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user