basic toggle mute and toggle video
This commit is contained in:
parent
46d7db8214
commit
91f28bfb8a
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.call
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -44,6 +45,13 @@ class CallControlsView @JvmOverloads constructor(
|
||||||
@BindView(R.id.connectedControls)
|
@BindView(R.id.connectedControls)
|
||||||
lateinit var connectedControls: ViewGroup
|
lateinit var connectedControls: ViewGroup
|
||||||
|
|
||||||
|
|
||||||
|
@BindView(R.id.iv_mute_toggle)
|
||||||
|
lateinit var muteIcon: ImageView
|
||||||
|
|
||||||
|
@BindView(R.id.iv_video_toggle)
|
||||||
|
lateinit var videoToggleIcon: ImageView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ConstraintLayout.inflate(context, R.layout.fragment_call_controls, this)
|
ConstraintLayout.inflate(context, R.layout.fragment_call_controls, this)
|
||||||
// layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
// layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
@ -65,11 +73,21 @@ class CallControlsView @JvmOverloads constructor(
|
||||||
interactionListener?.didEndCall()
|
interactionListener?.didEndCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.iv_end_call)
|
@OnClick(R.id.iv_mute_toggle)
|
||||||
fun hangupCall() {
|
fun toggleMute() {
|
||||||
|
interactionListener?.didTapToggleMute()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateForState(callState: CallState?) {
|
@OnClick(R.id.iv_video_toggle)
|
||||||
|
fun toggleVideo() {
|
||||||
|
interactionListener?.didTapToggleVideo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateForState(state: VectorCallViewState) {
|
||||||
|
val callState = state.callState.invoke()
|
||||||
|
muteIcon.setImageResource(if (state.isAudioMuted) R.drawable.ic_microphone_off else R.drawable.ic_microphone_on)
|
||||||
|
videoToggleIcon.setImageResource(if (state.isVideoEnabled) R.drawable.ic_video else R.drawable.ic_video_off)
|
||||||
|
|
||||||
when (callState) {
|
when (callState) {
|
||||||
CallState.DIALING -> {
|
CallState.DIALING -> {
|
||||||
}
|
}
|
||||||
|
@ -94,11 +112,15 @@ class CallControlsView @JvmOverloads constructor(
|
||||||
connectedControls.isVisible = false
|
connectedControls.isVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InteractionListener {
|
interface InteractionListener {
|
||||||
fun didAcceptIncomingCall()
|
fun didAcceptIncomingCall()
|
||||||
fun didDeclineIncomingCall()
|
fun didDeclineIncomingCall()
|
||||||
fun didEndCall()
|
fun didEndCall()
|
||||||
|
fun didTapToggleMute()
|
||||||
|
fun didTapToggleVideo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
|
|
||||||
private fun renderState(state: VectorCallViewState) {
|
private fun renderState(state: VectorCallViewState) {
|
||||||
Timber.v("## VOIP renderState call $state")
|
Timber.v("## VOIP renderState call $state")
|
||||||
callControlsView.updateForState(state.callState.invoke())
|
callControlsView.updateForState(state)
|
||||||
when (state.callState.invoke()) {
|
when (state.callState.invoke()) {
|
||||||
CallState.IDLE -> {
|
CallState.IDLE -> {
|
||||||
}
|
}
|
||||||
|
@ -339,4 +339,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
override fun didEndCall() {
|
override fun didEndCall() {
|
||||||
callViewModel.handle(VectorCallViewActions.EndCall)
|
callViewModel.handle(VectorCallViewActions.EndCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun didTapToggleMute() {
|
||||||
|
callViewModel.handle(VectorCallViewActions.ToggleMute)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didTapToggleVideo() {
|
||||||
|
callViewModel.handle(VectorCallViewActions.ToggleVideo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@ data class VectorCallViewState(
|
||||||
val callId: String? = null,
|
val callId: String? = null,
|
||||||
val roomId: String = "",
|
val roomId: String = "",
|
||||||
val isVideoCall: Boolean,
|
val isVideoCall: Boolean,
|
||||||
|
val isAudioMuted: Boolean = false,
|
||||||
|
val isVideoEnabled: Boolean = true,
|
||||||
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
|
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
|
||||||
val callState: Async<CallState> = Uninitialized
|
val callState: Async<CallState> = Uninitialized
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
@ -49,6 +51,8 @@ sealed class VectorCallViewActions : VectorViewModelAction {
|
||||||
object EndCall : VectorCallViewActions()
|
object EndCall : VectorCallViewActions()
|
||||||
object AcceptCall : VectorCallViewActions()
|
object AcceptCall : VectorCallViewActions()
|
||||||
object DeclineCall : VectorCallViewActions()
|
object DeclineCall : VectorCallViewActions()
|
||||||
|
object ToggleMute : VectorCallViewActions()
|
||||||
|
object ToggleVideo : VectorCallViewActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class VectorCallViewEvents : VectorViewEvents {
|
sealed class VectorCallViewEvents : VectorViewEvents {
|
||||||
|
@ -65,26 +69,6 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
||||||
) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) {
|
) : VectorViewModel<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(initialState) {
|
||||||
|
|
||||||
// private val callServiceListener: CallsListener = object : CallsListener {
|
|
||||||
// override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
|
||||||
// withState { state ->
|
|
||||||
// if (callAnswerContent.callId == state.callId) {
|
|
||||||
// _viewEvents.post(VectorCallViewEvents.CallAnswered(callAnswerContent))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
|
||||||
// withState { state ->
|
|
||||||
// if (callHangupContent.callId == state.callId) {
|
|
||||||
// _viewEvents.post(VectorCallViewEvents.CallHangup(callHangupContent))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
var autoReplyIfNeeded: Boolean = false
|
var autoReplyIfNeeded: Boolean = false
|
||||||
|
|
||||||
var call: MxCall? = null
|
var call: MxCall? = null
|
||||||
|
@ -120,7 +104,6 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// session.callService().addCallListener(callServiceListener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
@ -144,6 +127,26 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
webRtcPeerConnectionManager.endCall()
|
webRtcPeerConnectionManager.endCall()
|
||||||
}
|
}
|
||||||
|
VectorCallViewActions.ToggleMute -> {
|
||||||
|
withState {
|
||||||
|
val muted = it.isAudioMuted
|
||||||
|
webRtcPeerConnectionManager.muteCall(!muted)
|
||||||
|
setState {
|
||||||
|
copy(isAudioMuted = !muted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VectorCallViewActions.ToggleVideo -> {
|
||||||
|
withState {
|
||||||
|
if(it.isVideoCall) {
|
||||||
|
val videoEnabled = it.isVideoEnabled
|
||||||
|
webRtcPeerConnectionManager.enableVideo(!videoEnabled)
|
||||||
|
setState {
|
||||||
|
copy(isVideoEnabled = !videoEnabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,10 +196,10 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendSdpOffer(callContext: CallContext) {
|
private fun sendSdpOffer(callContext: CallContext) {
|
||||||
// executor.execute {
|
|
||||||
val constraints = MediaConstraints()
|
val constraints = MediaConstraints()
|
||||||
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
// These are deprecated options
|
||||||
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.mxCall?.isVideoCall == true) "true" else "false"))
|
// constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
||||||
|
// constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.mxCall?.isVideoCall == true) "true" else "false"))
|
||||||
|
|
||||||
Timber.v("## VOIP creating offer...")
|
Timber.v("## VOIP creating offer...")
|
||||||
callContext.peerConnection?.createOffer(object : SdpObserverAdapter() {
|
callContext.peerConnection?.createOffer(object : SdpObserverAdapter() {
|
||||||
|
@ -211,7 +211,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
currentCall?.mxCall?.offerSdp(p0)
|
currentCall?.mxCall?.offerSdp(p0)
|
||||||
}
|
}
|
||||||
}, constraints)
|
}, constraints)
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTurnServer(callback: ((TurnServer?) -> Unit)) {
|
private fun getTurnServer(callback: ((TurnServer?) -> Unit)) {
|
||||||
|
@ -355,7 +355,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
|
|
||||||
// localViewRenderer?.let { localVideoTrack?.addSink(it) }
|
// localViewRenderer?.let { localVideoTrack?.addSink(it) }
|
||||||
localMediaStream?.addTrack(localVideoTrack)
|
localMediaStream?.addTrack(localVideoTrack)
|
||||||
callContext.localMediaStream = localMediaStream
|
// callContext.localMediaStream = localMediaStream
|
||||||
// remoteVideoTrack?.setEnabled(true)
|
// remoteVideoTrack?.setEnabled(true)
|
||||||
// remoteVideoTrack?.let {
|
// remoteVideoTrack?.let {
|
||||||
// it.setEnabled(true)
|
// it.setEnabled(true)
|
||||||
|
@ -418,20 +418,20 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
|
|
||||||
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints().apply {
|
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints().apply {
|
||||||
// add all existing audio filters to avoid having echos
|
// add all existing audio filters to avoid having echos
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation", "true"))
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation2", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation2", "true"))
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googDAEchoCancellation", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googDAEchoCancellation", "true"))
|
||||||
|
//
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true"))
|
||||||
|
//
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl", "true"))
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl2", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl2", "true"))
|
||||||
|
//
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression", "true"))
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression2", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression2", "true"))
|
||||||
|
//
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googAudioMirroring", "false"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googAudioMirroring", "false"))
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("googHighpassFilter", "true"))
|
// mandatory.add(MediaConstraints.KeyValuePair("googHighpassFilter", "true"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +509,14 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun muteCall(muted: Boolean) {
|
||||||
|
currentCall?.localAudioTrack?.setEnabled(!muted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableVideo(enabled: Boolean) {
|
||||||
|
currentCall?.localVideoTrack?.setEnabled(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
fun endCall() {
|
fun endCall() {
|
||||||
currentCall?.mxCall?.hangUp()
|
currentCall?.mxCall?.hangUp()
|
||||||
currentCall = null
|
currentCall = null
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<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="M10.66,5H14C14.5304,5 15.0391,5.2107 15.4142,5.5858C15.7893,5.9609 16,6.4696 16,7V10.34L17,11.34L23,7V17M16,16V17C16,17.5304 15.7893,18.0391 15.4142,18.4142C15.0391,18.7893 14.5304,19 14,19H3C2.4696,19 1.9609,18.7893 1.5858,18.4142C1.2107,18.0391 1,17.5304 1,17V7C1,6.4696 1.2107,5.9609 1.5858,5.5858C1.9609,5.2107 2.4696,5 3,5H5L16,16Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M1,1L23,23"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#2E2F32"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
Loading…
Reference in New Issue