From 723221bc19cfb9c8b19affd731de916a5ff7a211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 08:50:41 +0200 Subject: [PATCH 1/6] Return early from function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../com/nextcloud/talk/webrtc/WebSocketInstance.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index c6d1a9df254..24e5fe75d52 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -424,11 +424,13 @@ class WebSocketInstance internal constructor( if (!reconnecting) { restartWebSocket() } - } else { - if (!internalWebSocket!!.send(message)) { - messagesQueue.add(message) - restartWebSocket() - } + + return + } + + if (!internalWebSocket!!.send(message)) { + messagesQueue.add(message) + restartWebSocket() } } From e679267765d70bd81a74f90cb6ed7fe11835ad9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 08:58:08 +0200 Subject: [PATCH 2/6] Add interface for common fields of "overall" websocket messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interface is now passed to "sendMessage" instead of a serialized message, which will make possible to customize the "id" property (still to be added) if needed before sending the message. Note that, for consistency, the interface was applied to all "overall" data objects, even if some of them are only for received messages (like "ErrorOverallWebSocketMessage"). Signed-off-by: Daniel Calviño Sánchez --- .../models/json/websocket/BaseWebSocketMessage.kt | 4 ++-- .../websocket/BaseWebSocketMessageInterface.kt | 14 ++++++++++++++ .../json/websocket/CallOverallWebSocketMessage.kt | 4 ++-- .../websocket/ErrorOverallWebSocketMessage.kt | 4 ++-- .../websocket/EventOverallWebSocketMessage.kt | 4 ++-- .../websocket/HelloOverallWebSocketMessage.kt | 4 ++-- .../HelloResponseOverallWebSocketMessage.kt | 4 ++-- .../JoinedRoomOverallWebSocketMessage.kt | 4 ++-- .../json/websocket/RoomOverallWebSocketMessage.kt | 4 ++-- .../nextcloud/talk/webrtc/WebSocketInstance.kt | 15 ++++++--------- 10 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt index 1208e8e6c39..6fbb0364738 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt @@ -16,8 +16,8 @@ import kotlinx.parcelize.Parcelize @JsonObject data class BaseWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null -) : Parcelable { + override var type: String? = null +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt new file mode 100644 index 00000000000..9e31bbb6d89 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt @@ -0,0 +1,14 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2025 Daniel Calviño Sánchez + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.nextcloud.talk.models.json.websocket + +/** + * Interface with the properties common to all websocket signaling messages. + */ +interface BaseWebSocketMessageInterface { + var type: String? +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt index 5e13a1a33e4..99ac64c1f80 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject data class CallOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["message"]) var callWebSocketMessage: CallWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt index 90ceafdacd0..da0bc56768e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject data class ErrorOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["error"]) var errorWebSocketMessage: ErrorWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt index 969cf2bb34f..64348cdb276 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt @@ -20,10 +20,10 @@ import java.util.HashMap @TypeParceler class EventOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["event"]) var eventMap: HashMap? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt index 8d213df396c..1cef3cbf373 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject data class HelloOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["hello"]) var helloWebSocketMessage: HelloWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt index 025bddf5135..9be669aed77 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject data class HelloResponseOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["hello"]) var helloResponseWebSocketMessage: HelloResponseWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt index 780367b236c..bd46a814c2e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject data class JoinedRoomOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["room"]) var roomWebSocketMessage: RoomWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt index a347d18b83a..0201ebfd594 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt @@ -16,10 +16,10 @@ import kotlinx.parcelize.Parcelize @JsonObject class RoomOverallWebSocketMessage( @JsonField(name = ["type"]) - var type: String? = null, + override var type: String? = null, @JsonField(name = ["room"]) var roomWebSocketMessage: RoomWebSocketMessage? = null -) : Parcelable { +) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index 24e5fe75d52..e5605245ffc 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -21,6 +21,7 @@ import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant.ActorType import com.nextcloud.talk.models.json.signaling.NCSignalingMessage import com.nextcloud.talk.models.json.signaling.settings.FederationSettings +import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessageInterface import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage import com.nextcloud.talk.models.json.websocket.ByeWebSocketMessage import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage @@ -79,7 +80,7 @@ class WebSocketInstance internal constructor( private var currentFederation: FederationSettings? = null private var reconnecting = false private val usersHashMap: HashMap - private var messagesQueue: MutableList = ArrayList() + private var messagesQueue: MutableList = ArrayList() private val signalingMessageReceiver = ExternalSignalingMessageReceiver() val signalingMessageSender = ExternalSignalingMessageSender() @@ -379,9 +380,7 @@ class WebSocketInstance internal constructor( Log.d(TAG, " roomToken: $roomToken") Log.d(TAG, " session: $normalBackendSession") try { - val message = LoganSquare.serialize( - webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession, federation) - ) + val message = webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession, federation) if (roomToken == "") { Log.d(TAG, "sending 'leave room' via websocket") currentNormalBackendSession = "" @@ -408,16 +407,14 @@ class WebSocketInstance internal constructor( private fun sendCallMessage(ncSignalingMessage: NCSignalingMessage) { try { - val message = LoganSquare.serialize( - webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage) - ) + val message = webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage) sendMessage(message) } catch (e: IOException) { Log.e(TAG, "Failed to serialize signaling message", e) } } - private fun sendMessage(message: String) { + private fun sendMessage(message: BaseWebSocketMessageInterface) { if (!isConnected || reconnecting) { messagesQueue.add(message) @@ -428,7 +425,7 @@ class WebSocketInstance internal constructor( return } - if (!internalWebSocket!!.send(message)) { + if (!internalWebSocket!!.send(LoganSquare.serialize(message))) { messagesQueue.add(message) restartWebSocket() } From 82c4ed126d02ce3cfea299f1d30809de78528f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 09:17:53 +0200 Subject: [PATCH 3/6] Add "id" property to base WebSocket messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebSocket messages may contain an "id" property used to match a response message with its request. Signed-off-by: Daniel Calviño Sánchez --- .../talk/models/json/websocket/BaseWebSocketMessage.kt | 4 +++- .../json/websocket/BaseWebSocketMessageInterface.kt | 1 + .../json/websocket/CallOverallWebSocketMessage.kt | 4 +++- .../json/websocket/ErrorOverallWebSocketMessage.kt | 4 +++- .../json/websocket/EventOverallWebSocketMessage.kt | 4 +++- .../json/websocket/HelloOverallWebSocketMessage.kt | 4 +++- .../websocket/HelloResponseOverallWebSocketMessage.kt | 4 +++- .../websocket/JoinedRoomOverallWebSocketMessage.kt | 4 +++- .../json/websocket/RoomOverallWebSocketMessage.kt | 4 +++- .../com/nextcloud/talk/webrtc/WebSocketInstance.kt | 10 +++++----- 10 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt index 6fbb0364738..6d05cb28729 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessage.kt @@ -15,9 +15,11 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class BaseWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null) + constructor() : this(null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt index 9e31bbb6d89..bccb1a4862f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/BaseWebSocketMessageInterface.kt @@ -10,5 +10,6 @@ package com.nextcloud.talk.models.json.websocket * Interface with the properties common to all websocket signaling messages. */ interface BaseWebSocketMessageInterface { + var id: String? var type: String? } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt index 99ac64c1f80..8855a61956a 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/CallOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class CallOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["message"]) var callWebSocketMessage: CallWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt index da0bc56768e..c56658af970 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ErrorOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class ErrorOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["error"]) var errorWebSocketMessage: ErrorWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt index 64348cdb276..ebfb3062e0e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/EventOverallWebSocketMessage.kt @@ -19,11 +19,13 @@ import java.util.HashMap @JsonObject @TypeParceler class EventOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["event"]) var eventMap: HashMap? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt index 1cef3cbf373..ec7dd4b8c71 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class HelloOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["hello"]) var helloWebSocketMessage: HelloWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt index 9be669aed77..41b1d7fa4bb 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class HelloResponseOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["hello"]) var helloResponseWebSocketMessage: HelloResponseWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt index bd46a814c2e..d22cb2db12f 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/JoinedRoomOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject data class JoinedRoomOverallWebSocketMessage( + @JsonField(name = ["id"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["room"]) var roomWebSocketMessage: RoomWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt index 0201ebfd594..ce875aa76d6 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/RoomOverallWebSocketMessage.kt @@ -15,11 +15,13 @@ import kotlinx.parcelize.Parcelize @Parcelize @JsonObject class RoomOverallWebSocketMessage( + @JsonField(name = ["type"]) + override var id: String? = null, @JsonField(name = ["type"]) override var type: String? = null, @JsonField(name = ["room"]) var roomWebSocketMessage: RoomWebSocketMessage? = null ) : BaseWebSocketMessageInterface, Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' - constructor() : this(null, null) + constructor() : this(null, null, null) } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index e5605245ffc..2c5b4bf23bc 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -150,7 +150,7 @@ class WebSocketInstance internal constructor( if (webSocket === internalWebSocket) { Log.d(TAG, "Receiving : $webSocket $text") try { - val (messageType) = LoganSquare.parse(text, BaseWebSocketMessage::class.java) + val (_, messageType) = LoganSquare.parse(text, BaseWebSocketMessage::class.java) if (messageType != null) { when (messageType) { "hello" -> processHelloMessage(webSocket, text) @@ -176,7 +176,7 @@ class WebSocketInstance internal constructor( @Throws(IOException::class) private fun processMessage(text: String) { - val (_, callWebSocketMessage) = LoganSquare.parse(text, CallOverallWebSocketMessage::class.java) + val (_, _, callWebSocketMessage) = LoganSquare.parse(text, CallOverallWebSocketMessage::class.java) if (callWebSocketMessage != null) { val ncSignalingMessage = callWebSocketMessage.ncSignalingMessage @@ -285,7 +285,7 @@ class WebSocketInstance internal constructor( @Throws(IOException::class) private fun processJoinedRoomMessage(text: String) { - val (_, roomWebSocketMessage) = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage::class.java) + val (_, _, roomWebSocketMessage) = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage::class.java) if (roomWebSocketMessage != null) { currentRoomToken = roomWebSocketMessage.roomId if (roomWebSocketMessage.roomPropertiesWebSocketMessage != null && !TextUtils.isEmpty(currentRoomToken)) { @@ -297,7 +297,7 @@ class WebSocketInstance internal constructor( @Throws(IOException::class) private fun processErrorMessage(webSocket: WebSocket, text: String) { Log.e(TAG, "Received error: $text") - val (_, message) = LoganSquare.parse(text, ErrorOverallWebSocketMessage::class.java) + val (_, _, message) = LoganSquare.parse(text, ErrorOverallWebSocketMessage::class.java) if (message != null) { if ("no_such_session" == message.code) { Log.d(TAG, "WebSocket " + webSocket.hashCode() + " resumeID " + resumeId + " expired") @@ -316,7 +316,7 @@ class WebSocketInstance internal constructor( isConnected = true reconnecting = false val oldResumeId = resumeId - val (_, helloResponseWebSocketMessage1) = LoganSquare.parse( + val (_, _, helloResponseWebSocketMessage1) = LoganSquare.parse( text, HelloResponseOverallWebSocketMessage::class.java ) From 66eacf1fdd3fa3f831a6a35a3d208ca18a789ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 09:33:20 +0200 Subject: [PATCH 4/6] fixup! Add interface for common fields of "overall" websocket messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index 2c5b4bf23bc..abb73c38acb 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -326,7 +326,7 @@ class WebSocketInstance internal constructor( hasMCU = helloResponseWebSocketMessage1.serverHasMCUSupport() } for (i in messagesQueue.indices) { - webSocket.send(messagesQueue[i]) + webSocket.send(LoganSquare.serialize(messagesQueue[i])) } messagesQueue = ArrayList() val helloHashMap = HashMap() From 947ada188336866b07bb1ea53a295b0788b71250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 09:38:08 +0200 Subject: [PATCH 5/6] Add support for running callbacks on signaling message responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the external signaling server, if the "id" property is set when sending a signaling message the response to that specific message will be identified by the same id. Now a callback can be passed to "sendMessage", which automatically adds an id to the message and makes possible to run the callback when/if the response to the message is received. Signed-off-by: Daniel Calviño Sánchez --- .../talk/webrtc/WebSocketInstance.kt | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index abb73c38acb..5dbb769a82e 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -80,7 +80,9 @@ class WebSocketInstance internal constructor( private var currentFederation: FederationSettings? = null private var reconnecting = false private val usersHashMap: HashMap - private var messagesQueue: MutableList = ArrayList() + private var messagesQueue: MutableList Unit)?>> = ArrayList() + private var callbacks: MutableMap Unit> = HashMap() + private var lastCallbackId: Int = 1 private val signalingMessageReceiver = ExternalSignalingMessageReceiver() val signalingMessageSender = ExternalSignalingMessageSender() @@ -141,6 +143,7 @@ class WebSocketInstance internal constructor( fun restartWebSocket() { reconnecting = true + callbacks.clear() Log.d(TAG, "restartWebSocket: $connectionUrl") val request = Request.Builder().url(connectionUrl).build() okHttpClient!!.newWebSocket(request, this) @@ -150,7 +153,14 @@ class WebSocketInstance internal constructor( if (webSocket === internalWebSocket) { Log.d(TAG, "Receiving : $webSocket $text") try { - val (_, messageType) = LoganSquare.parse(text, BaseWebSocketMessage::class.java) + val (id, messageType) = LoganSquare.parse(text, BaseWebSocketMessage::class.java) + + if (callbacks.contains(id)) { + val callback = callbacks[id]!! + callbacks.remove(id) + callback(text) + } + if (messageType != null) { when (messageType) { "hello" -> processHelloMessage(webSocket, text) @@ -326,7 +336,12 @@ class WebSocketInstance internal constructor( hasMCU = helloResponseWebSocketMessage1.serverHasMCUSupport() } for (i in messagesQueue.indices) { - webSocket.send(LoganSquare.serialize(messagesQueue[i])) + // Safety check to ensure that it will not end in an endless loop + // trying to send the messages, queueing them, and then trying to + // send them again and again. + if (isConnected && !reconnecting) { + sendMessage(messagesQueue[i].first, messagesQueue[i].second) + } } messagesQueue = ArrayList() val helloHashMap = HashMap() @@ -414,9 +429,9 @@ class WebSocketInstance internal constructor( } } - private fun sendMessage(message: BaseWebSocketMessageInterface) { + private fun sendMessage(message: BaseWebSocketMessageInterface, callback: ((String) -> Unit)? = null) { if (!isConnected || reconnecting) { - messagesQueue.add(message) + messagesQueue.add(Pair(message, callback)) if (!reconnecting) { restartWebSocket() @@ -425,8 +440,14 @@ class WebSocketInstance internal constructor( return } + if (callback != null) { + val callbackId = lastCallbackId++ + callbacks[callbackId.toString()] = callback + message.id = callbackId.toString() + } + if (!internalWebSocket!!.send(LoganSquare.serialize(message))) { - messagesQueue.add(message) + messagesQueue.add(Pair(message, callback)) restartWebSocket() } } From f07039fc1981f9c26355b259b3ba22c702a8ed9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 2 Jul 2025 13:23:11 +0200 Subject: [PATCH 6/6] Fix handling of signaling message responses when joining a room MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When joining a room, the signaling server confirms that joining the room was successful by sending a "room" message. Until now the Android app assumed that "room" messages were sent only in that case, but a "room" message can be sent also if the properties of the room change. When the room is joined while the call activity is active the call is also joined, so if the properties of the room changed while in a call the Android app rejoined the call, which caused strange issues due to how it was done. For example, when a recording started additional guests appeared in the UI. To solve that now only the "room" message that actually confirms that the join was successful, which can be identified by setting an ID in the request to join the room, is treated as such. Signed-off-by: Daniel Calviño Sánchez --- .../java/com/nextcloud/talk/webrtc/WebSocketInstance.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index 5dbb769a82e..86e6b1c1390 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -165,7 +165,6 @@ class WebSocketInstance internal constructor( when (messageType) { "hello" -> processHelloMessage(webSocket, text) "error" -> processErrorMessage(webSocket, text) - "room" -> processJoinedRoomMessage(text) "event" -> processEventMessage(text) "message" -> processMessage(text) "bye" -> { @@ -396,11 +395,14 @@ class WebSocketInstance internal constructor( Log.d(TAG, " session: $normalBackendSession") try { val message = webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession, federation) + val processJoinedRoomMessageCallback = { text: String -> + processJoinedRoomMessage(text) + } if (roomToken == "") { Log.d(TAG, "sending 'leave room' via websocket") currentNormalBackendSession = "" currentFederation = null - sendMessage(message) + sendMessage(message, processJoinedRoomMessageCallback) } else if ( roomToken == currentRoomToken && normalBackendSession == currentNormalBackendSession && @@ -413,7 +415,7 @@ class WebSocketInstance internal constructor( Log.d(TAG, "Sending join room message via websocket") currentNormalBackendSession = normalBackendSession currentFederation = federation - sendMessage(message) + sendMessage(message, processJoinedRoomMessageCallback) } } catch (e: IOException) { Log.e(TAG, "Failed to serialize signaling message", e)