Show error on connecting timeout + refactoring

This commit is contained in:
Valere 2020-06-17 16:37:17 +02:00
parent c85ba51274
commit fd3f591541
10 changed files with 167 additions and 171 deletions

View file

@ -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()
}

View file

@ -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
// )
// }
// }
}

View file

@ -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)

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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<VectorCallViewState, VectorCallViewActions, VectorCallViewEvents>(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<TurnServerResponse> {
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)

View file

@ -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 -> {
}

View file

@ -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)
}

View file

@ -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)

View file

@ -210,6 +210,10 @@
<string name="call_failed_no_ice_description">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."</string>
<string name="call_failed_no_ice_use_alt">Try using %s</string>
<string name="call_failed_dont_ask_again">Do not ask me again</string>
<string name="call_failed_no_connection">RiotX Call Failed</string>
<string name="call_failed_no_connection_description">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.</string>
<string name="call_select_sound_device">Select Sound Device</string>
<string name="sound_device_phone">Phone</string>
<string name="sound_device_speaker">Speaker</string>