mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-22 04:55:29 +03:00
Merge pull request #4207 from nextcloud/backport/4120/stable-20.0
[stable-20.0] Add support for federated calls
This commit is contained in:
commit
32ed934c12
20 changed files with 291 additions and 27 deletions
|
@ -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
|
||||
|
|
|
@ -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 + '\'' +
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue