diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 642c5757e..b7759d4c9 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -2679,6 +2679,10 @@ public class CallActivity extends CallBaseActivity { this.sessionId = sessionId; } + @Override + public void onRaiseHand(boolean state, long timestamp) { + } + @Override public void onUnshareScreen() { endPeerConnection(sessionId, "screen"); diff --git a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.kt b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.kt index 6387649f3..ce078f11e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/signaling/NCMessagePayload.kt @@ -38,8 +38,12 @@ data class NCMessagePayload( @JsonField(name = ["candidate"]) var iceCandidate: NCIceCandidate? = null, @JsonField(name = ["name"]) - var name: String? = null + var name: String? = null, + @JsonField(name = ["state"]) + var state: Boolean? = null, + @JsonField(name = ["timestamp"]) + var timestamp: Long? = null ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null, null, null, null) + constructor() : this(null, null, null, null, null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/signaling/CallParticipantMessageNotifier.java b/app/src/main/java/com/nextcloud/talk/signaling/CallParticipantMessageNotifier.java index f06e72629..f1cf54e9b 100644 --- a/app/src/main/java/com/nextcloud/talk/signaling/CallParticipantMessageNotifier.java +++ b/app/src/main/java/com/nextcloud/talk/signaling/CallParticipantMessageNotifier.java @@ -87,6 +87,12 @@ class CallParticipantMessageNotifier { return callParticipantMessageListeners; } + public synchronized void notifyRaiseHand(String sessionId, boolean state, long timestamp) { + for (SignalingMessageReceiver.CallParticipantMessageListener listener : getListenersFor(sessionId)) { + listener.onRaiseHand(state, timestamp); + } + } + public synchronized void notifyUnshareScreen(String sessionId) { for (SignalingMessageReceiver.CallParticipantMessageListener listener : getListenersFor(sessionId)) { listener.onUnshareScreen(); diff --git a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java index 161dae555..47dd83ca9 100644 --- a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java +++ b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java @@ -128,6 +128,7 @@ public abstract class SignalingMessageReceiver { * message on the call participant. */ public interface CallParticipantMessageListener { + void onRaiseHand(boolean state, long timestamp); void onUnshareScreen(); } @@ -415,6 +416,63 @@ public abstract class SignalingMessageReceiver { String sessionId = signalingMessage.getFrom(); String roomType = signalingMessage.getRoomType(); + if ("raiseHand".equals(type)) { + // Message schema (external signaling server): + // { + // "type": "message", + // "message": { + // "sender": { + // ... + // }, + // "data": { + // "to": #STRING#, + // "sid": #STRING#, + // "roomType": "video", + // "type": "raiseHand", + // "payload": { + // "state": #BOOLEAN#, + // "timestamp": #LONG#, + // }, + // "from": #STRING#, + // }, + // }, + // } + // + // Message schema (internal signaling server): + // { + // "type": "message", + // "data": { + // "to": #STRING#, + // "sid": #STRING#, + // "roomType": "video", + // "type": "raiseHand", + // "payload": { + // "state": #BOOLEAN#, + // "timestamp": #LONG#, + // }, + // "from": #STRING#, + // }, + // } + + NCMessagePayload payload = signalingMessage.getPayload(); + if (payload == null) { + // Broken message, this should not happen. + return; + } + + Boolean state = payload.getState(); + Long timestamp = payload.getTimestamp(); + + if (state == null || timestamp == null) { + // Broken message, this should not happen. + return; + } + + callParticipantMessageNotifier.notifyRaiseHand(sessionId, state, timestamp); + + return; + } + // "unshareScreen" messages are directly sent to the screen peer connection when the internal signaling // server is used, and to the room when the external signaling server is used. However, the (relevant) data // of the received message ("from" and "type") is the same in both cases. diff --git a/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverCallParticipantTest.java b/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverCallParticipantTest.java index 01963682e..96d90b23d 100644 --- a/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverCallParticipantTest.java +++ b/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverCallParticipantTest.java @@ -19,6 +19,7 @@ */ package com.nextcloud.talk.signaling; +import com.nextcloud.talk.models.json.signaling.NCMessagePayload; import com.nextcloud.talk.models.json.signaling.NCSignalingMessage; import org.junit.Assert; @@ -62,6 +63,27 @@ public class SignalingMessageReceiverCallParticipantTest { }); } + @Test + public void testCallParticipantMessageRaiseHand() { + SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener = + mock(SignalingMessageReceiver.CallParticipantMessageListener.class); + + signalingMessageReceiver.addListener(mockedCallParticipantMessageListener, "theSessionId"); + + NCSignalingMessage signalingMessage = new NCSignalingMessage(); + signalingMessage.setFrom("theSessionId"); + signalingMessage.setType("raiseHand"); + signalingMessage.setRoomType("theRoomType"); + NCMessagePayload messagePayload = new NCMessagePayload(); + messagePayload.setType("raiseHand"); + messagePayload.setState(Boolean.TRUE); + messagePayload.setTimestamp(4815162342L); + signalingMessage.setPayload(messagePayload); + signalingMessageReceiver.processSignalingMessage(signalingMessage); + + verify(mockedCallParticipantMessageListener, only()).onRaiseHand(true, 4815162342L); + } + @Test public void testCallParticipantMessageUnshareScreen() { SignalingMessageReceiver.CallParticipantMessageListener mockedCallParticipantMessageListener =