mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-07 06:39:45 +00:00
Add helper class to send messages to call participants
For now it just provides support for sending a data channel message to all participants, so notifying all participants when the media is toggled or the speaking status change can be directly refactored to use it. While it would have been fine to use a single class for both MCU and no MCU they were split for easier and cleaner unit testing in future stages. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
parent
8fa3224879
commit
3e36c85015
@ -63,6 +63,9 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
|
||||
import com.nextcloud.talk.call.CallParticipant
|
||||
import com.nextcloud.talk.call.CallParticipantList
|
||||
import com.nextcloud.talk.call.CallParticipantModel
|
||||
import com.nextcloud.talk.call.MessageSender
|
||||
import com.nextcloud.talk.call.MessageSenderMcu
|
||||
import com.nextcloud.talk.call.MessageSenderNoMcu
|
||||
import com.nextcloud.talk.call.ReactionAnimator
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
@ -242,6 +245,7 @@ class CallActivity : CallBaseActivity() {
|
||||
private var signalingMessageReceiver: SignalingMessageReceiver? = null
|
||||
private val internalSignalingMessageSender = InternalSignalingMessageSender()
|
||||
private var signalingMessageSender: SignalingMessageSender? = null
|
||||
private var messageSender: MessageSender? = null
|
||||
private val offerAnswerNickProviders: MutableMap<String?, OfferAnswerNickProvider?> = HashMap()
|
||||
private val callParticipantMessageListeners: MutableMap<String?, CallParticipantMessageListener> = HashMap()
|
||||
private val selfPeerConnectionObserver: PeerConnectionObserver = CallActivitySelfPeerConnectionObserver()
|
||||
@ -1172,18 +1176,7 @@ class CallActivity : CallBaseActivity() {
|
||||
if (isSpeaking) SIGNALING_MESSAGE_SPEAKING_STARTED else SIGNALING_MESSAGE_SPEAKING_STOPPED
|
||||
|
||||
if (isConnectionEstablished && othersInCall) {
|
||||
if (!hasMCU) {
|
||||
for (peerConnectionWrapper in peerConnectionWrapperList) {
|
||||
peerConnectionWrapper.send(DataChannelMessage(isSpeakingMessage))
|
||||
}
|
||||
} else {
|
||||
for (peerConnectionWrapper in peerConnectionWrapperList) {
|
||||
if (peerConnectionWrapper.sessionId == webSocketClient!!.sessionId) {
|
||||
peerConnectionWrapper.send(DataChannelMessage(isSpeakingMessage))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
messageSender!!.sendToAll(DataChannelMessage(isSpeakingMessage))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1368,18 +1361,7 @@ class CallActivity : CallBaseActivity() {
|
||||
}
|
||||
}
|
||||
if (isConnectionEstablished) {
|
||||
if (!hasMCU) {
|
||||
for (peerConnectionWrapper in peerConnectionWrapperList) {
|
||||
peerConnectionWrapper.send(DataChannelMessage(message))
|
||||
}
|
||||
} else {
|
||||
for (peerConnectionWrapper in peerConnectionWrapperList) {
|
||||
if (peerConnectionWrapper.sessionId == webSocketClient!!.sessionId) {
|
||||
peerConnectionWrapper.send(DataChannelMessage(message))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
messageSender!!.sendToAll(DataChannelMessage(message))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1621,6 +1603,10 @@ class CallActivity : CallBaseActivity() {
|
||||
|
||||
hasMCU = false
|
||||
|
||||
messageSender = MessageSenderNoMcu(
|
||||
peerConnectionWrapperList
|
||||
)
|
||||
|
||||
joinRoomAndCall()
|
||||
}
|
||||
}
|
||||
@ -1911,6 +1897,17 @@ class CallActivity : CallBaseActivity() {
|
||||
// be overwritten with the right value once the response to the "hello" message is received.
|
||||
hasMCU = webSocketClient!!.hasMCU()
|
||||
Log.d(TAG, "hasMCU is $hasMCU")
|
||||
|
||||
if (hasMCU) {
|
||||
messageSender = MessageSenderMcu(
|
||||
peerConnectionWrapperList,
|
||||
webSocketClient!!.sessionId
|
||||
)
|
||||
} else {
|
||||
messageSender = MessageSenderNoMcu(
|
||||
peerConnectionWrapperList
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (webSocketClient!!.isConnected && currentCallStatus === CallStatus.PUBLISHER_FAILED) {
|
||||
webSocketClient!!.restartWebSocket()
|
||||
@ -1940,6 +1937,17 @@ class CallActivity : CallBaseActivity() {
|
||||
hasMCU = webSocketClient!!.hasMCU()
|
||||
Log.d(TAG, "hasMCU is $hasMCU")
|
||||
|
||||
if (hasMCU) {
|
||||
messageSender = MessageSenderMcu(
|
||||
peerConnectionWrapperList,
|
||||
webSocketClient!!.sessionId
|
||||
)
|
||||
} else {
|
||||
messageSender = MessageSenderNoMcu(
|
||||
peerConnectionWrapperList
|
||||
)
|
||||
}
|
||||
|
||||
if (!webSocketCommunicationEvent.getHashMap()!!.containsKey("oldResumeId")) {
|
||||
if (currentCallStatus === CallStatus.RECONNECTING) {
|
||||
hangup(false, false)
|
||||
|
47
app/src/main/java/com/nextcloud/talk/call/MessageSender.java
Normal file
47
app/src/main/java/com/nextcloud/talk/call/MessageSender.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.webrtc.PeerConnectionWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to send messages to participants in a call.
|
||||
* <p>
|
||||
* A specific subclass has to be created depending on whether an MCU is being used or not.
|
||||
* <p>
|
||||
* 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
|
||||
* neither the local and remote participants have publishing rights).
|
||||
*/
|
||||
public abstract class MessageSender {
|
||||
|
||||
protected final List<PeerConnectionWrapper> peerConnectionWrappers;
|
||||
|
||||
public MessageSender(List<PeerConnectionWrapper> peerConnectionWrappers) {
|
||||
this.peerConnectionWrappers = peerConnectionWrappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given data channel message to all the participants in the call.
|
||||
*
|
||||
* @param dataChannelMessage the message to send
|
||||
*/
|
||||
public abstract void sendToAll(DataChannelMessage dataChannelMessage);
|
||||
|
||||
protected PeerConnectionWrapper getPeerConnectionWrapper(String sessionId) {
|
||||
for (PeerConnectionWrapper peerConnectionWrapper: peerConnectionWrappers) {
|
||||
if (peerConnectionWrapper.getSessionId().equals(sessionId)) {
|
||||
return peerConnectionWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.webrtc.PeerConnectionWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to send messages to participants in a call when an MCU is used.
|
||||
*/
|
||||
public class MessageSenderMcu extends MessageSender {
|
||||
|
||||
private final String ownSessionId;
|
||||
|
||||
public MessageSenderMcu(List<PeerConnectionWrapper> peerConnectionWrappers,
|
||||
String ownSessionId) {
|
||||
super(peerConnectionWrappers);
|
||||
|
||||
this.ownSessionId = ownSessionId;
|
||||
}
|
||||
|
||||
public void sendToAll(DataChannelMessage dataChannelMessage) {
|
||||
PeerConnectionWrapper ownPeerConnectionWrapper = getPeerConnectionWrapper(ownSessionId);
|
||||
if (ownPeerConnectionWrapper != null) {
|
||||
ownPeerConnectionWrapper.send(dataChannelMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.webrtc.PeerConnectionWrapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class to send messages to participants in a call when an MCU is not used.
|
||||
*/
|
||||
public class MessageSenderNoMcu extends MessageSender {
|
||||
|
||||
public MessageSenderNoMcu(List<PeerConnectionWrapper> peerConnectionWrappers) {
|
||||
super(peerConnectionWrappers);
|
||||
}
|
||||
|
||||
public void sendToAll(DataChannelMessage dataChannelMessage) {
|
||||
for (PeerConnectionWrapper peerConnectionWrapper: peerConnectionWrappers) {
|
||||
peerConnectionWrapper.send(dataChannelMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.webrtc.PeerConnectionWrapper
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.never
|
||||
|
||||
class MessageSenderMcuTest {
|
||||
|
||||
private var peerConnectionWrappers: MutableList<PeerConnectionWrapper?>? = null
|
||||
private var peerConnectionWrapper1: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2: PeerConnectionWrapper? = null
|
||||
private var ownPeerConnectionWrapper: PeerConnectionWrapper? = null
|
||||
|
||||
private var messageSender: MessageSenderMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
peerConnectionWrappers = ArrayList()
|
||||
|
||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper1!!.sessionId).thenReturn("theSessionId1")
|
||||
Mockito.`when`(peerConnectionWrapper1!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper1)
|
||||
|
||||
peerConnectionWrapper2 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2)
|
||||
|
||||
ownPeerConnectionWrapper = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(ownPeerConnectionWrapper!!.sessionId).thenReturn("ownSessionId")
|
||||
Mockito.`when`(ownPeerConnectionWrapper!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(ownPeerConnectionWrapper)
|
||||
|
||||
messageSender = MessageSenderMcu(peerConnectionWrappers, "ownSessionId")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAll() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(ownPeerConnectionWrapper!!).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAllWithoutOwnPeerConnection() {
|
||||
peerConnectionWrappers!!.remove(ownPeerConnectionWrapper)
|
||||
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(ownPeerConnectionWrapper!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper1!!, never()).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!, never()).send(message)
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.webrtc.PeerConnectionWrapper
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
|
||||
class MessageSenderNoMcuTest {
|
||||
|
||||
private var peerConnectionWrappers: MutableList<PeerConnectionWrapper?>? = null
|
||||
private var peerConnectionWrapper1: PeerConnectionWrapper? = null
|
||||
private var peerConnectionWrapper2: PeerConnectionWrapper? = null
|
||||
|
||||
private var messageSender: MessageSenderNoMcu? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
peerConnectionWrappers = ArrayList()
|
||||
|
||||
peerConnectionWrapper1 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper1!!.sessionId).thenReturn("theSessionId1")
|
||||
Mockito.`when`(peerConnectionWrapper1!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper1)
|
||||
|
||||
peerConnectionWrapper2 = Mockito.mock(PeerConnectionWrapper::class.java)
|
||||
Mockito.`when`(peerConnectionWrapper2!!.sessionId).thenReturn("theSessionId2")
|
||||
Mockito.`when`(peerConnectionWrapper2!!.videoStreamType).thenReturn("video")
|
||||
peerConnectionWrappers!!.add(peerConnectionWrapper2)
|
||||
|
||||
messageSender = MessageSenderNoMcu(peerConnectionWrappers)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSendDataChannelMessageToAll() {
|
||||
val message = DataChannelMessage()
|
||||
messageSender!!.sendToAll(message)
|
||||
|
||||
Mockito.verify(peerConnectionWrapper1!!).send(message)
|
||||
Mockito.verify(peerConnectionWrapper2!!).send(message)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user