Browse Source

More work towards working websockets

Mario Danic 6 years ago
parent
commit
72d3f5ef66

+ 70 - 45
app/src/main/java/com/nextcloud/talk/controllers/CallController.java

@@ -57,6 +57,7 @@ import com.nextcloud.talk.events.ConfigurationChangeEvent;
 import com.nextcloud.talk.events.MediaStreamEvent;
 import com.nextcloud.talk.events.PeerConnectionEvent;
 import com.nextcloud.talk.events.SessionDescriptionSendEvent;
+import com.nextcloud.talk.events.WebSocketCommunicationEvent;
 import com.nextcloud.talk.models.ExternalSignalingServer;
 import com.nextcloud.talk.models.database.UserEntity;
 import com.nextcloud.talk.models.json.call.CallOverall;
@@ -87,6 +88,7 @@ import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
 import com.nextcloud.talk.webrtc.MagicAudioManager;
 import com.nextcloud.talk.webrtc.MagicPeerConnectionWrapper;
 import com.nextcloud.talk.webrtc.MagicWebRTCUtils;
+import com.nextcloud.talk.webrtc.MagicWebSocketInstance;
 import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
 import com.wooplr.spotlight.SpotlightView;
 
@@ -244,7 +246,7 @@ public class CallController extends BaseController {
     private SpotlightView spotlightView;
 
     private ExternalSignalingServer externalSignalingServer;
-    private okhttp3.WebSocket webSocketClient;
+    private MagicWebSocketInstance webSocketClient;
     private WebSocketConnectionHelper webSocketConnectionHelper;
 
     public CallController(Bundle args) {
@@ -1175,6 +1177,15 @@ public class CallController extends BaseController {
                 conversationUser, externalSignalingServer.getExternalSignalingTicket());
     }
 
+    @Subscribe(threadMode = ThreadMode.BACKGROUND)
+    public void onMessageEvent(WebSocketCommunicationEvent webSocketCommunicationEvent) {
+        if (webSocketCommunicationEvent.getType().equals("hello")) {
+            callSession = webSocketClient.getSessionId();
+            MagicPeerConnectionWrapper magicPeerConnectionWrapper = alwaysGetPeerConnectionWrapperForSessionId(callSession);
+        } else if (webSocketCommunicationEvent.equals("MCUPeerReady")) {
+        }
+    }
+
     @OnClick({R.id.pip_video_view, R.id.remote_renderers_layout})
     public void showCallControls() {
         animateCallControls(true, 0);
@@ -1474,8 +1485,18 @@ public class CallController extends BaseController {
         if ((magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId)) != null) {
             return magicPeerConnectionWrapper;
         } else {
+            boolean hasMCU = webSocketClient != null && webSocketClient.hasMCU();
+
+            if (hasMCU) {
+                magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory,
+                        iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, hasMCU);
+            } else {
+                magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory,
+                        iceServers, sdpConstraints, sessionId, callSession, localMediaStream, hasMCU);
+            }
+
             magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory,
-                    iceServers, sdpConstraints, sessionId, callSession, localMediaStream);
+                    iceServers, sdpConstraints, sessionId, callSession, localMediaStream, hasMCU);
             magicPeerConnectionWrapperList.add(magicPeerConnectionWrapper);
             return magicPeerConnectionWrapper;
         }
