From 6846eb950bf0aa54f47398a20d000cb82f8ed81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 1 Oct 2020 13:45:20 +0200 Subject: [PATCH 1/6] Fix websocket reconnection when a guest joins the conversation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the standalone signaling server is used and a participant joins a conversation a "join" signaling message is received. The code assumed that the event always contains a "user" attribute, but that attribute is empty for guests. As the "user" attribute was used unconditionally this caused an exception, and as the exception happened when handling a websocket message it propagated until it caused a reconnection of the websocket. Signed-off-by: Daniel Calviño Sánchez --- .../com/nextcloud/talk/webrtc/MagicWebSocketInstance.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 38adaca89..10a9529d7 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -247,7 +247,10 @@ public class MagicWebSocketInstance extends WebSocketListener { HashMap userMap = (HashMap) internalHashMap.get("user"); participant = new Participant(); participant.setUserId((String) internalHashMap.get("userid")); - participant.setDisplayName((String) userMap.get("displayname")); + if (userMap != null) { + // There is no "user" attribute for guest participants. + participant.setDisplayName((String) userMap.get("displayname")); + } usersHashMap.put((String) internalHashMap.get("sessionid"), participant); } } From 5b9f02e99cbde0e0a05e662e313114eab3313869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 1 Oct 2020 19:00:07 +0200 Subject: [PATCH 2/6] Fix setting guest nicks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "nick" attribute was set with itself instead of with the "internalNick" variable that holds the value received in the "nickChanged" event. Signed-off-by: Daniel Calviño Sánchez --- .../com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java index a63e92695..103129dd5 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java @@ -271,7 +271,7 @@ public class MagicPeerConnectionWrapper { if (dataChannelMessage.getPayload() instanceof String) { internalNick = (String) dataChannelMessage.getPayload(); if (!internalNick.equals(nick)) { - setNick(nick); + setNick(internalNick); EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType .NICK_CHANGE, sessionId, getNick(), null, videoStreamType)); } From 6b2720653a5cc964cf519cac5ba46df47f3bc001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 1 Oct 2020 19:07:25 +0200 Subject: [PATCH 3/6] Always pass the session id in "NICK_CHANGE" PeerConnectionEvents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before the NICK_CHANGE event include either the session id or the user id, depending on whether the participant was a guest or a user. However, as the session id is also known for users the event can be unified to always include the session id only. This also fixes an exception when handling the "NICK_CHANGE" event, as the session id was got from the user id given in the event, but if the event already included the session id the look up failed and the session id was replaced with an empty value. This in turn caused an exception when trying to use the view for the now invalid session id. Now the session id provided in the event is always directly used. Signed-off-by: Daniel Calviño Sánchez --- .../nextcloud/talk/controllers/CallController.java | 11 +++-------- .../talk/webrtc/MagicPeerConnectionWrapper.java | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) 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 2f605148f..75b3d54bc 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -2031,16 +2031,11 @@ public class CallController extends BaseController { } } - private void gotNick(String sessionOrUserId, String nick, boolean isFromAnEvent, String type) { - if (isFromAnEvent && hasExternalSignalingServer) { - // get session based on userId - sessionOrUserId = webSocketClient.getSessionForUserId(sessionOrUserId); - } - - sessionOrUserId += "+" + type; + private void gotNick(String sessionId, String nick, boolean isFromAnEvent, String type) { + sessionId += "+" + type; if (relativeLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionOrUserId); + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view); if (!textView.getText().equals(nick)) { textView.setText(nick); diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java index 103129dd5..b514fc186 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java @@ -279,7 +279,7 @@ public class MagicPeerConnectionWrapper { if (dataChannelMessage.getPayload() != null) { HashMap payloadHashMap = (HashMap) dataChannelMessage.getPayload(); EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType - .NICK_CHANGE, payloadHashMap.get("userid"), payloadHashMap.get("name"), null, videoStreamType)); + .NICK_CHANGE, sessionId, payloadHashMap.get("name"), null, videoStreamType)); } } From 8f748caeaa0510ebe7db90aca5a1d0130d31f6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 1 Oct 2020 19:18:20 +0200 Subject: [PATCH 4/6] Remove no longer needed parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../com/nextcloud/talk/controllers/CallController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 75b3d54bc..13dc47fb4 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -1746,7 +1746,7 @@ public class CallController extends BaseController { } } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent .PeerConnectionEventType.NICK_CHANGE)) { - gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), true, peerConnectionEvent.getVideoStreamType()); + gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), peerConnectionEvent.getVideoStreamType()); } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent .PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { gotAudioOrVideoChange(true, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), @@ -2017,9 +2017,9 @@ public class CallController extends BaseController { surfaceViewRenderer.setOnClickListener(videoOnClickListener); remoteRenderersLayout.addView(relativeLayout); if (hasExternalSignalingServer) { - gotNick(session, webSocketClient.getDisplayNameForSession(session), false, type); + gotNick(session, webSocketClient.getDisplayNameForSession(session), type); } else { - gotNick(session, getPeerConnectionWrapperForSessionIdAndType(session, type, false).getNick(), false, type); + gotNick(session, getPeerConnectionWrapperForSessionIdAndType(session, type, false).getNick(), type); } if ("video".equals(type)) { @@ -2031,7 +2031,7 @@ public class CallController extends BaseController { } } - private void gotNick(String sessionId, String nick, boolean isFromAnEvent, String type) { + private void gotNick(String sessionId, String nick, String type) { sessionId += "+" + type; if (relativeLayout != null) { From 783f09316f568d6d8860923eea87370af3c5b254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 1 Oct 2020 20:34:48 +0200 Subject: [PATCH 5/6] Remove no longer used method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../nextcloud/talk/webrtc/MagicWebSocketInstance.java | 10 ---------- 1 file changed, 10 deletions(-) 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 10a9529d7..c6534dcbc 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -406,16 +406,6 @@ public class MagicWebSocketInstance extends WebSocketListener { return NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_nick_guest); } - public String getSessionForUserId(String userId) { - for (String session : usersHashMap.keySet()) { - if (userId.equals(usersHashMap.get(session).getUserId())) { - return session; - } - } - - return ""; - } - public String getUserIdForSession(String session) { if (usersHashMap.containsKey(session)) { return usersHashMap.get(session).getUserId(); From ed8ac14d94b078584c94578f232f62e46ee9e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Fri, 2 Oct 2020 01:56:32 +0200 Subject: [PATCH 6/6] Show avatar for guests in call view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The avatar of guests is based on their display name/nick. When the HPB is not used the nick is provided in the offer/answer signaling messages, and later updated through data channel messages when it changes. When the HPB is used the nick is periodically sent through data channel messages. Therefore the avatar is based on the nick set in the peer connection and reloaded when the nick changes (although it is currently a bit hacky and brittle, as it is based on whether the nick shown in the text view changed rather than whether the nick itself changed, but it works nevertheless). Note that currently it is required that the guest has a peer connection to know its nick and, therefore, its avatar; some changes would be needed in the clients to also send the nick when there is no peer connection. Signed-off-by: Daniel Calviño Sánchez --- .../talk/controllers/CallController.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) 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 13dc47fb4..7f86bec98 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -1914,23 +1914,35 @@ public class CallController extends BaseController { SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); String userId; + String displayName; if (hasMCU) { userId = webSocketClient.getUserIdForSession(session); + displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick(); } else { userId = participantMap.get(session).getUserId(); + displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick(); } - if (!TextUtils.isEmpty(userId)) { + if (!TextUtils.isEmpty(userId) || !TextUtils.isEmpty(displayName)) { if (getActivity() != null) { avatarImageView.setController(null); + String urlForAvatar; + if (!TextUtils.isEmpty(userId)) { + urlForAvatar = ApiUtils.getUrlForAvatarWithName(baseUrl, + userId, + R.dimen.avatar_size_big); + } else { + urlForAvatar = ApiUtils.getUrlForAvatarWithNameForGuests(baseUrl, + displayName, + R.dimen.avatar_size_big); + } + DraweeController draweeController = Fresco.newDraweeControllerBuilder() .setOldController(avatarImageView.getController()) - .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(baseUrl, - userId, - R.dimen.avatar_size_big), null)) + .setImageRequest(DisplayUtils.getImageRequestForUrl(urlForAvatar, null)) .build(); avatarImageView.setController(draweeController); } @@ -2032,13 +2044,17 @@ public class CallController extends BaseController { } private void gotNick(String sessionId, String nick, String type) { - sessionId += "+" + type; + String remoteRendererTag = sessionId + "+" + type; if (relativeLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); + RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(remoteRendererTag); TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view); if (!textView.getText().equals(nick)) { textView.setText(nick); + + if (getActivity() != null && type.equals("video")) { + getActivity().runOnUiThread(() -> setupAvatarForSession(sessionId)); + } } } }