mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-06 14:27:24 +00:00
More work towards working websockets
This commit is contained in:
parent
70aaedd953
commit
72d3f5ef66
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user