From 08ffe8a152ab51f3c54d6e3580ed5d8feb1ca9fd Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Fri, 26 Jul 2024 11:09:07 +0200 Subject: [PATCH 1/2] chore: resolve conflicts --- .../logic/feature/call/CallManagerTest.kt | 8 + .../call/scenario/OnIncomingCallTest.kt | 7 +- .../logic/feature/call/CallManagerImpl.kt | 2 + .../logic/feature/call/GlobalCallManager.kt | 2 + .../logic/feature/call/CallManagerImpl.kt | 18 +- .../logic/feature/call/GlobalCallManager.kt | 3 + .../feature/call/scenario/OnIncomingCall.kt | 4 +- .../kalium/logic/data/call/CallRepository.kt | 4 +- ...tionType.kt => ConversationTypeForCall.kt} | 2 +- .../logic/data/call/mapper/CallMapper.kt | 72 +++-- .../data/conversation/ConversationMapper.kt | 2 + .../conversation/ConversationRepository.kt | 2 + .../data/featureConfig/FeatureConfigMapper.kt | 2 +- .../kalium/logic/feature/UserSessionScope.kt | 36 ++- .../kalium/logic/feature/call/CallManager.kt | 2 + .../kalium/logic/feature/call/CallsScope.kt | 6 +- .../logic/feature/call/GlobalCallManager.kt | 3 + .../GetCallConversationTypeProvider.kt | 71 +++++ .../feature/call/usecase/StartCallUseCase.kt | 6 + .../kalium/logic/data/call/CallMapperTest.kt | 24 +- .../logic/data/call/CallRepositoryTest.kt | 20 +- .../GetCallConversationTypeProviderTest.kt | 259 ++++++++++++++++++ .../call/usecase/StartCallUseCaseTest.kt | 24 +- .../featureConfigs/FeatureConfigResponse.kt | 2 +- .../wire/kalium/persistence/Conversations.sq | 3 + .../dao/conversation/ConversationDAO.kt | 1 + .../dao/conversation/ConversationDAOImpl.kt | 5 + 27 files changed, 510 insertions(+), 80 deletions(-) rename logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/{ConversationType.kt => ConversationTypeForCall.kt} (95%) create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProvider.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt 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 0cf2dca1731..cd126782a03 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 @@ -49,6 +49,10 @@ 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 class CallManagerTest { @@ -85,6 +89,9 @@ class CallManagerTest { @Mock private val videoStateChecker = mock(classOf()) + @Mock + private val getCallConversationType = mock(GetCallConversationTypeProvider::class) + private val dispatcher = TestKaliumDispatcher private lateinit var callManagerImpl: CallManagerImpl @@ -108,6 +115,7 @@ class CallManagerTest { qualifiedIdMapper = qualifiedIdMapper, videoStateChecker = videoStateChecker, callMapper = callMapper, + getCallConversationType = getCallConversationType, conversationClientsInCallUpdater = conversationClientsInCallUpdater, 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 df38d77048c..964694f3a20 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.configure 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 @@ -64,7 +63,7 @@ class OnIncomingCallTest { .suspendFunction(arrangement.callRepository::createCall) .with( eq(TestConversation.CONVERSATION.id), - eq(ConversationType.Conference), + eq(ConversationTypeForCall.Conference), eq(CallStatus.INCOMING), eq(TestUser.USER_ID.toString()), eq(true), @@ -95,7 +94,7 @@ class OnIncomingCallTest { .suspendFunction(arrangement.callRepository::createCall) .with( 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 59dd7230f91..a136495738a 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 @@ -35,6 +36,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 8ba800004ba..ac3af705113 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 @@ -30,6 +30,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 @@ -48,6 +49,7 @@ actual class GlobalCallManager { qualifiedIdMapper: QualifiedIdMapper, videoStateChecker: VideoStateChecker, conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + getCallConversationType: GetCallConversationTypeProvider, kaliumConfigs: KaliumConfigs ): CallManager { return CallManagerImpl() 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 0b32be4cb27..507397c67db 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 @@ -21,6 +21,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 @@ -34,7 +35,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.VideoState import com.wire.kalium.logic.data.call.VideoStateChecker @@ -67,6 +67,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 @@ -102,6 +103,8 @@ class CallManagerImpl internal constructor( private val qualifiedIdMapper: QualifiedIdMapper, private val videoStateChecker: VideoStateChecker, private val conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + private val networkStateObserver: NetworkStateObserver, + private val getCallConversationType: GetCallConversationTypeProvider, private val kaliumConfigs: KaliumConfigs, private val json: Json = Json { ignoreUnknownKeys = true }, private val shouldRemoteMuteChecker: ShouldRemoteMuteChecker = ShouldRemoteMuteCheckerImpl(), @@ -232,9 +235,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(), @@ -254,6 +256,7 @@ class CallManagerImpl internal constructor( override suspend fun startCall( conversationId: ConversationId, callType: CallType, + conversationTypeCalling: ConversationTypeCalling, isAudioCbr: Boolean ) { callingLogger.d( @@ -261,9 +264,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, @@ -277,13 +278,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 fcc30c6e080..090de749688 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 @@ -35,6 +35,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.logic.util.CurrentPlatform @@ -84,6 +85,7 @@ actual class GlobalCallManager( qualifiedIdMapper: QualifiedIdMapper, videoStateChecker: VideoStateChecker, conversationClientsInCallUpdater: ConversationClientsInCallUpdater, + getCallConversationType: GetCallConversationTypeProvider, kaliumConfigs: KaliumConfigs ): CallManager { return callManagerHolder.computeIfAbsent(userId) { @@ -100,6 +102,7 @@ actual class GlobalCallManager( qualifiedIdMapper = qualifiedIdMapper, videoStateChecker = videoStateChecker, conversationClientsInCallUpdater = conversationClientsInCallUpdater, + getCallConversationType = getCallConversationType, kaliumConfigs = kaliumConfigs ) } 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 52ca28b5867..d8f02129023 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 @@ -106,7 +106,7 @@ interface CallRepository { @Suppress("LongParameterList") suspend fun createCall( conversationId: ConversationId, - type: ConversationType, + type: ConversationTypeForCall, status: CallStatus, callerId: String, isMuted: Boolean, @@ -185,7 +185,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/ConversationType.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationTypeForCall.kt similarity index 95% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationType.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationTypeForCall.kt index ca39a3d820c..8f010c91fbd 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/ConversationType.kt +++ b/logic/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/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 19bdd46b4f9..10b74a8ee4a 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") 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 5b245f0a1e8..e2a871c06fb 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 @@ -133,6 +133,8 @@ 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 suspend fun getConversationRecipients(conversationId: ConversationId): Either> 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 2aceb2a1fb8..25355fc8ce2 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 b41fb897030..3346c37ee7d 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 @@ -168,6 +168,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 @@ -1195,6 +1197,7 @@ class UserSessionScope internal constructor( videoStateChecker = videoStateChecker, callMapper = callMapper, conversationClientsInCallUpdater = conversationClientsInCallUpdater, + getCallConversationType = getCallConversationType, kaliumConfigs = kaliumConfigs ) } @@ -1214,6 +1217,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) @@ -1902,18 +1913,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 cb0199bc603..780d4da06d5 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 @@ -34,6 +35,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 ab8a4650e4b..3b99a5cf29c 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,21 +25,23 @@ 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.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 @@ -86,6 +88,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 ) { @@ -121,6 +124,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 6bc1da5e8c7..9fcd81de371 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 @@ -30,6 +30,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 @@ -49,6 +50,8 @@ 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 3a913755b4d..d2ad21ee05c 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 @@ -197,7 +197,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -247,7 +247,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -293,7 +293,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -348,7 +348,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -397,7 +397,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.Conference + type = ConversationTypeForCall.Conference ) // then @@ -432,7 +432,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -471,7 +471,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -510,7 +510,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) verify(arrangement.persistMessage) @@ -557,7 +557,7 @@ class CallRepositoryTest { isMuted = true, isCameraOn = false, isCbrEnabled = false, - type = ConversationType.OneOnOne + type = ConversationTypeForCall.OneOnOne ) // then @@ -595,7 +595,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 1bc5bc3c2e3..c341b82417f 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 @@ -52,6 +53,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement() .withWaitingForSyncSucceeding() .withAnIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() startCall.invoke(conversationId) @@ -74,6 +76,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement() .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() startCall.invoke(conversationId, CallType.AUDIO) @@ -96,6 +99,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement() .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrange() val result = startCall.invoke(conversationId, CallType.AUDIO) @@ -113,6 +117,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement() .withWaitingForSyncFailing() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.OneOnOne) .arrange() startCall.invoke(conversationId, CallType.AUDIO) @@ -143,6 +148,7 @@ class StartCallUseCaseTest { val (arrangement, startCall) = Arrangement() .withWaitingForSyncSucceeding() .withNoIncomingCall() + .withCallConversationTypeUseCaseReturning(ConversationTypeCalling.Conference) .arrangeWithCBR() startCall.invoke(conversationId, CallType.AUDIO) @@ -168,13 +174,16 @@ class StartCallUseCaseTest { @Mock val answerCall = mock(classOf()) + @Mock + val getCallConversationType = mock(GetCallConversationTypeProvider::class) + @Mock val callRepository = mock(classOf()) private val kaliumConfigs = KaliumConfigs() private val startCallUseCase = StartCallUseCase( - lazy { callManager }, syncManager, kaliumConfigs, callRepository, answerCall + lazy { callManager }, syncManager, kaliumConfigs, callRepository, getCallConversationType, answerCall ) private val startCallUseCaseWithCBR = StartCallUseCase( @@ -182,7 +191,8 @@ class StartCallUseCaseTest { syncManager, KaliumConfigs(forceConstantBitrateCalls = true), callRepository, - answerCall + answerCall, + getCallConversationType ) fun withWaitingForSyncSucceeding() = withSyncReturning(Either.Right(Unit)) @@ -195,6 +205,16 @@ class StartCallUseCaseTest { } } + suspend fun withCallConversationTypeUseCaseReturning(result: ConversationTypeCalling) = apply { + coEvery { + getCallConversationType.invoke(any()) + }.returns(result) + given(getCallConversationType) + .suspendFunction(getCallConversationType::invoke) + .whenInvoked() + .then { result } + } + fun withNoIncomingCall() = apply { given(callRepository) .suspendFunction(callRepository::incomingCallsFlow) diff --git a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/featureConfigs/FeatureConfigResponse.kt b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/featureConfigs/FeatureConfigResponse.kt index 0f211ab1d21..5a7864fd622 100644 --- a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/featureConfigs/FeatureConfigResponse.kt +++ b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/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 368b2ab68a0..13f52b4ddab 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 @@ -331,6 +331,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 70d0a989b6a..6496b54ab87 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 @@ -53,6 +53,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 7dd972f6c4c..f711e90a8b0 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 @@ -190,6 +190,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) From 2e5a9f300888125f7f7a79c46b22ca64118b69c4 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Fri, 26 Jul 2024 12:25:27 +0200 Subject: [PATCH 2/2] chore: make useSFTForOneToOneCalls default to true --- .../wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 25355fc8ce2..e07f2d21087 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 ?: false + useSFTForOneOnOneCalls = data.config?.useSFTForOneToOneCalls ?: true ) override fun fromDTO(data: FeatureConfigData.E2EI?): E2EIModel =