From fd3f591541fe6c91dd394d149b70b3bee34a8be3 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 17 Jun 2020 16:37:17 +0200 Subject: [PATCH] Show error on connecting timeout + refactoring --- .../android/api/session/call/CallState.kt | 25 ++-- .../call/DefaultCallSignalingService.kt | 22 +-- .../internal/session/call/model/MxCallImpl.kt | 12 +- .../riotx/features/call/CallControlsView.kt | 29 ++-- .../riotx/features/call/VectorCallActivity.kt | 126 +++++++----------- .../features/call/VectorCallViewModel.kt | 37 ++++- .../call/WebRtcPeerConnectionManager.kt | 25 ++-- .../home/room/detail/RoomDetailFragment.kt | 17 ++- .../notifications/NotificationUtils.kt | 41 +++--- vector/src/main/res/values/strings.xml | 4 + 10 files changed, 167 insertions(+), 171 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallState.kt index 1ab53854aa..3b3a393026 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallState.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallState.kt @@ -16,26 +16,29 @@ package im.vector.matrix.android.api.session.call -enum class CallState { +import org.webrtc.PeerConnection + +sealed class CallState { /** Idle, setting up objects */ - IDLE, + object Idle : CallState() /** Dialing. Outgoing call is signaling the remote peer */ - DIALING, + object Dialing : CallState() /** Local ringing. Incoming call offer received */ - LOCAL_RINGING, + object LocalRinging : CallState() /** Answering. Incoming call is responding to remote peer */ - ANSWERING, + object Answering : CallState() - /** Connecting. Incoming/Outgoing Offer and answer are known, Currently checking and testing pairs of ice candidates */ - CONNECTING, - - /** Connected. Incoming/Outgoing call, the call is connected */ - CONNECTED, + /** + * Connected. Incoming/Outgoing call, ice layer connecting or connected + * Notice that the PeerState failed is not always final, if you switch network, new ice candidtates + * could be exchanged, and the connection could go back to connected + * */ + data class Connected(val iceConnectionState: PeerConnection.PeerConnectionState) : CallState() /** Terminated. Incoming/Outgoing call, the call is terminated */ - TERMINATED, + object Terminated : CallState() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt index a3bbcd6444..ed9361b4c5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt @@ -101,6 +101,7 @@ internal class DefaultCallSignalingService @Inject constructor( override fun removeCallListener(listener: CallsListener) { callListeners.remove(listener) } + override fun getCallWithId(callId: String): MxCall? { Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}") return activeCalls.find { it.callId == callId } @@ -189,25 +190,4 @@ internal class DefaultCallSignalingService @Inject constructor( companion object { const val CALL_TIMEOUT_MS = 120_000 } - -// internal class PeerSignalingClientFactory @Inject constructor( -// @UserId private val userId: String, -// private val localEchoEventFactory: LocalEchoEventFactory, -// private val sendEventTask: SendEventTask, -// private val taskExecutor: TaskExecutor, -// private val cryptoService: CryptoService -// ) { -// -// fun create(roomId: String, callId: String): PeerSignalingClient { -// return RoomPeerSignalingClient( -// callID = callId, -// roomId = roomId, -// userId = userId, -// localEchoEventFactory = localEchoEventFactory, -// sendEventTask = sendEventTask, -// taskExecutor = taskExecutor, -// cryptoService = cryptoService -// ) -// } -// } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt index 90b475d5b8..fe9b9e447c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt @@ -46,7 +46,7 @@ internal class MxCallImpl( private val roomEventSender: RoomEventSender ) : MxCall { - override var state: CallState = CallState.IDLE + override var state: CallState = CallState.Idle set(value) { field = value dispatchStateChange() @@ -74,17 +74,17 @@ internal class MxCallImpl( init { if (isOutgoing) { - state = CallState.IDLE + state = CallState.Idle } else { // because it's created on reception of an offer - state = CallState.LOCAL_RINGING + state = CallState.LocalRinging } } override fun offerSdp(sdp: SessionDescription) { if (!isOutgoing) return Timber.v("## VOIP offerSdp $callId") - state = CallState.DIALING + state = CallState.Dialing CallInviteContent( callId = callId, lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS, @@ -120,13 +120,13 @@ internal class MxCallImpl( ) .let { createEventAndLocalEcho(type = EventType.CALL_HANGUP, roomId = roomId, content = it.toContent()) } .also { roomEventSender.sendEvent(it) } - state = CallState.TERMINATED + state = CallState.Terminated } override fun accept(sdp: SessionDescription) { Timber.v("## VOIP accept $callId") if (isOutgoing) return - state = CallState.ANSWERING + state = CallState.Answering CallAnswerContent( callId = callId, answer = CallAnswerContent.Answer(sdp = sdp.description) diff --git a/vector/src/main/java/im/vector/riotx/features/call/CallControlsView.kt b/vector/src/main/java/im/vector/riotx/features/call/CallControlsView.kt index 9c4bf5f6e1..adf05184b2 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/CallControlsView.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/CallControlsView.kt @@ -29,6 +29,7 @@ import butterknife.OnClick import im.vector.matrix.android.api.session.call.CallState import im.vector.riotx.R import kotlinx.android.synthetic.main.fragment_call_controls.view.* +import org.webrtc.PeerConnection class CallControlsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 @@ -99,28 +100,34 @@ class CallControlsView @JvmOverloads constructor( videoToggleIcon.setImageResource(if (state.isVideoEnabled) R.drawable.ic_video else R.drawable.ic_video_off) when (callState) { - CallState.IDLE, - CallState.DIALING, - CallState.CONNECTING, - CallState.ANSWERING -> { + is CallState.Idle, + is CallState.Dialing, + is CallState.Answering -> { ringingControls.isVisible = true ringingControlAccept.isVisible = false ringingControlDecline.isVisible = true connectedControls.isVisible = false } - CallState.LOCAL_RINGING -> { + is CallState.LocalRinging -> { ringingControls.isVisible = true ringingControlAccept.isVisible = true ringingControlDecline.isVisible = true connectedControls.isVisible = false } - CallState.CONNECTED -> { - ringingControls.isVisible = false - connectedControls.isVisible = true - iv_video_toggle.isVisible = state.isVideoCall + is CallState.Connected -> { + if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { + ringingControls.isVisible = false + connectedControls.isVisible = true + iv_video_toggle.isVisible = state.isVideoCall + } else { + ringingControls.isVisible = true + ringingControlAccept.isVisible = false + ringingControlDecline.isVisible = true + connectedControls.isVisible = false + } } - CallState.TERMINATED, - null -> { + is CallState.Terminated, + null -> { ringingControls.isVisible = false connectedControls.isVisible = false } diff --git a/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt index a6aede7f48..b0fe9865fd 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt @@ -27,6 +27,7 @@ import android.os.Parcelable import android.view.View import android.view.Window import android.view.WindowManager +import androidx.appcompat.app.AlertDialog import androidx.core.view.ViewCompat import androidx.core.view.isInvisible import androidx.core.view.isVisible @@ -39,6 +40,7 @@ import com.jakewharton.rxbinding3.view.clicks import im.vector.matrix.android.api.session.call.CallState import im.vector.matrix.android.api.session.call.EglUtils import im.vector.matrix.android.api.session.call.MxCallDetail +import im.vector.matrix.android.api.session.call.TurnServerResponse import im.vector.riotx.R import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.platform.VectorBaseActivity @@ -54,6 +56,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.activity_call.* import org.webrtc.EglBase +import org.webrtc.PeerConnection import org.webrtc.RendererCommon import org.webrtc.SurfaceViewRenderer import timber.log.Timber @@ -66,8 +69,7 @@ data class CallArgs( val callId: String?, val participantUserId: String, val isIncomingCall: Boolean, - val isVideoCall: Boolean, - val autoAccept: Boolean + val isVideoCall: Boolean ) : Parcelable class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionListener { @@ -216,53 +218,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis turnScreenOffAndKeyguardOn() } -// override fun onResume() { -// super.onResume() -// } -// -// override fun onStop() { -// super.onStop() -// when(callViewModel.call?.state) { -// CallState.DIALING -> { -// CallService.onIncomingCall( -// this, -// callArgs.isVideoCall, -// callArgs.participantUserId, -// callArgs.roomId, -// "", -// callArgs.callId ?: "" -// ) -// } -// CallState.LOCAL_RINGING -> { -// CallService.onIncomingCall( -// this, -// callArgs.isVideoCall, -// callArgs.participantUserId, -// callArgs.roomId, -// "", -// callArgs.callId ?: "" -// ) -// } -// CallState.ANSWERING, -// CallState.CONNECTING, -// CallState.CONNECTED -> { -// CallService.onPendingCall( -// this, -// callArgs.isVideoCall, -// callArgs.participantUserId, -// callArgs.roomId, -// "", -// callArgs.callId ?: "" -// ) -// } -// CallState.TERMINATED , -// CallState.IDLE , -// null -> { -// -// } -// } -// } - private fun renderState(state: VectorCallViewState) { Timber.v("## VOIP renderState call $state") if (state.callState is Fail) { @@ -273,52 +228,55 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis } callControlsView.updateForState(state) - when (state.callState.invoke()) { - CallState.IDLE, - CallState.DIALING -> { + val callState = state.callState.invoke() + when (callState) { + is CallState.Idle, + is CallState.Dialing -> { callVideoGroup.isInvisible = true callInfoGroup.isVisible = true callStatusText.setText(R.string.call_ring) configureCallInfo(state) } - CallState.LOCAL_RINGING -> { + is CallState.LocalRinging -> { callVideoGroup.isInvisible = true callInfoGroup.isVisible = true callStatusText.text = null configureCallInfo(state) } - CallState.ANSWERING -> { + is CallState.Answering -> { callVideoGroup.isInvisible = true callInfoGroup.isVisible = true callStatusText.setText(R.string.call_connecting) configureCallInfo(state) } - CallState.CONNECTING -> { - callVideoGroup.isInvisible = true - callInfoGroup.isVisible = true - configureCallInfo(state) - callStatusText.setText(R.string.call_connecting) - } - CallState.CONNECTED -> { - if (callArgs.isVideoCall) { - callVideoGroup.isVisible = true - callInfoGroup.isVisible = false - pip_video_view.isVisible = !state.isVideoCaptureInError + is CallState.Connected -> { + if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { + if (callArgs.isVideoCall) { + callVideoGroup.isVisible = true + callInfoGroup.isVisible = false + pip_video_view.isVisible = !state.isVideoCaptureInError + } else { + callVideoGroup.isInvisible = true + callInfoGroup.isVisible = true + configureCallInfo(state) + callStatusText.text = null + } } else { + // This state is not final, if you change network, new candidates will be sent callVideoGroup.isInvisible = true callInfoGroup.isVisible = true configureCallInfo(state) - callStatusText.text = null + callStatusText.setText(R.string.call_connecting) } // ensure all attached? peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer, null) } - CallState.TERMINATED -> { + is CallState.Terminated -> { finish() } - null -> { + null -> { } } } @@ -382,20 +340,29 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis private fun handleViewEvents(event: VectorCallViewEvents?) { Timber.v("## VOIP handleViewEvents $event") when (event) { - VectorCallViewEvents.DismissNoCall -> { + VectorCallViewEvents.DismissNoCall -> { CallService.onNoActiveCall(this) finish() } - null -> { + is VectorCallViewEvents.ConnectionTimout -> { + onErrorTimoutConnect(event.turn) + } + null -> { } } -// when (event) { -// is VectorCallViewEvents.CallAnswered -> { -// } -// is VectorCallViewEvents.CallHangup -> { -// finish() -// } -// } + } + + private fun onErrorTimoutConnect(turn: TurnServerResponse?) { + Timber.d("## VOIP onErrorTimoutConnect $turn") + // TODO ask to use default stun, etc... + AlertDialog + .Builder(this) + .setTitle(R.string.call_failed_no_connection) + .setMessage(getString(R.string.call_failed_no_connection_description)) + .setNegativeButton(R.string.ok) { _, _ -> + callViewModel.handle(VectorCallViewActions.EndCall) + } + .show() } companion object { @@ -411,7 +378,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_NEW_TASK - putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.otherUserId, !mxCall.isOutgoing, mxCall.isVideoCall, false)) + putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.otherUserId, !mxCall.isOutgoing, mxCall.isVideoCall)) putExtra(EXTRA_MODE, OUTGOING_CREATED) } } @@ -422,12 +389,11 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis otherUserId: String, isIncomingCall: Boolean, isVideoCall: Boolean, - accept: Boolean, mode: String?): Intent { return Intent(context, VectorCallActivity::class.java).apply { // what could be the best flags? flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall, accept)) + putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall)) putExtra(EXTRA_MODE, mode) } } diff --git a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt index b8af552dc4..ea15c07841 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt @@ -26,15 +26,20 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.call.CallState import im.vector.matrix.android.api.session.call.MxCall +import im.vector.matrix.android.api.session.call.TurnServerResponse import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewEvents import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModelAction +import org.webrtc.PeerConnection +import java.util.Timer +import java.util.TimerTask data class VectorCallViewState( val callId: String? = null, @@ -60,6 +65,7 @@ sealed class VectorCallViewActions : VectorViewModelAction { sealed class VectorCallViewEvents : VectorViewEvents { object DismissNoCall : VectorCallViewEvents() + data class ConnectionTimout(val turn: TurnServerResponse?) : VectorCallViewEvents() // data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents() // data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents() // object CallAccepted : VectorCallViewEvents() @@ -72,15 +78,38 @@ class VectorCallViewModel @AssistedInject constructor( val webRtcPeerConnectionManager: WebRtcPeerConnectionManager ) : VectorViewModel(initialState) { - var autoReplyIfNeeded: Boolean = false - var call: MxCall? = null + var connectionTimoutTimer: Timer? = null + private val callStateListener = object : MxCall.StateListener { override fun onStateUpdate(call: MxCall) { + val callState = call.state + if (callState is CallState.Connected && callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { + connectionTimoutTimer?.cancel() + connectionTimoutTimer = null + } else { + // do we reset as long as it's moving? + connectionTimoutTimer?.cancel() + connectionTimoutTimer = Timer().apply { + schedule(object : TimerTask() { + override fun run() { + session.callSignalingService().getTurnServer(object : MatrixCallback { + override fun onFailure(failure: Throwable) { + _viewEvents.post(VectorCallViewEvents.ConnectionTimout(null)) + } + + override fun onSuccess(data: TurnServerResponse) { + _viewEvents.post(VectorCallViewEvents.ConnectionTimout(data)) + } + }) + } + }, 30_000) + } + } setState { copy( - callState = Success(call.state) + callState = Success(callState) ) } } @@ -99,8 +128,6 @@ class VectorCallViewModel @AssistedInject constructor( init { - autoReplyIfNeeded = args.autoAccept - initialState.callId?.let { webRtcPeerConnectionManager.addCurrentCallListener(currentCallListener) diff --git a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt index d75184ecca..be8380a3d8 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt @@ -269,7 +269,7 @@ class WebRtcPeerConnectionManager @Inject constructor( // The call is going to resume from background, we can reduce notif currentCall?.mxCall - ?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED } + ?.takeIf { it.state is CallState.Connected } ?.let { mxCall -> val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName() ?: mxCall.roomId @@ -466,7 +466,7 @@ class WebRtcPeerConnectionManager @Inject constructor( fun acceptIncomingCall() { Timber.v("## VOIP acceptIncomingCall from state ${currentCall?.mxCall?.state}") val mxCall = currentCall?.mxCall - if (mxCall?.state == CallState.LOCAL_RINGING) { + if (mxCall?.state == CallState.LocalRinging) { getTurnServer { turnServer -> internalAcceptIncomingCall(currentCall!!, turnServer) } @@ -476,7 +476,7 @@ class WebRtcPeerConnectionManager @Inject constructor( fun detachRenderers() { // The call is going to continue in background, so ensure notification is visible currentCall?.mxCall - ?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED } + ?.takeIf { it.state is CallState.Connected } ?.let { mxCall -> // Start background service with notification @@ -687,7 +687,7 @@ class WebRtcPeerConnectionManager @Inject constructor( if (call.mxCall.callId != callHangupContent.callId) return Unit.also { Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}") } - call.mxCall.state = CallState.TERMINATED + call.mxCall.state = CallState.Terminated currentCall = null close() } @@ -702,7 +702,7 @@ class WebRtcPeerConnectionManager @Inject constructor( * or is closed (state "closed"); in addition, at least one transport is either "connected" or "completed" */ PeerConnection.PeerConnectionState.CONNECTED -> { - callContext.mxCall.state = CallState.CONNECTED + callContext.mxCall.state = CallState.Connected(newState) } /** * One or more of the ICE transports on the connection is in the "failed" state. @@ -710,6 +710,7 @@ class WebRtcPeerConnectionManager @Inject constructor( PeerConnection.PeerConnectionState.FAILED -> { // This can be temporary, e.g when other ice not yet received... // callContext.mxCall.state = CallState.ERROR + callContext.mxCall.state = CallState.Connected(newState) } /** * At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state, @@ -723,20 +724,20 @@ class WebRtcPeerConnectionManager @Inject constructor( * 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 + callContext.mxCall.state = CallState.Connected(PeerConnection.PeerConnectionState.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 -> { - } - /** - * 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.CLOSED, + /** + * 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 -> { + callContext.mxCall.state = CallState.Connected(newState) } null -> { } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index c4c87ac2f5..0f8c98128f 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -296,7 +296,7 @@ class RoomDetailFragment @Inject constructor( sharedCallActionViewModel .activeCall .observe(viewLifecycleOwner, Observer { - val hasActiveCall = it?.state == CallState.CONNECTED + val hasActiveCall = it?.state is CallState.Connected activeCallView.isVisible = hasActiveCall }) @@ -1517,14 +1517,13 @@ class RoomDetailFragment @Inject constructor( override fun onTapToReturnToCall() { sharedCallActionViewModel.activeCall.value?.let { call -> VectorCallActivity.newIntent( - requireContext(), - call.callId, - call.roomId, - call.otherUserId, - !call.isOutgoing, - call.isVideoCall, - false, - null + context = requireContext(), + callId = call.callId, + roomId = call.roomId, + otherUserId = call.otherUserId, + isIncomingCall = !call.isOutgoing, + isVideoCall = call.isVideoCall, + mode = null ).let { startActivity(it) } diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt index 88a19fff89..d936935330 100755 --- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt @@ -293,30 +293,34 @@ class NotificationUtils @Inject constructor(private val context: Context, // val pendingIntent = stackBuilder.getPendingIntent(requestId, PendingIntent.FLAG_UPDATE_CURRENT) val contentIntent = VectorCallActivity.newIntent( - context, callId, roomId, otherUserId, true, isVideo, - false, VectorCallActivity.INCOMING_RINGING + context = context, + callId = callId, + roomId = roomId, + otherUserId = otherUserId, + isIncomingCall = true, + isVideoCall = isVideo, + mode = VectorCallActivity.INCOMING_RINGING ).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP data = Uri.parse("foobar://$callId") } val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0) -// val contentPendingIntent = TaskStackBuilder.create(context) -// .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java)) -// .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId = roomId)) -// .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, false, VectorCallActivity.INCOMING_RINGING)) -// .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) - val answerCallPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java)) - .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, true, VectorCallActivity.INCOMING_ACCEPT)) + .addNextIntent(VectorCallActivity.newIntent( + context = context, + callId = callId, + roomId = roomId, + otherUserId = otherUserId, + isIncomingCall = true, + isVideoCall = isVideo, + mode = VectorCallActivity.INCOMING_ACCEPT) + ) .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) -// val answerCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply { -// putExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, CallHeadsUpService.CALL_ACTION_ANSWER) -// } val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply { - // putExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, CallHeadsUpService.CALL_ACTION_REJECT) + putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT) } // val answerCallPendingIntent = PendingIntent.getBroadcast(context, requestId, answerCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT) val rejectCallPendingIntent = PendingIntent.getBroadcast( @@ -367,8 +371,13 @@ class NotificationUtils @Inject constructor(private val context: Context, val requestId = Random.nextInt(1000) val contentIntent = VectorCallActivity.newIntent( - context, callId, roomId, otherUserId, true, isVideo, - false, null).apply { + context = context, + callId = callId, + roomId = roomId, + otherUserId = otherUserId, + isIncomingCall = true, + isVideoCall = isVideo, + mode = null).apply { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP data = Uri.parse("foobar://$callId") } @@ -451,7 +460,7 @@ class NotificationUtils @Inject constructor(private val context: Context, val contentPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java)) // TODO other userId - .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, false, null)) + .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, null)) .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) builder.setContentIntent(contentPendingIntent) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ebbd78f11a..0a6d46e937 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -210,6 +210,10 @@ Please ask the administrator of your homeserver (%1$s) to configure a TURN server in order for calls to work reliably.\n\nAlternatively, you can try to use the public server at %2$s, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings." Try using %s Do not ask me again + + RiotX Call Failed + Failed to establish real time connection.\nPlease ask the administrator of your homeserver to configure a TURN server in order for calls to work reliably. + Select Sound Device Phone Speaker