diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationType.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationTypeForCall.kt similarity index 95% rename from data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationType.kt rename to data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationTypeForCall.kt index ca39a3d820c..8f010c91fbd 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationType.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationTypeForCall.kt @@ -22,7 +22,7 @@ package com.wire.kalium.logic.data.call * [OneOnOne] for a 1:1 call * [Conference] for a group cal */ -enum class ConversationType { +enum class ConversationTypeForCall { OneOnOne, Conference, ConferenceMls, diff --git a/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/CallManagerTest.kt b/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/CallManagerTest.kt index 9ef3f1fa6c6..7b87ab28bc6 100644 --- a/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/CallManagerTest.kt +++ b/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/CallManagerTest.kt @@ -48,6 +48,7 @@ import kotlin.test.BeforeTest import kotlin.test.Ignore import kotlin.test.Test import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.network.NetworkStateObserver import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString import kotlinx.datetime.Instant @@ -96,6 +97,9 @@ class CallManagerTest { @Mock private val networkStateObserver = mock(NetworkStateObserver::class) + @Mock + private val getCallConversationType = mock(GetCallConversationTypeProvider::class) + private val dispatcher = TestKaliumDispatcher private lateinit var callManagerImpl: CallManagerImpl @@ -119,6 +123,7 @@ class CallManagerTest { qualifiedIdMapper = qualifiedIdMapper, videoStateChecker = videoStateChecker, callMapper = callMapper, + getCallConversationType = getCallConversationType, conversationClientsInCallUpdater = conversationClientsInCallUpdater, networkStateObserver = networkStateObserver, kaliumConfigs = kaliumConfigs, diff --git a/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCallTest.kt b/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCallTest.kt index 4d70e012a59..c2c58431013 100644 --- a/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCallTest.kt +++ b/logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCallTest.kt @@ -20,7 +20,7 @@ package com.wire.kalium.logic.feature.call.scenario import com.wire.kalium.calling.types.Uint32_t import com.wire.kalium.calling.ConversationTypeCalling import com.wire.kalium.logic.data.call.CallRepository -import com.wire.kalium.logic.data.call.ConversationType +import com.wire.kalium.logic.data.call.ConversationTypeForCall import com.wire.kalium.logic.data.call.mapper.CallMapperImpl import com.wire.kalium.logic.data.id.QualifiedIdMapperImpl import com.wire.kalium.logic.data.call.CallStatus @@ -33,7 +33,6 @@ import io.mockative.coVerify import io.mockative.eq import io.mockative.mock import io.mockative.once -import io.mockative.verify import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest @@ -63,7 +62,7 @@ class OnIncomingCallTest { coVerify { arrangement.callRepository.createCall( eq(TestConversation.CONVERSATION.id), - eq(ConversationType.Conference), + eq(ConversationTypeForCall.Conference), eq(CallStatus.INCOMING), eq(TestUser.USER_ID.toString()), eq(true), @@ -93,7 +92,7 @@ class OnIncomingCallTest { coVerify { arrangement.callRepository.createCall( eq(TestConversation.CONVERSATION.id), - eq(ConversationType.Conference), + eq(ConversationTypeForCall.Conference), eq(CallStatus.STILL_ONGOING), eq(TestUser.USER_ID.toString()), eq(true), diff --git a/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt b/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt index baa54a0ced2..ca5b9648249 100644 --- a/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt +++ b/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt @@ -18,6 +18,7 @@ package com.wire.kalium.logic.feature.call +import com.wire.kalium.calling.ConversationTypeCalling import com.wire.kalium.logic.data.call.CallClientList import com.wire.kalium.logic.data.call.CallType import com.wire.kalium.logic.data.call.EpochInfo @@ -38,6 +39,7 @@ class CallManagerImpl : CallManager { override suspend fun startCall( conversationId: ConversationId, callType: CallType, + conversationTypeCalling: ConversationTypeCalling, isAudioCbr: Boolean ) { TODO("Not yet implemented") diff --git a/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt b/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt index 31fbbb4537e..f2a4229f634 100644 --- a/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt +++ b/logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt @@ -32,6 +32,7 @@ import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.network.NetworkStateObserver @@ -51,6 +52,7 @@ actual class GlobalCallManager { qualifiedIdMapper: QualifiedIdMapper, videoStateChecker: VideoStateChecker, conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, kaliumConfigs: KaliumConfigs ): CallManager { diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt index 965b124db8c..614bbfed39a 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt @@ -23,6 +23,7 @@ package com.wire.kalium.logic.feature.call import com.sun.jna.Pointer import com.wire.kalium.calling.CallTypeCalling import com.wire.kalium.calling.Calling +import com.wire.kalium.calling.ConversationTypeCalling import com.wire.kalium.calling.callbacks.ConstantBitRateStateChangeHandler import com.wire.kalium.calling.callbacks.MetricsHandler import com.wire.kalium.calling.callbacks.ReadyHandler @@ -36,7 +37,6 @@ import com.wire.kalium.logic.data.call.CallClientList import com.wire.kalium.logic.data.call.CallRepository import com.wire.kalium.logic.data.call.CallStatus import com.wire.kalium.logic.data.call.CallType -import com.wire.kalium.logic.data.call.ConversationType import com.wire.kalium.logic.data.call.EpochInfo import com.wire.kalium.logic.data.call.Participant import com.wire.kalium.logic.data.call.TestVideoType @@ -71,6 +71,7 @@ import com.wire.kalium.logic.feature.call.scenario.OnRequestNewEpoch import com.wire.kalium.logic.feature.call.scenario.OnSFTRequest import com.wire.kalium.logic.feature.call.scenario.OnSendOTR import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.functional.fold @@ -108,6 +109,7 @@ class CallManagerImpl internal constructor( private val videoStateChecker: VideoStateChecker, private val conversationClientsInCallUpdater: ConversationClientsInCallUpdater, private val networkStateObserver: NetworkStateObserver, + private val getCallConversationType: GetCallConversationTypeProvider, private val kaliumConfigs: KaliumConfigs, private val mediaManagerService: MediaManagerService, private val flowManagerService: FlowManagerService, @@ -249,9 +251,8 @@ class CallManagerImpl internal constructor( message.conversationId } - val type = conversationRepository.getConversationById(targetConversationId)?.let { - callMapper.fromConversationToConversationType(it) - } ?: ConversationType.Unknown + val callConversationType = getCallConversationType(targetConversationId) + val type = callMapper.toConversationType(callConversationType) wcall_recv_msg( inst = deferredHandle.await(), @@ -271,6 +272,7 @@ class CallManagerImpl internal constructor( override suspend fun startCall( conversationId: ConversationId, callType: CallType, + conversationTypeCalling: ConversationTypeCalling, isAudioCbr: Boolean ) { callingLogger.d( @@ -278,9 +280,7 @@ class CallManagerImpl internal constructor( "${conversationId.toLogString()}.." ) val isCameraOn = callType == CallType.VIDEO - val type = conversationRepository.getConversationById(conversationId)?.let { - callMapper.fromConversationToConversationType(it) - } ?: ConversationType.Unknown + val type = callMapper.toConversationType(conversationTypeCalling) callRepository.createCall( conversationId = conversationId, @@ -294,13 +294,12 @@ class CallManagerImpl internal constructor( withCalling { val avsCallType = callMapper.toCallTypeCalling(callType) - val avsConversationType = callMapper.toConversationTypeCalling(type) // TODO: Handle response. Possible failure? wcall_start( deferredHandle.await(), federatedIdMapper.parseToFederatedId(conversationId), avsCallType.avsValue, - avsConversationType.avsValue, + conversationTypeCalling.avsValue, isAudioCbr.toInt() ) diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt index 939ce6286ce..f88f5494be8 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt @@ -37,6 +37,7 @@ import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.util.CurrentPlatform @@ -88,6 +89,7 @@ actual class GlobalCallManager( qualifiedIdMapper: QualifiedIdMapper, videoStateChecker: VideoStateChecker, conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, kaliumConfigs: KaliumConfigs ): CallManager { @@ -105,6 +107,7 @@ actual class GlobalCallManager( qualifiedIdMapper = qualifiedIdMapper, videoStateChecker = videoStateChecker, conversationClientsInCallUpdater = conversationClientsInCallUpdater, + getCallConversationType = getCallConversationType, networkStateObserver = networkStateObserver, mediaManagerService = mediaManager, flowManagerService = flowManager, diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCall.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCall.kt index b626d045bb9..4c23e8fe868 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCall.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnIncomingCall.kt @@ -25,7 +25,7 @@ import com.wire.kalium.logger.obfuscateId import com.wire.kalium.logic.callingLogger import com.wire.kalium.logic.data.call.mapper.CallMapper import com.wire.kalium.logic.data.call.CallRepository -import com.wire.kalium.logic.data.call.ConversationType +import com.wire.kalium.logic.data.call.ConversationTypeForCall import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.call.CallStatus import com.wire.kalium.logic.featureFlags.KaliumConfigs @@ -55,7 +55,7 @@ class OnIncomingCall( " | UserId: ${userId.obfuscateId()} | shouldRing: $shouldRing | type: $conversationType" ) val mappedConversationType = callMapper.fromIntToConversationType(conversationType) - val isMuted = setOf(ConversationType.Conference, ConversationType.ConferenceMls).contains(mappedConversationType) + val isMuted = setOf(ConversationTypeForCall.Conference, ConversationTypeForCall.ConferenceMls).contains(mappedConversationType) val status = if (shouldRing) CallStatus.INCOMING else CallStatus.STILL_ONGOING val qualifiedConversationId = qualifiedIdMapper.fromStringToQualifiedID(conversationId) scope.launch { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallRepository.kt index e1a92783307..6667c304c78 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallRepository.kt @@ -107,7 +107,7 @@ interface CallRepository { @Suppress("LongParameterList") suspend fun createCall( conversationId: ConversationId, - type: ConversationType, + type: ConversationTypeForCall, status: CallStatus, callerId: String, isMuted: Boolean, @@ -189,7 +189,7 @@ internal class CallDataSource( @Suppress("LongMethod", "NestedBlockDepth") override suspend fun createCall( conversationId: ConversationId, - type: ConversationType, + type: ConversationTypeForCall, status: CallStatus, callerId: String, isMuted: Boolean, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/mapper/CallMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/mapper/CallMapper.kt index 4693b6483fa..d9f8cf80c02 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/mapper/CallMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/mapper/CallMapper.kt @@ -24,7 +24,7 @@ import com.wire.kalium.calling.VideoStateCalling import com.wire.kalium.logic.data.call.CallClientList import com.wire.kalium.logic.data.call.CallMetadata import com.wire.kalium.logic.data.call.CallType -import com.wire.kalium.logic.data.call.ConversationType +import com.wire.kalium.logic.data.call.ConversationTypeForCall import com.wire.kalium.logic.data.call.VideoState import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.conversation.Conversation @@ -41,17 +41,21 @@ import com.wire.kalium.persistence.dao.conversation.ConversationEntity interface CallMapper { fun toCallTypeCalling(callType: CallType): CallTypeCalling - fun toConversationTypeCalling(conversationType: ConversationType): ConversationTypeCalling - fun fromIntToConversationType(conversationType: Int): ConversationType + fun toConversationTypeCalling(conversationTypeForCall: ConversationTypeForCall): ConversationTypeCalling + fun toConversationType(conversationTypeCalling: ConversationTypeCalling): ConversationTypeForCall + fun fromIntToConversationType(conversationType: Int): ConversationTypeForCall fun fromIntToCallingVideoState(videStateInt: Int): VideoStateCalling fun toVideoStateCalling(videoState: VideoState): VideoStateCalling - fun fromConversationToConversationType(conversation: Conversation): ConversationType + fun fromConversationTypeToConversationTypeForCall( + conversationType: Conversation.Type, + conversationProtocol: Conversation.ProtocolInfo + ): ConversationTypeForCall @Suppress("LongParameterList") fun toCallEntity( conversationId: ConversationId, id: String, - type: ConversationType, + type: ConversationTypeForCall, status: CallStatus, conversationType: Conversation.Type, callerId: UserId @@ -81,21 +85,30 @@ class CallMapperImpl( } } - override fun toConversationTypeCalling(conversationType: ConversationType): ConversationTypeCalling { - return when (conversationType) { - ConversationType.OneOnOne -> ConversationTypeCalling.OneOnOne - ConversationType.Conference -> ConversationTypeCalling.Conference - ConversationType.ConferenceMls -> ConversationTypeCalling.ConferenceMls + override fun toConversationTypeCalling(conversationTypeForCall: ConversationTypeForCall): ConversationTypeCalling { + return when (conversationTypeForCall) { + ConversationTypeForCall.OneOnOne -> ConversationTypeCalling.OneOnOne + ConversationTypeForCall.Conference -> ConversationTypeCalling.Conference + ConversationTypeForCall.ConferenceMls -> ConversationTypeCalling.ConferenceMls else -> ConversationTypeCalling.Unknown } } - override fun fromIntToConversationType(conversationType: Int): ConversationType { + override fun toConversationType(conversationTypeCalling: ConversationTypeCalling): ConversationTypeForCall { + return when (conversationTypeCalling) { + ConversationTypeCalling.OneOnOne -> ConversationTypeForCall.OneOnOne + ConversationTypeCalling.Conference -> ConversationTypeForCall.Conference + ConversationTypeCalling.ConferenceMls -> ConversationTypeForCall.ConferenceMls + else -> ConversationTypeForCall.Unknown + } + } + + override fun fromIntToConversationType(conversationType: Int): ConversationTypeForCall { return when (conversationType) { - ConversationTypeCalling.OneOnOne.avsValue -> ConversationType.OneOnOne - ConversationTypeCalling.Conference.avsValue -> ConversationType.Conference - ConversationTypeCalling.ConferenceMls.avsValue -> ConversationType.ConferenceMls - else -> ConversationType.Unknown + ConversationTypeCalling.OneOnOne.avsValue -> ConversationTypeForCall.OneOnOne + ConversationTypeCalling.Conference.avsValue -> ConversationTypeForCall.Conference + ConversationTypeCalling.ConferenceMls.avsValue -> ConversationTypeForCall.ConferenceMls + else -> ConversationTypeForCall.Unknown } } @@ -120,23 +133,26 @@ class CallMapperImpl( VideoState.UNKNOWN -> VideoStateCalling.UNKNOWN } - override fun fromConversationToConversationType(conversation: Conversation): ConversationType = - when (conversation.type) { + override fun fromConversationTypeToConversationTypeForCall( + conversationType: Conversation.Type, + conversationProtocol: Conversation.ProtocolInfo + ): ConversationTypeForCall = + when (conversationType) { Conversation.Type.GROUP -> { - when (conversation.protocol) { - is Conversation.ProtocolInfo.MLS -> ConversationType.ConferenceMls + when (conversationProtocol) { + is Conversation.ProtocolInfo.MLS -> ConversationTypeForCall.ConferenceMls is Conversation.ProtocolInfo.Proteus, - is Conversation.ProtocolInfo.Mixed -> ConversationType.Conference + is Conversation.ProtocolInfo.Mixed -> ConversationTypeForCall.Conference } } - Conversation.Type.ONE_ON_ONE -> ConversationType.OneOnOne - else -> ConversationType.Unknown + Conversation.Type.ONE_ON_ONE -> ConversationTypeForCall.OneOnOne + else -> ConversationTypeForCall.Unknown } override fun toCallEntity( conversationId: ConversationId, id: String, - type: ConversationType, + type: ConversationTypeForCall, status: CallStatus, conversationType: Conversation.Type, callerId: UserId @@ -179,11 +195,11 @@ class CallMapperImpl( else -> ConversationEntity.Type.ONE_ON_ONE } - private fun toCallEntityType(conversationType: ConversationType): CallEntity.Type = when (conversationType) { - ConversationType.OneOnOne -> CallEntity.Type.ONE_ON_ONE - ConversationType.Conference -> CallEntity.Type.CONFERENCE - ConversationType.ConferenceMls -> CallEntity.Type.MLS_CONFERENCE - ConversationType.Unknown -> CallEntity.Type.UNKNOWN + private fun toCallEntityType(conversationTypeForCall: ConversationTypeForCall): CallEntity.Type = when (conversationTypeForCall) { + ConversationTypeForCall.OneOnOne -> CallEntity.Type.ONE_ON_ONE + ConversationTypeForCall.Conference -> CallEntity.Type.CONFERENCE + ConversationTypeForCall.ConferenceMls -> CallEntity.Type.MLS_CONFERENCE + ConversationTypeForCall.Unknown -> CallEntity.Type.UNKNOWN } override fun toConversationType(conversationType: ConversationEntity.Type): Conversation.Type = when (conversationType) { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt index 5823ffdf930..36a933570ac 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationMapper.kt @@ -83,6 +83,8 @@ interface ConversationMapper { fun verificationStatusFromEntity(verificationStatus: ConversationEntity.VerificationStatus): Conversation.VerificationStatus fun legalHoldStatusToEntity(legalHoldStatus: Conversation.LegalHoldStatus): ConversationEntity.LegalHoldStatus fun legalHoldStatusFromEntity(legalHoldStatus: ConversationEntity.LegalHoldStatus): Conversation.LegalHoldStatus + + fun fromConversationEntityType(type: ConversationEntity.Type): Conversation.Type } @Suppress("TooManyFunctions", "LongParameterList") @@ -454,6 +456,10 @@ internal class ConversationMapperImpl( ConversationEntity.LegalHoldStatus.DEGRADED -> Conversation.LegalHoldStatus.DEGRADED ConversationEntity.LegalHoldStatus.DISABLED -> Conversation.LegalHoldStatus.DISABLED } + + override fun fromConversationEntityType(type: ConversationEntity.Type): Conversation.Type { + return type.fromDaoModelToType() + } } internal fun ConversationResponse.toConversationType(selfUserTeamId: TeamId?): ConversationEntity.Type { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt index 1bd8962da59..6c45160d352 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt @@ -134,6 +134,7 @@ interface ConversationRepository { suspend fun fetchConversationIfUnknown(conversationID: ConversationId): Either suspend fun observeById(conversationId: ConversationId): Flow> suspend fun getConversationById(conversationId: ConversationId): Conversation? + suspend fun getConversationTypeById(conversationId: ConversationId): Either suspend fun observeCacheDetailsById(conversationId: ConversationId): Either> suspend fun detailsById(conversationId: ConversationId): Either suspend fun baseInfoById(conversationId: ConversationId): Either @@ -621,6 +622,13 @@ internal class ConversationDataSource internal constructor( conversationEntity?.let { conversationMapper.fromDaoModel(it) } }.firstOrNull() + override suspend fun getConversationTypeById(conversationId: ConversationId): Either = + wrapStorageRequest { + conversationDAO.getConversationTypeById(conversationId.toDao())?.let { + conversationMapper.fromConversationEntityType(it) + } + } + override suspend fun observeCacheDetailsById(conversationId: ConversationId): Either> = wrapStorageRequest { conversationDAO.observeConversationDetailsById(conversationId.toDao()) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt index 6e58e177dff..950251a8792 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt @@ -132,7 +132,7 @@ class FeatureConfigMapperImpl : FeatureConfigMapper { override fun fromDTO(data: FeatureConfigData.ConferenceCalling): ConferenceCallingModel = ConferenceCallingModel( status = fromDTO(data.status), - useSFTForOneOnOneCalls = data.config.useSFTForOneToOneCalls + useSFTForOneOnOneCalls = data.config?.useSFTForOneToOneCalls ?: false ) override fun fromDTO(data: FeatureConfigData.E2EI?): E2EIModel = diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 37a1d77697a..b86e7c0da9e 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -171,6 +171,8 @@ import com.wire.kalium.logic.feature.call.CallsScope import com.wire.kalium.logic.feature.call.GlobalCallManager import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdaterImpl +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProviderImpl import com.wire.kalium.logic.feature.call.usecase.UpdateConversationClientsForCurrentCallUseCase import com.wire.kalium.logic.feature.call.usecase.UpdateConversationClientsForCurrentCallUseCaseImpl import com.wire.kalium.logic.feature.client.ClientScope @@ -1233,6 +1235,7 @@ class UserSessionScope internal constructor( videoStateChecker = videoStateChecker, callMapper = callMapper, conversationClientsInCallUpdater = conversationClientsInCallUpdater, + getCallConversationType = getCallConversationType, networkStateObserver = networkStateObserver, kaliumConfigs = kaliumConfigs ) @@ -1253,6 +1256,14 @@ class UserSessionScope internal constructor( federatedIdMapper = federatedIdMapper ) + private val getCallConversationType: GetCallConversationTypeProvider by lazy { + GetCallConversationTypeProviderImpl( + userConfigRepository = userConfigRepository, + conversationRepository = conversationRepository, + callMapper = callMapper + ) + } + private val updateConversationClientsForCurrentCall: Lazy get() = lazy { UpdateConversationClientsForCurrentCallUseCaseImpl(callRepository, conversationClientsInCallUpdater) @@ -1950,18 +1961,19 @@ class UserSessionScope internal constructor( val calls: CallsScope get() = CallsScope( - callManager, - callRepository, - conversationRepository, - userRepository, - flowManagerService, - mediaManagerService, - syncManager, - qualifiedIdMapper, - clientIdProvider, - userConfigRepository, - conversationClientsInCallUpdater, - kaliumConfigs + callManager = callManager, + callRepository = callRepository, + conversationRepository = conversationRepository, + userRepository = userRepository, + flowManagerService = flowManagerService, + mediaManagerService = mediaManagerService, + syncManager = syncManager, + qualifiedIdMapper = qualifiedIdMapper, + currentClientIdProvider = clientIdProvider, + userConfigRepository = userConfigRepository, + getCallConversationType = getCallConversationType, + conversationClientsInCallUpdater = conversationClientsInCallUpdater, + kaliumConfigs = kaliumConfigs ) val connection: ConnectionScope diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallManager.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallManager.kt index 402742a7454..08849709965 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallManager.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallManager.kt @@ -18,6 +18,7 @@ package com.wire.kalium.logic.feature.call +import com.wire.kalium.calling.ConversationTypeCalling import com.wire.kalium.logic.data.call.CallClientList import com.wire.kalium.logic.data.call.CallType import com.wire.kalium.logic.data.call.EpochInfo @@ -37,6 +38,7 @@ interface CallManager { suspend fun startCall( conversationId: ConversationId, callType: CallType, + conversationTypeCalling: ConversationTypeCalling, isAudioCbr: Boolean ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallsScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallsScope.kt index 0e57586b978..05fc2c1d16d 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallsScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallsScope.kt @@ -25,24 +25,22 @@ import com.wire.kalium.logic.data.call.CallingParticipantsOrderImpl import com.wire.kalium.logic.data.call.ParticipantsFilterImpl import com.wire.kalium.logic.data.call.ParticipantsOrderByNameImpl import com.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.user.UserRepository -import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.call.usecase.AnswerCallUseCase import com.wire.kalium.logic.feature.call.usecase.AnswerCallUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater -import com.wire.kalium.logic.feature.call.usecase.SetTestPreviewActiveUseCase -import com.wire.kalium.logic.feature.call.usecase.SetTestVideoTypeUseCase -import com.wire.kalium.logic.feature.call.usecase.SetTestRemoteVideoStatesUseCase -import com.wire.kalium.logic.feature.call.usecase.EndCallResultListenerImpl import com.wire.kalium.logic.feature.call.usecase.EndCallOnConversationChangeUseCase import com.wire.kalium.logic.feature.call.usecase.EndCallOnConversationChangeUseCaseImpl +import com.wire.kalium.logic.feature.call.usecase.EndCallResultListenerImpl import com.wire.kalium.logic.feature.call.usecase.EndCallUseCase import com.wire.kalium.logic.feature.call.usecase.EndCallUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.FlipToBackCameraUseCase import com.wire.kalium.logic.feature.call.usecase.FlipToFrontCameraUseCase import com.wire.kalium.logic.feature.call.usecase.GetAllCallsWithSortedParticipantsUseCase import com.wire.kalium.logic.feature.call.usecase.GetAllCallsWithSortedParticipantsUseCaseImpl +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.call.usecase.GetIncomingCallsUseCase import com.wire.kalium.logic.feature.call.usecase.GetIncomingCallsUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.IsCallRunningUseCase @@ -56,13 +54,16 @@ import com.wire.kalium.logic.feature.call.usecase.ObserveEndCallDueToConversatio import com.wire.kalium.logic.feature.call.usecase.ObserveEndCallDueToConversationDegradationUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCase import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCaseImpl -import com.wire.kalium.logic.feature.call.usecase.ObserveOutgoingCallUseCase -import com.wire.kalium.logic.feature.call.usecase.ObserveOutgoingCallUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.ObserveOngoingCallsUseCase import com.wire.kalium.logic.feature.call.usecase.ObserveOngoingCallsUseCaseImpl +import com.wire.kalium.logic.feature.call.usecase.ObserveOutgoingCallUseCase +import com.wire.kalium.logic.feature.call.usecase.ObserveOutgoingCallUseCaseImpl import com.wire.kalium.logic.feature.call.usecase.ObserveSpeakerUseCase import com.wire.kalium.logic.feature.call.usecase.RejectCallUseCase import com.wire.kalium.logic.feature.call.usecase.RequestVideoStreamsUseCase +import com.wire.kalium.logic.feature.call.usecase.SetTestPreviewActiveUseCase +import com.wire.kalium.logic.feature.call.usecase.SetTestRemoteVideoStatesUseCase +import com.wire.kalium.logic.feature.call.usecase.SetTestVideoTypeUseCase import com.wire.kalium.logic.feature.call.usecase.SetVideoPreviewUseCase import com.wire.kalium.logic.feature.call.usecase.StartCallUseCase import com.wire.kalium.logic.feature.call.usecase.TurnLoudSpeakerOffUseCase @@ -91,6 +92,7 @@ class CallsScope internal constructor( private val currentClientIdProvider: CurrentClientIdProvider, private val userConfigRepository: UserConfigRepository, private val conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + private val getCallConversationType: GetCallConversationTypeProvider, private val kaliumConfigs: KaliumConfigs, internal val dispatcher: KaliumDispatcher = KaliumDispatcherImpl ) { @@ -130,6 +132,7 @@ class CallsScope internal constructor( syncManager = syncManager, callRepository = callRepository, answerCall = answerCall, + getCallConversationType = getCallConversationType, kaliumConfigs = kaliumConfigs ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt index 20de4e912ae..e4aa653be79 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt @@ -32,6 +32,7 @@ import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater +import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.network.NetworkStateObserver @@ -52,6 +53,7 @@ expect class GlobalCallManager { qualifiedIdMapper: QualifiedIdMapper, videoStateChecker: VideoStateChecker, conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, kaliumConfigs: KaliumConfigs ): CallManager diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProvider.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProvider.kt new file mode 100644 index 00000000000..de362ab863d --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProvider.kt @@ -0,0 +1,71 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.call.usecase + +import com.wire.kalium.calling.ConversationTypeCalling +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.call.ConversationTypeForCall +import com.wire.kalium.logic.data.call.mapper.CallMapper +import com.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.ConversationId +import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.functional.map + +/** + * This class is responsible for providing the conversation type for a call. + */ +interface GetCallConversationTypeProvider { + suspend operator fun invoke(conversationId: ConversationId): ConversationTypeCalling +} + +internal class GetCallConversationTypeProviderImpl( + private val userConfigRepository: UserConfigRepository, + private val conversationRepository: ConversationRepository, + private val callMapper: CallMapper, +) : GetCallConversationTypeProvider { + override suspend fun invoke(conversationId: ConversationId): ConversationTypeCalling { + + val type = conversationRepository.getConversationTypeById(conversationId).fold( + { ConversationTypeForCall.Unknown }, + { + conversationRepository.getConversationProtocolInfo(conversationId).fold({ + ConversationTypeForCall.Unknown + }, { protocol -> + callMapper.fromConversationTypeToConversationTypeForCall(it, protocol) + }) + } + ) + + val callConversationType = callMapper.toConversationTypeCalling(type) + + return userConfigRepository.shouldUseSFTForOneOnOneCalls().fold({ + callConversationType + }, { shouldUseSFTForOneOnOneCalls -> + if (shouldUseSFTForOneOnOneCalls) { + userConfigRepository.isMLSEnabled().map { + if (it) { + return@fold ConversationTypeCalling.ConferenceMls + } + } + ConversationTypeCalling.Conference + } else { + callConversationType + } + }) + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCase.kt index 6a1c1dff931..b7b7e353acf 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCase.kt @@ -36,11 +36,13 @@ import kotlinx.coroutines.withContext * Will wait for sync to finish or fail if it is pending, * and return one [Result]. */ +@Suppress("LongParameterList") class StartCallUseCase internal constructor( private val callManager: Lazy, private val syncManager: SyncManager, private val kaliumConfigs: KaliumConfigs, private val callRepository: CallRepository, + private val getCallConversationType: GetCallConversationTypeProvider, private val answerCall: AnswerCallUseCase, private val dispatchers: KaliumDispatcher = KaliumDispatcherImpl ) { @@ -60,9 +62,13 @@ class StartCallUseCase internal constructor( return@withContext Result.Success } } + + val callConversationType = getCallConversationType(conversationId) + callManager.value.startCall( conversationId = conversationId, callType = callType, + conversationTypeCalling = callConversationType, isAudioCbr = kaliumConfigs.forceConstantBitrateCalls ) return@withContext Result.Success diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallMapperTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallMapperTest.kt index e6cc4393a24..d0fbdc0345e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallMapperTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallMapperTest.kt @@ -31,6 +31,7 @@ import com.wire.kalium.logic.data.id.QualifiedIdMapperImpl import com.wire.kalium.logic.data.message.MessageTarget import com.wire.kalium.logic.framework.TestCall import com.wire.kalium.persistence.dao.call.CallEntity +import com.wire.kalium.persistence.dao.conversation.ConversationEntity import kotlinx.coroutines.test.runTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -59,15 +60,24 @@ class CallMapperTest { @Test fun whenMappingToConversationTypeCalling_withConversationType_thenReturnConversationTypeCalling() = runTest { - val oneOnOneMap = callMapper.toConversationTypeCalling(conversationType = ConversationType.OneOnOne) - val conferenceMap = callMapper.toConversationTypeCalling(conversationType = ConversationType.Conference) - val unknown = callMapper.toConversationTypeCalling(conversationType = ConversationType.Unknown) + val oneOnOneMap = callMapper.toConversationTypeCalling(conversationTypeForCall = ConversationTypeForCall.OneOnOne) + val conferenceMap = callMapper.toConversationTypeCalling(conversationTypeForCall = ConversationTypeForCall.Conference) + val unknown = callMapper.toConversationTypeCalling(conversationTypeForCall = ConversationTypeForCall.Unknown) assertEquals(ConversationTypeCalling.OneOnOne, oneOnOneMap) assertEquals(ConversationTypeCalling.Conference, conferenceMap) assertEquals(ConversationTypeCalling.Unknown, unknown) } + @Test + fun givenCallMapper_whenMappingToConversationType_thenReturnConversationType() = runTest { + val oneOnOneMap = callMapper.toConversationType(conversationType = ConversationEntity.Type.ONE_ON_ONE) + val conferenceMap = callMapper.toConversationType(conversationType = ConversationEntity.Type.GROUP) + + assertEquals(Conversation.Type.ONE_ON_ONE, oneOnOneMap) + assertEquals(Conversation.Type.GROUP, conferenceMap) + } + @Test fun givenVideoStates_whenMappingWithToVideoStateCalling_thenReturnsTheCorrespondentValues() = runTest { val stopped = callMapper.toVideoStateCalling(videoState = VideoState.STOPPED) @@ -87,7 +97,7 @@ class CallMapperTest { @Test fun given0AsAConversationTypeInputValue_whenMappingToConversationType_ThenReturnOneOnOneType() { - val expected = ConversationType.OneOnOne + val expected = ConversationTypeForCall.OneOnOne val actual = callMapper.fromIntToConversationType(0) @@ -96,7 +106,7 @@ class CallMapperTest { @Test fun given2AsAConversationTypeInputValue_whenMappingToConversationType_ThenReturnConferenceType() { - val expected = ConversationType.Conference + val expected = ConversationTypeForCall.Conference val actual = callMapper.fromIntToConversationType(2) @@ -105,7 +115,7 @@ class CallMapperTest { @Test fun givenADifferentAConversationTypeInputValue_whenMappingToConversationType_ThenReturnConferenceType() { - val expected = ConversationType.Unknown + val expected = ConversationTypeForCall.Unknown val actual = callMapper.fromIntToConversationType(4) @@ -124,7 +134,7 @@ class CallMapperTest { status = CallStatus.ESTABLISHED, conversationType = Conversation.Type.ONE_ON_ONE, callerId = TestCall.CALLER_ID, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallRepositoryTest.kt index d1c1fa8e1b9..dfcc9752f98 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/call/CallRepositoryTest.kt @@ -195,7 +195,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -245,7 +245,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -291,7 +291,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -346,7 +346,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -395,7 +395,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -430,7 +430,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -469,7 +469,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -507,7 +507,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) coVerify { @@ -553,7 +553,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -590,7 +590,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt new file mode 100644 index 00000000000..068f0a21787 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt @@ -0,0 +1,259 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.call.usecase + +import com.wire.kalium.calling.ConversationTypeCalling +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.call.ConversationTypeForCall +import com.wire.kalium.logic.data.call.mapper.CallMapper +import com.wire.kalium.logic.data.conversation.Conversation +import com.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.ConversationId +import com.wire.kalium.logic.data.id.GroupID +import com.wire.kalium.logic.data.mls.CipherSuite +import com.wire.kalium.logic.framework.TestConversation +import com.wire.kalium.logic.functional.Either +import io.mockative.Mock +import io.mockative.any +import io.mockative.coEvery +import io.mockative.eq +import io.mockative.every +import io.mockative.mock +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Clock +import kotlin.test.Test +import kotlin.test.assertEquals + +class GetCallConversationTypeProviderTest { + + @Test + fun givenShouldUseSFTForOneOnOneCallsAndMLSEnabled_whenRunningUseCase_thenReturnConferenceMls() = + runTest { + val conversationId = TestConversation.ID + val groupId = GroupID("groupid") + + val (arrangement, getCallConversationType) = Arrangement() + .withGetConversationTypeByIdSuccess(conversationId, Conversation.Type.ONE_ON_ONE) + .withGetConversationProtocolInfoSuccess( + conversationId, + Conversation.ProtocolInfo.MLS( + groupId, + Conversation.ProtocolInfo.MLSCapable.GroupState.ESTABLISHED, + 1UL, + Clock.System.now(), + CipherSuite.MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 + ) + ) + .withMlsConferenceCallMapping() + .withShouldUseSFTForOneOnOneCalls() + .withMLSEnabled() + .arrange() + + val result = getCallConversationType.invoke(conversationId) + + assertEquals(ConversationTypeCalling.ConferenceMls, result) + } + + @Test + fun givenShouldUseSFTForOneOnOneCallsAndMLSDisabled_whenRunningUseCase_thenReturnConference() = + runTest { + val conversationId = TestConversation.ID + + val (_, getCallConversationType) = Arrangement() + .withGetConversationTypeByIdSuccess(conversationId, Conversation.Type.GROUP) + .withGetConversationProtocolInfoSuccess( + conversationId, + Conversation.ProtocolInfo.Proteus + ) + .withConferenceCallMapping() + .withShouldUseSFTForOneOnOneCalls() + .withMLSDisabled() + .arrange() + + val result = getCallConversationType(conversationId) + + assertEquals(ConversationTypeCalling.Conference, result) + } + + @Test + fun givenShouldNotUseSFTForOneOnOneCallsAndOneOnOneConversation_whenRunningUseCase_thenReturnOneOnOneType() = + runTest { + val conversationId = TestConversation.ID + + val (_, getCallConversationType) = Arrangement() + .withShouldNotUseSFTForOneOnOneCalls() + .withGetConversationTypeByIdSuccess(conversationId, Conversation.Type.ONE_ON_ONE) + .withGetConversationProtocolInfoSuccess( + conversationId, + Conversation.ProtocolInfo.Proteus + ) + .withOneOnOneCallMapping() + .arrange() + + val result = getCallConversationType.invoke(conversationId) + + assertEquals(ConversationTypeCalling.OneOnOne, result) + } + + @Test + fun givenUserConfigRepositoryReturnsFailure_whenRunningUseCase_thenReturnConversationType() = + runTest { + val conversationId = TestConversation.ID + + val (_, getCallConversationType) = Arrangement() + .withShouldUseSFTForOneOnOneCallsFailure() + .withGetConversationTypeByIdSuccess(conversationId, Conversation.Type.GROUP) + .withGetConversationProtocolInfoSuccess( + conversationId, + Conversation.ProtocolInfo.Proteus + ) + .withConferenceCallMapping() + .arrange() + + val result = getCallConversationType.invoke(conversationId) + + assertEquals(ConversationTypeCalling.Conference, result) + } + + @Test + fun givenShouldNotUseSFTAndConversationRepositoryFailure_whenRunningUseCase_thenReturnUnknown() = + runTest { + val conversationId = TestConversation.ID + + val (_, getCallConversationType) = Arrangement() + .withShouldNotUseSFTForOneOnOneCalls() + .withGetConversationTypeByIdFailure(conversationId) + .withUnknownCallMapping() + .arrange() + + val result = getCallConversationType.invoke(conversationId) + + assertEquals(ConversationTypeCalling.Unknown, result) + } + + private class Arrangement { + + @Mock + val userConfigRepository = mock(UserConfigRepository::class) + + @Mock + val conversationRepository = mock(ConversationRepository::class) + + @Mock + val callMapper = mock(CallMapper::class) + + private val getCallConversationType = GetCallConversationTypeProviderImpl( + userConfigRepository = userConfigRepository, + conversationRepository = conversationRepository, + callMapper = callMapper + ) + + fun arrange() = this to getCallConversationType + + fun withMLSEnabled() = apply { + every { + userConfigRepository.isMLSEnabled() + }.returns(Either.Right(true)) + } + + fun withMLSDisabled() = apply { + every { + userConfigRepository.isMLSEnabled() + }.returns(Either.Right(false)) + } + + fun withShouldUseSFTForOneOnOneCalls() = apply { + every { + userConfigRepository.shouldUseSFTForOneOnOneCalls() + }.returns(Either.Right(true)) + } + + fun withShouldNotUseSFTForOneOnOneCalls() = apply { + every { + userConfigRepository.shouldUseSFTForOneOnOneCalls() + }.returns(Either.Right(false)) + } + + fun withShouldUseSFTForOneOnOneCallsFailure() = apply { + every { + userConfigRepository.shouldUseSFTForOneOnOneCalls() + }.returns(Either.Left(StorageFailure.DataNotFound)) + } + + suspend fun withGetConversationTypeByIdSuccess( + conversationId: ConversationId, + result: Conversation.Type + ) = apply { + coEvery { + conversationRepository.getConversationTypeById(eq(conversationId)) + }.returns(Either.Right(result)) + } + + suspend fun withGetConversationTypeByIdFailure(conversationId: ConversationId) = apply { + coEvery { + conversationRepository.getConversationTypeById(eq(conversationId)) + }.returns(Either.Left(StorageFailure.DataNotFound)) + } + + suspend fun withGetConversationProtocolInfoSuccess( + conversationId: ConversationId, + protocolResult: Conversation.ProtocolInfo + ) = apply { + coEvery { + conversationRepository.getConversationProtocolInfo(eq(conversationId)) + }.returns(Either.Right(protocolResult)) + } + + fun withOneOnOneCallMapping() = apply { + every { + callMapper.fromConversationTypeToConversationTypeForCall(any(), any()) + }.returns(ConversationTypeForCall.OneOnOne) + + every { + callMapper.toConversationTypeCalling(any()) + }.returns(ConversationTypeCalling.OneOnOne) + } + + fun withConferenceCallMapping() = apply { + every { + callMapper.fromConversationTypeToConversationTypeForCall(any(), any()) + }.returns(ConversationTypeForCall.Conference) + + every { + callMapper.toConversationTypeCalling(any()) + }.returns(ConversationTypeCalling.Conference) + } + + fun withMlsConferenceCallMapping() = apply { + every { + callMapper.fromConversationTypeToConversationTypeForCall(any(), any()) + }.returns(ConversationTypeForCall.ConferenceMls) + + every { + callMapper.toConversationTypeCalling(any()) + }.returns(ConversationTypeCalling.ConferenceMls) + } + + fun withUnknownCallMapping() = apply { + every { + callMapper.toConversationTypeCalling(eq(ConversationTypeForCall.Unknown)) + }.returns(ConversationTypeCalling.Unknown) + } + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCaseTest.kt index 5cc011d10c9..84e8395dd44 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/StartCallUseCaseTest.kt @@ -18,6 +18,7 @@ package com.wire.kalium.logic.feature.call.usecase +import com.wire.kalium.calling.ConversationTypeCalling import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.data.call.CallRepository @@ -53,6 +54,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement(testKaliumDispatcher) .withWaitingForSyncSucceeding() .withAnIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() startCall.invoke(conversationId) @@ -62,7 +64,7 @@ class StartCallUseCaseTest { }.wasInvoked(once) coVerify { - arrangement.callManager.startCall(any(), any(), any()) + arrangement.callManager.startCall(any(), any(), any(), any()) }.wasNotInvoked() } @@ -73,12 +75,13 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement(testKaliumDispatcher) .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() startCall.invoke(conversationId, CallType.AUDIO) coVerify { - arrangement.callManager.startCall(eq(conversationId), eq(CallType.AUDIO), eq(false)) + arrangement.callManager.startCall(eq(conversationId), eq(CallType.AUDIO), any(), eq(false)) }.wasInvoked(once) coVerify { @@ -93,6 +96,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement(testKaliumDispatcher) .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() val result = startCall.invoke(conversationId, CallType.AUDIO) @@ -109,12 +113,13 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement(testKaliumDispatcher) .withWaitingForSyncFailing() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.OneOnOne) .arrange() startCall.invoke(conversationId, CallType.AUDIO) coVerify { - arrangement.callManager.startCall(any(), any(), any()) + arrangement.callManager.startCall(any(), any(), any(), any()) }.wasNotInvoked() } @@ -138,12 +143,13 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement(testKaliumDispatcher) .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrangeWithCBR() startCall.invoke(conversationId, CallType.AUDIO) coVerify { - arrangement.callManager.startCall(eq(conversationId), eq(CallType.AUDIO), eq(true)) + arrangement.callManager.startCall(eq(conversationId), eq(CallType.AUDIO), any(), eq(true)) }.wasInvoked(once) coVerify { arrangement.answerCall.invoke(any()) @@ -161,6 +167,9 @@ class StartCallUseCaseTest { @Mock val answerCall = mock(AnswerCallUseCase::class) + @Mock + val getCallConversationType = mock(GetCallConversationTypeProvider::class) + @Mock val callRepository = mock(CallRepository::class) @@ -171,6 +180,7 @@ class StartCallUseCaseTest { syncManager, kaliumConfigs, callRepository, + getCallConversationType, answerCall, dispatcher ) @@ -180,6 +190,7 @@ class StartCallUseCaseTest { syncManager, KaliumConfigs(forceConstantBitrateCalls = true), callRepository, + getCallConversationType, answerCall, dispatcher ) @@ -191,6 +202,12 @@ class StartCallUseCaseTest { }.returns(flowOf(listOf(TestCall.groupIncomingCall(TestConversation.ID)))) } + suspend fun withCallConversationTypeUseCaseReturning(result: ConversationTypeCalling) = apply { + coEvery { + getCallConversationType.invoke(any()) + }.returns(result) + } + suspend fun withNoIncomingCall() = apply { coEvery { callRepository.incomingCallsFlow() diff --git a/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/featureConfigs/FeatureConfigResponse.kt b/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/featureConfigs/FeatureConfigResponse.kt index d7b03c79d95..2ed2fa0da8c 100644 --- a/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/featureConfigs/FeatureConfigResponse.kt +++ b/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/featureConfigs/FeatureConfigResponse.kt @@ -163,7 +163,7 @@ sealed class FeatureConfigData { @SerialName("status") val status: FeatureFlagStatusDTO, @SerialName("config") - val config: ConferenceCallingConfigDTO + val config: ConferenceCallingConfigDTO? // TODO make it non optional when the minimum supported API is v6 ) : FeatureConfigData() @SerialName("conversationGuestLinks") diff --git a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq index adf0877e106..ebc0abcd3cb 100644 --- a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq +++ b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Conversations.sq @@ -333,6 +333,9 @@ SELECT qualified_id FROM Conversation WHERE mls_group_id = ?; selectConversationIds: SELECT qualified_id FROM Conversation WHERE protocol = :protocol AND type = :type AND (:teamId IS NULL OR team_id = :teamId); +getConversationTypeById: +SELECT type FROM Conversation WHERE qualified_id = ?; + updateConversationMutingStatus: UPDATE Conversation SET muted_status = ?, muted_time = ? diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt index 87f0c314ed5..088096d6246 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAO.kt @@ -52,6 +52,7 @@ interface ConversationDAO { protocol: ConversationEntity.Protocol, teamId: String? = null ): List + suspend fun getConversationTypeById(conversationId: QualifiedIDEntity): ConversationEntity.Type? suspend fun getTeamConversationIdsReadyToCompleteMigration(teamId: String): List suspend fun observeGetConversationByQualifiedID(qualifiedID: QualifiedIDEntity): Flow diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt index 488f231108f..09ae0f898ba 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/conversation/ConversationDAOImpl.kt @@ -197,6 +197,11 @@ internal class ConversationDAOImpl internal constructor( } } + override suspend fun getConversationTypeById(conversationId: QualifiedIDEntity): ConversationEntity.Type? = + withContext(coroutineContext) { + conversationQueries.getConversationTypeById(conversationId).executeAsOneOrNull() + } + override suspend fun getTeamConversationIdsReadyToCompleteMigration(teamId: String): List { return withContext(coroutineContext) { conversationQueries.selectAllTeamProteusConversationsReadyForMigration(teamId)