mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-22 21:15:30 +03:00
Add listener for participant list messages
For now only the same participant list messages that were already handled are taken into account, but at a later point further messages, like participants joining or leaving the conversation, could be added too. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
parent
c8e77c3d3b
commit
e0c676bb35
4 changed files with 802 additions and 1 deletions
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Daniel Calviño Sánchez
|
||||
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper class to register and notify ParticipantListMessageListeners.
|
||||
*
|
||||
* This class is only meant for internal use by SignalingMessageReceiver; listeners must register themselves against
|
||||
* a SignalingMessageReceiver rather than against a ParticipantListMessageNotifier.
|
||||
*/
|
||||
class ParticipantListMessageNotifier {
|
||||
|
||||
private final Set<SignalingMessageReceiver.ParticipantListMessageListener> participantListMessageListeners = new LinkedHashSet<>();
|
||||
|
||||
public synchronized void addListener(SignalingMessageReceiver.ParticipantListMessageListener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("participantListMessageListeners can not be null");
|
||||
}
|
||||
|
||||
participantListMessageListeners.add(listener);
|
||||
}
|
||||
|
||||
public synchronized void removeListener(SignalingMessageReceiver.ParticipantListMessageListener listener) {
|
||||
participantListMessageListeners.remove(listener);
|
||||
}
|
||||
|
||||
public synchronized void notifyUsersInRoom(List<Participant> participants) {
|
||||
for (SignalingMessageReceiver.ParticipantListMessageListener listener : new ArrayList<>(participantListMessageListeners)) {
|
||||
listener.onUsersInRoom(participants);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyParticipantsUpdate(List<Participant> participants) {
|
||||
for (SignalingMessageReceiver.ParticipantListMessageListener listener : new ArrayList<>(participantListMessageListeners)) {
|
||||
listener.onParticipantsUpdate(participants);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyAllParticipantsUpdate(long inCall) {
|
||||
for (SignalingMessageReceiver.ParticipantListMessageListener listener : new ArrayList<>(participantListMessageListeners)) {
|
||||
listener.onAllParticipantsUpdate(inCall);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,10 +19,16 @@
|
|||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Hub to register listeners for signaling messages of different kinds.
|
||||
*
|
||||
|
@ -44,6 +50,74 @@ import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
|||
*/
|
||||
public abstract class SignalingMessageReceiver {
|
||||
|
||||
/**
|
||||
* Listener for participant list messages.
|
||||
*
|
||||
* The messages are implicitly bound to the room currently joined in the signaling server; listeners are expected
|
||||
* to know the current room.
|
||||
*/
|
||||
public interface ParticipantListMessageListener {
|
||||
|
||||
/**
|
||||
* List of all the participants in the room.
|
||||
*
|
||||
* This message is received only when the internal signaling server is used.
|
||||
*
|
||||
* The message is received periodically, and the participants may not have been modified since the last message.
|
||||
*
|
||||
* Only the following participant properties are set:
|
||||
* - inCall
|
||||
* - lastPing
|
||||
* - sessionId
|
||||
* - userId (if the participant is not a guest)
|
||||
*
|
||||
* "participantPermissions" is provided in the message (since Talk 13), but not currently set in the
|
||||
* participant. "publishingPermissions" was provided instead in Talk 12, but it was not used anywhere, so it is
|
||||
* ignored.
|
||||
*
|
||||
* @param participants all the participants (users and guests) in the room
|
||||
*/
|
||||
void onUsersInRoom(List<Participant> participants);
|
||||
|
||||
/**
|
||||
* List of all the participants in the call or the room (depending on what triggered the event).
|
||||
*
|
||||
* This message is received only when the external signaling server is used.
|
||||
*
|
||||
* The message is received when any participant changed, although what changed is not provided and should be
|
||||
* derived from the difference with previous messages. The list of participants may include only the
|
||||
* participants in the call (including those that just left it and thus triggered the event) or all the
|
||||
* participants currently in the room (participants in the room but not currently active, that is, without a
|
||||
* session, are not included).
|
||||
*
|
||||
* Only the following participant properties are set:
|
||||
* - inCall
|
||||
* - lastPing
|
||||
* - sessionId
|
||||
* - type
|
||||
* - userId (if the participant is not a guest)
|
||||
*
|
||||
* "nextcloudSessionId" is provided in the message (when the "inCall" property of any participant changed), but
|
||||
* not currently set in the participant.
|
||||
*
|
||||
* "participantPermissions" is provided in the message (since Talk 13), but not currently set in the
|
||||
* participant. "publishingPermissions" was provided instead in Talk 12, but it was not used anywhere, so it is
|
||||
* ignored.
|
||||
*
|
||||
* @param participants all the participants (users and guests) in the room
|
||||
*/
|
||||
void onParticipantsUpdate(List<Participant> participants);
|
||||
|
||||
/**
|
||||
* Update of the properties of all the participants in the room.
|
||||
*
|
||||
* This message is received only when the external signaling server is used.
|
||||
*
|
||||
* @param inCall the new value of the inCall property
|
||||
*/
|
||||
void onAllParticipantsUpdate(long inCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for call participant messages.
|
||||
*
|
||||
|
@ -83,12 +157,29 @@ public abstract class SignalingMessageReceiver {
|
|||
void onEndOfCandidates();
|
||||
}
|
||||
|
||||
private final ParticipantListMessageNotifier participantListMessageNotifier = new ParticipantListMessageNotifier();
|
||||
|
||||
private final CallParticipantMessageNotifier callParticipantMessageNotifier = new CallParticipantMessageNotifier();
|
||||
|
||||
private final OfferMessageNotifier offerMessageNotifier = new OfferMessageNotifier();
|
||||
|
||||
private final WebRtcMessageNotifier webRtcMessageNotifier = new WebRtcMessageNotifier();
|
||||
|
||||
/**
|
||||
* Adds a listener for participant list messages.
|
||||
*
|
||||
* A listener is expected to be added only once. If the same listener is added again it will be notified just once.
|
||||
*
|
||||
* @param listener the ParticipantListMessageListener
|
||||
*/
|
||||
public void addListener(ParticipantListMessageListener listener) {
|
||||
participantListMessageNotifier.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(ParticipantListMessageListener listener) {
|
||||
participantListMessageNotifier.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for call participant messages.
|
||||
*
|
||||
|
@ -139,6 +230,182 @@ public abstract class SignalingMessageReceiver {
|
|||
webRtcMessageNotifier.removeListener(listener);
|
||||
}
|
||||
|
||||
protected void processEvent(Map<String, Object> eventMap) {
|
||||
if (!"update".equals(eventMap.get("type")) || !"participants".equals(eventMap.get("target"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> updateMap;
|
||||
try {
|
||||
updateMap = (Map<String, Object>) eventMap.get("update");
|
||||
} catch (RuntimeException e) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateMap == null) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateMap.get("all") != null && Boolean.parseBoolean(updateMap.get("all").toString())) {
|
||||
processAllParticipantsUpdate(updateMap);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateMap.get("users") != null) {
|
||||
processParticipantsUpdate(updateMap);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void processAllParticipantsUpdate(Map<String, Object> updateMap) {
|
||||
// Message schema:
|
||||
// {
|
||||
// "type": "event",
|
||||
// "event": {
|
||||
// "target": "participants",
|
||||
// "type": "update",
|
||||
// "update": {
|
||||
// "roomid": #STRING#,
|
||||
// "incall": 0,
|
||||
// "all": true,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
|
||||
long inCall;
|
||||
try {
|
||||
inCall = Long.parseLong(updateMap.get("inCall").toString());
|
||||
} catch (RuntimeException e) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
participantListMessageNotifier.notifyAllParticipantsUpdate(inCall);
|
||||
}
|
||||
|
||||
private void processParticipantsUpdate(Map<String, Object> updateMap) {
|
||||
// Message schema:
|
||||
// {
|
||||
// "type": "event",
|
||||
// "event": {
|
||||
// "target": "participants",
|
||||
// "type": "update",
|
||||
// "update": {
|
||||
// "roomid": #INTEGER#,
|
||||
// "users": [
|
||||
// {
|
||||
// "inCall": #INTEGER#,
|
||||
// "lastPing": #INTEGER#,
|
||||
// "sessionId": #STRING#,
|
||||
// "participantType": #INTEGER#,
|
||||
// "userId": #STRING#, // Optional
|
||||
// "nextcloudSessionId": #STRING#, // Optional
|
||||
// "participantPermissions": #INTEGER#, // Talk >= 13
|
||||
// },
|
||||
// ...
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// Note that "userId" in participants->update comes from the Nextcloud server, so it is "userId"; in other
|
||||
// messages, like room->join, it comes directly from the external signaling server, so it is "userid" instead.
|
||||
|
||||
List<Map<String, Object>> users;
|
||||
try {
|
||||
users = (List<Map<String, Object>>) updateMap.get("users");
|
||||
} catch (RuntimeException e) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
if (users == null) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
List<Participant> participants = new ArrayList<>(users.size());
|
||||
|
||||
for (Map<String, Object> user: users) {
|
||||
try {
|
||||
participants.add(getParticipantFromMessageMap(user));
|
||||
} catch (RuntimeException e) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
participantListMessageNotifier.notifyParticipantsUpdate(participants);
|
||||
}
|
||||
|
||||
protected void processUsersInRoom(List<Map<String, Object>> users) {
|
||||
// Message schema:
|
||||
// {
|
||||
// "type": "usersInRoom",
|
||||
// "data": [
|
||||
// {
|
||||
// "inCall": #INTEGER#,
|
||||
// "lastPing": #INTEGER#,
|
||||
// "roomId": #INTEGER#,
|
||||
// "sessionId": #STRING#,
|
||||
// "userId": #STRING#, // Always included, although it can be empty
|
||||
// "participantPermissions": #INTEGER#, // Talk >= 13
|
||||
// },
|
||||
// ...
|
||||
// ],
|
||||
// }
|
||||
|
||||
List<Participant> participants = new ArrayList<>(users.size());
|
||||
|
||||
for (Map<String, Object> user: users) {
|
||||
try {
|
||||
participants.add(getParticipantFromMessageMap(user));
|
||||
} catch (RuntimeException e) {
|
||||
// Broken message, this should not happen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
participantListMessageNotifier.notifyUsersInRoom(participants);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a Participant from the data in the given map.
|
||||
*
|
||||
* Maps from internal and external signaling server messages can be used. Nevertheless, besides the differences
|
||||
* between the messages and the optional properties, it is expected that the message is correct and the given data
|
||||
* is parseable. Broken messages (for example, a string instead of an integer for "inCall" or a missing
|
||||
* "sessionId") may cause a RuntimeException to be thrown.
|
||||
*
|
||||
* @param participantMap the map with the participant data
|
||||
* @return the Participant
|
||||
*/
|
||||
private Participant getParticipantFromMessageMap(Map<String, Object> participantMap) {
|
||||
Participant participant = new Participant();
|
||||
|
||||
participant.setInCall(Long.parseLong(participantMap.get("inCall").toString()));
|
||||
participant.setLastPing(Long.parseLong(participantMap.get("lastPing").toString()));
|
||||
participant.setSessionId(participantMap.get("sessionId").toString());
|
||||
|
||||
if (participantMap.get("userId") != null && !participantMap.get("userId").toString().isEmpty()) {
|
||||
participant.setUserId(participantMap.get("userId").toString());
|
||||
}
|
||||
|
||||
// Only in external signaling messages
|
||||
if (participantMap.get("participantType") != null) {
|
||||
int participantTypeInt = Integer.parseInt(participantMap.get("participantType").toString());
|
||||
|
||||
EnumParticipantTypeConverter converter = new EnumParticipantTypeConverter();
|
||||
participant.setType(converter.getFromInt(participantTypeInt));
|
||||
}
|
||||
|
||||
return participant;
|
||||
}
|
||||
|
||||
protected void processSignalingMessage(NCSignalingMessage signalingMessage) {
|
||||
// Note that in the internal signaling server message "data" is the String representation of a JSON
|
||||
// object, although it is already decoded when used here.
|
||||
|
|
|
@ -49,7 +49,7 @@ public class SignalingMessageReceiverOfferTest {
|
|||
@Test
|
||||
public void testAddOfferMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener(null);
|
||||
signalingMessageReceiver.addListener((SignalingMessageReceiver.OfferMessageListener) null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Daniel Calviño Sánchez
|
||||
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.signaling;
|
||||
|
||||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class SignalingMessageReceiverParticipantListTest {
|
||||
|
||||
private SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate
|
||||
// protected methods.
|
||||
signalingMessageReceiver = new SignalingMessageReceiver() {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWithNullListener() {
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> {
|
||||
signalingMessageReceiver.addListener((SignalingMessageReceiver.ParticipantListMessageListener) null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageUsersInRoom() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(2);
|
||||
Map<String, Object> user1 = new HashMap<>();
|
||||
user1.put("inCall", 7);
|
||||
user1.put("lastPing", 4815);
|
||||
user1.put("roomId", 108);
|
||||
user1.put("sessionId", "theSessionId1");
|
||||
user1.put("userId", "theUserId");
|
||||
// If "participantPermissions" is set in any of the participants all the other participants in the message
|
||||
// would have it too. But for test simplicity, and as it is not relevant for the processing, in this test it
|
||||
// is included only in one of the participants.
|
||||
user1.put("participantPermissions", 42);
|
||||
users.add(user1);
|
||||
Map<String, Object> user2 = new HashMap<>();
|
||||
user2.put("inCall", 0);
|
||||
user2.put("lastPing", 162342);
|
||||
user2.put("roomId", 108);
|
||||
user2.put("sessionId", "theSessionId2");
|
||||
user2.put("userId", "");
|
||||
users.add(user2);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant1 = new Participant();
|
||||
expectedParticipant1.setInCall(Participant.InCallFlags.IN_CALL | Participant.InCallFlags.WITH_AUDIO | Participant.InCallFlags.WITH_VIDEO);
|
||||
expectedParticipant1.setLastPing(4815);
|
||||
expectedParticipant1.setSessionId("theSessionId1");
|
||||
expectedParticipant1.setUserId("theUserId");
|
||||
expectedParticipantList.add(expectedParticipant1);
|
||||
|
||||
Participant expectedParticipant2 = new Participant();
|
||||
expectedParticipant2.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant2.setLastPing(162342);
|
||||
expectedParticipant2.setSessionId("theSessionId2");
|
||||
expectedParticipantList.add(expectedParticipant2);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
verifyNoInteractions(mockedParticipantListMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener3 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onUsersInRoom(expectedParticipantList);
|
||||
verify(mockedParticipantListMessageListener3, only()).onUsersInRoom(expectedParticipantList);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInternalSignalingParticipantListMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWhenHandlingInternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onUsersInRoom(expectedParticipantList);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveParticipantListMessageListenerWhenHandlingInternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>();
|
||||
Participant expectedParticipant = new Participant();
|
||||
expectedParticipant.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant.setLastPing(4815);
|
||||
expectedParticipant.setSessionId("theSessionId");
|
||||
expectedParticipantList.add(expectedParticipant);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
|
||||
List<Map<String, Object>> users = new ArrayList<>(1);
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("inCall", 0);
|
||||
user.put("lastPing", 4815);
|
||||
user.put("roomId", 108);
|
||||
user.put("sessionId", "theSessionId");
|
||||
user.put("userId", "");
|
||||
users.add(user);
|
||||
signalingMessageReceiver.processUsersInRoom(users);
|
||||
|
||||
InOrder inOrder = inOrder(mockedParticipantListMessageListener1, mockedParticipantListMessageListener2);
|
||||
|
||||
inOrder.verify(mockedParticipantListMessageListener1).onUsersInRoom(expectedParticipantList);
|
||||
inOrder.verify(mockedParticipantListMessageListener2).onUsersInRoom(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageParticipantsUpdate() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
Map<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
List<Map<String, Object>> users = new ArrayList<>(2);
|
||||
Map<String, Object> user1 = new HashMap<>();
|
||||
user1.put("inCall", 7);
|
||||
user1.put("lastPing", 4815);
|
||||
user1.put("sessionId", "theSessionId1");
|
||||
user1.put("participantType", 3);
|
||||
user1.put("userId", "theUserId");
|
||||
// If "nextcloudSessionId" or "participantPermissions" is set in any of the participants all the other
|
||||
// participants in the message would have them too. But for test simplicity, and as it is not relevant for
|
||||
// the processing, in this test they are included only in one of the participants.
|
||||
user1.put("nextcloudSessionId", "theNextcloudSessionId");
|
||||
user1.put("participantPermissions", 42);
|
||||
users.add(user1);
|
||||
Map<String, Object> user2 = new HashMap<>();
|
||||
user2.put("inCall", 0);
|
||||
user2.put("lastPing", 162342);
|
||||
user2.put("sessionId", "theSessionId2");
|
||||
user2.put("participantType", 4);
|
||||
users.add(user2);
|
||||
updateMap.put("users", users);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
List<Participant> expectedParticipantList = new ArrayList<>(2);
|
||||
Participant expectedParticipant1 = new Participant();
|
||||
expectedParticipant1.setInCall(Participant.InCallFlags.IN_CALL | Participant.InCallFlags.WITH_AUDIO | Participant.InCallFlags.WITH_VIDEO);
|
||||
expectedParticipant1.setLastPing(4815);
|
||||
expectedParticipant1.setSessionId("theSessionId1");
|
||||
expectedParticipant1.setType(Participant.ParticipantType.USER);
|
||||
expectedParticipant1.setUserId("theUserId");
|
||||
expectedParticipantList.add(expectedParticipant1);
|
||||
|
||||
Participant expectedParticipant2 = new Participant();
|
||||
expectedParticipant2.setInCall(Participant.InCallFlags.DISCONNECTED);
|
||||
expectedParticipant2.setLastPing(162342);
|
||||
expectedParticipant2.setSessionId("theSessionId2");
|
||||
expectedParticipant2.setType(Participant.ParticipantType.GUEST);
|
||||
expectedParticipantList.add(expectedParticipant2);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onParticipantsUpdate(expectedParticipantList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAllParticipantsUpdate() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
Map<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterRemovingListener() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verifyNoInteractions(mockedParticipantListMessageListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterRemovingSingleListenerOfSeveral() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener3 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener3);
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verify(mockedParticipantListMessageListener3, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalSignalingParticipantListMessageAfterAddingListenerAgain() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParticipantListMessageListenerWhenHandlingExternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
verify(mockedParticipantListMessageListener1, only()).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
verifyNoInteractions(mockedParticipantListMessageListener2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveParticipantListMessageListenerWhenHandlingExternalSignalingParticipantListMessage() {
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener1 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
SignalingMessageReceiver.ParticipantListMessageListener mockedParticipantListMessageListener2 =
|
||||
mock(SignalingMessageReceiver.ParticipantListMessageListener.class);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
signalingMessageReceiver.removeListener(mockedParticipantListMessageListener2);
|
||||
return null;
|
||||
}).when(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener1);
|
||||
signalingMessageReceiver.addListener(mockedParticipantListMessageListener2);
|
||||
|
||||
Map<String, Object> eventMap = new HashMap<>();
|
||||
eventMap.put("type", "update");
|
||||
eventMap.put("target", "participants");
|
||||
HashMap<String, Object> updateMap = new HashMap<>();
|
||||
updateMap.put("roomId", 108);
|
||||
updateMap.put("all", true);
|
||||
updateMap.put("inCall", 0);
|
||||
eventMap.put("update", updateMap);
|
||||
signalingMessageReceiver.processEvent(eventMap);
|
||||
|
||||
InOrder inOrder = inOrder(mockedParticipantListMessageListener1, mockedParticipantListMessageListener2);
|
||||
|
||||
inOrder.verify(mockedParticipantListMessageListener1).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
inOrder.verify(mockedParticipantListMessageListener2).onAllParticipantsUpdate(Participant.InCallFlags.DISCONNECTED);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue