More work towards working websockets

This commit is contained in:
Mario Danic 2018-10-12 20:07:12 +02:00
parent 70aaedd953
commit 72d3f5ef66
7 changed files with 229 additions and 70 deletions

View File

@ -57,6 +57,7 @@ import com.nextcloud.talk.events.ConfigurationChangeEvent;
import com.nextcloud.talk.events.MediaStreamEvent; import com.nextcloud.talk.events.MediaStreamEvent;
import com.nextcloud.talk.events.PeerConnectionEvent; import com.nextcloud.talk.events.PeerConnectionEvent;
import com.nextcloud.talk.events.SessionDescriptionSendEvent; import com.nextcloud.talk.events.SessionDescriptionSendEvent;
import com.nextcloud.talk.events.WebSocketCommunicationEvent;
import com.nextcloud.talk.models.ExternalSignalingServer; import com.nextcloud.talk.models.ExternalSignalingServer;
import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.call.CallOverall; 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.MagicAudioManager;
import com.nextcloud.talk.webrtc.MagicPeerConnectionWrapper; import com.nextcloud.talk.webrtc.MagicPeerConnectionWrapper;
import com.nextcloud.talk.webrtc.MagicWebRTCUtils; import com.nextcloud.talk.webrtc.MagicWebRTCUtils;
import com.nextcloud.talk.webrtc.MagicWebSocketInstance;
import com.nextcloud.talk.webrtc.WebSocketConnectionHelper; import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
import com.wooplr.spotlight.SpotlightView; import com.wooplr.spotlight.SpotlightView;
@ -244,7 +246,7 @@ public class CallController extends BaseController {
private SpotlightView spotlightView; private SpotlightView spotlightView;
private ExternalSignalingServer externalSignalingServer; private ExternalSignalingServer externalSignalingServer;
private okhttp3.WebSocket webSocketClient; private MagicWebSocketInstance webSocketClient;
private WebSocketConnectionHelper webSocketConnectionHelper; private WebSocketConnectionHelper webSocketConnectionHelper;
public CallController(Bundle args) { public CallController(Bundle args) {
@ -1175,6 +1177,15 @@ public class CallController extends BaseController {
conversationUser, externalSignalingServer.getExternalSignalingTicket()); 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}) @OnClick({R.id.pip_video_view, R.id.remote_renderers_layout})
public void showCallControls() { public void showCallControls() {
animateCallControls(true, 0); animateCallControls(true, 0);
@ -1474,8 +1485,18 @@ public class CallController extends BaseController {
if ((magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId)) != null) { if ((magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionId(sessionId)) != null) {
return magicPeerConnectionWrapper; return magicPeerConnectionWrapper;
} else { } else {
boolean hasMCU = webSocketClient != null && webSocketClient.hasMCU();
if (hasMCU) {
magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory,
iceServers, sdpConstraints, sessionId, callSession, localMediaStream); 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, hasMCU);
magicPeerConnectionWrapperList.add(magicPeerConnectionWrapper); magicPeerConnectionWrapperList.add(magicPeerConnectionWrapper);
return magicPeerConnectionWrapper; return magicPeerConnectionWrapper;
} }
@ -1593,6 +1614,7 @@ public class CallController extends BaseController {
ncMessageWrapper.setSignalingMessage(ncSignalingMessage); ncMessageWrapper.setSignalingMessage(ncSignalingMessage);
if (externalSignalingServer == null) {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("{") stringBuilder.append("{")
.append("\"fn\":\"") .append("\"fn\":\"")
@ -1645,6 +1667,9 @@ public class CallController extends BaseController {
} }
}); });
} else {
webSocketClient.getWebSocket().send(LoganSquare.serialize(ncMessageWrapper));
}
} }
@Override @Override

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

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

View File

@ -24,26 +24,54 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.bluelinelabs.logansquare.LoganSquare; 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.database.UserEntity;
import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage; 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.io.IOException;
import java.util.HashMap;
import javax.inject.Inject;
import autodagger.AutoInjector;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.WebSocket; import okhttp3.WebSocket;
import okhttp3.WebSocketListener; import okhttp3.WebSocketListener;
import okio.ByteString; 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 String TAG = "MagicWebSocketListener";
private static final int NORMAL_CLOSURE_STATUS = 1000;
@Inject
OkHttpClient okHttpClient;
@Inject
EventBus eventBus;
private UserEntity conversationUser; private UserEntity conversationUser;
private String webSocketTicket; private String webSocketTicket;
private String resumeId; private String resumeId;
private String sessionId;
private boolean hasMCU;
private boolean connected;
private WebSocketConnectionHelper webSocketConnectionHelper; 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.conversationUser = conversationUser;
this.webSocketTicket = webSocketTicket; this.webSocketTicket = webSocketTicket;
this.webSocketConnectionHelper = new WebSocketConnectionHelper(); this.webSocketConnectionHelper = new WebSocketConnectionHelper();
@ -60,7 +88,6 @@ public class MagicWebSocketListener extends WebSocketListener {
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to serialize hello model"); Log.e(TAG, "Failed to serialize hello model");
} }
//webSocket.close(NORMAL_CLOSURE_STATUS, "Goodbye !");
} }
@Override @Override
@ -69,8 +96,33 @@ public class MagicWebSocketListener extends WebSocketListener {
try { try {
BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class); 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) { } 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 @Override
public void onClosing(WebSocket webSocket, int code, String reason) { public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
Log.d(TAG, "Closing : " + code + " / " + reason); Log.d(TAG, "Closing : " + code + " / " + reason);
connected = false;
} }
@Override @Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) { public void onFailure(WebSocket webSocket, Throwable t, Response response) {
Log.d(TAG, "Error : " + t.getMessage()); Log.d(TAG, "Error : " + t.getMessage());
connected = false;
} }
public String getSessionId() {
return sessionId;
}
public boolean hasMCU() {
return hasMCU;
}
public WebSocket getWebSocket() {
return webSocket;
}
} }

View File

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