Merge pull request #4207 from nextcloud/backport/4120/stable-20.0

[stable-20.0] Add support for federated calls
This commit is contained in:
Marcel Hibbe 2024-09-13 17:02:10 +02:00 committed by GitHub
commit 32ed934c12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 291 additions and 27 deletions

View file

@ -1453,7 +1453,7 @@ class CallActivity : CallBaseActivity() {
private fun fetchSignalingSettings() {
Log.d(TAG, "fetchSignalingSettings")
val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.API_V3, 2, 1))
ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(apiVersion, baseUrl))
ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(apiVersion, baseUrl, roomToken!!))
.subscribeOn(Schedulers.io())
.retry(API_RETRIES)
.observeOn(AndroidSchedulers.mainThread())
@ -1475,6 +1475,8 @@ class CallActivity : CallBaseActivity() {
signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer
externalSignalingServer!!.externalSignalingTicket =
signalingSettingsOverall.ocs!!.settings!!.externalSignalingTicket
externalSignalingServer!!.federation =
signalingSettingsOverall.ocs!!.settings!!.federation
hasExternalSignalingServer = true
} else {
hasExternalSignalingServer = false
@ -1630,7 +1632,9 @@ class CallActivity : CallBaseActivity() {
private fun callOrJoinRoomViaWebSocket() {
if (hasExternalSignalingServer) {
webSocketClient!!.joinRoomWithRoomTokenAndSession(roomToken!!, callSession)
webSocketClient!!.joinRoomWithRoomTokenAndSession(
roomToken!!, callSession, externalSignalingServer!!.federation
)
} else {
performCall()
}
@ -2157,6 +2161,10 @@ class CallActivity : CallBaseActivity() {
Log.d(TAG, " newSession joined: $sessionId")
addCallParticipant(sessionId)
if (participant.actorType != null && participant.actorId != null) {
callParticipants[sessionId]!!.setActor(participant.actorType, participant.actorId)
}
val userId = participant.userId
if (userId != null) {
callParticipants[sessionId]!!.setUserId(userId)
@ -2510,10 +2518,12 @@ class CallActivity : CallBaseActivity() {
}
val defaultGuestNick = resources.getString(R.string.nc_nick_guest)
val participantDisplayItem = ParticipantDisplayItem(
context,
baseUrl,
defaultGuestNick,
rootEglBase,
videoStreamType,
roomToken,
callParticipantModel
)
val sessionId = callParticipantModel.sessionId

View file

@ -8,13 +8,16 @@
*/
package com.nextcloud.talk.adapters;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import com.nextcloud.talk.call.CallParticipantModel;
import com.nextcloud.talk.call.RaisedHand;
import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils;
import org.webrtc.EglBase;
import org.webrtc.MediaStream;
@ -29,6 +32,8 @@ public class ParticipantDisplayItem {
private final ParticipantDisplayItemNotifier participantDisplayItemNotifier = new ParticipantDisplayItemNotifier();
private final Context context;
private final String baseUrl;
private final String defaultGuestNick;
private final EglBase rootEglBase;
@ -36,8 +41,12 @@ public class ParticipantDisplayItem {
private final String session;
private final String streamType;
private final String roomToken;
private final CallParticipantModel callParticipantModel;
private Participant.ActorType actorType;
private String actorId;
private String userId;
private PeerConnection.IceConnectionState iceConnectionState;
private String nick;
@ -62,8 +71,10 @@ public class ParticipantDisplayItem {
}
};
public ParticipantDisplayItem(String baseUrl, String defaultGuestNick, EglBase rootEglBase, String streamType,
CallParticipantModel callParticipantModel) {
public ParticipantDisplayItem(Context context, String baseUrl, String defaultGuestNick, EglBase rootEglBase,
String streamType, String roomToken, CallParticipantModel callParticipantModel) {
this.context = context;
this.baseUrl = baseUrl;
this.defaultGuestNick = defaultGuestNick;
this.rootEglBase = rootEglBase;
@ -71,6 +82,8 @@ public class ParticipantDisplayItem {
this.session = callParticipantModel.getSessionId();
this.streamType = streamType;
this.roomToken = roomToken;
this.callParticipantModel = callParticipantModel;
this.callParticipantModel.addObserver(callParticipantModelObserver, handler);
@ -82,6 +95,8 @@ public class ParticipantDisplayItem {
}
private void updateFromModel() {
actorType = callParticipantModel.getActorType();
actorId = callParticipantModel.getActorId();
userId = callParticipantModel.getUserId();
nick = callParticipantModel.getNick();
@ -107,7 +122,10 @@ public class ParticipantDisplayItem {
}
private void updateUrlForAvatar() {
if (!TextUtils.isEmpty(userId)) {
if (actorType == Participant.ActorType.FEDERATED) {
int darkTheme = DisplayUtils.INSTANCE.isDarkModeOn(context) ? 1 : 0;
urlForAvatar = ApiUtils.getUrlForFederatedAvatar(baseUrl, roomToken, actorId, darkTheme, true);
} else if (!TextUtils.isEmpty(userId)) {
urlForAvatar = ApiUtils.getUrlForAvatar(baseUrl, userId, true);
} else {
urlForAvatar = ApiUtils.getUrlForGuestAvatar(baseUrl, getNick(), true);
@ -166,6 +184,8 @@ public class ParticipantDisplayItem {
public String toString() {
return "ParticipantSession{" +
"userId='" + userId + '\'' +
", actorType='" + actorType + '\'' +
", actorId='" + actorId + '\'' +
", session='" + session + '\'' +
", nick='" + nick + '\'' +
", urlForAvatar='" + urlForAvatar + '\'' +

View file

@ -6,6 +6,7 @@
*/
package com.nextcloud.talk.call;
import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.signaling.SignalingMessageReceiver;
import com.nextcloud.talk.webrtc.PeerConnectionWrapper;
@ -132,6 +133,10 @@ public class CallParticipant {
return callParticipantModel;
}
public void setActor(Participant.ActorType actorType, String actorId) {
callParticipantModel.setActor(actorType, actorId);
}
public void setUserId(String userId) {
callParticipantModel.setUserId(userId);
}

View file

@ -122,6 +122,8 @@ public class CallParticipantList {
private Participant copyParticipant(Participant participant) {
Participant copiedParticipant = new Participant();
copiedParticipant.setActorId(participant.getActorId());
copiedParticipant.setActorType(participant.getActorType());
copiedParticipant.setInCall(participant.getInCall());
copiedParticipant.setInternal(participant.getInternal());
copiedParticipant.setLastPing(participant.getLastPing());

View file

@ -8,6 +8,8 @@ package com.nextcloud.talk.call;
import android.os.Handler;
import com.nextcloud.talk.models.json.participants.Participant;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
@ -25,6 +27,8 @@ import java.util.Objects;
*
* Audio and video in screen shares, on the other hand, are always seen as available.
*
* Actor type and actor id will be set only in Talk >= 20.
*
* Clients of the model can observe it with CallParticipantModel.Observer to be notified when any value changes.
* Getters called after receiving a notification are guaranteed to provide at least the value that triggered the
* notification, but it may return even a more up to date one (so getting the value again on the following
@ -39,6 +43,8 @@ public class CallParticipantModel {
protected final String sessionId;
protected Data<Participant.ActorType> actorType;
protected Data<String> actorId;
protected Data<String> userId;
protected Data<String> nick;
@ -81,6 +87,8 @@ public class CallParticipantModel {
public CallParticipantModel(String sessionId) {
this.sessionId = sessionId;
this.actorType = new Data<>();
this.actorId = new Data<>();
this.userId = new Data<>();
this.nick = new Data<>();
@ -101,6 +109,14 @@ public class CallParticipantModel {
return sessionId;
}
public Participant.ActorType getActorType() {
return actorType.getValue();
}
public String getActorId() {
return actorId.getValue();
}
public String getUserId() {
return userId.getValue();
}

View file

@ -6,6 +6,8 @@
*/
package com.nextcloud.talk.call;
import com.nextcloud.talk.models.json.participants.Participant;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
@ -20,6 +22,11 @@ public class MutableCallParticipantModel extends CallParticipantModel {
super(sessionId);
}
public void setActor(Participant.ActorType actorType, String actorId) {
this.actorType.setValue(actorType);
this.actorId.setValue(actorId);
}
public void setUserId(String userId) {
this.userId.setValue(userId);
}

View file

@ -120,10 +120,12 @@ import com.nextcloud.talk.jobs.ShareOperationWorker
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
import com.nextcloud.talk.location.LocationPickerActivity
import com.nextcloud.talk.messagesearch.MessageSearchActivity
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
import com.nextcloud.talk.polls.ui.PollCreateDialogFragment
import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
@ -177,6 +179,8 @@ import com.stfalcon.chatkit.messages.MessageHolders
import com.stfalcon.chatkit.messages.MessageHolders.ContentChecker
import com.stfalcon.chatkit.messages.MessagesListAdapter
import com.stfalcon.chatkit.utils.DateFormatter
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
@ -302,6 +306,7 @@ class ChatActivity :
var webSocketInstance: WebSocketInstance? = null
var signalingMessageSender: SignalingMessageSender? = null
var externalSignalingServer: ExternalSignalingServer? = null
var getRoomInfoTimerHandler: Handler? = null
@ -631,10 +636,12 @@ class ChatActivity :
logConversationInfos("joinRoomWithPassword#onNext")
setupWebsocket()
if (webSocketInstance != null) {
webSocketInstance?.joinRoomWithRoomTokenAndSession(
roomToken,
sessionIdAfterRoomJoined
sessionIdAfterRoomJoined,
externalSignalingServer?.federation
)
}
if (startCallFromNotification != null && startCallFromNotification) {
@ -952,7 +959,6 @@ class ChatActivity :
pullChatMessagesPending = false
setupWebsocket()
webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener)
webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener)
@ -2391,10 +2397,12 @@ class ChatActivity :
} else {
Log.d(TAG, "sessionID was valid -> skip joinRoom")
setupWebsocket()
if (webSocketInstance != null) {
webSocketInstance?.joinRoomWithRoomTokenAndSession(
roomToken,
sessionIdAfterRoomJoined
sessionIdAfterRoomJoined,
externalSignalingServer?.federation
)
}
}
@ -2423,16 +2431,59 @@ class ChatActivity :
}
private fun setupWebsocket() {
if (conversationUser == null) {
if (currentConversation == null || conversationUser == null) {
return
}
webSocketInstance = WebSocketConnectionHelper.getWebSocketInstanceForUser(conversationUser!!)
if (currentConversation!!.remoteServer != null) {
val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V3, 2, 1))
ncApi!!.getSignalingSettings(
credentials,
ApiUtils.getUrlForSignalingSettings(apiVersion, conversationUser!!.baseUrl, roomToken!!)
).blockingSubscribe(object : Observer<SignalingSettingsOverall> {
override fun onSubscribe(d: Disposable) {
// unused atm
}
override fun onNext(signalingSettingsOverall: SignalingSettingsOverall) {
if (signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer == null) {
return
}
externalSignalingServer = ExternalSignalingServer()
externalSignalingServer!!.externalSignalingServer = signalingSettingsOverall.ocs!!.settings!!
.externalSignalingServer
externalSignalingServer!!.externalSignalingTicket = signalingSettingsOverall.ocs!!.settings!!
.externalSignalingTicket
externalSignalingServer!!.federation = signalingSettingsOverall.ocs!!.settings!!.federation
webSocketInstance = WebSocketConnectionHelper.getExternalSignalingInstanceForServer(
externalSignalingServer!!.externalSignalingServer,
conversationUser,
externalSignalingServer!!.externalSignalingTicket,
TextUtils.isEmpty(credentials)
)
}
override fun onError(e: Throwable) {
Log.e(CallActivity.TAG, e.message, e)
}
override fun onComplete() {
// unused atm
}
})
} else {
webSocketInstance = WebSocketConnectionHelper.getWebSocketInstanceForUser(conversationUser!!)
}
if (webSocketInstance == null) {
Log.d(TAG, "webSocketInstance not set up. This should only happen when not using the HPB")
}
signalingMessageSender = webSocketInstance?.signalingMessageSender
webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener)
webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener)
}
private fun processCallStartedMessages(chatMessageList: List<ChatMessage>) {
@ -3190,7 +3241,7 @@ class ChatActivity :
val lon = data["longitude"]!!
metaData =
"{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," +
"\"longitude\":\"$lon\",\"name\":\"$name\"}"
"\"longitude\":\"$lon\",\"name\":\"$name\"}"
}
when (type) {

View file

@ -10,6 +10,7 @@ package com.nextcloud.talk.models
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import com.nextcloud.talk.models.json.signaling.settings.FederationSettings
import kotlinx.parcelize.Parcelize
@Parcelize
@ -18,8 +19,10 @@ data class ExternalSignalingServer(
@JsonField(name = ["externalSignalingServer"])
var externalSignalingServer: String? = null,
@JsonField(name = ["externalSignalingTicket"])
var externalSignalingTicket: String? = null
var externalSignalingTicket: String? = null,
@JsonField(name = ["federation"])
var federation: FederationSettings? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null)
constructor() : this(null, null, null)
}

View file

@ -0,0 +1,24 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.models.json.signaling.settings
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
@Parcelize
@JsonObject
@Serializable
data class FederationHelloAuthParams(
@JsonField(name = ["token"])
var token: String? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null)
}

View file

@ -0,0 +1,30 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.models.json.signaling.settings
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
@Parcelize
@JsonObject
@Serializable
data class FederationSettings(
@JsonField(name = ["server"])
var server: String? = null,
@JsonField(name = ["nextcloudServer"])
var nextcloudServer: String? = null,
@JsonField(name = ["helloAuthParams"])
var helloAuthParams: FederationHelloAuthParams? = null,
@JsonField(name = ["roomId"])
var roomId: String? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null, null, null)
}

View file

@ -24,8 +24,10 @@ data class SignalingSettings(
@JsonField(name = ["server"])
var externalSignalingServer: String? = null,
@JsonField(name = ["ticket"])
var externalSignalingTicket: String? = null
var externalSignalingTicket: String? = null,
@JsonField(name = ["federation"])
var federation: FederationSettings? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null, null)
constructor() : this(null, null, null, null)
}

View file

@ -0,0 +1,28 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Daniel Calviño Sánchez <danxuliu@gmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.models.json.websocket
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.parcelize.Parcelize
@Parcelize
@JsonObject
class RoomFederationWebSocketMessage(
@JsonField(name = ["signaling"])
var signaling: String? = null,
@JsonField(name = ["url"])
var url: String? = null,
@JsonField(name = ["roomid"])
var roomid: String? = null,
@JsonField(name = ["token"])
var token: String? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null, null, null)
}

View file

@ -20,8 +20,10 @@ class RoomWebSocketMessage(
@JsonField(name = ["sessionid"])
var sessionId: String? = null,
@JsonField(name = ["properties"])
var roomPropertiesWebSocketMessage: RoomPropertiesWebSocketMessage? = null
var roomPropertiesWebSocketMessage: RoomPropertiesWebSocketMessage? = null,
@JsonField(name = ["federation"])
var roomFederationWebSocketMessage: RoomFederationWebSocketMessage? = null
) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
constructor() : this(null, null, null)
constructor() : this(null, null, null, null)
}

View file

@ -6,6 +6,7 @@
*/
package com.nextcloud.talk.signaling;
import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter;
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
@ -38,6 +39,8 @@ import java.util.Map;
*/
public abstract class SignalingMessageReceiver {
private final EnumActorTypeConverter enumActorTypeConverter = new EnumActorTypeConverter();
private final ParticipantListMessageNotifier participantListMessageNotifier = new ParticipantListMessageNotifier();
private final LocalParticipantMessageNotifier localParticipantMessageNotifier = new LocalParticipantMessageNotifier();
@ -398,6 +401,8 @@ public abstract class SignalingMessageReceiver {
// "nextcloudSessionId": #STRING#, // Optional
// "internal": #BOOLEAN#, // Optional
// "participantPermissions": #INTEGER#, // Talk >= 13
// "actorType": #STRING#, // Talk >= 20
// "actorId": #STRING#, // Talk >= 20
// },
// ...
// ],
@ -447,6 +452,8 @@ public abstract class SignalingMessageReceiver {
// "sessionId": #STRING#,
// "userId": #STRING#, // Always included, although it can be empty
// "participantPermissions": #INTEGER#, // Talk >= 13
// "actorType": #STRING#, // Talk >= 20
// "actorId": #STRING#, // Talk >= 20
// },
// ...
// ],
@ -492,6 +499,14 @@ public abstract class SignalingMessageReceiver {
participant.setInternal(Boolean.TRUE);
}
if (participantMap.get("actorType") != null && !participantMap.get("actorType").toString().isEmpty()) {
participant.setActorType(enumActorTypeConverter.getFromString(participantMap.get("actorType").toString()));
}
if (participantMap.get("actorId") != null && !participantMap.get("actorId").toString().isEmpty()) {
participant.setActorId(participantMap.get("actorId").toString());
}
// Only in external signaling messages
if (participantMap.get("participantType") != null) {
int participantTypeInt = Integer.parseInt(participantMap.get("participantType").toString());

View file

@ -283,6 +283,10 @@ object ApiUtils {
return getUrlForSignaling(version, baseUrl) + "/settings"
}
fun getUrlForSignalingSettings(version: Int, baseUrl: String?, token: String): String {
return getUrlForSignaling(version, baseUrl) + "/settings?token=" + token
}
fun getUrlForSignaling(version: Int, baseUrl: String?, token: String): String {
return getUrlForSignaling(version, baseUrl) + "/" + token
}

View file

@ -12,6 +12,7 @@ import android.util.Log;
import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.data.user.model.User;
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
import com.nextcloud.talk.models.json.signaling.settings.FederationSettings;
import com.nextcloud.talk.models.json.websocket.ActorWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.AuthParametersWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.AuthWebSocketMessage;
@ -19,6 +20,7 @@ import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.CallWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.HelloOverallWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.HelloWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.RoomFederationWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.RoomOverallWebSocketMessage;
import com.nextcloud.talk.models.json.websocket.RoomWebSocketMessage;
import com.nextcloud.talk.utils.ApiUtils;
@ -128,12 +130,25 @@ public class WebSocketConnectionHelper {
return helloOverallWebSocketMessage;
}
RoomOverallWebSocketMessage getAssembledJoinOrLeaveRoomModel(String roomId, String sessionId) {
RoomOverallWebSocketMessage getAssembledJoinOrLeaveRoomModel(String roomId, String sessionId,
FederationSettings federation) {
RoomOverallWebSocketMessage roomOverallWebSocketMessage = new RoomOverallWebSocketMessage();
roomOverallWebSocketMessage.setType("room");
RoomWebSocketMessage roomWebSocketMessage = new RoomWebSocketMessage();
roomWebSocketMessage.setRoomId(roomId);
roomWebSocketMessage.setSessionId(sessionId);
if (federation != null) {
String federationAuthToken = null;
if (federation.getHelloAuthParams() != null) {
federationAuthToken = federation.getHelloAuthParams().getToken();
}
RoomFederationWebSocketMessage roomFederationWebSocketMessage = new RoomFederationWebSocketMessage();
roomFederationWebSocketMessage.setSignaling(federation.getServer());
roomFederationWebSocketMessage.setUrl(federation.getNextcloudServer() + "/ocs/v2.php/apps/spreed/api/v3/signaling/backend");
roomFederationWebSocketMessage.setRoomid(federation.getRoomId());
roomFederationWebSocketMessage.setToken(federationAuthToken);
roomWebSocketMessage.setRoomFederationWebSocketMessage(roomFederationWebSocketMessage);
}
roomOverallWebSocketMessage.setRoomWebSocketMessage(roomWebSocketMessage);
return roomOverallWebSocketMessage;
}

View file

@ -20,6 +20,7 @@ import com.nextcloud.talk.events.WebSocketCommunicationEvent
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.Participant.ActorType
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage
import com.nextcloud.talk.models.json.signaling.settings.FederationSettings
import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage
import com.nextcloud.talk.models.json.websocket.ByeWebSocketMessage
import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage
@ -75,6 +76,7 @@ class WebSocketInstance internal constructor(
private val connectionUrl: String
private var currentRoomToken: String? = null
private var currentNormalBackendSession: String? = null
private var currentFederation: FederationSettings? = null
private var reconnecting = false
private val usersHashMap: HashMap<String?, Participant>
private var messagesQueue: MutableList<String> = ArrayList()
@ -367,24 +369,36 @@ class WebSocketInstance internal constructor(
return hasMCU
}
fun joinRoomWithRoomTokenAndSession(roomToken: String, normalBackendSession: String?) {
@Suppress("Detekt.ComplexMethod")
fun joinRoomWithRoomTokenAndSession(
roomToken: String,
normalBackendSession: String?,
federation: FederationSettings? = null
) {
Log.d(TAG, "joinRoomWithRoomTokenAndSession")
Log.d(TAG, " roomToken: $roomToken")
Log.d(TAG, " session: $normalBackendSession")
try {
val message = LoganSquare.serialize(
webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession)
webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession, federation)
)
if (roomToken == "") {
Log.d(TAG, "sending 'leave room' via websocket")
currentNormalBackendSession = ""
currentFederation = null
sendMessage(message)
} else if (roomToken == currentRoomToken && normalBackendSession == currentNormalBackendSession) {
} else if (
roomToken == currentRoomToken &&
normalBackendSession == currentNormalBackendSession &&
federation?.roomId == currentFederation?.roomId &&
federation?.nextcloudServer == federation?.nextcloudServer
) {
Log.d(TAG, "roomToken & session are unchanged. Joining locally without to send websocket message")
sendRoomJoinedEvent()
} else {
Log.d(TAG, "Sending join room message via websocket")
currentNormalBackendSession = normalBackendSession
currentFederation = federation
sendMessage(message)
}
} catch (e: IOException) {

View file

@ -71,6 +71,8 @@ public class CallParticipantListExternalSignalingTest {
participant.setSessionId(sessionId);
participant.setType(type);
participant.setUserId(userId);
participant.setActorType(Participant.ActorType.USERS);
participant.setActorId(userId);
return participant;
}
@ -81,6 +83,8 @@ public class CallParticipantListExternalSignalingTest {
participant.setLastPing(lastPing);
participant.setSessionId(sessionId);
participant.setType(type);
participant.setActorType(Participant.ActorType.GUESTS);
participant.setActorId("sha1-" + sessionId);
return participant;
}

View file

@ -61,6 +61,8 @@ public class CallParticipantListInternalSignalingTest {
participant.setLastPing(lastPing);
participant.setSessionId(sessionId);
participant.setUserId(userId);
participant.setActorType(Participant.ActorType.USERS);
participant.setActorId(userId);
return participant;
}
@ -70,6 +72,8 @@ public class CallParticipantListInternalSignalingTest {
participant.setInCall(inCall);
participant.setLastPing(lastPing);
participant.setSessionId(sessionId);
participant.setActorType(Participant.ActorType.GUESTS);
participant.setActorId("sha1-" + sessionId);
return participant;
}

View file

@ -58,10 +58,12 @@ public class SignalingMessageReceiverParticipantListTest {
user1.put("roomId", 108);
user1.put("sessionId", "theSessionId1");
user1.put("userId", "theUserId");
// If "participantPermissions" is set in any of the participants all the other participants in the message
// would have it too. But for test simplicity, and as it is not relevant for the processing, in this test it
// is included only in one of the participants.
// If any of the following properties is set in any of the participants all the other participants in the
// message would have it too. But for test simplicity, and as it is not relevant for the processing, in this
// test they are included only in one of the participants.
user1.put("participantPermissions", 42);
user1.put("actorType", "federated_users");
user1.put("actorId", "theActorId");
users.add(user1);
Map<String, Object> user2 = new HashMap<>();
user2.put("inCall", 0);
@ -78,6 +80,8 @@ public class SignalingMessageReceiverParticipantListTest {
expectedParticipant1.setLastPing(4815);
expectedParticipant1.setSessionId("theSessionId1");
expectedParticipant1.setUserId("theUserId");
expectedParticipant1.setActorType(Participant.ActorType.FEDERATED);
expectedParticipant1.setActorId("theActorId");
expectedParticipantList.add(expectedParticipant1);
Participant expectedParticipant2 = new Participant();
@ -266,11 +270,13 @@ public class SignalingMessageReceiverParticipantListTest {
user1.put("sessionId", "theSessionId1");
user1.put("participantType", 3);
user1.put("userId", "theUserId");
// If "nextcloudSessionId" or "participantPermissions" is set in any of the participants all the other
// participants in the message would have them too. But for test simplicity, and as it is not relevant for
// the processing, in this test they are included only in one of the participants.
// If any of the following properties is set in any of the participants all the other participants in the
// message would have it too. But for test simplicity, and as it is not relevant for the processing, in this
// test they are included only in one of the participants.
user1.put("nextcloudSessionId", "theNextcloudSessionId");
user1.put("participantPermissions", 42);
user1.put("actorType", "federated_users");
user1.put("actorId", "theActorId");
users.add(user1);
Map<String, Object> user2 = new HashMap<>();
user2.put("inCall", 0);
@ -289,6 +295,8 @@ public class SignalingMessageReceiverParticipantListTest {
expectedParticipant1.setSessionId("theSessionId1");
expectedParticipant1.setType(Participant.ParticipantType.USER);
expectedParticipant1.setUserId("theUserId");
expectedParticipant1.setActorType(Participant.ActorType.FEDERATED);
expectedParticipant1.setActorId("theActorId");
expectedParticipantList.add(expectedParticipant1);
Participant expectedParticipant2 = new Participant();