Toggle HD/SD
This commit is contained in:
parent
f3e2a55869
commit
374790176f
|
@ -19,11 +19,14 @@ 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.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
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
|
||||||
|
import kotlinx.android.synthetic.main.activity_call.*
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet_call_controls.*
|
import kotlinx.android.synthetic.main.bottom_sheet_call_controls.*
|
||||||
|
import kotlinx.android.synthetic.main.vector_preference_push_rule.view.*
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
|
|
||||||
class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
|
@ -47,6 +50,11 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callControlsToggleSDHD.clickableView.debouncedClicks {
|
||||||
|
callViewModel.handle(VectorCallViewActions.ToggleHDSD)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
callViewModel.observeViewEvents {
|
callViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is VectorCallViewEvents.ShowSoundDeviceChooser -> {
|
is VectorCallViewEvents.ShowSoundDeviceChooser -> {
|
||||||
|
@ -112,5 +120,20 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
|
|
||||||
callControlsSwitchCamera.isVisible = state.canSwitchCamera
|
callControlsSwitchCamera.isVisible = state.canSwitchCamera
|
||||||
callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back)
|
callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back)
|
||||||
|
|
||||||
|
if (state.isVideoCall) {
|
||||||
|
callControlsToggleSDHD.isVisible = true
|
||||||
|
if (state.isHD) {
|
||||||
|
callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_off)
|
||||||
|
callControlsToggleSDHD.subTitle = null
|
||||||
|
callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd)
|
||||||
|
} else {
|
||||||
|
callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_on)
|
||||||
|
callControlsToggleSDHD.subTitle = null
|
||||||
|
callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd_disabled)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callControlsToggleSDHD.isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,3 +25,8 @@ data class CameraProxy(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: CameraType
|
val type: CameraType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sealed class CaptureFormat(val width: Int, val height: Int, val fps: Int) {
|
||||||
|
object HD : CaptureFormat(1280, 720, 30)
|
||||||
|
object SD : CaptureFormat(640, 480, 30)
|
||||||
|
}
|
||||||
|
|
|
@ -33,12 +33,12 @@ class SharedActiveCallViewModel @Inject constructor(
|
||||||
|
|
||||||
val activeCall: MutableLiveData<MxCall?> = MutableLiveData()
|
val activeCall: MutableLiveData<MxCall?> = MutableLiveData()
|
||||||
|
|
||||||
val callStateListener = object: MxCall.StateListener {
|
val callStateListener = object : MxCall.StateListener {
|
||||||
|
|
||||||
override fun onStateUpdate(call: MxCall) {
|
override fun onStateUpdate(call: MxCall) {
|
||||||
if (activeCall.value?.callId == call.callId) {
|
if (activeCall.value?.callId == call.callId) {
|
||||||
activeCall.postValue(call)
|
activeCall.postValue(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,10 +48,6 @@ class SharedActiveCallViewModel @Inject constructor(
|
||||||
activeCall.postValue(call)
|
activeCall.postValue(call)
|
||||||
call?.addListener(callStateListener)
|
call?.addListener(callStateListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCaptureStateChanged(captureInError: Boolean) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -48,6 +48,7 @@ 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 isHD: Boolean = false,
|
||||||
val isFrontCamera: Boolean = true,
|
val isFrontCamera: Boolean = true,
|
||||||
val canSwitchCamera: Boolean = true,
|
val canSwitchCamera: Boolean = true,
|
||||||
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
|
val soundDevice: CallAudioManager.SoundDevice = CallAudioManager.SoundDevice.PHONE,
|
||||||
|
@ -66,6 +67,7 @@ sealed class VectorCallViewActions : VectorViewModelAction {
|
||||||
object SwitchSoundDevice : VectorCallViewActions()
|
object SwitchSoundDevice : VectorCallViewActions()
|
||||||
object HeadSetButtonPressed : VectorCallViewActions()
|
object HeadSetButtonPressed : VectorCallViewActions()
|
||||||
object ToggleCamera : VectorCallViewActions()
|
object ToggleCamera : VectorCallViewActions()
|
||||||
|
object ToggleHDSD : VectorCallViewActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class VectorCallViewEvents : VectorViewEvents {
|
sealed class VectorCallViewEvents : VectorViewEvents {
|
||||||
|
@ -129,9 +131,12 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
override fun onCurrentCallChange(call: MxCall?) {
|
override fun onCurrentCallChange(call: MxCall?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCaptureStateChanged(captureInError: Boolean) {
|
override fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) {
|
||||||
setState {
|
setState {
|
||||||
copy(isVideoCaptureInError = captureInError)
|
copy(
|
||||||
|
isVideoCaptureInError = mgr.capturerIsInError,
|
||||||
|
isHD = mgr.currentCaptureFormat() is CaptureFormat.HD
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +179,8 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice(),
|
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice(),
|
||||||
availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices(),
|
availableSoundDevices = webRtcPeerConnectionManager.audioManager.getAvailableSoundDevices(),
|
||||||
isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT,
|
isFrontCamera = webRtcPeerConnectionManager.currentCameraType() == CameraType.FRONT,
|
||||||
canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera()
|
canSwitchCamera = webRtcPeerConnectionManager.canSwitchCamera(),
|
||||||
|
isHD = mxCall.isVideoCall && webRtcPeerConnectionManager.currentCaptureFormat() is CaptureFormat.HD
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
|
@ -253,6 +259,10 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
VectorCallViewActions.ToggleCamera -> {
|
VectorCallViewActions.ToggleCamera -> {
|
||||||
webRtcPeerConnectionManager.switchCamera()
|
webRtcPeerConnectionManager.switchCamera()
|
||||||
}
|
}
|
||||||
|
VectorCallViewActions.ToggleHDSD -> {
|
||||||
|
if (!state.isVideoCall) return@withState
|
||||||
|
webRtcPeerConnectionManager.setCaptureFormat(if (state.isHD) CaptureFormat.SD else CaptureFormat.HD)
|
||||||
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface CurrentCallListener {
|
||||||
fun onCurrentCallChange(call: MxCall?)
|
fun onCurrentCallChange(call: MxCall?)
|
||||||
fun onCaptureStateChanged(captureInError: Boolean)
|
fun onCaptureStateChanged(mgr: WebRtcPeerConnectionManager) {}
|
||||||
fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {}
|
fun onAudioDevicesChange(mgr: WebRtcPeerConnectionManager) {}
|
||||||
fun onCameraChange(mgr: WebRtcPeerConnectionManager) {}
|
fun onCameraChange(mgr: WebRtcPeerConnectionManager) {}
|
||||||
}
|
}
|
||||||
|
@ -165,11 +165,13 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
private val availableCamera = ArrayList<CameraProxy>()
|
private val availableCamera = ArrayList<CameraProxy>()
|
||||||
private var cameraInUse: CameraProxy? = null
|
private var cameraInUse: CameraProxy? = null
|
||||||
|
|
||||||
|
private var currentCaptureMode: CaptureFormat = CaptureFormat.HD
|
||||||
|
|
||||||
var capturerIsInError = false
|
var capturerIsInError = false
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
currentCallsListeners.forEach {
|
currentCallsListeners.forEach {
|
||||||
tryThis { it.onCaptureStateChanged(value) }
|
tryThis { it.onCaptureStateChanged(this) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +338,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
// Fallback for old android, try to restart capture when attached
|
// Fallback for old android, try to restart capture when attached
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && capturerIsInError && call.mxCall.isVideoCall) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && capturerIsInError && call.mxCall.isVideoCall) {
|
||||||
// try to restart capture?
|
// try to restart capture?
|
||||||
videoCapturer?.startCapture(1280, 720, 30)
|
videoCapturer?.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps)
|
||||||
}
|
}
|
||||||
// sink existing tracks (configuration change, e.g screen rotation)
|
// sink existing tracks (configuration change, e.g screen rotation)
|
||||||
attachViewRenderersInternal()
|
attachViewRenderersInternal()
|
||||||
|
@ -463,7 +465,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
|
|
||||||
videoCapturer.initialize(surfaceTextureHelper, context.applicationContext, videoSource!!.capturerObserver)
|
videoCapturer.initialize(surfaceTextureHelper, context.applicationContext, videoSource!!.capturerObserver)
|
||||||
// HD
|
// HD
|
||||||
videoCapturer.startCapture(1280, 720, 30)
|
videoCapturer.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps)
|
||||||
this.videoCapturer = videoCapturer
|
this.videoCapturer = videoCapturer
|
||||||
|
|
||||||
val localVideoTrack = peerConnectionFactory!!.createVideoTrack("ARDAMSv0", videoSource)
|
val localVideoTrack = peerConnectionFactory!!.createVideoTrack("ARDAMSv0", videoSource)
|
||||||
|
@ -706,6 +708,21 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
return cameraInUse?.type
|
return cameraInUse?.type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCaptureFormat(format: CaptureFormat) {
|
||||||
|
Timber.v("## VOIP setCaptureFormat $format")
|
||||||
|
currentCall ?: return
|
||||||
|
executor.execute {
|
||||||
|
// videoCapturer?.stopCapture()
|
||||||
|
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
|
||||||
|
currentCaptureMode = format
|
||||||
|
currentCallsListeners.forEach { tryThis { it.onCaptureStateChanged(this) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun currentCaptureFormat(): CaptureFormat {
|
||||||
|
return currentCaptureMode
|
||||||
|
}
|
||||||
|
|
||||||
fun endCall() {
|
fun endCall() {
|
||||||
// Update service state
|
// Update service state
|
||||||
CallService.onNoActiveCall(context)
|
CallService.onNoActiveCall(context)
|
||||||
|
@ -971,7 +988,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
if (this.cameraId == cameraId && currentCall?.mxCall?.callId == callId) {
|
if (this.cameraId == cameraId && currentCall?.mxCall?.callId == callId) {
|
||||||
// re-start the capture
|
// re-start the capture
|
||||||
// TODO notify that video is enabled
|
// TODO notify that video is enabled
|
||||||
videoCapturer?.startCapture(1280, 720, 30)
|
videoCapturer?.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps)
|
||||||
(context.getSystemService(Context.CAMERA_SERVICE) as? CameraManager)
|
(context.getSystemService(Context.CAMERA_SERVICE) as? CameraManager)
|
||||||
?.unregisterAvailabilityCallback(this)
|
?.unregisterAvailabilityCallback(this)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<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="M19,3H5C3.89,3 3,3.9 3,5V19C3,20.1 3.89,21 5,21H19C20.1,21 21,20.1 21,19V5C21,3.9 20.1,3 19,3ZM11,15H9.5V13H7.5V15H6V9H7.5V11.5H9.5V9H11V15ZM13,9H17C17.55,9 18,9.45 18,10V14C18,14.55 17.55,15 17,15H13V9ZM14.5,13.5H16.5V10.5H14.5V13.5Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<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="M17.5,15V13H18.6L19.5,15H21L20.1,12.9C20.6,12.7 21,12.1 21,11.5V10.5C21,9.7 20.3,9 19.5,9H16V13.9L17.1,15H17.5ZM17.5,10.5H19.5V11.5H17.5V10.5ZM13,10.5V10.9L14.5,12.4V10.5C14.5,9.7 13.8,9 13,9H11.1L12.6,10.5H13ZM9.5,9.5L2.5,2.5L1.4,3.5L6.9,9H6.5V11H4.5V9H3V15H4.5V12.5H6.5V15H8V10.1L9.5,11.6V15H12.9L20.5,22.6L21.6,21.5L9.5,9.5Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
|
@ -25,4 +25,13 @@
|
||||||
app:leftIcon="@drawable/ic_video_flip"
|
app:leftIcon="@drawable/ic_video_flip"
|
||||||
app:tint="?attr/riotx_text_primary" />
|
app:tint="?attr/riotx_text_primary" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.BottomSheetActionButton
|
||||||
|
android:id="@+id/callControlsToggleSDHD"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:actionTitle="@string/call_switch_camera"
|
||||||
|
tools:actionDescription="Front"
|
||||||
|
app:leftIcon="@drawable/ic_hd"
|
||||||
|
app:tint="?attr/riotx_text_primary" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -222,6 +222,9 @@
|
||||||
<string name="call_switch_camera">Switch Camera</string>
|
<string name="call_switch_camera">Switch Camera</string>
|
||||||
<string name="call_camera_front">Front</string>
|
<string name="call_camera_front">Front</string>
|
||||||
<string name="call_camera_back">Back</string>
|
<string name="call_camera_back">Back</string>
|
||||||
|
<string name="call_format_turn_hd_off">Turn HD off</string>
|
||||||
|
<string name="call_format_turn_hd_on">Turn HD on</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<string name="option_send_files">Send files</string>
|
<string name="option_send_files">Send files</string>
|
||||||
|
|
Loading…
Reference in New Issue