mirror of
https://github.com/nextcloud/talk-android
synced 2025-01-18 21:18:15 +00:00
Add support for sending signaling messages in the MessageSender
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
parent
8644d05636
commit
ea2bebe3b0
@ -1590,6 +1590,8 @@ class CallActivity : CallBaseActivity() {
|
|||||||
hasMCU = false
|
hasMCU = false
|
||||||
|
|
||||||
messageSender = MessageSenderNoMcu(
|
messageSender = MessageSenderNoMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
peerConnectionWrapperList
|
peerConnectionWrapperList
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1895,11 +1897,15 @@ class CallActivity : CallBaseActivity() {
|
|||||||
|
|
||||||
if (hasMCU) {
|
if (hasMCU) {
|
||||||
messageSender = MessageSenderMcu(
|
messageSender = MessageSenderMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
peerConnectionWrapperList,
|
peerConnectionWrapperList,
|
||||||
webSocketClient!!.sessionId
|
webSocketClient!!.sessionId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
messageSender = MessageSenderNoMcu(
|
messageSender = MessageSenderNoMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
peerConnectionWrapperList
|
peerConnectionWrapperList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1934,11 +1940,15 @@ class CallActivity : CallBaseActivity() {
|
|||||||
|
|
||||||
if (hasMCU) {
|
if (hasMCU) {
|
||||||
messageSender = MessageSenderMcu(
|
messageSender = MessageSenderMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
peerConnectionWrapperList,
|
peerConnectionWrapperList,
|
||||||
webSocketClient!!.sessionId
|
webSocketClient!!.sessionId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
messageSender = MessageSenderNoMcu(
|
messageSender = MessageSenderNoMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
peerConnectionWrapperList
|
peerConnectionWrapperList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,22 @@
|
|||||||
package com.nextcloud.talk.call;
|
package com.nextcloud.talk.call;
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
||||||
|
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender;
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to send messages to participants in a call.
|
* Helper class to send messages to participants in a call.
|
||||||
* <p>
|
* <p>
|
||||||
* A specific subclass has to be created depending on whether an MCU is being used or not.
|
* A specific subclass has to be created depending on whether an MCU is being used or not.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that, unlike signaling messages, data channel messages require a peer connection. Therefore data channel
|
* Note that recipients of signaling messages are not validated, so no error will be triggered if trying to send a
|
||||||
|
* message to a participant with a session ID that does not exist or is not in the call.
|
||||||
|
* <p>
|
||||||
|
* Also note that, unlike signaling messages, data channel messages require a peer connection. Therefore data channel
|
||||||
* messages may not be received by a participant if there is no peer connection with that participant (for example, if
|
* messages may not be received by a participant if there is no peer connection with that participant (for example, if
|
||||||
* neither the local and remote participants have publishing rights). Moreover, data channel messages are expected to
|
* neither the local and remote participants have publishing rights). Moreover, data channel messages are expected to
|
||||||
* be received only on peer connections with type "video", so data channel messages will not be sent on other peer
|
* be received only on peer connections with type "video", so data channel messages will not be sent on other peer
|
||||||
@ -24,9 +30,17 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public abstract class MessageSender {
|
public abstract class MessageSender {
|
||||||
|
|
||||||
|
private final SignalingMessageSender signalingMessageSender;
|
||||||
|
|
||||||
|
private final Set<String> callParticipantSessionIds;
|
||||||
|
|
||||||
protected final List<PeerConnectionWrapper> peerConnectionWrappers;
|
protected final List<PeerConnectionWrapper> peerConnectionWrappers;
|
||||||
|
|
||||||
public MessageSender(List<PeerConnectionWrapper> peerConnectionWrappers) {
|
public MessageSender(SignalingMessageSender signalingMessageSender,
|
||||||
|
Set<String> callParticipantSessionIds,
|
||||||
|
List<PeerConnectionWrapper> peerConnectionWrappers) {
|
||||||
|
this.signalingMessageSender = signalingMessageSender;
|
||||||
|
this.callParticipantSessionIds = callParticipantSessionIds;
|
||||||
this.peerConnectionWrappers = peerConnectionWrappers;
|
this.peerConnectionWrappers = peerConnectionWrappers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +51,35 @@ public abstract class MessageSender {
|
|||||||
*/
|
*/
|
||||||
public abstract void sendToAll(DataChannelMessage dataChannelMessage);
|
public abstract void sendToAll(DataChannelMessage dataChannelMessage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the given signaling message to the given session ID.
|
||||||
|
* <p>
|
||||||
|
* Note that the signaling message will be modified to set the recipient in the "to" field.
|
||||||
|
*
|
||||||
|
* @param ncSignalingMessage the message to send
|
||||||
|
* @param sessionId the signaling session ID of the participant to send the message to
|
||||||
|
*/
|
||||||
|
public void send(NCSignalingMessage ncSignalingMessage, String sessionId) {
|
||||||
|
ncSignalingMessage.setTo(sessionId);
|
||||||
|
|
||||||
|
signalingMessageSender.send(ncSignalingMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the given signaling message to all the participants in the call.
|
||||||
|
* <p>
|
||||||
|
* Note that the signaling message will be modified to set each of the recipients in the "to" field.
|
||||||
|
*
|
||||||
|
* @param ncSignalingMessage the message to send
|
||||||
|
*/
|
||||||
|
public void sendToAll(NCSignalingMessage ncSignalingMessage) {
|
||||||
|
for (String sessionId: callParticipantSessionIds) {
|
||||||
|
ncSignalingMessage.setTo(sessionId);
|
||||||
|
|
||||||
|
signalingMessageSender.send(ncSignalingMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected PeerConnectionWrapper getPeerConnectionWrapper(String sessionId) {
|
protected PeerConnectionWrapper getPeerConnectionWrapper(String sessionId) {
|
||||||
for (PeerConnectionWrapper peerConnectionWrapper: peerConnectionWrappers) {
|
for (PeerConnectionWrapper peerConnectionWrapper: peerConnectionWrappers) {
|
||||||
if (peerConnectionWrapper.getSessionId().equals(sessionId)
|
if (peerConnectionWrapper.getSessionId().equals(sessionId)
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
package com.nextcloud.talk.call;
|
package com.nextcloud.talk.call;
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender;
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to send messages to participants in a call when an MCU is used.
|
* Helper class to send messages to participants in a call when an MCU is used.
|
||||||
@ -21,9 +23,11 @@ public class MessageSenderMcu extends MessageSender {
|
|||||||
|
|
||||||
private final String ownSessionId;
|
private final String ownSessionId;
|
||||||
|
|
||||||
public MessageSenderMcu(List<PeerConnectionWrapper> peerConnectionWrappers,
|
public MessageSenderMcu(SignalingMessageSender signalingMessageSender,
|
||||||
|
Set<String> callParticipantSessionIds,
|
||||||
|
List<PeerConnectionWrapper> peerConnectionWrappers,
|
||||||
String ownSessionId) {
|
String ownSessionId) {
|
||||||
super(peerConnectionWrappers);
|
super(signalingMessageSender, callParticipantSessionIds, peerConnectionWrappers);
|
||||||
|
|
||||||
this.ownSessionId = ownSessionId;
|
this.ownSessionId = ownSessionId;
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,21 @@
|
|||||||
package com.nextcloud.talk.call;
|
package com.nextcloud.talk.call;
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender;
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to send messages to participants in a call when an MCU is not used.
|
* Helper class to send messages to participants in a call when an MCU is not used.
|
||||||
*/
|
*/
|
||||||
public class MessageSenderNoMcu extends MessageSender {
|
public class MessageSenderNoMcu extends MessageSender {
|
||||||
|
|
||||||
public MessageSenderNoMcu(List<PeerConnectionWrapper> peerConnectionWrappers) {
|
public MessageSenderNoMcu(SignalingMessageSender signalingMessageSender,
|
||||||
super(peerConnectionWrappers);
|
Set<String> callParticipantSessionIds,
|
||||||
|
List<PeerConnectionWrapper> peerConnectionWrappers) {
|
||||||
|
super(signalingMessageSender, callParticipantSessionIds, peerConnectionWrappers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package com.nextcloud.talk.call
|
package com.nextcloud.talk.call
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -27,6 +28,10 @@ class MessageSenderMcuTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
val signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||||
|
|
||||||
|
val callParticipants = HashMap<String, CallParticipant>()
|
||||||
|
|
||||||
peerConnectionWrappers = ArrayList()
|
peerConnectionWrappers = ArrayList()
|
||||||
|
|
||||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||||
@ -59,7 +64,12 @@ class MessageSenderMcuTest {
|
|||||||
Mockito.`when`(ownPeerConnectionWrapperScreen!!.videoStreamType).thenReturn("screen")
|
Mockito.`when`(ownPeerConnectionWrapperScreen!!.videoStreamType).thenReturn("screen")
|
||||||
peerConnectionWrappers!!.add(ownPeerConnectionWrapperScreen)
|
peerConnectionWrappers!!.add(ownPeerConnectionWrapperScreen)
|
||||||
|
|
||||||
messageSender = MessageSenderMcu(peerConnectionWrappers, "ownSessionId")
|
messageSender = MessageSenderMcu(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipants.keys,
|
||||||
|
peerConnectionWrappers,
|
||||||
|
"ownSessionId"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package com.nextcloud.talk.call
|
package com.nextcloud.talk.call
|
||||||
|
|
||||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||||
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -25,6 +26,10 @@ class MessageSenderNoMcuTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
val signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||||
|
|
||||||
|
val callParticipants = HashMap<String, CallParticipant>()
|
||||||
|
|
||||||
peerConnectionWrappers = ArrayList()
|
peerConnectionWrappers = ArrayList()
|
||||||
|
|
||||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||||
@ -47,7 +52,7 @@ class MessageSenderNoMcuTest {
|
|||||||
Mockito.`when`(peerConnectionWrapper4Screen!!.videoStreamType).thenReturn("screen")
|
Mockito.`when`(peerConnectionWrapper4Screen!!.videoStreamType).thenReturn("screen")
|
||||||
peerConnectionWrappers!!.add(peerConnectionWrapper4Screen)
|
peerConnectionWrappers!!.add(peerConnectionWrapper4Screen)
|
||||||
|
|
||||||
messageSender = MessageSenderNoMcu(peerConnectionWrappers)
|
messageSender = MessageSenderNoMcu(signalingMessageSender, callParticipants.keys, peerConnectionWrappers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
134
app/src/test/java/com/nextcloud/talk/call/MessageSenderTest.kt
Normal file
134
app/src/test/java/com/nextcloud/talk/call/MessageSenderTest.kt
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Nextcloud Talk - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
package com.nextcloud.talk.call
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.signaling.DataChannelMessage
|
||||||
|
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
|
||||||
|
import com.nextcloud.talk.signaling.SignalingMessageSender
|
||||||
|
import com.nextcloud.talk.webrtc.PeerConnectionWrapper
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.doAnswer
|
||||||
|
import org.mockito.Mockito.times
|
||||||
|
import org.mockito.invocation.InvocationOnMock
|
||||||
|
|
||||||
|
class MessageSenderTest {
|
||||||
|
|
||||||
|
private class MessageSender(
|
||||||
|
signalingMessageSender: SignalingMessageSender?,
|
||||||
|
callParticipantSessionIds: Set<String>?,
|
||||||
|
peerConnectionWrappers: List<PeerConnectionWrapper>?
|
||||||
|
) : com.nextcloud.talk.call.MessageSender(
|
||||||
|
signalingMessageSender,
|
||||||
|
callParticipantSessionIds,
|
||||||
|
peerConnectionWrappers
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun sendToAll(dataChannelMessage: DataChannelMessage?) {
|
||||||
|
// Not used in base class tests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var signalingMessageSender: SignalingMessageSender? = null
|
||||||
|
|
||||||
|
private var callParticipants: MutableMap<String, CallParticipant>? = null
|
||||||
|
|
||||||
|
private var messageSender: MessageSender? = null
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
signalingMessageSender = Mockito.mock(SignalingMessageSender::class.java)
|
||||||
|
|
||||||
|
callParticipants = HashMap()
|
||||||
|
|
||||||
|
val callParticipant1: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||||
|
callParticipants!!["theSessionId1"] = callParticipant1
|
||||||
|
|
||||||
|
val callParticipant2: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||||
|
callParticipants!!["theSessionId2"] = callParticipant2
|
||||||
|
|
||||||
|
val callParticipant3: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||||
|
callParticipants!!["theSessionId3"] = callParticipant3
|
||||||
|
|
||||||
|
val callParticipant4: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||||
|
callParticipants!!["theSessionId4"] = callParticipant4
|
||||||
|
|
||||||
|
val peerConnectionWrappers = ArrayList<PeerConnectionWrapper>()
|
||||||
|
|
||||||
|
messageSender = MessageSender(signalingMessageSender, callParticipants!!.keys, peerConnectionWrappers)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendSignalingMessage() {
|
||||||
|
val message: NCSignalingMessage = Mockito.mock(NCSignalingMessage::class.java)
|
||||||
|
messageSender!!.send(message, "theSessionId2")
|
||||||
|
|
||||||
|
Mockito.verify(message).to = "theSessionId2"
|
||||||
|
Mockito.verify(signalingMessageSender!!).send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendSignalingMessageIfUnknownSessionId() {
|
||||||
|
val message: NCSignalingMessage = Mockito.mock(NCSignalingMessage::class.java)
|
||||||
|
messageSender!!.send(message, "unknownSessionId")
|
||||||
|
|
||||||
|
Mockito.verify(message).to = "unknownSessionId"
|
||||||
|
Mockito.verify(signalingMessageSender!!).send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendSignalingMessageToAll() {
|
||||||
|
val sentTo: MutableList<String?> = ArrayList()
|
||||||
|
doAnswer { invocation: InvocationOnMock ->
|
||||||
|
val arguments = invocation.arguments
|
||||||
|
val message = (arguments[0] as NCSignalingMessage)
|
||||||
|
|
||||||
|
sentTo.add(message.to)
|
||||||
|
null
|
||||||
|
}.`when`(signalingMessageSender!!).send(any())
|
||||||
|
|
||||||
|
val message = NCSignalingMessage()
|
||||||
|
messageSender!!.sendToAll(message)
|
||||||
|
|
||||||
|
assertTrue(sentTo.contains("theSessionId1"))
|
||||||
|
assertTrue(sentTo.contains("theSessionId2"))
|
||||||
|
assertTrue(sentTo.contains("theSessionId3"))
|
||||||
|
assertTrue(sentTo.contains("theSessionId4"))
|
||||||
|
Mockito.verify(signalingMessageSender!!, times(4)).send(message)
|
||||||
|
Mockito.verifyNoMoreInteractions(signalingMessageSender)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSendSignalingMessageToAllWhenParticipantsWereUpdated() {
|
||||||
|
val callParticipant5: CallParticipant = Mockito.mock(CallParticipant::class.java)
|
||||||
|
callParticipants!!["theSessionId5"] = callParticipant5
|
||||||
|
|
||||||
|
callParticipants!!.remove("theSessionId2")
|
||||||
|
callParticipants!!.remove("theSessionId3")
|
||||||
|
|
||||||
|
val sentTo: MutableList<String?> = ArrayList()
|
||||||
|
doAnswer { invocation: InvocationOnMock ->
|
||||||
|
val arguments = invocation.arguments
|
||||||
|
val message = (arguments[0] as NCSignalingMessage)
|
||||||
|
|
||||||
|
sentTo.add(message.to)
|
||||||
|
null
|
||||||
|
}.`when`(signalingMessageSender!!).send(any())
|
||||||
|
|
||||||
|
val message = NCSignalingMessage()
|
||||||
|
messageSender!!.sendToAll(message)
|
||||||
|
|
||||||
|
assertTrue(sentTo.contains("theSessionId1"))
|
||||||
|
assertTrue(sentTo.contains("theSessionId4"))
|
||||||
|
assertTrue(sentTo.contains("theSessionId5"))
|
||||||
|
Mockito.verify(signalingMessageSender!!, times(3)).send(message)
|
||||||
|
Mockito.verifyNoMoreInteractions(signalingMessageSender)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user