From 473b8b238d2e891fd32cb5c32b83e35629a18744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 3 Nov 2022 20:20:27 +0100 Subject: [PATCH] Extract interface to send signaling messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like done with SignalingMessageReceiver, an implementation specific to each signaling server type (internal or external) is added. Signed-off-by: Daniel Calviño Sánchez --- .../talk/activities/CallActivity.java | 123 ++++++++++-------- .../signaling/SignalingMessageSender.java | 36 +++++ .../talk/webrtc/MagicWebSocketInstance.java | 16 ++- 3 files changed, 122 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageSender.java 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 69ff36f3b..cb0b400ae 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -85,6 +85,7 @@ import com.nextcloud.talk.models.json.signaling.SignalingOverall; import com.nextcloud.talk.models.json.signaling.settings.IceServer; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.signaling.SignalingMessageReceiver; +import com.nextcloud.talk.signaling.SignalingMessageSender; import com.nextcloud.talk.ui.dialog.AudioOutputDialog; import com.nextcloud.talk.users.UserManager; import com.nextcloud.talk.utils.ApiUtils; @@ -261,6 +262,9 @@ public class CallActivity extends CallBaseActivity { private InternalSignalingMessageReceiver internalSignalingMessageReceiver = new InternalSignalingMessageReceiver(); private SignalingMessageReceiver signalingMessageReceiver; + private InternalSignalingMessageSender internalSignalingMessageSender = new InternalSignalingMessageSender(); + private SignalingMessageSender signalingMessageSender; + private Map callParticipantMessageListeners = new HashMap<>(); @@ -1368,6 +1372,7 @@ public class CallActivity extends CallBaseActivity { signalingMessageReceiver = internalSignalingMessageReceiver; signalingMessageReceiver.addListener(participantListMessageListener); signalingMessageReceiver.addListener(offerMessageListener); + signalingMessageSender = internalSignalingMessageSender; joinRoomAndCall(); } } @@ -1571,6 +1576,7 @@ public class CallActivity extends CallBaseActivity { signalingMessageReceiver = webSocketClient.getSignalingMessageReceiver(); signalingMessageReceiver.addListener(participantListMessageListener); signalingMessageReceiver.addListener(offerMessageListener); + signalingMessageSender = webSocketClient.getSignalingMessageSender(); } else { if (webSocketClient.isConnected() && currentCallStatus == CallStatus.PUBLISHER_FAILED) { webSocketClient.restartWebSocket(); @@ -1624,7 +1630,7 @@ public class CallActivity extends CallBaseActivity { ncSignalingMessage.setRoomType("video"); ncSignalingMessage.setType("requestoffer"); - webSocketClient.sendCallMessage(ncSignalingMessage); + signalingMessageSender.send(ncSignalingMessage); break; } } @@ -2241,7 +2247,7 @@ public class CallActivity extends CallBaseActivity { } @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(SessionDescriptionSendEvent sessionDescriptionSend) throws IOException { + public void onMessageEvent(SessionDescriptionSendEvent sessionDescriptionSend) { NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); ncSignalingMessage.setTo(sessionDescriptionSend.getPeerId()); ncSignalingMessage.setRoomType(sessionDescriptionSend.getVideoStreamType()); @@ -2258,56 +2264,7 @@ public class CallActivity extends CallBaseActivity { ncSignalingMessage.setPayload(ncMessagePayload); - if (!hasExternalSignalingServer) { - // The message wrapper can not be defined in a JSON model to be directly serialized, as sent messages - // need to be serialized twice; first the signaling message, and then the wrapper as a whole. Received - // messages, on the other hand, just need to be deserialized once. - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append('{') - .append("\"fn\":\"") - .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncSignalingMessage))) - .append('\"') - .append(',') - .append("\"sessionId\":") - .append('\"').append(StringEscapeUtils.escapeJson(callSession)).append('\"') - .append(',') - .append("\"ev\":\"message\"") - .append('}'); - - List strings = new ArrayList<>(); - String stringToSend = stringBuilder.toString(); - strings.add(stringToSend); - - int apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, new int[]{ApiUtils.APIv3, 2, 1}); - - ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken), - strings.toString()) - .retry(3) - .subscribeOn(Schedulers.io()) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - // unused atm - } - - @Override - public void onNext(@io.reactivex.annotations.NonNull SignalingOverall signalingOverall) { - receivedSignalingMessages(signalingOverall.getOcs().getSignalings()); - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - Log.e(TAG, "", e); - } - - @Override - public void onComplete() { - // unused atm - } - }); - } else { - webSocketClient.sendCallMessage(ncSignalingMessage); - } + signalingMessageSender.send(ncSignalingMessage); } @Override @@ -2646,6 +2603,68 @@ public class CallActivity extends CallBaseActivity { } } + private class InternalSignalingMessageSender implements SignalingMessageSender { + + @Override + public void send(NCSignalingMessage ncSignalingMessage) { + String serializedNcSignalingMessage; + try { + serializedNcSignalingMessage = LoganSquare.serialize(ncSignalingMessage); + } catch (IOException e) { + Log.e(TAG, "Failed to serialize signaling message", e); + return; + } + + // The message wrapper can not be defined in a JSON model to be directly serialized, as sent messages + // need to be serialized twice; first the signaling message, and then the wrapper as a whole. Received + // messages, on the other hand, just need to be deserialized once. + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('{') + .append("\"fn\":\"") + .append(StringEscapeUtils.escapeJson(serializedNcSignalingMessage)) + .append('\"') + .append(',') + .append("\"sessionId\":") + .append('\"').append(StringEscapeUtils.escapeJson(callSession)).append('\"') + .append(',') + .append("\"ev\":\"message\"") + .append('}'); + + List strings = new ArrayList<>(); + String stringToSend = stringBuilder.toString(); + strings.add(stringToSend); + + int apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, new int[]{ApiUtils.APIv3, 2, 1}); + + ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken), + strings.toString()) + .retry(3) + .subscribeOn(Schedulers.io()) + .subscribe(new Observer() { + @Override + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { + } + + @Override + public void onNext(@io.reactivex.annotations.NonNull SignalingOverall signalingOverall) { + // When sending messages to the internal signaling server the response has been empty since + // Talk v2.9.0, so it is not really needed to process it, but there is no harm either in + // doing that, as technically messages could be returned. + receivedSignalingMessages(signalingOverall.getOcs().getSignalings()); + } + + @Override + public void onError(@io.reactivex.annotations.NonNull Throwable e) { + Log.e(TAG, "", e); + } + + @Override + public void onComplete() { + } + }); + } + } + private class MicrophoneButtonTouchListener implements View.OnTouchListener { @SuppressLint("ClickableViewAccessibility") diff --git a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageSender.java b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageSender.java new file mode 100644 index 000000000..72df249fa --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageSender.java @@ -0,0 +1,36 @@ +/* + * Nextcloud Talk application + * + * @author Daniel Calviño Sánchez + * Copyright (C) 2022 Daniel Calviño Sánchez + * + * 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 . + */ +package com.nextcloud.talk.signaling; + +import com.nextcloud.talk.models.json.signaling.NCSignalingMessage; + +/** + * Interface to send signaling messages. + */ +public interface SignalingMessageSender { + + /** + * Sends the given signaling message. + * + * @param ncSignalingMessage the message to send + */ + void send(NCSignalingMessage ncSignalingMessage); + +} diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java index cbc288e6b..ef965eb3b 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -40,6 +40,7 @@ import com.nextcloud.talk.models.json.websocket.EventOverallWebSocketMessage; import com.nextcloud.talk.models.json.websocket.HelloResponseOverallWebSocketMessage; import com.nextcloud.talk.models.json.websocket.JoinedRoomOverallWebSocketMessage; import com.nextcloud.talk.signaling.SignalingMessageReceiver; +import com.nextcloud.talk.signaling.SignalingMessageSender; import com.nextcloud.talk.utils.bundle.BundleKeys; import org.greenrobot.eventbus.EventBus; @@ -101,6 +102,8 @@ public class MagicWebSocketInstance extends WebSocketListener { private final ExternalSignalingMessageReceiver signalingMessageReceiver = new ExternalSignalingMessageReceiver(); + private final ExternalSignalingMessageSender signalingMessageSender = new ExternalSignalingMessageSender(); + MagicWebSocketInstance(User conversationUser, String connectionUrl, String webSocketTicket) { NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); @@ -343,7 +346,7 @@ public class MagicWebSocketInstance extends WebSocketListener { } } - public void sendCallMessage(NCSignalingMessage ncSignalingMessage) { + private void sendCallMessage(NCSignalingMessage ncSignalingMessage) { try { String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage)); if (!connected || reconnecting) { @@ -406,6 +409,10 @@ public class MagicWebSocketInstance extends WebSocketListener { return signalingMessageReceiver; } + public SignalingMessageSender getSignalingMessageSender() { + return signalingMessageSender; + } + /** * Temporary implementation of SignalingMessageReceiver until signaling related code is extracted to a Signaling * class. @@ -422,4 +429,11 @@ public class MagicWebSocketInstance extends WebSocketListener { processSignalingMessage(message); } } + + private class ExternalSignalingMessageSender implements SignalingMessageSender { + @Override + public void send(NCSignalingMessage ncSignalingMessage) { + sendCallMessage(ncSignalingMessage); + } + } }