mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
basic toggle mute and toggle video
This commit is contained in:
parent
46d7db8214
commit
91f28bfb8a
5 changed files with 105 additions and 44 deletions
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.call
|
|||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
|
@ -44,6 +45,13 @@ class CallControlsView @JvmOverloads constructor(
|
|||
@BindView(R.id.connectedControls)
|
||||
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 {
|
||||
ConstraintLayout.inflate(context, R.layout.fragment_call_controls, this)
|
||||
// layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
@ -65,11 +73,21 @@ class CallControlsView @JvmOverloads constructor(
|
|||
interactionListener?.didEndCall()
|
||||
}
|
||||
|
||||
@OnClick(R.id.iv_end_call)
|
||||
fun hangupCall() {
|
||||
@OnClick(R.id.iv_mute_toggle)
|
||||
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) {
|
||||
CallState.DIALING -> {
|
||||
}
|
||||
|
@ -94,11 +112,15 @@ class CallControlsView @JvmOverloads constructor(
|
|||
connectedControls.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface InteractionListener {
|
||||
fun didAcceptIncomingCall()
|
||||
fun didDeclineIncomingCall()
|
||||
fun didEndCall()
|
||||
fun didTapToggleMute()
|
||||
fun didTapToggleVideo()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
|||
|
||||
private fun renderState(state: VectorCallViewState) {
|
||||
Timber.v("## VOIP renderState call $state")
|
||||
callControlsView.updateForState(state.callState.invoke())
|
||||
callControlsView.updateForState(state)
|
||||
when (state.callState.invoke()) {
|
||||
CallState.IDLE -> {
|
||||
}
|
||||
|
@ -339,4 +339,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
|||
override fun didEndCall() {
|
||||
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 roomId: String = "",
|
||||
val isVideoCall: Boolean,
|
||||
val isAudioMuted: Boolean = false,
|
||||
val isVideoEnabled: Boolean = true,
|
||||
val otherUserMatrixItem: Async<MatrixItem> = Uninitialized,
|
||||
val callState: Async<CallState> = Uninitialized
|
||||
) : MvRxState
|
||||
|
@ -49,6 +51,8 @@ sealed class VectorCallViewActions : VectorViewModelAction {
|
|||
object EndCall : VectorCallViewActions()
|
||||
object AcceptCall : VectorCallViewActions()
|
||||
object DeclineCall : VectorCallViewActions()
|
||||
object ToggleMute : VectorCallViewActions()
|
||||
object ToggleVideo : VectorCallViewActions()
|
||||
}
|
||||
|
||||
sealed class VectorCallViewEvents : VectorViewEvents {
|
||||
|
@ -65,26 +69,6 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
||||
) : 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 call: MxCall? = null
|
||||
|
@ -120,7 +104,6 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// session.callService().addCallListener(callServiceListener)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
@ -144,6 +127,26 @@ class VectorCallViewModel @AssistedInject constructor(
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -196,10 +196,10 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
}
|
||||
|
||||
private fun sendSdpOffer(callContext: CallContext) {
|
||||
// executor.execute {
|
||||
val constraints = MediaConstraints()
|
||||
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
||||
constraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", if (currentCall?.mxCall?.isVideoCall == true) "true" else "false"))
|
||||
// These are deprecated options
|
||||
// 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...")
|
||||
callContext.peerConnection?.createOffer(object : SdpObserverAdapter() {
|
||||
|
@ -211,7 +211,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
currentCall?.mxCall?.offerSdp(p0)
|
||||
}
|
||||
}, constraints)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
private fun getTurnServer(callback: ((TurnServer?) -> Unit)) {
|
||||
|
@ -355,7 +355,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
|
||||
// localViewRenderer?.let { localVideoTrack?.addSink(it) }
|
||||
localMediaStream?.addTrack(localVideoTrack)
|
||||
callContext.localMediaStream = localMediaStream
|
||||
// callContext.localMediaStream = localMediaStream
|
||||
// remoteVideoTrack?.setEnabled(true)
|
||||
// remoteVideoTrack?.let {
|
||||
// it.setEnabled(true)
|
||||
|
@ -418,20 +418,20 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
|
||||
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints().apply {
|
||||
// add all existing audio filters to avoid having echos
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation", "true"))
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation2", "true"))
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googDAEchoCancellation", "true"))
|
||||
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true"))
|
||||
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl", "true"))
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl2", "true"))
|
||||
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression", "true"))
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression2", "true"))
|
||||
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googAudioMirroring", "false"))
|
||||
mandatory.add(MediaConstraints.KeyValuePair("googHighpassFilter", "true"))
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation", "true"))
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googEchoCancellation2", "true"))
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googDAEchoCancellation", "true"))
|
||||
//
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googTypingNoiseDetection", "true"))
|
||||
//
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl", "true"))
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googAutoGainControl2", "true"))
|
||||
//
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression", "true"))
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googNoiseSuppression2", "true"))
|
||||
//
|
||||
// mandatory.add(MediaConstraints.KeyValuePair("googAudioMirroring", "false"))
|
||||
// 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() {
|
||||
currentCall?.mxCall?.hangUp()
|
||||
currentCall = null
|
||||
|
|
20
vector/src/main/res/drawable/ic_video_off.xml
Normal file
20
vector/src/main/res/drawable/ic_video_off.xml
Normal file
|
@ -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 a new issue