@@ -1593,58 +1614,62 @@ public class CallController extends BaseController {
         ncMessageWrapper.setSignalingMessage(ncSignalingMessage);
 
 
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append("{")
-                .append("\"fn\":\"")
-                .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"")
-                .append(",")
-                .append("\"sessionId\":")
-                .append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"")
-                .append(",")
-                .append("\"ev\":\"message\"")
-                .append("}");
-
-        List<String> strings = new ArrayList<>();
-        String stringToSend = stringBuilder.toString();
-        strings.add(stringToSend);
-
-        String urlToken = null;
-        if (isMultiSession) {
-            urlToken = roomToken;
-        }
+        if (externalSignalingServer == null) {
+            StringBuilder stringBuilder = new StringBuilder();
+            stringBuilder.append("{")
+                    .append("\"fn\":\"")
+                    .append(StringEscapeUtils.escapeJson(LoganSquare.serialize(ncMessageWrapper.getSignalingMessage()))).append("\"")
+                    .append(",")
+                    .append("\"sessionId\":")
+                    .append("\"").append(StringEscapeUtils.escapeJson(callSession)).append("\"")
+                    .append(",")
+                    .append("\"ev\":\"message\"")
+                    .append("}");
+
+            List<String> strings = new ArrayList<>();
+            String stringToSend = stringBuilder.toString();
+            strings.add(stringToSend);
+
+            String urlToken = null;
+            if (isMultiSession) {
+                urlToken = roomToken;
+            }
 
-        ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken),
-                strings.toString())
-                .retry(3)
-                .subscribeOn(Schedulers.newThread())
-                .subscribe(new Observer<SignalingOverall>() {
-                    @Override
-                    public void onSubscribe(Disposable d) {
+            ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken),
+                    strings.toString())
+                    .retry(3)
+                    .subscribeOn(Schedulers.newThread())
+                    .subscribe(new Observer<SignalingOverall>() {
+                        @Override
+                        public void onSubscribe(Disposable d) {
 
-                    }
+                        }
 
-                    @Override
-                    public void onNext(SignalingOverall signalingOverall) {
-                        if (signalingOverall.getOcs().getSignalings() != null) {
-                            for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) {
-                                try {
-                                    receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i));
-                                } catch (IOException e) {
-                                    e.printStackTrace();
+                        @Override
+                        public void onNext(SignalingOverall signalingOverall) {
+                            if (signalingOverall.getOcs().getSignalings() != null) {
+                                for (int i = 0; i < signalingOverall.getOcs().getSignalings().size(); i++) {
+                                    try {
+                                        receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i));
+                                    } catch (IOException e) {
+                                        e.printStackTrace();
+                                    }
                                 }
                             }
                         }
-                    }
 
-                    @Override
-                    public void onError(Throwable e) {
-                    }
+                        @Override
+                        public void onError(Throwable e) {
+                        }
 
-                    @Override
-                    public void onComplete() {
+                        @Override
+                        public void onComplete() {
 
-                    }
-                });
+                        }
+                    });
+        } else {
+            webSocketClient.getWebSocket().send(LoganSquare.serialize(ncMessageWrapper));
+        }
     }
 
     @Override

+ 33 - 0
app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java

@@ -0,0 +1,33 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.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.events;
+
+import java.util.HashMap;
+
+import androidx.annotation.Nullable;
+import lombok.Data;
+
+@Data
+public class WebSocketCommunicationEvent {
+    public final String type;
+    @Nullable
+    public final HashMap<String, String> hashMap;
+}

+ 36 - 0
app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseOverallWebSocketMessage.java

@@ -0,0 +1,36 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.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.models.json.websocket;
+
+import com.bluelinelabs.logansquare.annotation.JsonField;
+import com.bluelinelabs.logansquare.annotation.JsonObject;
+
+import org.parceler.Parcel;
+
+import lombok.Data;
+
+@Data
+@JsonObject
+@Parcel
+public class HelloResponseOverallWebSocketMessage extends BaseWebSocketMessage {
+    @JsonField(name = "hello")
+    HelloResponseWebSocketMessage helloResponseWebSocketMessage;
+}

+ 5 - 6
app/src/main/java/com/nextcloud/talk/models/json/websocket/HelloResponseWebSocketMessage.java

@@ -34,15 +34,14 @@ public class HelloResponseWebSocketMessage {
     @JsonField(name = "resumeid")
     String resumeId;
 
+    @JsonField(name = "sessionid")
+    String sessionId;
+
     @JsonField(name = "server")
     ServerHelloResponseFeaturesWebSocketMessage serverHelloResponseFeaturesWebSocketMessage;
 
     public boolean serverHasMCUSupport() {
-        if (serverHelloResponseFeaturesWebSocketMessage != null && serverHelloResponseFeaturesWebSocketMessage.getFeatures() != null
-                && serverHelloResponseFeaturesWebSocketMessage.getFeatures().contains("mcu")) {
-            return true;
-        }
-
-        return false;
+        return serverHelloResponseFeaturesWebSocketMessage != null && serverHelloResponseFeaturesWebSocketMessage.getFeatures() != null
+                && serverHelloResponseFeaturesWebSocketMessage.getFeatures().contains("mcu");
     }
 }

