Merge pull request #4277 from nextcloud/bugfix/4272/fixToHandleOldServerVersions

Bugfix/4272/fix to handle old server versions
This commit is contained in:
Marcel Hibbe 2024-09-26 13:34:21 +02:00 committed by GitHub
commit 21bb71fba4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 438 additions and 220 deletions

View File

@ -1745,9 +1745,8 @@ class CallActivity : CallBaseActivity() {
ApplicationWideCurrentRoomHolder.getInstance().callStartTime = conversation.callStartTime ApplicationWideCurrentRoomHolder.getInstance().callStartTime = conversation.callStartTime
} }
private fun startCallTimeCounter(callStartTime: Long?) { private fun startCallTimeCounter(callStartTime: Long) {
if (callStartTime != null && if (callStartTime != 0L &&
callStartTime != 0L &&
hasSpreedFeatureCapability( hasSpreedFeatureCapability(
conversationUser!!.capabilities!!.spreedCapability!!, SpreedFeatures.RECORDING_V1 conversationUser!!.capabilities!!.spreedCapability!!, SpreedFeatures.RECORDING_V1
) )

View File

@ -356,8 +356,8 @@ class ConversationInfoActivity :
binding.webinarInfoView.startTimeButton.setOnClickListener { binding.webinarInfoView.startTimeButton.setOnClickListener {
MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show { MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show {
val currentTimeCalendar = Calendar.getInstance() val currentTimeCalendar = Calendar.getInstance()
if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != 0L) { if (conversation!!.lobbyTimer != 0L) {
currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer!! * DateConstants.SECOND_DIVIDER currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * DateConstants.SECOND_DIVIDER
} }
dateTimePicker( dateTimePicker(
@ -746,13 +746,13 @@ class ConversationInfoActivity :
setupWebinaryView() setupWebinaryView()
if (!ConversationUtils.canLeave(conversation!!)) { if (!conversation!!.canLeaveConversation) {
binding.leaveConversationAction.visibility = GONE binding.leaveConversationAction.visibility = GONE
} else { } else {
binding.leaveConversationAction.visibility = VISIBLE binding.leaveConversationAction.visibility = VISIBLE
} }
if (!ConversationUtils.canDelete(conversation!!, spreedCapabilities)) { if (!conversation!!.canDeleteConversation) {
binding.deleteConversationAction.visibility = GONE binding.deleteConversationAction.visibility = GONE
} else { } else {
binding.deleteConversationAction.visibility = VISIBLE binding.deleteConversationAction.visibility = VISIBLE

View File

@ -116,36 +116,36 @@ fun Conversation.asEntity(accountId: Long) =
ConversationEntity( ConversationEntity(
internalId = "$accountId@$token", internalId = "$accountId@$token",
accountId = accountId, accountId = accountId,
token = token!!, token = token,
name = name!!, name = name,
displayName = displayName!!, displayName = displayName,
description = description!!, description = description,
type = type!!, type = type,
lastPing = lastPing, lastPing = lastPing,
participantType = participantType!!, participantType = participantType,
hasPassword = hasPassword, hasPassword = hasPassword,
sessionId = sessionId!!, sessionId = sessionId,
actorId = actorId!!, actorId = actorId,
actorType = actorType!!, actorType = actorType,
favorite = favorite, favorite = favorite,
lastActivity = lastActivity, lastActivity = lastActivity,
unreadMessages = unreadMessages, unreadMessages = unreadMessages,
unreadMention = unreadMention, unreadMention = unreadMention,
lastMessage = lastMessage?.let { LoganSquare.serialize(lastMessage) }, lastMessage = lastMessage?.let { LoganSquare.serialize(lastMessage) },
objectType = objectType!!, objectType = objectType,
notificationLevel = notificationLevel!!, notificationLevel = notificationLevel,
conversationReadOnlyState = conversationReadOnlyState!!, conversationReadOnlyState = conversationReadOnlyState,
lobbyState = lobbyState!!, lobbyState = lobbyState,
lobbyTimer = lobbyTimer!!, lobbyTimer = lobbyTimer,
lastReadMessage = lastReadMessage, lastReadMessage = lastReadMessage,
lastCommonReadMessage = lastCommonReadMessage, lastCommonReadMessage = lastCommonReadMessage,
hasCall = hasCall, hasCall = hasCall,
callFlag = callFlag, callFlag = callFlag,
canStartCall = canStartCall, canStartCall = canStartCall,
canLeaveConversation = canLeaveConversation!!, canLeaveConversation = canLeaveConversation,
canDeleteConversation = canDeleteConversation!!, canDeleteConversation = canDeleteConversation,
unreadMentionDirect = unreadMentionDirect!!, unreadMentionDirect = unreadMentionDirect,
notificationCalls = notificationCalls!!, notificationCalls = notificationCalls,
permissions = permissions, permissions = permissions,
messageExpiration = messageExpiration, messageExpiration = messageExpiration,
status = status, status = status,
@ -153,9 +153,9 @@ fun Conversation.asEntity(accountId: Long) =
statusMessage = statusMessage, statusMessage = statusMessage,
statusClearAt = statusClearAt, statusClearAt = statusClearAt,
callRecording = callRecording, callRecording = callRecording,
avatarVersion = avatarVersion!!, avatarVersion = avatarVersion,
hasCustomAvatar = hasCustomAvatar!!, hasCustomAvatar = hasCustomAvatar,
callStartTime = callStartTime!!, callStartTime = callStartTime,
recordingConsentRequired = recordingConsentRequired, recordingConsentRequired = recordingConsentRequired,
remoteServer = remoteServer, remoteServer = remoteServer,
remoteToken = remoteToken remoteToken = remoteToken

View File

@ -16,7 +16,6 @@ import com.nextcloud.talk.models.json.participants.Participant
class ConversationModel( class ConversationModel(
var internalId: String, var internalId: String,
var accountId: Long, var accountId: Long,
// var roomId: String? = null,
var token: String, var token: String,
var name: String, var name: String,
var displayName: String, var displayName: String,
@ -70,46 +69,45 @@ class ConversationModel(
return ConversationModel( return ConversationModel(
internalId = user.id!!.toString() + "@" + conversation.token, internalId = user.id!!.toString() + "@" + conversation.token,
accountId = user.id!!, accountId = user.id!!,
// roomId = conversation.roomId, token = conversation.token,
token = conversation.token!!, name = conversation.name,
name = conversation.name!!, displayName = conversation.displayName,
displayName = conversation.displayName!!, description = conversation.description,
description = conversation.description!!, type = conversation.type.let { ConversationEnums.ConversationType.valueOf(it.name) },
type = conversation.type.let { ConversationEnums.ConversationType.valueOf(it!!.name) },
lastPing = conversation.lastPing, lastPing = conversation.lastPing,
participantType = conversation.participantType.let { Participant.ParticipantType.valueOf(it!!.name) }, participantType = conversation.participantType.let { Participant.ParticipantType.valueOf(it.name) },
hasPassword = conversation.hasPassword, hasPassword = conversation.hasPassword,
sessionId = conversation.sessionId!!, sessionId = conversation.sessionId,
actorId = conversation.actorId!!, actorId = conversation.actorId,
actorType = conversation.actorType!!, actorType = conversation.actorType,
password = conversation.password, password = conversation.password,
favorite = conversation.favorite, favorite = conversation.favorite,
lastActivity = conversation.lastActivity, lastActivity = conversation.lastActivity,
unreadMessages = conversation.unreadMessages, unreadMessages = conversation.unreadMessages,
unreadMention = conversation.unreadMention, unreadMention = conversation.unreadMention,
lastMessage = conversation.lastMessage, lastMessage = conversation.lastMessage,
objectType = conversation.objectType.let { ConversationEnums.ObjectType.valueOf(it!!.name) }, objectType = conversation.objectType.let { ConversationEnums.ObjectType.valueOf(it.name) },
notificationLevel = conversation.notificationLevel.let { notificationLevel = conversation.notificationLevel.let {
ConversationEnums.NotificationLevel.valueOf( ConversationEnums.NotificationLevel.valueOf(
it!!.name it.name
) )
}, },
conversationReadOnlyState = conversation.conversationReadOnlyState.let { conversationReadOnlyState = conversation.conversationReadOnlyState.let {
ConversationEnums.ConversationReadOnlyState.valueOf( ConversationEnums.ConversationReadOnlyState.valueOf(
it!!.name it.name
) )
}, },
lobbyState = conversation.lobbyState.let { ConversationEnums.LobbyState.valueOf(it!!.name) }, lobbyState = conversation.lobbyState.let { ConversationEnums.LobbyState.valueOf(it.name) },
lobbyTimer = conversation.lobbyTimer!!, lobbyTimer = conversation.lobbyTimer,
lastReadMessage = conversation.lastReadMessage, lastReadMessage = conversation.lastReadMessage,
lastCommonReadMessage = conversation.lastCommonReadMessage, lastCommonReadMessage = conversation.lastCommonReadMessage,
hasCall = conversation.hasCall, hasCall = conversation.hasCall,
callFlag = conversation.callFlag, callFlag = conversation.callFlag,
canStartCall = conversation.canStartCall, canStartCall = conversation.canStartCall,
canLeaveConversation = conversation.canLeaveConversation!!, canLeaveConversation = conversation.canLeaveConversation,
canDeleteConversation = conversation.canDeleteConversation!!, canDeleteConversation = conversation.canDeleteConversation,
unreadMentionDirect = conversation.unreadMentionDirect!!, unreadMentionDirect = conversation.unreadMentionDirect,
notificationCalls = conversation.notificationCalls!!, notificationCalls = conversation.notificationCalls,
permissions = conversation.permissions, permissions = conversation.permissions,
messageExpiration = conversation.messageExpiration, messageExpiration = conversation.messageExpiration,
status = conversation.status, status = conversation.status,
@ -117,9 +115,9 @@ class ConversationModel(
statusMessage = conversation.statusMessage, statusMessage = conversation.statusMessage,
statusClearAt = conversation.statusClearAt, statusClearAt = conversation.statusClearAt,
callRecording = conversation.callRecording, callRecording = conversation.callRecording,
avatarVersion = conversation.avatarVersion!!, avatarVersion = conversation.avatarVersion,
hasCustomAvatar = conversation.hasCustomAvatar!!, hasCustomAvatar = conversation.hasCustomAvatar,
callStartTime = conversation.callStartTime!!, callStartTime = conversation.callStartTime,
recordingConsentRequired = conversation.recordingConsentRequired, recordingConsentRequired = conversation.recordingConsentRequired,
remoteServer = conversation.remoteServer, remoteServer = conversation.remoteServer,
remoteToken = conversation.remoteToken remoteToken = conversation.remoteToken
@ -127,47 +125,3 @@ class ConversationModel(
} }
} }
} }
// enum class ConversationType {
// DUMMY,
// ROOM_TYPE_ONE_TO_ONE_CALL,
// ROOM_GROUP_CALL,
// ROOM_PUBLIC_CALL,
// ROOM_SYSTEM,
// FORMER_ONE_TO_ONE,
// NOTE_TO_SELF
// }
//
// enum class ParticipantType {
// DUMMY,
// OWNER,
// MODERATOR,
// USER,
// GUEST,
// USER_FOLLOWING_LINK,
// GUEST_MODERATOR
// }
//
// enum class ObjectType {
// DEFAULT,
// SHARE_PASSWORD,
// FILE,
// ROOM
// }
//
// enum class NotificationLevel {
// DEFAULT,
// ALWAYS,
// MENTION,
// NEVER
// }
//
// enum class ConversationReadOnlyState {
// CONVERSATION_READ_WRITE,
// CONVERSATION_READ_ONLY
// }
//
// enum class LobbyState {
// LOBBY_STATE_ALL_PARTICIPANTS,
// LOBBY_STATE_MODERATORS_ONLY
// }

View File

@ -12,8 +12,6 @@ package com.nextcloud.talk.models.json.conversations
import android.os.Parcelable import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject import com.bluelinelabs.logansquare.annotation.JsonObject
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.chat.ChatMessageJson import com.nextcloud.talk.models.json.chat.ChatMessageJson
import com.nextcloud.talk.models.json.converters.ConversationObjectTypeConverter import com.nextcloud.talk.models.json.converters.ConversationObjectTypeConverter
import com.nextcloud.talk.models.json.converters.EnumLobbyStateConverter import com.nextcloud.talk.models.json.converters.EnumLobbyStateConverter
@ -22,48 +20,45 @@ import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter
import com.nextcloud.talk.models.json.converters.EnumReadOnlyConversationConverter import com.nextcloud.talk.models.json.converters.EnumReadOnlyConversationConverter
import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter
import com.nextcloud.talk.models.json.participants.Participant.ParticipantType import com.nextcloud.talk.models.json.participants.Participant.ParticipantType
import com.nextcloud.talk.utils.ConversationUtils
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@JsonObject @JsonObject
data class Conversation( data class Conversation(
// @JsonField(name = ["id"])
// var roomId: String? = null,
@JsonField(name = ["token"]) @JsonField(name = ["token"])
var token: String? = null, var token: String = "",
@JsonField(name = ["name"]) @JsonField(name = ["name"])
var name: String? = null, var name: String = "",
@JsonField(name = ["displayName"]) @JsonField(name = ["displayName"])
var displayName: String? = null, var displayName: String = "",
@JsonField(name = ["description"]) @JsonField(name = ["description"])
var description: String? = null, var description: String = "",
@JsonField(name = ["type"], typeConverter = EnumRoomTypeConverter::class) @JsonField(name = ["type"], typeConverter = EnumRoomTypeConverter::class)
var type: ConversationEnums.ConversationType? = null, var type: ConversationEnums.ConversationType = ConversationEnums.ConversationType.DUMMY,
@JsonField(name = ["lastPing"]) @JsonField(name = ["lastPing"])
var lastPing: Long = 0, var lastPing: Long = 0,
@JsonField(name = ["participantType"], typeConverter = EnumParticipantTypeConverter::class) @JsonField(name = ["participantType"], typeConverter = EnumParticipantTypeConverter::class)
var participantType: ParticipantType? = null, var participantType: ParticipantType = ParticipantType.DUMMY,
@JsonField(name = ["hasPassword"]) @JsonField(name = ["hasPassword"])
var hasPassword: Boolean = false, var hasPassword: Boolean = false,
@JsonField(name = ["sessionId"]) @JsonField(name = ["sessionId"])
var sessionId: String? = null, var sessionId: String = "0",
@JsonField(name = ["actorId"]) @JsonField(name = ["actorId"])
var actorId: String? = null, var actorId: String = "",
@JsonField(name = ["actorType"]) @JsonField(name = ["actorType"])
var actorType: String? = null, var actorType: String = "",
var password: String? = null, var password: String? = null, //check if this can be removed.Does not belong to api response but is used internally?
@JsonField(name = ["isFavorite"]) @JsonField(name = ["isFavorite"])
var favorite: Boolean = false, var favorite: Boolean = false,
@ -81,19 +76,20 @@ data class Conversation(
var lastMessage: ChatMessageJson? = null, var lastMessage: ChatMessageJson? = null,
@JsonField(name = ["objectType"], typeConverter = ConversationObjectTypeConverter::class) @JsonField(name = ["objectType"], typeConverter = ConversationObjectTypeConverter::class)
var objectType: ConversationEnums.ObjectType? = null, var objectType: ConversationEnums.ObjectType = ConversationEnums.ObjectType.DEFAULT,
@JsonField(name = ["notificationLevel"], typeConverter = EnumNotificationLevelConverter::class) @JsonField(name = ["notificationLevel"], typeConverter = EnumNotificationLevelConverter::class)
var notificationLevel: ConversationEnums.NotificationLevel? = null, var notificationLevel: ConversationEnums.NotificationLevel = ConversationEnums.NotificationLevel.DEFAULT,
@JsonField(name = ["readOnly"], typeConverter = EnumReadOnlyConversationConverter::class) @JsonField(name = ["readOnly"], typeConverter = EnumReadOnlyConversationConverter::class)
var conversationReadOnlyState: ConversationEnums.ConversationReadOnlyState? = null, var conversationReadOnlyState: ConversationEnums.ConversationReadOnlyState =
ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_WRITE,
@JsonField(name = ["lobbyState"], typeConverter = EnumLobbyStateConverter::class) @JsonField(name = ["lobbyState"], typeConverter = EnumLobbyStateConverter::class)
var lobbyState: ConversationEnums.LobbyState? = null, var lobbyState: ConversationEnums.LobbyState = ConversationEnums.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS,
@JsonField(name = ["lobbyTimer"]) @JsonField(name = ["lobbyTimer"])
var lobbyTimer: Long? = null, var lobbyTimer: Long = 0,
@JsonField(name = ["lastReadMessage"]) @JsonField(name = ["lastReadMessage"])
var lastReadMessage: Int = 0, var lastReadMessage: Int = 0,
@ -111,16 +107,16 @@ data class Conversation(
var canStartCall: Boolean = false, var canStartCall: Boolean = false,
@JsonField(name = ["canLeaveConversation"]) @JsonField(name = ["canLeaveConversation"])
var canLeaveConversation: Boolean? = null, var canLeaveConversation: Boolean = true,
@JsonField(name = ["canDeleteConversation"]) @JsonField(name = ["canDeleteConversation"])
var canDeleteConversation: Boolean? = null, var canDeleteConversation: Boolean = false,
@JsonField(name = ["unreadMentionDirect"]) @JsonField(name = ["unreadMentionDirect"])
var unreadMentionDirect: Boolean? = null, var unreadMentionDirect: Boolean = false,
@JsonField(name = ["notificationCalls"]) @JsonField(name = ["notificationCalls"])
var notificationCalls: Int? = null, var notificationCalls: Int = 0,
@JsonField(name = ["permissions"]) @JsonField(name = ["permissions"])
var permissions: Int = 0, var permissions: Int = 0,
@ -129,107 +125,38 @@ data class Conversation(
var messageExpiration: Int = 0, var messageExpiration: Int = 0,
@JsonField(name = ["status"]) @JsonField(name = ["status"])
var status: String? = null, var status: String? = "",
@JsonField(name = ["statusIcon"]) @JsonField(name = ["statusIcon"])
var statusIcon: String? = null, var statusIcon: String? = "",
@JsonField(name = ["statusMessage"]) @JsonField(name = ["statusMessage"])
var statusMessage: String? = null, var statusMessage: String? = "",
@JsonField(name = ["statusClearAt"]) @JsonField(name = ["statusClearAt"])
var statusClearAt: Long? = 0, var statusClearAt: Long? = null,
@JsonField(name = ["callRecording"]) @JsonField(name = ["callRecording"])
var callRecording: Int = 0, var callRecording: Int = 0,
@JsonField(name = ["avatarVersion"]) @JsonField(name = ["avatarVersion"])
var avatarVersion: String? = "", var avatarVersion: String = "",
// Be aware that variables with "is" at the beginning will lead to the error: // Be aware that variables with "is" at the beginning will lead to the error:
// "@JsonField annotation can only be used on private fields if both getter and setter are present." // "@JsonField annotation can only be used on private fields if both getter and setter are present."
// Instead, name it with "has" at the beginning: isCustomAvatar -> hasCustomAvatar // Instead, name it with "has" at the beginning: isCustomAvatar -> hasCustomAvatar
@JsonField(name = ["isCustomAvatar"]) @JsonField(name = ["isCustomAvatar"])
var hasCustomAvatar: Boolean? = false, var hasCustomAvatar: Boolean = false,
@JsonField(name = ["callStartTime"]) @JsonField(name = ["callStartTime"])
var callStartTime: Long? = 0L, var callStartTime: Long = 0L,
@JsonField(name = ["recordingConsent"]) @JsonField(name = ["recordingConsent"])
var recordingConsentRequired: Int = 0, var recordingConsentRequired: Int = 0,
@JsonField(name = ["remoteServer"]) @JsonField(name = ["remoteServer"])
var remoteServer: String? = null, var remoteServer: String? = "",
@JsonField(name = ["remoteToken"]) @JsonField(name = ["remoteToken"])
var remoteToken: String? = null var remoteToken: String? = ""
) : Parcelable { ) : Parcelable
@Deprecated("Use ConversationUtil")
val isPublic: Boolean
get() = ConversationEnums.ConversationType.ROOM_PUBLIC_CALL == type
@Deprecated("Use ConversationUtil")
val isGuest: Boolean
get() = ParticipantType.GUEST == participantType ||
ParticipantType.GUEST_MODERATOR == participantType ||
ParticipantType.USER_FOLLOWING_LINK == participantType
@Deprecated("Use ConversationUtil")
val isParticipantOwnerOrModerator: Boolean
get() = ParticipantType.OWNER == participantType ||
ParticipantType.GUEST_MODERATOR == participantType ||
ParticipantType.MODERATOR == participantType
@Deprecated("Use ConversationUtil")
fun canModerate(conversationUser: User): Boolean {
return isParticipantOwnerOrModerator &&
!ConversationUtils.isLockedOneToOne(
ConversationModel.mapToConversationModel(this, conversationUser),
conversationUser.capabilities?.spreedCapability!!
) &&
type != ConversationEnums.ConversationType.FORMER_ONE_TO_ONE &&
!ConversationUtils.isNoteToSelfConversation(
ConversationModel.mapToConversationModel(this, conversationUser)
)
}
@Deprecated("Use ConversationUtil")
fun isLobbyViewApplicable(conversationUser: User): Boolean {
return !canModerate(conversationUser) &&
(
type == ConversationEnums.ConversationType.ROOM_GROUP_CALL ||
type == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL
)
}
@Deprecated("Use ConversationUtil")
fun isNameEditable(conversationUser: User): Boolean {
return canModerate(conversationUser) && ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != type
}
@Deprecated("Use ConversationUtil")
fun canLeave(): Boolean {
return if (canLeaveConversation != null) {
// Available since APIv2
canLeaveConversation!!
} else {
true
}
}
@Deprecated("Use ConversationUtil")
fun canDelete(conversationUser: User): Boolean {
return if (canDeleteConversation != null) {
// Available since APIv2
canDeleteConversation!!
} else {
canModerate(conversationUser)
// Fallback for APIv1
}
}
@Deprecated("Use ConversationUtil")
fun isNoteToSelfConversation(): Boolean {
return type == ConversationEnums.ConversationType.NOTE_TO_SELF
}
}

View File

@ -134,7 +134,7 @@ class ConversationsListBottomDialog(
) )
binding.conversationOperationLeave.visibility = setVisibleIf( binding.conversationOperationLeave.visibility = setVisibleIf(
ConversationUtils.canLeave(conversation) && conversation.canLeaveConversation &&
// leaving is by api not possible for the last user with moderator permissions. // leaving is by api not possible for the last user with moderator permissions.
// for now, hide this option for all moderators. // for now, hide this option for all moderators.
!ConversationUtils.canModerate(conversation, currentUser.capabilities!!.spreedCapability!!) !ConversationUtils.canModerate(conversation, currentUser.capabilities!!.spreedCapability!!)

View File

@ -295,6 +295,6 @@ object CapabilitiesUtil {
const val RECORDING_CONSENT_NOT_REQUIRED = 0 const val RECORDING_CONSENT_NOT_REQUIRED = 0
const val RECORDING_CONSENT_REQUIRED = 1 const val RECORDING_CONSENT_REQUIRED = 1
const val RECORDING_CONSENT_DEPEND_ON_CONVERSATION = 2 const val RECORDING_CONSENT_DEPEND_ON_CONVERSATION = 2
private const val SERVER_VERSION_MIN_SUPPORTED = 14 private const val SERVER_VERSION_MIN_SUPPORTED = 17
private const val SERVER_VERSION_SUPPORT_WARNING = 18 private const val SERVER_VERSION_SUPPORT_WARNING = 26
} }

View File

@ -55,25 +55,6 @@ object ConversationUtils {
ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation.type ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation.type
} }
fun canLeave(conversation: ConversationModel): Boolean {
return if (conversation.canLeaveConversation != null) {
// Available since APIv2
conversation.canLeaveConversation!!
} else {
true
}
}
fun canDelete(conversation: ConversationModel, spreedCapability: SpreedCapability): Boolean {
return if (conversation.canDeleteConversation != null) {
// Available since APIv2
conversation.canDeleteConversation!!
} else {
canModerate(conversation, spreedCapability)
// Fallback for APIv1
}
}
fun isNoteToSelfConversation(currentConversation: ConversationModel?): Boolean { fun isNoteToSelfConversation(currentConversation: ConversationModel?): Boolean {
return currentConversation != null && return currentConversation != null &&
currentConversation.type == ConversationEnums.ConversationType.NOTE_TO_SELF currentConversation.type == ConversationEnums.ConversationType.NOTE_TO_SELF

View File

@ -0,0 +1,157 @@
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.data.database.mappers.asEntity
import com.nextcloud.talk.data.database.mappers.asModel
import com.nextcloud.talk.data.database.model.ConversationEntity
import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.participants.Participant
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.io.File
@RunWith(Parameterized::class)
class ConversationConversionTest(
private val jsonFileName: String
) {
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{index}: testDeserialization({0})")
fun data(): List<String> {
return listOf(
"RoomOverallExample_APIv1.json",
"RoomOverallExample_APIv2.json",
"RoomOverallExample_APIv4.json"
)
}
}
@Test
fun testDeserialization() {
val jsonFile = File("src/test/resources/$jsonFileName")
val jsonString = jsonFile.readText()
val roomOverall: RoomOverall = LoganSquare.parse(jsonString, RoomOverall::class.java)
assertNotNull(roomOverall)
val conversationJson = roomOverall.ocs!!.data!!
assertNotNull(conversationJson)
val conversationEntity = conversationJson.asEntity(1)
assertNotNull(conversationEntity)
val apiVersion : Int = jsonFileName.substringAfterLast("APIv").first().digitToInt()
checkConversationEntity(conversationEntity, apiVersion)
val conversationModel = conversationEntity.asModel()
val conversationEntityConvertedBack = conversationModel.asEntity()
checkConversationEntity(conversationEntityConvertedBack, apiVersion)
}
private fun checkConversationEntity(
conversationEntity: ConversationEntity,
apiVersion: Int
) {
assertEquals("1@juwd77g6", conversationEntity.internalId)
assertEquals(1, conversationEntity.accountId)
// check if default values are set for the fields when API_V1 is used
if (apiVersion == 1) {
// default values for API_V2 fields
assertEquals(false, conversationEntity.canDeleteConversation)
assertEquals(true, conversationEntity.canLeaveConversation)
// default values for API_V3 fields
assertEquals("", conversationEntity.description)
assertEquals("", conversationEntity.actorType)
assertEquals("", conversationEntity.actorId)
assertEquals(0, conversationEntity.callFlag)
assertEquals(0, conversationEntity.lastCommonReadMessage)
// default values for API_V4 fields
assertEquals("", conversationEntity.avatarVersion)
assertEquals(0, conversationEntity.callStartTime)
assertEquals(0, conversationEntity.callRecording)
assertEquals(false, conversationEntity.unreadMentionDirect)
assertEquals("", conversationEntity.status)
assertEquals("", conversationEntity.statusIcon)
assertEquals("", conversationEntity.statusMessage)
assertEquals(null, conversationEntity.statusClearAt)
assertEquals("", conversationEntity.avatarVersion)
assertEquals(0, conversationEntity.callStartTime)
assertEquals(0, conversationEntity.callRecording)
}
if (apiVersion >= 1) {
assertEquals("juwd77g6", conversationEntity.token)
assertEquals(ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL, conversationEntity.type)
assertEquals("marcel", conversationEntity.name)
assertEquals("Marcel", conversationEntity.displayName)
assertEquals(Participant.ParticipantType.OWNER, conversationEntity.participantType)
assertEquals(
ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_WRITE,
conversationEntity.conversationReadOnlyState
)
assertEquals(1727185155, conversationEntity.lastPing)
assertEquals("0", conversationEntity.sessionId)
assertEquals(false, conversationEntity.hasPassword)
assertEquals(false, conversationEntity.hasCall)
assertEquals(true, conversationEntity.canStartCall)
assertEquals(1727098966, conversationEntity.lastActivity)
assertEquals(false, conversationEntity.favorite)
assertEquals(ConversationEnums.NotificationLevel.ALWAYS, conversationEntity.notificationLevel)
assertEquals(ConversationEnums.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS, conversationEntity.lobbyState)
assertEquals(0, conversationEntity.lobbyTimer)
assertEquals(0, conversationEntity.unreadMessages)
assertEquals(false, conversationEntity.unreadMention)
assertEquals(92320, conversationEntity.lastReadMessage)
assertNotNull(conversationEntity.lastMessage)
assertTrue(conversationEntity.lastMessage is String)
assertTrue(conversationEntity.lastMessage!!.contains("token"))
assertEquals(ConversationEnums.ObjectType.DEFAULT, conversationEntity.objectType)
}
if (apiVersion >= 2) {
assertEquals(false, conversationEntity.canDeleteConversation)
assertEquals(true, conversationEntity.canLeaveConversation)
}
if (apiVersion >= 3) {
assertEquals("test", conversationEntity.description)
// assertEquals("", conversationEntity.attendeeId) // Not implemented
// assertEquals("", conversationEntity.attendeePin) // Not implemented
assertEquals("users", conversationEntity.actorType)
assertEquals("marcel2", conversationEntity.actorId)
// assertEquals("", conversationEntity.listable) // Not implemented
assertEquals(0, conversationEntity.callFlag)
// assertEquals("", conversationEntity.sipEnabled) // Not implemented
// assertEquals("", conversationEntity.canEnableSIP) // Not implemented
assertEquals(92320, conversationEntity.lastCommonReadMessage)
}
if (apiVersion >= 4) {
assertEquals("143a9df3", conversationEntity.avatarVersion)
assertEquals(0, conversationEntity.callStartTime)
assertEquals(0, conversationEntity.callRecording)
assertEquals(false, conversationEntity.unreadMentionDirect)
// assertEquals(, conversationEntity.breakoutRoomMode) // Not implemented
// assertEquals(, conversationEntity.breakoutRoomStatus) // Not implemented
assertEquals("away", conversationEntity.status)
assertEquals("👻", conversationEntity.statusIcon)
assertEquals("buuuuh", conversationEntity.statusMessage)
assertEquals(null, conversationEntity.statusClearAt)
assertEquals("143a9df3", conversationEntity.avatarVersion)
// assertEquals("", conversationEntity.isCustomAvatar) // Not implemented
assertEquals(0, conversationEntity.callStartTime)
assertEquals(0, conversationEntity.callRecording)
// assertEquals("", conversationEntity.recordingConsent) // Not implemented
// assertEquals("", conversationEntity.mentionPermissions) // Not implemented
// assertEquals("", conversationEntity.isArchived) // Not implemented
}
}
}

View File

@ -0,0 +1,66 @@
{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 200,
"message": "OK"
},
"data": {
"id": 6410,
"token": "juwd77g6",
"type": 1,
"name": "marcel",
"displayName": "Marcel",
"objectType": "",
"objectId": "",
"participantType": 1,
"participantInCall": false,
"participantFlags": 0,
"readOnly": 0,
"count": 0,
"hasPassword": false,
"hasCall": false,
"canStartCall": true,
"lastActivity": 1727098966,
"lastReadMessage": 92320,
"unreadMessages": 0,
"unreadMention": false,
"isFavorite": false,
"notificationLevel": 1,
"lobbyState": 0,
"lobbyTimer": 0,
"lastPing": 1727185155,
"sessionId": "0",
"participants": {
"marcel": {
"name": "marcel",
"type": 1,
"call": 0,
"sessionId": "tonIuryMpwk5mR2h6lFqPC6M8m6hxdT4YN9X9I1v4i2LOJb9r3FzANx\/7HT0j6r8fzBFPJqnOrT\/mdvHzFlfqQsr6Gxo0\/aLkDfIRL32XlGIzficfLaBNCsyctPxwgHv0fwmJUOS2i0ONdC+QWyTJ4bpYw1Ch9hiybxCgEtss3xy9fbjPwWj3gLvpMpcH0OuNkfEwHqByhpiAb3xuu60\/6j671uwYiGe2Ba7PzLFJ3LVuVRXXTbeaZlVwTAioOu"
},
"Testuser.Lastname": {
"name": "Testuser",
"type": 1,
"call": 0,
"sessionId": "0"
}
},
"numGuests": 0,
"guestList": "",
"lastMessage": {
"id": 175430,
"token": "juwd77g6",
"actorType": "users",
"actorId": "marcel",
"actorDisplayName": "Marcel",
"timestamp": 1727182442,
"message": "test",
"messageParameters": [],
"systemMessage": "",
"messageType": "comment",
"isReplyable": true,
"referenceId": ""
}
}
}
}

View File

@ -0,0 +1,51 @@
{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 200,
"message": "OK"
},
"data": {
"id": 6410,
"token": "juwd77g6",
"type": 1,
"name": "marcel",
"displayName": "Marcel",
"objectType": "",
"objectId": "",
"participantType": 1,
"participantFlags": 0,
"readOnly": 0,
"hasPassword": false,
"hasCall": false,
"canStartCall": true,
"lastActivity": 1727098966,
"lastReadMessage": 92320,
"unreadMessages": 0,
"unreadMention": false,
"isFavorite": false,
"canLeaveConversation": true,
"canDeleteConversation": false,
"notificationLevel": 1,
"lobbyState": 0,
"lobbyTimer": 0,
"lastPing": 1727185155,
"sessionId": "0",
"guestList": "",
"lastMessage": {
"id": 175430,
"token": "juwd77g6",
"actorType": "users",
"actorId": "marcel",
"actorDisplayName": "Marcel",
"timestamp": 1727182442,
"message": "test",
"messageParameters": [],
"systemMessage": "",
"messageType": "comment",
"isReplyable": true,
"referenceId": ""
}
}
}
}

View File

@ -0,0 +1,83 @@
{
"ocs": {
"meta": {
"status": "ok",
"statuscode": 200,
"message": "OK"
},
"data": {
"id": 181,
"token": "juwd77g6",
"type": 1,
"name": "marcel",
"displayName": "Marcel",
"objectType": "",
"objectId": "",
"participantType": 1,
"participantFlags": 0,
"readOnly": 0,
"hasPassword": false,
"hasCall": false,
"callStartTime": 0,
"callRecording": 0,
"canStartCall": true,
"lastActivity": 1727098966,
"lastReadMessage": 92320,
"unreadMessages": 0,
"unreadMention": false,
"unreadMentionDirect": false,
"isFavorite": false,
"canLeaveConversation": true,
"canDeleteConversation": false,
"notificationLevel": 1,
"notificationCalls": 1,
"lobbyState": 0,
"lobbyTimer": 0,
"lastPing": 1727185155,
"sessionId": "0",
"lastMessage": {
"id": 92320,
"token": "3853979093",
"actorType": "users",
"actorId": "marcel",
"actorDisplayName": "Marcel",
"timestamp": 1726673204,
"message": "Test",
"messageParameters": [],
"systemMessage": "",
"messageType": "comment",
"isReplyable": true,
"referenceId": "",
"reactions": {},
"expirationTimestamp": 0,
"markdown": true
},
"sipEnabled": 0,
"actorType": "users",
"actorId": "marcel2",
"attendeeId": 5810,
"permissions": 254,
"attendeePermissions": 0,
"callPermissions": 0,
"defaultPermissions": 0,
"canEnableSIP": false,
"attendeePin": "",
"description": "test",
"lastCommonReadMessage": 92320,
"listable": 0,
"callFlag": 0,
"messageExpiration": 0,
"avatarVersion": "143a9df3",
"isCustomAvatar": false,
"breakoutRoomMode": 0,
"breakoutRoomStatus": 0,
"recordingConsent": 0,
"mentionPermissions": 0,
"isArchived": false,
"status": "away",
"statusIcon": "👻",
"statusMessage": "buuuuh",
"statusClearAt": null
}
}
}