VoIP: add partyId and handle version as string

This commit is contained in:
ganfra 2020-11-13 11:53:22 +01:00
parent f2cb6ed82c
commit ba11ca0e9d
19 changed files with 322 additions and 160 deletions

View File

@ -20,8 +20,9 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
interface CallsListener {
interface CallListener {
/**
* Called when there is an incoming call within the room.
*/
@ -39,5 +40,10 @@ interface CallsListener {
*/
fun onCallHangupReceived(callHangupContent: CallHangupContent)
/**
* Called when a called has been rejected
*/
fun onCallRejectReceived(callRejectContent: CallRejectContent)
fun onCallManagedByOtherSession(callId: String)
}

View File

@ -28,9 +28,9 @@ interface CallSignalingService {
*/
fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall
fun addCallListener(listener: CallsListener)
fun addCallListener(listener: CallListener)
fun removeCallListener(listener: CallsListener)
fun removeCallListener(listener: CallListener)
fun getCallWithId(callId: String): MxCall?

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.api.session.call
import org.matrix.android.sdk.api.util.Optional
import org.webrtc.IceCandidate
import org.webrtc.SessionDescription
@ -23,8 +24,12 @@ interface MxCallDetail {
val callId: String
val isOutgoing: Boolean
val roomId: String
val otherUserId: String
val opponentUserId: String
val ourPartyId: String
val isVideoCall: Boolean
var opponentPartyId: Optional<String>?
var opponentVersion: Int
}
/**
@ -32,6 +37,12 @@ interface MxCallDetail {
*/
interface MxCall : MxCallDetail {
companion object {
const val VOIP_PROTO_VERSION = 0
}
var state: CallState
/**
@ -42,9 +53,8 @@ interface MxCall : MxCallDetail {
/**
* Reject an incoming call
* It's an alias to hangUp
*/
fun reject() = hangUp()
fun reject()
/**
* End the call

View File

@ -27,11 +27,11 @@ data class CallAnswerContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. The session description object
*/
@ -39,8 +39,8 @@ data class CallAnswerContent(
/**
* Required. The version of the VoIP specification this messages adheres to. This specification is version 0.
*/
@Json(name = "version") val version: String? = "0"
) {
@Json(name = "version") override val version: String? = "0"
): CallSignallingContent {
@JsonClass(generateAdapter = true)
data class Answer(

View File

@ -28,11 +28,11 @@ data class CallCandidatesContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. Array of objects describing the candidates.
*/
@ -40,8 +40,8 @@ data class CallCandidatesContent(
/**
* Required. The version of the VoIP specification this messages adheres to. This specification is version 0.
*/
@Json(name = "version") val version: String? = "0"
) {
@Json(name = "version") override val version: String? = "0"
): CallSignallingContent {
@JsonClass(generateAdapter = true)
data class Candidate(

View File

@ -28,22 +28,22 @@ data class CallHangupContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
@Json(name = "version") val version: String? = "0",
@Json(name = "version") override val version: String? = "0",
/**
* Optional error reason for the hangup. This should not be provided when the user naturally ends or rejects the call.
* When there was an error in the call negotiation, this should be `ice_failed` for when ICE negotiation fails
* or `invite_timeout` for when the other party did not answer in time. One of: ["ice_failed", "invite_timeout"]
*/
@Json(name = "reason") val reason: Reason? = null
) {
) : CallSignallingContent {
@JsonClass(generateAdapter = false)
enum class Reason {
@Json(name = "ice_failed")

View File

@ -27,11 +27,11 @@ data class CallInviteContent(
/**
* Required. A unique identifier for the call.
*/
@Json(name = "call_id") val callId: String?,
@Json(name = "call_id") override val callId: String?,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. The session description object
*/
@ -39,14 +39,14 @@ data class CallInviteContent(
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
@Json(name = "version") val version: String? = "0",
@Json(name = "version") override val version: String? = "0",
/**
* Required. The time in milliseconds that the invite is valid for.
* Once the invite age exceeds this value, clients should discard it.
* They should also no longer show the call as awaiting an answer in the UI.
*/
@Json(name = "lifetime") val lifetime: Int?
) {
): CallSignallingContent {
@JsonClass(generateAdapter = true)
data class Offer(
/**

View File

@ -27,11 +27,11 @@ data class CallNegociateContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. The time in milliseconds that the negotiation is valid for. Once exceeded the sender
* of the negotiate event should consider the negotiation failed (timed out) and the recipient should ignore it.
@ -41,7 +41,13 @@ data class CallNegociateContent(
* Required. The session description object
*/
@Json(name = "description") val description: Description? = null,
) {
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
@Json(name = "version") override val version: String? = "0",
): CallSignallingContent {
@JsonClass(generateAdapter = true)
data class Description(
/**

View File

@ -28,13 +28,13 @@ data class CallRejectContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
@Json(name = "version") val version: String? = "0",
)
@Json(name = "version") override val version: String? = "0",
):CallSignallingContent

View File

@ -27,13 +27,18 @@ data class CallSelectAnswerContent(
/**
* Required. The ID of the call this event relates to.
*/
@Json(name = "call_id") val callId: String,
@Json(name = "call_id") override val callId: String,
/**
* Required. ID to let user identify remote echo of their own events
*/
@Json(name = "party_id") val partyId: String? = null,
@Json(name = "party_id") override val partyId: String? = null,
/**
* Required. Indicates the answer user has chosen.
*/
@Json(name = "selected_party_id") val selectedPartyId: String? = null,
)
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
@Json(name = "version") override val version: String? = "0",
): CallSignallingContent

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.api.session.room.model.call
interface CallSignallingContent {
/**
* Required. A unique identifier for the call.
*/
val callId: String?
/**
* Required. ID to let user identify remote echo of their own events
*/
val partyId: String?
/**
* Required. The version of the VoIP specification this message adheres to. This specification is version 0.
*/
val version: String?
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.call
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.call.CallListener
import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
/**
* Dispatch each method safely to all listeners.
*/
class CallListenersDispatcher(private val listeners: Set<CallListener>) : CallListener {
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) = dispatch {
it.onCallInviteReceived(mxCall, callInviteContent)
}
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) = dispatch {
it.onCallIceCandidateReceived(mxCall, iceCandidatesContent)
}
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) = dispatch {
it.onCallAnswerReceived(callAnswerContent)
}
override fun onCallHangupReceived(callHangupContent: CallHangupContent) = dispatch {
it.onCallHangupReceived(callHangupContent)
}
override fun onCallRejectReceived(callRejectContent: CallRejectContent) = dispatch {
it.onCallRejectReceived(callRejectContent)
}
override fun onCallManagedByOtherSession(callId: String) = dispatch {
it.onCallManagedByOtherSession(callId)
}
private fun dispatch(lambda: (CallListener) -> Unit) {
listeners.toList().forEach {
tryOrNull {
lambda(it)
}
}
}
}

View File

@ -18,10 +18,9 @@ package org.matrix.android.sdk.internal.session.call
import android.os.SystemClock
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.call.CallListener
import org.matrix.android.sdk.api.session.call.CallSignalingService
import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.CallsListener
import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.call.TurnServerResponse
import org.matrix.android.sdk.api.session.events.model.Event
@ -31,16 +30,21 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
import org.matrix.android.sdk.api.session.room.model.call.CallSignallingContent
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.call.model.MxCallImpl
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import timber.log.Timber
import java.math.BigDecimal
import java.util.UUID
import javax.inject.Inject
@ -48,6 +52,8 @@ import javax.inject.Inject
internal class DefaultCallSignalingService @Inject constructor(
@UserId
private val userId: String,
@DeviceId
private val deviceId: String?,
private val activeCallHandler: ActiveCallHandler,
private val localEchoEventFactory: LocalEchoEventFactory,
private val eventSenderProcessor: EventSenderProcessor,
@ -55,7 +61,8 @@ internal class DefaultCallSignalingService @Inject constructor(
private val turnServerTask: GetTurnServerTask
) : CallSignalingService {
private val callListeners = mutableSetOf<CallsListener>()
private val callListeners = mutableSetOf<CallListener>()
private val callListenersDispatcher = CallListenersDispatcher(callListeners)
private val cachedTurnServerResponse = object {
// Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it.
@ -100,7 +107,8 @@ internal class DefaultCallSignalingService @Inject constructor(
isOutgoing = true,
roomId = roomId,
userId = userId,
otherUserId = otherUserId,
ourPartyId = deviceId ?: "",
opponentUserId = otherUserId,
isVideoCall = isVideoCall,
localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor
@ -110,11 +118,11 @@ internal class DefaultCallSignalingService @Inject constructor(
}
}
override fun addCallListener(listener: CallsListener) {
override fun addCallListener(listener: CallListener) {
callListeners.add(listener)
}
override fun removeCallListener(listener: CallsListener) {
override fun removeCallListener(listener: CallListener) {
callListeners.remove(listener)
}
@ -129,125 +137,115 @@ internal class DefaultCallSignalingService @Inject constructor(
internal fun onCallEvent(event: Event) {
when (event.getClearType()) {
EventType.CALL_ANSWER -> {
event.getClearContent().toModel<CallAnswerContent>()?.let {
if (event.senderId == userId) {
// ok it's an answer from me.. is it remote echo or other session
val knownCall = getCallWithId(it.callId)
if (knownCall == null) {
Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${it.callId} send by me")
} else if (!knownCall.isOutgoing) {
// incoming call
// if it was anwsered by this session, the call state would be in Answering(or connected) state
if (knownCall.state == CallState.LocalRinging) {
// discard current call, it's answered by another of my session
onCallManageByOtherSession(it.callId)
}
}
return
}
onCallAnswer(it)
}
EventType.CALL_ANSWER -> {
handleCallAnswerEvent(event)
}
EventType.CALL_INVITE -> {
if (event.senderId == userId) {
// Always ignore local echos of invite
return
}
event.getClearContent().toModel<CallInviteContent>()?.let { content ->
val incomingCall = MxCallImpl(
callId = content.callId ?: return@let,
isOutgoing = false,
roomId = event.roomId ?: return@let,
userId = userId,
otherUserId = event.senderId ?: return@let,
isVideoCall = content.isVideo(),
localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor
)
activeCallHandler.addCall(incomingCall)
onCallInvite(incomingCall, content)
}
EventType.CALL_INVITE -> {
handleCallInviteEvent(event)
}
EventType.CALL_HANGUP -> {
event.getClearContent().toModel<CallHangupContent>()?.let { content ->
if (event.senderId == userId) {
// ok it's an answer from me.. is it remote echo or other session
val knownCall = getCallWithId(content.callId)
if (knownCall == null) {
Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${content.callId} send by me")
} else if (!knownCall.isOutgoing) {
// incoming call
if (knownCall.state == CallState.LocalRinging) {
// discard current call, it's answered by another of my session
onCallManageByOtherSession(content.callId)
}
}
return
}
activeCallHandler.removeCall(content.callId)
onCallHangup(content)
}
EventType.CALL_HANGUP -> {
handleCallHangupEvent(event)
}
EventType.CALL_REJECT -> {
handleCallRejectEvent(event)
}
EventType.CALL_CANDIDATES -> {
if (event.senderId == userId) {
// Always ignore local echos of invite
return
}
event.getClearContent().toModel<CallCandidatesContent>()?.let { content ->
activeCallHandler.getCallWithId(content.callId)?.let {
onCallIceCandidate(it, content)
}
}
handleCallCandidatesEvent(event)
}
}
}
private fun onCallHangup(hangup: CallHangupContent) {
callListeners.toList().forEach {
tryOrNull {
it.onCallHangupReceived(hangup)
}
private fun handleCallCandidatesEvent(event: Event) {
val content = event.getClearContent().toModel<CallCandidatesContent>() ?: return
val call = content.getCall() ?: return
if (call.ourPartyId == content.partyId) {
// Ignore remote echo
return
}
if (call.opponentPartyId != Optional.from(content.partyId)) {
Timber.v("Ignoring candidates from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
return
}
callListenersDispatcher.onCallIceCandidateReceived(call, content)
}
private fun handleCallRejectEvent(event: Event) {
val content = event.getClearContent().toModel<CallRejectContent>() ?: return
val call = content.getCall() ?: return
activeCallHandler.removeCall(content.callId)
// No need to check party_id for reject because if we'd received either
// an answer or reject, we wouldn't be in state InviteSent
if (call.state != CallState.Dialing) {
return
}
callListenersDispatcher.onCallRejectReceived(content)
}
private fun handleCallHangupEvent(event: Event) {
val content = event.getClearContent().toModel<CallHangupContent>() ?: return
val call = content.getCall() ?: return
if (call.state != CallState.Terminated) {
// Need to check for party_id?
activeCallHandler.removeCall(content.callId)
callListenersDispatcher.onCallHangupReceived(content)
}
}
private fun onCallAnswer(answer: CallAnswerContent) {
callListeners.toList().forEach {
tryOrNull {
it.onCallAnswerReceived(answer)
private fun handleCallInviteEvent(event: Event) {
val content = event.getClearContent().toModel<CallInviteContent>() ?: return
if (content.partyId == deviceId) {
// Ignore remote echo
return
}
val incomingCall = MxCallImpl(
callId = content.callId ?: return,
isOutgoing = false,
roomId = event.roomId ?: return,
userId = userId,
ourPartyId = deviceId ?: "",
opponentUserId = event.senderId ?: return,
isVideoCall = content.isVideo(),
localEchoEventFactory = localEchoEventFactory,
eventSenderProcessor = eventSenderProcessor
).apply {
opponentPartyId = Optional.from(content.partyId)
opponentVersion = content.version?.let { BigDecimal(it).intValueExact() } ?: MxCall.VOIP_PROTO_VERSION
}
activeCallHandler.addCall(incomingCall)
callListenersDispatcher.onCallInviteReceived(incomingCall, content)
}
private fun handleCallAnswerEvent(event: Event) {
val content = event.getClearContent().toModel<CallAnswerContent>() ?: return
val call = content.getCall() ?: return
if (call.ourPartyId == content.partyId) {
// Ignore remote echo
return
}
if (event.senderId == userId) {
// discard current call, it's answered by another of my session
callListenersDispatcher.onCallManagedByOtherSession(content.callId)
} else {
if (call.opponentPartyId != null) {
Timber.v("Ignoring answer from party ID ${content.partyId} we already have an answer from ${call.opponentPartyId}")
return
}
call.apply {
opponentPartyId = Optional.from(content.partyId)
opponentVersion = content.version?.let { BigDecimal(it).intValueExact() } ?: MxCall.VOIP_PROTO_VERSION
}
callListenersDispatcher.onCallAnswerReceived(content)
}
}
private fun onCallManageByOtherSession(callId: String) {
callListeners.toList().forEach {
tryOrNull {
it.onCallManagedByOtherSession(callId)
}
private fun CallSignallingContent.getCall(): MxCall? {
val currentCall = callId?.let {
activeCallHandler.getCallWithId(it)
}
}
private fun onCallInvite(incomingCall: MxCall, invite: CallInviteContent) {
// Ignore the invitation from current user
if (incomingCall.otherUserId == userId) return
callListeners.toList().forEach {
tryOrNull {
it.onCallInviteReceived(incomingCall, invite)
}
}
}
private fun onCallIceCandidate(incomingCall: MxCall, candidates: CallCandidatesContent) {
callListeners.toList().forEach {
tryOrNull {
it.onCallIceCandidateReceived(incomingCall, candidates)
}
if (currentCall == null) {
Timber.v("Call for content: $this is null")
}
return currentCall
}
companion object {

View File

@ -28,6 +28,8 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
@ -40,12 +42,16 @@ internal class MxCallImpl(
override val isOutgoing: Boolean,
override val roomId: String,
private val userId: String,
override val otherUserId: String,
override val opponentUserId: String,
override val isVideoCall: Boolean,
override val ourPartyId: String,
private val localEchoEventFactory: LocalEchoEventFactory,
private val eventSenderProcessor: EventSenderProcessor
) : MxCall {
override var opponentPartyId: Optional<String>? = null
override var opponentVersion: Int = MxCall.VOIP_PROTO_VERSION
override var state: CallState = CallState.Idle
set(value) {
field = value
@ -87,6 +93,7 @@ internal class MxCallImpl(
state = CallState.Dialing
CallInviteContent(
callId = callId,
partyId = ourPartyId,
lifetime = DefaultCallSignalingService.CALL_TIMEOUT_MS,
offer = CallInviteContent.Offer(sdp = sdp.description)
)
@ -97,6 +104,7 @@ internal class MxCallImpl(
override fun sendLocalIceCandidates(candidates: List<IceCandidate>) {
CallCandidatesContent(
callId = callId,
partyId = ourPartyId,
candidates = candidates.map {
CallCandidatesContent.Candidate(
sdpMid = it.sdpMid,
@ -113,10 +121,28 @@ internal class MxCallImpl(
// For now we don't support this flow
}
override fun reject() {
if(opponentVersion < 1){
Timber.v("Opponent version is less than 1 (${opponentVersion}): sending hangup instead of reject")
hangUp()
return
}
Timber.v("## VOIP reject $callId")
CallRejectContent(
callId = callId,
partyId = ourPartyId,
version = MxCall.VOIP_PROTO_VERSION.toString()
)
.let { createEventAndLocalEcho(type = EventType.CALL_REJECT, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) }
state = CallState.Terminated
}
override fun hangUp() {
Timber.v("## VOIP hangup $callId")
CallHangupContent(
callId = callId
callId = callId,
partyId = ourPartyId,
)
.let { createEventAndLocalEcho(type = EventType.CALL_HANGUP, roomId = roomId, content = it.toContent()) }
.also { eventSenderProcessor.postEvent(it) }
@ -129,6 +155,7 @@ internal class MxCallImpl(
state = CallState.Answering
CallAnswerContent(
callId = callId,
partyId = ourPartyId,
answer = CallAnswerContent.Answer(sdp = sdp.description)
)
.let { createEventAndLocalEcho(type = EventType.CALL_ANSWER, roomId = roomId, content = it.toContent()) }
@ -147,4 +174,5 @@ internal class MxCallImpl(
)
.also { localEchoEventFactory.createLocalEcho(it) }
}
}

View File

@ -375,7 +375,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))
putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.opponentUserId, !mxCall.isOutgoing, mxCall.isVideoCall))
putExtra(EXTRA_MODE, OUTGOING_CREATED)
}
}

View File

@ -136,8 +136,8 @@ class VectorCallViewModel @AssistedInject constructor(
session.callSignalingService().getCallWithId(it)?.let { mxCall ->
this.call = mxCall
mxCall.otherUserId
val item: MatrixItem? = session.getUser(mxCall.otherUserId)?.toMatrixItem()
mxCall.opponentUserId
val item: MatrixItem? = session.getUser(mxCall.opponentUserId)?.toMatrixItem()
mxCall.addListener(callStateListener)

View File

@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.CallsListener
import org.matrix.android.sdk.api.session.call.CallListener
import org.matrix.android.sdk.api.session.call.EglUtils
import org.matrix.android.sdk.api.session.call.MxCall
import org.matrix.android.sdk.api.session.call.TurnServerResponse
@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
import org.webrtc.AudioSource
import org.webrtc.AudioTrack
import org.webrtc.Camera1Enumerator
@ -76,7 +77,7 @@ import javax.inject.Singleton
class WebRtcPeerConnectionManager @Inject constructor(
private val context: Context,
private val activeSessionDataSource: ActiveSessionDataSource
) : CallsListener, LifecycleObserver {
) : CallListener, LifecycleObserver {
private val currentSession: Session?
get() = activeSessionDataSource.currentValue?.orNull()
@ -330,7 +331,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCall?.mxCall
?.takeIf { it.state is CallState.Connected }
?.let { mxCall ->
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
?: mxCall.roomId
// Start background service with notification
CallService.onPendingCall(
@ -388,7 +389,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
val mxCall = callContext.mxCall
// Update service state
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
?: mxCall.roomId
CallService.onPendingCall(
context = context,
@ -576,8 +577,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
?.let { mxCall ->
// Start background service with notification
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
?: mxCall.opponentUserId
CallService.onOnGoingCallBackground(
context = context,
isVideo = mxCall.isVideoCall,
@ -650,8 +651,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
callAudioManager.startForCall(createdCall)
currentCall = callContext
val name = currentSession?.getUser(createdCall.otherUserId)?.getBestName()
?: createdCall.otherUserId
val name = currentSession?.getUser(createdCall.opponentUserId)?.getBestName()
?: createdCall.opponentUserId
CallService.onOutgoingCallRinging(
context = context.applicationContext,
isVideo = createdCall.isVideoCall,
@ -706,8 +707,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
// Start background service with notification
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
?: mxCall.opponentUserId
CallService.onIncomingCallRinging(
context = context,
isVideo = mxCall.isVideoCall,
@ -845,8 +846,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
val mxCall = call.mxCall
// Update service state
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.otherUserId
val name = currentSession?.getUser(mxCall.opponentUserId)?.getBestName()
?: mxCall.opponentUserId
CallService.onPendingCall(
context = context,
isVideo = mxCall.isVideoCall,
@ -873,6 +874,16 @@ class WebRtcPeerConnectionManager @Inject constructor(
endCall(false)
}
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
val call = currentCall ?: return
// Remote echos are filtered, so it's only remote hangups that i will get here
if (call.mxCall.callId != callRejectContent.callId) return Unit.also {
Timber.w("onCallRejected for non active call? ${callRejectContent.callId}")
}
call.mxCall.state = CallState.Terminated
endCall(false)
}
override fun onCallManagedByOtherSession(callId: String) {
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
currentCall = null

View File

@ -332,7 +332,7 @@ class HomeDetailFragment @Inject constructor(
context = requireContext(),
callId = call.callId,
roomId = call.roomId,
otherUserId = call.otherUserId,
otherUserId = call.opponentUserId,
isIncomingCall = !call.isOutgoing,
isVideoCall = call.isVideoCall,
mode = null

View File

@ -1962,7 +1962,7 @@ class RoomDetailFragment @Inject constructor(
context = requireContext(),
callId = call.callId,
roomId = call.roomId,
otherUserId = call.otherUserId,
otherUserId = call.opponentUserId,
isIncomingCall = !call.isOutgoing,
isVideoCall = call.isVideoCall,
mode = null