+ 8 - 2
app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java

@@ -30,6 +30,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.events.MediaStreamEvent;
 import com.nextcloud.talk.events.PeerConnectionEvent;
 import com.nextcloud.talk.events.SessionDescriptionSendEvent;
+import com.nextcloud.talk.events.WebSocketCommunicationEvent;
 import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
 import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
 
@@ -71,7 +72,8 @@ public class MagicPeerConnectionWrapper {
     public MagicPeerConnectionWrapper(PeerConnectionFactory peerConnectionFactory,
                                       List<PeerConnection.IceServer> iceServerList,
                                       MediaConstraints mediaConstraints,
-                                      String sessionId, String localSession, MediaStream mediaStream) {
+                                      String sessionId, String localSession, MediaStream mediaStream,
+                                      boolean hasMCU) {
 
         this.localMediaStream = mediaStream;
 
@@ -87,7 +89,11 @@ public class MagicPeerConnectionWrapper {
         if (peerConnection != null) {
             peerConnection.addStream(localMediaStream);
 
-            if (hasInitiated) {
+            if (hasMCU) {
+                EventBus.getDefault().post(new WebSocketCommunicationEvent("MCUPeerReady", null));
+            }
+
+            if (hasInitiated || hasMCU) {
                 DataChannel.Init init = new DataChannel.Init();
                 init.negotiated = false;
                 magicDataChannel = peerConnection.createDataChannel("status", init);

+ 70 - 6
app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketListener.java → app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java

@@ -24,26 +24,54 @@ import android.text.TextUtils;
 import android.util.Log;
 
 import com.bluelinelabs.logansquare.LoganSquare;
+import com.nextcloud.talk.application.NextcloudTalkApplication;
+import com.nextcloud.talk.events.WebSocketCommunicationEvent;
 import com.nextcloud.talk.models.database.UserEntity;
 import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage;
+import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage;
+import com.nextcloud.talk.models.json.websocket.HelloResponseOverallWebSocketMessage;
+import com.nextcloud.talk.models.json.websocket.HelloResponseWebSocketMessage;
+
+import org.greenrobot.eventbus.EventBus;
 
 import java.io.IOException;
+import java.util.HashMap;
+
+import javax.inject.Inject;
 
+import autodagger.AutoInjector;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
 import okhttp3.Response;
 import okhttp3.WebSocket;
 import okhttp3.WebSocketListener;
 import okio.ByteString;
 
-public class MagicWebSocketListener extends WebSocketListener {
+@AutoInjector(NextcloudTalkApplication.class)
+public class MagicWebSocketInstance extends WebSocketListener {
     private static final String TAG = "MagicWebSocketListener";
-    private static final int NORMAL_CLOSURE_STATUS = 1000;
+
+    @Inject
+    OkHttpClient okHttpClient;
+
+    @Inject
+    EventBus eventBus;
 
     private UserEntity conversationUser;
     private String webSocketTicket;
     private String resumeId;
+    private String sessionId;
+    private boolean hasMCU;
+    private boolean connected;
     private WebSocketConnectionHelper webSocketConnectionHelper;
+    private WebSocket webSocket;
+
+    MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) {
+        NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
+        Request request = new Request.Builder().url(connectionUrl).build();
+
+        this.webSocket = okHttpClient.newWebSocket(request, this);
 
-    MagicWebSocketListener(UserEntity conversationUser, String webSocketTicket) {
         this.conversationUser = conversationUser;
         this.webSocketTicket = webSocketTicket;
         this.webSocketConnectionHelper = new WebSocketConnectionHelper();
@@ -60,7 +88,6 @@ public class MagicWebSocketListener extends WebSocketListener {
         } catch (IOException e) {
             Log.e(TAG, "Failed to serialize hello model");
         }
-        //webSocket.close(NORMAL_CLOSURE_STATUS, "Goodbye !");
     }
 
     @Override
@@ -69,8 +96,33 @@ public class MagicWebSocketListener extends WebSocketListener {
 
         try {
             BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class);
+            String messageType = baseWebSocketMessage.getType();
+            switch (messageType) {
+                case "hello":
+                    connected = true;
+                    HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class);
+                    resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId();
+                    sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId();
+                    hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport();
+                    eventBus.post(new WebSocketCommunicationEvent("hello", null));
+                    break;
+                case "error":
+                    // Nothing for now
+                    break;
+                case "room":
+                    // Nothing for now
+                    break;
+                case "event":
+                    // Nothing for now
+                    break;
+                case "message":
+                    CallOverallWebSocketMessage callOverallWebSocketMessage = LoganSquare.parse(text, CallOverallWebSocketMessage.class);
+                    break;
+                default:
+                    break;
+            }
         } catch (IOException e) {
-            Log.e(TAG, "Failed to parse base WebSocket message");
+            Log.e(TAG, "Failed to WebSocket message");
         }
     }
 
@@ -81,13 +133,25 @@ public class MagicWebSocketListener extends WebSocketListener {
 
     @Override
     public void onClosing(WebSocket webSocket, int code, String reason) {
-        webSocket.close(NORMAL_CLOSURE_STATUS, null);
         Log.d(TAG, "Closing : " + code + " / " + reason);
+        connected = false;
     }
 
     @Override
     public void onFailure(WebSocket webSocket, Throwable t, Response response) {
         Log.d(TAG, "Error : " + t.getMessage());
+        connected = false;
+    }
+
+    public String getSessionId() {
+        return sessionId;
     }
 
+    public boolean hasMCU() {
+        return hasMCU;
+    }
+
+    public WebSocket getWebSocket() {
+        return webSocket;
+    }
 }

+ 9 - 13
app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java

@@ -43,14 +43,13 @@ import java.util.Map;
 import javax.inject.Inject;
 
 import autodagger.AutoInjector;
-import io.requery.Nullable;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.WebSocket;
 
 @AutoInjector(NextcloudTalkApplication.class)
 public class WebSocketConnectionHelper {
-    private Map<String, WebSocket> webSocketMap = new HashMap<>();
+    private Map<String, MagicWebSocketInstance> magicWebSocketInstanceMap = new HashMap<>();
 
     @Inject
     OkHttpClient okHttpClient;
@@ -72,23 +71,20 @@ public class WebSocketConnectionHelper {
         return generatedURL;
     }
 
-    public WebSocket getExternalSignalingInstanceForServer(String url, boolean forceReconnect, UserEntity userEntity, String webSocketTicket) {
+    public MagicWebSocketInstance getExternalSignalingInstanceForServer(String url, boolean forceReconnect, UserEntity userEntity, String webSocketTicket) {
 
         String connectionUrl = getExternalSignalingServerUrlFromSettingsUrl(url);
 
-        if (webSocketMap.containsKey(connectionUrl) && !forceReconnect) {
-            return webSocketMap.get(connectionUrl);
+        if (magicWebSocketInstanceMap.containsKey(userEntity.getUserId()) && !forceReconnect) {
+            return magicWebSocketInstanceMap.get(userEntity.getUserId());
         } else {
-            Request request = new Request.Builder().url(connectionUrl).build();
-            MagicWebSocketListener listener = new MagicWebSocketListener(userEntity, webSocketTicket);
-            WebSocket webSocket = okHttpClient.newWebSocket(request, listener);
-
-            webSocketMap.put(connectionUrl, webSocket);
-            return webSocket;
+            MagicWebSocketInstance magicWebSocketInstance = new MagicWebSocketInstance(userEntity, connectionUrl, webSocketTicket);
+            magicWebSocketInstanceMap.put(userEntity.getUserId(), magicWebSocketInstance);
+            return magicWebSocketInstance;
         }
     }
 
-    public HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) {
+    HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) {
         HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage();
         helloOverallWebSocketMessage.setType("hello");
         HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage();
@@ -104,7 +100,7 @@ public class WebSocketConnectionHelper {
         return helloOverallWebSocketMessage;
     }
 
-    public HelloOverallWebSocketMessage getAssembledHelloModelForResume(String resumeId) {
+    HelloOverallWebSocketMessage getAssembledHelloModelForResume(String resumeId) {
         HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage();
         helloOverallWebSocketMessage.setType("hello");
         HelloWebSocketMessage helloWebSocketMessage = new HelloWebSocketMessage();