diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java index ef184baee..79b58a564 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -1191,7 +1191,7 @@ public class CallController extends BaseController { webSocketConnectionHelper = new WebSocketConnectionHelper(); webSocketClient = webSocketConnectionHelper.getExternalSignalingInstanceForServer( externalSignalingServer.getExternalSignalingServer(), - conversationUser, externalSignalingServer.getExternalSignalingTicket(), false); + conversationUser, externalSignalingServer.getExternalSignalingTicket()); if (webSocketClient.isConnected()) { joinRoomAndCall(); diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java index 561ca4d34..d8fb351ca 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/AccountRemovalWorker.java @@ -36,6 +36,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.models.json.push.PushConfigurationState; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.database.user.UserUtils; +import com.nextcloud.talk.webrtc.WebSocketConnectionHelper; import java.io.IOException; import java.net.CookieManager; @@ -135,6 +136,8 @@ public class AccountRemovalWorker extends Worker { } } + WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(userEntity.getId()); + userUtils.deleteUser(userEntity.getId()).subscribe(new CompletableObserver() { @Override public void onSubscribe(Disposable d) { diff --git a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java index 34210df77..5c14acbb5 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/WebsocketConnectionsWorker.java @@ -67,7 +67,7 @@ public class WebsocketConnectionsWorker extends Worker { !TextUtils.isEmpty(externalSignalingServer.getExternalSignalingTicket())) { webSocketConnectionHelper.getExternalSignalingInstanceForServer( externalSignalingServer.getExternalSignalingServer(), - userEntity, externalSignalingServer.getExternalSignalingTicket(), false); + userEntity, externalSignalingServer.getExternalSignalingTicket()); } } catch (IOException e) { Log.e(TAG, "Failed to parse external signaling server"); diff --git a/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java new file mode 100644 index 000000000..368f6aed3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/websocket/ByeWebSocketMessage.java @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * 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.models.json.websocket; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.HashMap; + +import lombok.Data; + +@JsonObject +@Parcel +@Data +public class ByeWebSocketMessage extends BaseWebSocketMessage { + @JsonField(name = "bye") + HashMap bye; +} 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 207b250cd..eed1ed600 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -29,6 +29,7 @@ import com.nextcloud.talk.events.WebSocketCommunicationEvent; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.signaling.NCMessageWrapper; import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage; +import com.nextcloud.talk.models.json.websocket.ByeWebSocketMessage; import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage; import com.nextcloud.talk.models.json.websocket.ErrorOverallWebSocketMessage; import com.nextcloud.talk.models.json.websocket.EventOverallWebSocketMessage; @@ -75,8 +76,8 @@ public class MagicWebSocketInstance extends WebSocketListener { private String connectionUrl; private String currentRoomToken; - - int restartCount = 0; + private boolean isPermanentlyClosed = false; + private int restartCount = 0; MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) { NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); @@ -92,14 +93,16 @@ public class MagicWebSocketInstance extends WebSocketListener { @Override public void onOpen(WebSocket webSocket, Response response) { - try { - if (TextUtils.isEmpty(resumeId)) { - webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket))); - } else { - webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId))); + if (isConnected()) { + try { + if (TextUtils.isEmpty(resumeId)) { + webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket))); + } else { + webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId))); + } + } catch (IOException e) { + Log.e(TAG, "Failed to serialize hello model"); } - } catch (IOException e) { - Log.e(TAG, "Failed to serialize hello model"); } } @@ -131,7 +134,9 @@ public class MagicWebSocketInstance extends WebSocketListener { resumeId = ""; } - restartWebSocket(); + if (!isPermanentlyClosed) { + restartWebSocket(); + } break; case "room": JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class); @@ -141,7 +146,7 @@ public class MagicWebSocketInstance extends WebSocketListener { currentRoomToken = joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomId(); eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap)); } - break; + break; case "event": EventOverallWebSocketMessage eventOverallWebSocketMessage = LoganSquare.parse(text, EventOverallWebSocketMessage.class); if (eventOverallWebSocketMessage.getEventMap() != null) { @@ -186,6 +191,9 @@ public class MagicWebSocketInstance extends WebSocketListener { eventBus.post(new WebSocketCommunicationEvent("signalingMessage", messageHashMap)); } break; + case "bye": + connected = false; + isPermanentlyClosed = true; default: break; } @@ -211,6 +219,8 @@ public class MagicWebSocketInstance extends WebSocketListener { connected = false; if (restartCount < 4) { restartWebSocket(); + } else { + isPermanentlyClosed = true; } } @@ -224,10 +234,12 @@ public class MagicWebSocketInstance extends WebSocketListener { public void joinRoomWithRoomTokenAndSession(String roomToken, String normalBackendSession) { if (!roomToken.equals(currentRoomToken)) { - try { - webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession))); - } catch (IOException e) { - Log.e(TAG, "Failed to serialize room overall websocket message"); + if (isConnected()) { + try { + webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession))); + } catch (IOException e) { + Log.e(TAG, "Failed to serialize room overall websocket message"); + } } } else { HashMap joinRoomHashMap = new HashMap<>(); @@ -237,10 +249,12 @@ public class MagicWebSocketInstance extends WebSocketListener { } public void sendCallMessage(NCMessageWrapper ncMessageWrapper) { - try { - webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper))); - } catch (IOException e) { - Log.e(TAG, "Failed to serialize signaling message"); + if (isConnected()) { + try { + webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncMessageWrapper))); + } catch (IOException e) { + Log.e(TAG, "Failed to serialize signaling message"); + } } } @@ -251,14 +265,33 @@ public class MagicWebSocketInstance extends WebSocketListener { } public void requestOfferForSessionIdWithType(String sessionIdParam, String roomType) { - try { - webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType))); - } catch (IOException e) { - Log.e(TAG, "Failed to offer request"); + if (isConnected()) { + try { + webSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledRequestOfferModel(sessionIdParam, roomType))); + } catch (IOException e) { + Log.e(TAG, "Failed to offer request"); + } + } + } + + public void sendBye() { + if (isConnected()) { + try { + ByeWebSocketMessage byeWebSocketMessage = new ByeWebSocketMessage(); + byeWebSocketMessage.setType("bye"); + byeWebSocketMessage.setBye(new HashMap<>()); + webSocket.send(LoganSquare.serialize(byeWebSocketMessage); + } catch (IOException e) { + Log.e(TAG, "Failed to serialize bye message"); + } } } public boolean isConnected() { return connected; } + + public boolean isPermanentlyClosed() { + return isPermanentlyClosed; + } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java index 7e8de01d9..3c89e1e56 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java @@ -47,7 +47,7 @@ import okhttp3.OkHttpClient; @AutoInjector(NextcloudTalkApplication.class) public class WebSocketConnectionHelper { - private static Map magicWebSocketInstanceMap = new HashMap<>(); + private static Map magicWebSocketInstanceMap = new HashMap<>(); @Inject OkHttpClient okHttpClient; @@ -57,7 +57,7 @@ public class WebSocketConnectionHelper { NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this); } - public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer(String url, UserEntity userEntity, String webSocketTicket, boolean forceReconnect) { + public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer(String url, UserEntity userEntity, String webSocketTicket) { String generatedURL = url.replace("https://", "wss://").replace("http://", "ws://"); if (generatedURL.endsWith("/")) { @@ -66,15 +66,25 @@ public class WebSocketConnectionHelper { generatedURL += "/spreed"; } - if (magicWebSocketInstanceMap.containsKey(userEntity.getUserId()) && !forceReconnect) { - return magicWebSocketInstanceMap.get(userEntity.getUserId()); + MagicWebSocketInstance magicWebSocketInstance; + if ((magicWebSocketInstance = magicWebSocketInstanceMap.get(userEntity.getId())) != null && !magicWebSocketInstance.isPermanentlyClosed()) { + return magicWebSocketInstance; } else { - MagicWebSocketInstance magicWebSocketInstance = new MagicWebSocketInstance(userEntity, generatedURL, webSocketTicket); - magicWebSocketInstanceMap.put(userEntity.getUserId(), magicWebSocketInstance); + magicWebSocketInstance = new MagicWebSocketInstance(userEntity, generatedURL, webSocketTicket); + magicWebSocketInstanceMap.put(userEntity.getId(), magicWebSocketInstance); return magicWebSocketInstance; } } + public static synchronized void deleteExternalSignalingInstanceForUserEntity(long id) { + MagicWebSocketInstance magicWebSocketInstance; + if ((magicWebSocketInstance = magicWebSocketInstanceMap.get(id)) != null) { + if (magicWebSocketInstance.isConnected()) { + magicWebSocketInstance.sendBye(); + magicWebSocketInstanceMap.remove(id); + } + } + } HelloOverallWebSocketMessage getAssembledHelloModel(UserEntity userEntity, String ticket) { HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); helloOverallWebSocketMessage.setType("hello");