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.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("}");
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);
List<String> strings = new ArrayList<>();
String stringToSend = stringBuilder.toString();
strings.add(stringToSend);
String urlToken = null;
if (isMultiSession) {
urlToken = roomToken;
}
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

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")
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");
}
}

View File

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

View File

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

View File

@ -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();