mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Cleaning call states
This commit is contained in:
parent
56ed56a986
commit
a1907aaddb
@ -21,17 +21,20 @@ enum class CallState {
|
||||
/** Idle, setting up objects */
|
||||
IDLE,
|
||||
|
||||
|
||||
/** Dialing. Outgoing call is signaling the remote peer */
|
||||
DIALING,
|
||||
|
||||
|
||||
/** Local ringing. Incoming call offer received */
|
||||
LOCAL_RINGING,
|
||||
|
||||
/** Answering. Incoming call is responding to remote peer */
|
||||
ANSWERING,
|
||||
|
||||
/** Remote ringing. Outgoing call, ICE negotiation is complete */
|
||||
REMOTE_RINGING,
|
||||
/** Connecting. Incoming/Outgoing Offer and answer are known, Currently checking and testing pairs of ice candidates */
|
||||
CONNECTING,
|
||||
|
||||
/** Local ringing. Incoming call, ICE negotiation is complete */
|
||||
LOCAL_RINGING,
|
||||
|
||||
/** Connected. Incoming/Outgoing call, the call is connected */
|
||||
CONNECTED,
|
||||
|
@ -74,15 +74,16 @@ internal class MxCallImpl(
|
||||
|
||||
init {
|
||||
if (isOutgoing) {
|
||||
state = CallState.DIALING
|
||||
state = CallState.IDLE
|
||||
} else {
|
||||
// because it's created on reception of an offer
|
||||
state = CallState.LOCAL_RINGING
|
||||
}
|
||||
}
|
||||
|
||||
override fun offerSdp(sdp: SessionDescription) {
|
||||
if (!isOutgoing) return
|
||||
state = CallState.REMOTE_RINGING
|
||||
state = CallState.DIALING
|
||||
CallInviteContent(
|
||||
callId = callId,
|
||||
lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS,
|
||||
@ -108,6 +109,7 @@ internal class MxCallImpl(
|
||||
}
|
||||
|
||||
override fun sendLocalIceCandidateRemovals(candidates: List<IceCandidate>) {
|
||||
// For now we don't support this flow
|
||||
}
|
||||
|
||||
override fun hangUp() {
|
||||
|
@ -35,17 +35,16 @@ class CallControlsView @JvmOverloads constructor(
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
@BindView(R.id.incomingRingingControls)
|
||||
lateinit var incomingRingingControls: ViewGroup
|
||||
// @BindView(R.id.iv_icr_accept_call)
|
||||
// lateinit var incomingRingingControlAccept: ImageView
|
||||
// @BindView(R.id.iv_icr_end_call)
|
||||
// lateinit var incomingRingingControlDecline: ImageView
|
||||
@BindView(R.id.ringingControls)
|
||||
lateinit var ringingControls: ViewGroup
|
||||
@BindView(R.id.iv_icr_accept_call)
|
||||
lateinit var ringingControlAccept: ImageView
|
||||
@BindView(R.id.iv_icr_end_call)
|
||||
lateinit var ringingControlDecline: ImageView
|
||||
|
||||
@BindView(R.id.connectedControls)
|
||||
lateinit var connectedControls: ViewGroup
|
||||
|
||||
|
||||
@BindView(R.id.iv_mute_toggle)
|
||||
lateinit var muteIcon: ImageView
|
||||
|
||||
@ -89,31 +88,31 @@ class CallControlsView @JvmOverloads constructor(
|
||||
videoToggleIcon.setImageResource(if (state.isVideoEnabled) R.drawable.ic_video else R.drawable.ic_video_off)
|
||||
|
||||
when (callState) {
|
||||
CallState.DIALING -> {
|
||||
}
|
||||
CallState.ANSWERING -> {
|
||||
incomingRingingControls.isVisible = false
|
||||
CallState.IDLE,
|
||||
CallState.DIALING,
|
||||
CallState.CONNECTING,
|
||||
CallState.ANSWERING -> {
|
||||
ringingControls.isVisible = true
|
||||
ringingControlAccept.isVisible = false
|
||||
ringingControlDecline.isVisible = true
|
||||
connectedControls.isVisible = false
|
||||
}
|
||||
CallState.REMOTE_RINGING -> {
|
||||
}
|
||||
CallState.LOCAL_RINGING -> {
|
||||
incomingRingingControls.isVisible = true
|
||||
CallState.LOCAL_RINGING -> {
|
||||
ringingControls.isVisible = true
|
||||
ringingControlAccept.isVisible = true
|
||||
ringingControlDecline.isVisible = true
|
||||
connectedControls.isVisible = false
|
||||
}
|
||||
CallState.CONNECTED -> {
|
||||
incomingRingingControls.isVisible = false
|
||||
CallState.CONNECTED -> {
|
||||
ringingControls.isVisible = false
|
||||
connectedControls.isVisible = true
|
||||
}
|
||||
CallState.TERMINATED,
|
||||
CallState.IDLE,
|
||||
null -> {
|
||||
incomingRingingControls.isVisible = false
|
||||
null -> {
|
||||
ringingControls.isVisible = false
|
||||
connectedControls.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface InteractionListener {
|
||||
|
@ -91,17 +91,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||
|
||||
private var rootEglBase: EglBase? = null
|
||||
|
||||
// var callHeadsUpService: CallHeadsUpService? = null
|
||||
// private val serviceConnection = object : ServiceConnection {
|
||||
// override fun onServiceDisconnected(name: ComponentName?) {
|
||||
// finish()
|
||||
// }
|
||||
//
|
||||
// override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
// callHeadsUpService = (service as? CallHeadsUpService.CallHeadsUpServiceBinder)?.getService()
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun doBeforeSetContentView() {
|
||||
// Set window styles for fullscreen-window size. Needs to be done before adding content.
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
@ -130,9 +119,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||
// window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
// window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
// tryThis { unbindService(serviceConnection) }
|
||||
// bindService(Intent(this, CallHeadsUpService::class.java), serviceConnection, 0)
|
||||
|
||||
if (intent.hasExtra(MvRx.KEY_ARG)) {
|
||||
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
|
||||
} else {
|
||||
@ -184,8 +170,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||
Timber.v("## VOIP renderState call $state")
|
||||
callControlsView.updateForState(state)
|
||||
when (state.callState.invoke()) {
|
||||
CallState.IDLE -> {
|
||||
}
|
||||
CallState.IDLE,
|
||||
CallState.DIALING -> {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
@ -196,35 +181,40 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||
callTypeText.setText(if (state.isVideoCall) R.string.action_video_call else R.string.action_voice_call)
|
||||
}
|
||||
}
|
||||
CallState.ANSWERING -> {
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.setText(R.string.call_connecting)
|
||||
state.otherUserMatrixItem.invoke()?.let {
|
||||
avatarRenderer.render(it, otherMemberAvatar)
|
||||
}
|
||||
// fullscreenRenderer.isVisible = true
|
||||
// pipRenderer.isVisible = true
|
||||
}
|
||||
CallState.REMOTE_RINGING -> {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.setText(
|
||||
if (state.isVideoCall) R.string.incoming_video_call else R.string.incoming_voice_call
|
||||
)
|
||||
}
|
||||
|
||||
CallState.LOCAL_RINGING -> {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.text = null
|
||||
state.otherUserMatrixItem.invoke()?.let {
|
||||
avatarRenderer.render(it, otherMemberAvatar)
|
||||
participantNameText.text = it.getBestName()
|
||||
callTypeText.setText(if (state.isVideoCall) R.string.action_video_call else R.string.action_voice_call)
|
||||
}
|
||||
}
|
||||
|
||||
CallState.ANSWERING -> {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.setText(R.string.call_connecting)
|
||||
state.otherUserMatrixItem.invoke()?.let {
|
||||
avatarRenderer.render(it, otherMemberAvatar)
|
||||
}
|
||||
}
|
||||
CallState.CONNECTING -> {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.setText(R.string.call_connecting)
|
||||
}
|
||||
CallState.CONNECTED -> {
|
||||
// TODO only if is video call
|
||||
callVideoGroup.isVisible = true
|
||||
callInfoGroup.isVisible = false
|
||||
if (callArgs.isVideoCall) {
|
||||
callVideoGroup.isVisible = true
|
||||
callInfoGroup.isVisible = false
|
||||
} else {
|
||||
callVideoGroup.isInvisible = true
|
||||
callInfoGroup.isVisible = true
|
||||
callStatusText.text = null
|
||||
}
|
||||
}
|
||||
CallState.TERMINATED -> {
|
||||
finish()
|
||||
@ -279,20 +269,20 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
override fun onPause() {
|
||||
peerConnectionManager.detachRenderers()
|
||||
// tryThis { unbindService(serviceConnection) }
|
||||
super.onDestroy()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun handleViewEvents(event: VectorCallViewEvents?) {
|
||||
when (event) {
|
||||
is VectorCallViewEvents.CallAnswered -> {
|
||||
}
|
||||
is VectorCallViewEvents.CallHangup -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
Timber.v("handleViewEvents $event")
|
||||
// when (event) {
|
||||
// is VectorCallViewEvents.CallAnswered -> {
|
||||
// }
|
||||
// is VectorCallViewEvents.CallHangup -> {
|
||||
// finish()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -57,9 +57,9 @@ sealed class VectorCallViewActions : VectorViewModelAction {
|
||||
|
||||
sealed class VectorCallViewEvents : VectorViewEvents {
|
||||
|
||||
data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
|
||||
data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
|
||||
object CallAccepted : VectorCallViewEvents()
|
||||
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
|
||||
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
|
||||
// object CallAccepted : VectorCallViewEvents()
|
||||
}
|
||||
|
||||
class VectorCallViewModel @AssistedInject constructor(
|
||||
|
@ -211,7 +211,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
currentCall?.mxCall?.offerSdp(p0)
|
||||
}
|
||||
}, constraints)
|
||||
|
||||
}
|
||||
|
||||
private fun getTurnServer(callback: ((TurnServer?) -> Unit)) {
|
||||
@ -374,7 +373,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
// If remote track exists, then sink it to surface
|
||||
remoteSurfaceRenderer?.get()?.let { participantSurface ->
|
||||
currentCall?.remoteVideoTrack?.let {
|
||||
it.setEnabled(true)
|
||||
it.addSink(participantSurface)
|
||||
}
|
||||
}
|
||||
@ -551,17 +549,47 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) {
|
||||
Timber.v("## VOIP StreamObserver onConnectionChange: $newState")
|
||||
when (newState) {
|
||||
PeerConnection.PeerConnectionState.CONNECTED -> {
|
||||
/**
|
||||
* Every ICE transport used by the connection is either in use (state "connected" or "completed")
|
||||
* or is closed (state "closed"); in addition, at least one transport is either "connected" or "completed"
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.CONNECTED -> {
|
||||
callContext.mxCall.state = CallState.CONNECTED
|
||||
}
|
||||
PeerConnection.PeerConnectionState.FAILED -> {
|
||||
/**
|
||||
* One or more of the ICE transports on the connection is in the "failed" state.
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.FAILED -> {
|
||||
endCall()
|
||||
}
|
||||
/**
|
||||
* At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state,
|
||||
* and none of them are in one of the following states: "connecting", "checking", "failed", or "disconnected",
|
||||
* or all of the connection's transports are in the "closed" state.
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.NEW,
|
||||
PeerConnection.PeerConnectionState.CONNECTING,
|
||||
PeerConnection.PeerConnectionState.DISCONNECTED,
|
||||
|
||||
/**
|
||||
* One or more of the ICE transports are currently in the process of establishing a connection;
|
||||
* that is, their RTCIceConnectionState is either "checking" or "connected", and no transports are in the "failed" state
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.CONNECTING -> {
|
||||
callContext.mxCall.state = CallState.CONNECTING
|
||||
}
|
||||
/**
|
||||
* The RTCPeerConnection is closed.
|
||||
* This value was in the RTCSignalingState enum (and therefore found by reading the value of the signalingState)
|
||||
* property until the May 13, 2016 draft of the specification.
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.CLOSED,
|
||||
null -> {
|
||||
/**
|
||||
* At least one of the ICE transports for the connection is in the "disconnected" state and none of the other transports are in the state "failed",
|
||||
* "connecting", or "checking".
|
||||
*/
|
||||
PeerConnection.PeerConnectionState.DISCONNECTED -> {
|
||||
|
||||
}
|
||||
null -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -580,14 +608,60 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
||||
Timber.v("## VOIP StreamObserver onIceConnectionChange IceConnectionState:$newState")
|
||||
when (newState) {
|
||||
PeerConnection.IceConnectionState.CONNECTED -> Timber.v("## VOIP StreamObserver onIceConnectionChange.CONNECTED")
|
||||
PeerConnection.IceConnectionState.DISCONNECTED -> {
|
||||
Timber.v("## VOIP StreamObserver onIceConnectionChange.DISCONNECTED")
|
||||
endCall()
|
||||
|
||||
/**
|
||||
* the ICE agent is gathering addresses or is waiting to be given remote candidates through
|
||||
* calls to RTCPeerConnection.addIceCandidate() (or both).
|
||||
*/
|
||||
PeerConnection.IceConnectionState.NEW -> {
|
||||
|
||||
}
|
||||
/**
|
||||
* The ICE agent has been given one or more remote candidates and is checking pairs of local and remote candidates
|
||||
* against one another to try to find a compatible match, but has not yet found a pair which will allow
|
||||
* the peer connection to be made. It's possible that gathering of candidates is also still underway.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.CHECKING -> {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A usable pairing of local and remote candidates has been found for all components of the connection,
|
||||
* and the connection has been established.
|
||||
* It's possible that gathering is still underway, and it's also possible that the ICE agent is still checking
|
||||
* candidates against one another looking for a better connection to use.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.CONNECTED -> {
|
||||
|
||||
}
|
||||
/**
|
||||
* Checks to ensure that components are still connected failed for at least one component of the RTCPeerConnection.
|
||||
* This is a less stringent test than "failed" and may trigger intermittently and resolve just as spontaneously on less reliable networks,
|
||||
* or during temporary disconnections. When the problem resolves, the connection may return to the "connected" state.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.DISCONNECTED -> {
|
||||
}
|
||||
/**
|
||||
* The ICE candidate has checked all candidates pairs against one another and has failed to find compatible matches for all components of the connection.
|
||||
* It is, however, possible that the ICE agent did find compatible connections for some components.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.FAILED -> {
|
||||
callContext.mxCall.hangUp()
|
||||
}
|
||||
/**
|
||||
* The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.COMPLETED -> {
|
||||
|
||||
}
|
||||
/**
|
||||
* The ICE agent for this RTCPeerConnection has shut down and is no longer handling requests.
|
||||
*/
|
||||
PeerConnection.IceConnectionState.CLOSED -> {
|
||||
|
||||
}
|
||||
PeerConnection.IceConnectionState.FAILED -> Timber.v("## VOIP StreamObserver onIceConnectionChange.FAILED")
|
||||
else -> Timber.v("## VOIP StreamObserver onIceConnectionChange.$newState")
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,7 +669,12 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||
Timber.v("## VOIP StreamObserver onAddStream: $stream")
|
||||
executor.execute {
|
||||
// reportError("Weird-looking stream: " + stream);
|
||||
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) return@execute
|
||||
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) {
|
||||
Timber.e("## VOIP StreamObserver weird looking stream: $stream")
|
||||
//TODO maybe do something more??
|
||||
callContext.mxCall.hangUp()
|
||||
return@execute
|
||||
}
|
||||
|
||||
if (stream.videoTracks.size == 1) {
|
||||
val remoteVideoTrack = stream.videoTracks.first()
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/incomingRingingControls"
|
||||
android:id="@+id/ringingControls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
|
Loading…
Reference in New Issue
Block a user