From 874660f51ecace98ffeeaf30b29dd12cc7d3f995 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Mon, 22 Jul 2024 17:40:31 +0200 Subject: [PATCH 01/10] feat: add useSFTForOneToOneCalls flag --- .../data/featureConfig/FeatureConfigModel.kt | 3 +- .../configuration/UserConfigRepository.kt | 10 ++ .../data/featureConfig/FeatureConfigMapper.kt | 3 +- .../SyncFeatureConfigsUseCase.kt | 2 +- .../handler/ConferenceCallingConfigHandler.kt | 6 +- .../data/event/FeatureConfigMapperTest.kt | 3 +- .../FeatureConfigRepositoryTest.kt | 5 +- .../data/featureConfig/FeatureConfigTest.kt | 2 +- .../SyncFeatureConfigsUseCaseTest.kt | 4 +- .../ConferenceCallingConfigHandlerTest.kt | 141 ++++++++++++++++++ .../FeatureConfigEventReceiverTest.kt | 4 +- .../featureConfigs/FeatureConfigResponse.kt | 10 +- .../persistence/config/UserConfigStorage.kt | 16 ++ 13 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandlerTest.kt diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigModel.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigModel.kt index 17d630a2025..1fa12985a76 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigModel.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigModel.kt @@ -90,7 +90,8 @@ data class MLSMigrationModel( ) data class ConferenceCallingModel( - val status: Status + val status: Status, + val useSFTForOneOnOneCalls: Boolean ) data class E2EIModel( diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt index e0fb55441ac..29424e92db5 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt @@ -93,6 +93,8 @@ interface UserConfigRepository { suspend fun getSupportedProtocols(): Either> fun setConferenceCallingEnabled(enabled: Boolean): Either fun isConferenceCallingEnabled(): Either + fun setUseSFTForOneOnOneCalls(shouldUse: Boolean): Either + fun shouldUseSFTForOneOnOneCalls(): Either fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either fun isSecondFactorPasswordChallengeRequired(): Either fun isReadReceiptsEnabled(): Flow> @@ -292,6 +294,14 @@ internal class UserConfigDataSource internal constructor( userConfigStorage.isConferenceCallingEnabled() } + override fun setUseSFTForOneOnOneCalls(shouldUse: Boolean): Either = wrapStorageRequest { + userConfigStorage.persistUseSftForOneOnOneCalls(shouldUse) + } + + override fun shouldUseSFTForOneOnOneCalls(): Either = wrapStorageRequest { + userConfigStorage.shouldUseSftForOneOnOneCalls() + } + override fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either = wrapStorageRequest { userConfigStorage.persistSecondFactorPasswordChallengeStatus(isRequired) 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 110da73387e..6e58e177dff 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 @@ -131,7 +131,8 @@ class FeatureConfigMapperImpl : FeatureConfigMapper { override fun fromDTO(data: FeatureConfigData.ConferenceCalling): ConferenceCallingModel = ConferenceCallingModel( - status = fromDTO(data.status) + status = fromDTO(data.status), + useSFTForOneOnOneCalls = data.config.useSFTForOneToOneCalls ) override fun fromDTO(data: FeatureConfigData.E2EI?): E2EIModel = diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt index 9299f7a54f7..3ab938a9753 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt @@ -71,7 +71,7 @@ internal class SyncFeatureConfigsUseCaseImpl( conferenceCallingConfigHandler.handle(it.conferenceCallingModel) passwordChallengeConfigHandler.handle(it.secondFactorPasswordChallengeModel) selfDeletingMessagesConfigHandler.handle(it.selfDeletingMessagesModel) - it.e2EIModel?.let { e2EIModel -> e2EIConfigHandler.handle(e2EIModel) } + it.e2EIModel.let { e2EIModel -> e2EIConfigHandler.handle(e2EIModel) } appLockConfigHandler.handle(it.appLockModel) Either.Right(Unit) }.onFailure { networkFailure -> diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandler.kt index 673aa533ca8..c014dbfbfe8 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandler.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandler.kt @@ -22,12 +22,16 @@ import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel import com.wire.kalium.logic.data.featureConfig.Status import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.flatMap class ConferenceCallingConfigHandler( private val userConfigRepository: UserConfigRepository ) { fun handle(conferenceCallingConfig: ConferenceCallingModel): Either { val conferenceCallingEnabled = conferenceCallingConfig.status == Status.ENABLED - return userConfigRepository.setConferenceCallingEnabled(conferenceCallingEnabled) + val result = userConfigRepository.setConferenceCallingEnabled(conferenceCallingEnabled).flatMap { + userConfigRepository.setUseSFTForOneOnOneCalls(conferenceCallingConfig.useSFTForOneOnOneCalls) + } + return result } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt index 83986b6af7b..83929b309b2 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt @@ -23,6 +23,7 @@ import com.wire.kalium.logic.data.featureConfig.FeatureConfigMapperImpl import com.wire.kalium.logic.data.featureConfig.Status import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigResponse import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureFlagStatusDTO @@ -141,7 +142,7 @@ class FeatureConfigMapperTest { ClassifiedDomainsConfigDTO(listOf("wire.com")), FeatureFlagStatusDTO.ENABLED ), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt index c4aeac2345b..14a6e2c181c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt @@ -25,6 +25,7 @@ import com.wire.kalium.logic.util.shouldFail import com.wire.kalium.logic.util.shouldSucceed import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig import com.wire.kalium.network.api.authenticated.featureConfigs.E2EIConfigDTO import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigApi import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData @@ -59,7 +60,7 @@ class FeatureConfigRepositoryTest { ClassifiedDomainsConfigModel(listOf()), Status.ENABLED ), - ConferenceCallingModel(Status.ENABLED), + ConferenceCallingModel(Status.ENABLED, false), ConfigsStatusModel(Status.ENABLED), ConfigsStatusModel(Status.ENABLED), ConfigsStatusModel(Status.ENABLED), @@ -145,7 +146,7 @@ class FeatureConfigRepositoryTest { AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED ), FeatureConfigData.ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt index e1aadb92060..b70e743aea6 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt @@ -29,7 +29,7 @@ object FeatureConfigTest { ClassifiedDomainsConfigModel(listOf()), Status.ENABLED ), - conferenceCallingModel: ConferenceCallingModel = ConferenceCallingModel(Status.ENABLED), + conferenceCallingModel: ConferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false), conversationGuestLinksModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), digitalSignaturesModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), fileSharingModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt index 9c47261cdae..78e0efaaa4e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt @@ -119,7 +119,7 @@ class SyncFeatureConfigsUseCaseTest { fun givenConferenceCallingIsEnabled_whenSyncing_thenItShouldBeStoredAsEnabled() = runTest { val (arrangement, syncFeatureConfigsUseCase) = Arrangement() .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.ENABLED)) + FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() .withGetSupportedProtocolsReturning(null) @@ -136,7 +136,7 @@ class SyncFeatureConfigsUseCaseTest { fun givenConferenceCallingIsDisasbled_whenSyncing_thenItShouldBeStoredAsDisabled() = runTest { val (arrangement, syncFeatureConfigsUseCase) = Arrangement() .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.DISABLED)) + FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.DISABLED, false)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() .withGetSupportedProtocolsReturning(null) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandlerTest.kt new file mode 100644 index 00000000000..5160a23d614 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/ConferenceCallingConfigHandlerTest.kt @@ -0,0 +1,141 @@ +/* + * 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.featureConfig.handler + +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel +import com.wire.kalium.logic.data.featureConfig.Status +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.isLeft +import com.wire.kalium.logic.functional.isRight +import io.mockative.Mock +import io.mockative.any +import io.mockative.every +import io.mockative.mock +import io.mockative.once +import io.mockative.verify +import kotlin.test.Test +import kotlin.test.assertTrue + +class ConferenceCallingConfigHandlerTest { + + @Test + fun givenUserConfigRepositoryFailureForConferenceCallingEnabled_whenHandlingTheEvent_ThenReturnFailure() { + val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false) + val (arrangement, conferenceCallingConfigHandler) = Arrangement() + .withSetConferenceCallingEnabledFailure() + .arrange() + + val result = conferenceCallingConfigHandler.handle(conferenceCallingModel) + + verify { + arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean()) + }.wasInvoked(exactly = once) + + verify { + arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.wasNotInvoked() + + assertTrue { result.isLeft() } + } + + @Test + fun givenUserConfigRepositoryFailureForUseSFTForOneOnOneCalls_whenHandlingTheEvent_ThenReturnFailure() { + val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false) + val (arrangement, conferenceCallingConfigHandler) = Arrangement() + .withSetConferenceCallingEnabledSuccess() + .withSetUseSFTForOneOnOneCallsFailure() + .arrange() + + val result = conferenceCallingConfigHandler.handle(conferenceCallingModel) + + verify { + arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean()) + }.wasInvoked(exactly = once) + + verify { + arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.wasInvoked(exactly = once) + + assertTrue { result.isLeft() } + } + + @Test + fun givenUserConfigRepositorySuccess_whenHandlingTheEvent_ThenReturnUnit() { + val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false) + val (arrangement, conferenceCallingConfigHandler) = Arrangement() + .withSetConferenceCallingEnabledSuccess() + .withSetUseSFTForOneOnOneCallsSuccess() + .arrange() + + val result = conferenceCallingConfigHandler.handle(conferenceCallingModel) + + verify { + arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean()) + }.wasInvoked(exactly = once) + + verify { + arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.wasInvoked(exactly = once) + + assertTrue { result.isRight() } + } + + private class Arrangement { + + @Mock + val userConfigRepository: UserConfigRepository = mock(UserConfigRepository::class) + + fun arrange() = run { + this@Arrangement to ConferenceCallingConfigHandler( + userConfigRepository = userConfigRepository + ) + } + + init { + every { + userConfigRepository.setAppLockStatus(any(), any(), any()) + }.returns(Either.Right(Unit)) + } + + fun withSetConferenceCallingEnabledFailure() = apply { + every { + userConfigRepository.setConferenceCallingEnabled(any()) + }.returns(Either.Left(StorageFailure.DataNotFound)) + } + + fun withSetConferenceCallingEnabledSuccess() = apply { + every { + userConfigRepository.setConferenceCallingEnabled(any()) + }.returns(Either.Right(Unit)) + } + + fun withSetUseSFTForOneOnOneCallsFailure() = apply { + every { + userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.returns(Either.Left(StorageFailure.DataNotFound)) + } + + fun withSetUseSFTForOneOnOneCallsSuccess() = apply { + every { + userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.returns(Either.Right(Unit)) + } + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt index 75a69328ceb..50ccf6bcf9d 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt @@ -118,7 +118,7 @@ class FeatureConfigEventReceiverTest { .arrange() featureConfigEventReceiver.onEvent( - arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.ENABLED)), + arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.ENABLED, false)), TestEvent.liveDeliveryInfo ) @@ -134,7 +134,7 @@ class FeatureConfigEventReceiverTest { .arrange() featureConfigEventReceiver.onEvent( - event = arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.DISABLED)), + event = arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.DISABLED, false)), deliveryInfo = TestEvent.liveDeliveryInfo ) 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 657fe3c7e52..f627a1b6ecc 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 @@ -68,6 +68,12 @@ enum class FeatureFlagStatusDTO { DISABLED; } +@Serializable +data class ConferenceCallingConfig( + @SerialName("useSFTForOneToOneCalls") + val useSFTForOneToOneCalls: Boolean +) + @Serializable data class AppLockConfigDTO( @SerialName("enforceAppLock") @@ -155,7 +161,9 @@ sealed class FeatureConfigData { @Serializable data class ConferenceCalling( @SerialName("status") - val status: FeatureFlagStatusDTO + val status: FeatureFlagStatusDTO, + @SerialName("config") + val config: ConferenceCallingConfig ) : FeatureConfigData() @SerialName("conversationGuestLinks") diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt index 57c8866264b..b3542fb10f7 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt @@ -145,6 +145,10 @@ interface UserConfigStorage { */ fun isConferenceCallingEnabled(): Boolean + fun persistUseSftForOneOnOneCalls(shouldUse: Boolean) + + fun shouldUseSftForOneOnOneCalls(): Boolean + /** * Get the saved flag to know whether user's Read Receipts are currently enabled or not */ @@ -503,6 +507,16 @@ class UserConfigStorageImpl( DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE ) + override fun persistUseSftForOneOnOneCalls(shouldUse: Boolean) { + kaliumPreferences.putBoolean(USE_SFT_FOR_ONE_ON_ONE_CALLS, shouldUse) + } + + override fun shouldUseSftForOneOnOneCalls(): Boolean = + kaliumPreferences.getBoolean( + USE_SFT_FOR_ONE_ON_ONE_CALLS, + DEFAULT_USE_SFT_FOR_ONE_ON_ONE_CALLS_VALUE + ) + override fun areReadReceiptsEnabled(): Flow = areReadReceiptsEnabledFlow .map { kaliumPreferences.getBoolean(ENABLE_READ_RECEIPTS, true) } .onStart { emit(kaliumPreferences.getBoolean(ENABLE_READ_RECEIPTS, true)) } @@ -577,8 +591,10 @@ class UserConfigStorageImpl( const val E2EI_SETTINGS = "end_to_end_identity_settings" const val E2EI_NOTIFICATION_TIME = "end_to_end_identity_notification_time" const val ENABLE_CONFERENCE_CALLING = "enable_conference_calling" + const val USE_SFT_FOR_ONE_ON_ONE_CALLS = "use_sft_for_one_on_one_calls" const val ENABLE_READ_RECEIPTS = "enable_read_receipts" const val DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE = false + const val DEFAULT_USE_SFT_FOR_ONE_ON_ONE_CALLS_VALUE = false const val REQUIRE_SECOND_FACTOR_PASSWORD_CHALLENGE = "require_second_factor_password_challenge" const val ENABLE_SCREENSHOT_CENSORING = "enable_screenshot_censoring" From 4360a3fb394e79af66c7984e3159f5ff066ccb75 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 10:31:09 +0200 Subject: [PATCH 02/10] chore: uni test --- .../com/wire/kalium/mocks/responses/FeatureConfigJson.kt | 6 +++++- .../kalium/mocks/responses/FeatureConfigResponseJson.kt | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt index b9c47553ad5..fe2d1752ac4 100644 --- a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt +++ b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt @@ -20,6 +20,7 @@ package com.wire.kalium.mocks.responses import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData.AppLock import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData.ClassifiedDomains @@ -64,6 +65,9 @@ object FeatureConfigJson { | "status": "enabled" | }, | "conferenceCalling": { + | "config": { + | "useSFTForOneToOneCalls": false, + | }, | "status": "enabled" | }, | "conversationGuestLinks": { @@ -116,7 +120,7 @@ object FeatureConfigJson { AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED ), ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED), - ConferenceCalling(FeatureFlagStatusDTO.ENABLED), + ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt index d1b0825837b..1793aa7d5c7 100644 --- a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt +++ b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt @@ -19,6 +19,7 @@ package com.wire.kalium.mocks.responses import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig import com.wire.kalium.network.api.authenticated.featureConfigs.E2EIConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigResponse @@ -46,7 +47,7 @@ object FeatureConfigResponseJson { ClassifiedDomainsConfigDTO(listOf("wire.com")), FeatureFlagStatusDTO.ENABLED ), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), From 1e5ab8129a0d18def6608d95722ae4bfc601c803 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 11:31:46 +0200 Subject: [PATCH 03/10] chore: unit test --- .../kalium/logic/data/event/FeatureConfigMapperTest.kt | 4 ++-- .../data/featureConfig/FeatureConfigRepositoryTest.kt | 4 ++-- .../com/wire/kalium/mocks/responses/FeatureConfigJson.kt | 8 ++++---- .../kalium/mocks/responses/FeatureConfigResponseJson.kt | 4 ++-- .../authenticated/featureConfigs/FeatureConfigResponse.kt | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt index 83929b309b2..80e73517187 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/event/FeatureConfigMapperTest.kt @@ -23,7 +23,7 @@ import com.wire.kalium.logic.data.featureConfig.FeatureConfigMapperImpl import com.wire.kalium.logic.data.featureConfig.Status import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO -import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigResponse import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureFlagStatusDTO @@ -142,7 +142,7 @@ class FeatureConfigMapperTest { ClassifiedDomainsConfigDTO(listOf("wire.com")), FeatureFlagStatusDTO.ENABLED ), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfigDTO(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt index 14a6e2c181c..1d1f62bdda4 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigRepositoryTest.kt @@ -25,7 +25,7 @@ import com.wire.kalium.logic.util.shouldFail import com.wire.kalium.logic.util.shouldSucceed import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO -import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.E2EIConfigDTO import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigApi import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData @@ -146,7 +146,7 @@ class FeatureConfigRepositoryTest { AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED ), FeatureConfigData.ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfigDTO(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt index fe2d1752ac4..230e95360e2 100644 --- a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt +++ b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigJson.kt @@ -20,7 +20,7 @@ package com.wire.kalium.mocks.responses import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO -import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData.AppLock import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData.ClassifiedDomains @@ -66,8 +66,8 @@ object FeatureConfigJson { | }, | "conferenceCalling": { | "config": { - | "useSFTForOneToOneCalls": false, - | }, + | "useSFTForOneToOneCalls": false + | }, | "status": "enabled" | }, | "conversationGuestLinks": { @@ -120,7 +120,7 @@ object FeatureConfigJson { AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED ), ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED), - ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), + ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfigDTO(false)), ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FileSharing(FeatureFlagStatusDTO.ENABLED), diff --git a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt index 1793aa7d5c7..d56af3ba048 100644 --- a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt +++ b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/FeatureConfigResponseJson.kt @@ -19,7 +19,7 @@ package com.wire.kalium.mocks.responses import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO -import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig +import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.E2EIConfigDTO import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigResponse @@ -47,7 +47,7 @@ object FeatureConfigResponseJson { ClassifiedDomainsConfigDTO(listOf("wire.com")), FeatureFlagStatusDTO.ENABLED ), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)), + FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfigDTO(false)), FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), 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 f627a1b6ecc..d7b03c79d95 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 @@ -69,7 +69,7 @@ enum class FeatureFlagStatusDTO { } @Serializable -data class ConferenceCallingConfig( +data class ConferenceCallingConfigDTO( @SerialName("useSFTForOneToOneCalls") val useSFTForOneToOneCalls: Boolean ) @@ -163,7 +163,7 @@ sealed class FeatureConfigData { @SerialName("status") val status: FeatureFlagStatusDTO, @SerialName("config") - val config: ConferenceCallingConfig + val config: ConferenceCallingConfigDTO ) : FeatureConfigData() @SerialName("conversationGuestLinks") From 3a2422389f400504e5f9de55944ad397904aa24b Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 14:10:53 +0200 Subject: [PATCH 04/10] chore: unit test --- .../FeatureConfigEventReceiverTest.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt index 50ccf6bcf9d..1c06a919b6a 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt @@ -112,8 +112,9 @@ class FeatureConfigEventReceiverTest { } @Test - fun givenConferenceCallingUpdatedEventGrantingAccess_whenProcessingEvent_ThenSetConferenceCallingEnabledToTrue() = runTest { + fun givenConferenceCallingEventEnabled_whenProcessingEvent_ThenSetConferenceCallingEnabledToTrueAndSetShouldUseSFTFlag() = runTest { val (arrangement, featureConfigEventReceiver) = Arrangement() + .withSetUseSFTForOneOnOneCallsSuccessful() .withSettingConferenceCallingEnabledSuccessful() .arrange() @@ -125,11 +126,16 @@ class FeatureConfigEventReceiverTest { verify { arrangement.userConfigRepository.setConferenceCallingEnabled(eq(true)) }.wasInvoked(once) + + verify { + arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(eq(false)) + }.wasInvoked(once) } @Test - fun givenConferenceCallingUpdatedEventGrantingAccess_whenProcessingEvent_ThenSetConferenceCallingEnabledToFalse() = runTest { + fun givenConferenceCallingEventDisabled_whenProcessingEvent_ThenSetConferenceCallingEnabledToFalseOnly() = runTest { val (arrangement, featureConfigEventReceiver) = Arrangement() + .withSetUseSFTForOneOnOneCallsSuccessful() .withSettingConferenceCallingEnabledSuccessful() .arrange() @@ -141,6 +147,10 @@ class FeatureConfigEventReceiverTest { verify { arrangement.userConfigRepository.setConferenceCallingEnabled(eq(false)) }.wasInvoked(once) + + verify { + arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(eq(true)) + }.wasNotInvoked() } @Test @@ -339,6 +349,12 @@ class FeatureConfigEventReceiverTest { }.returns(Either.Right(Unit)) } + fun withSetUseSFTForOneOnOneCallsSuccessful() = apply { + every { + userConfigRepository.setUseSFTForOneOnOneCalls(any()) + }.returns(Either.Right(Unit)) + } + fun withIsFileSharingEnabled(result: Either) = apply { every { userConfigRepository.isFileSharingEnabled() From 277a29a96b7f9192e2e24884868c048c7b0f61be Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 15:22:11 +0200 Subject: [PATCH 05/10] feat: use SFT for OneOnOne calls if the flag is true --- ...tionType.kt => ConversationTypeForCall.kt} | 2 +- .../call/scenario/OnIncomingCallTest.kt | 7 +- .../logic/feature/call/CallManagerImpl.kt | 2 + .../logic/feature/call/GlobalCallManager.kt | 2 + .../logic/feature/call/CallManagerImpl.kt | 17 +- .../logic/feature/call/GlobalCallManager.kt | 3 + .../feature/call/scenario/OnIncomingCall.kt | 4 +- .../kalium/logic/data/call/CallRepository.kt | 4 +- .../logic/data/call/mapper/CallMapper.kt | 72 +++-- .../data/conversation/ConversationMapper.kt | 6 + .../conversation/ConversationRepository.kt | 7 + .../kalium/logic/feature/UserSessionScope.kt | 36 ++- .../kalium/logic/feature/call/CallManager.kt | 2 + .../kalium/logic/feature/call/CallsScope.kt | 17 +- .../logic/feature/call/GlobalCallManager.kt | 2 + .../GetCallConversationTypeProvider.kt | 71 +++++ .../feature/call/usecase/StartCallUseCase.kt | 5 + .../kalium/logic/data/call/CallMapperTest.kt | 24 +- .../logic/data/call/CallRepositoryTest.kt | 20 +- .../GetCallConversationTypeUseCaseTest.kt | 259 ++++++++++++++++++ .../call/usecase/StartCallUseCaseTest.kt | 25 +- .../wire/kalium/persistence/Conversations.sq | 3 + .../dao/conversation/ConversationDAO.kt | 1 + .../dao/conversation/ConversationDAOImpl.kt | 4 + 24 files changed, 509 insertions(+), 86 deletions(-) rename data/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/GetCallConversationTypeUseCaseTest.kt 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/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..425af046bd9 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..fc27a686331 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,12 @@ 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/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..db0f203416a 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 @@ -41,6 +41,7 @@ class StartCallUseCase internal constructor( 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 +61,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/GetCallConversationTypeUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeUseCaseTest.kt new file mode 100644 index 00000000000..efaa28ea5ea --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeUseCaseTest.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 GetCallConversationTypeUseCaseTest { + + @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/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..3c0b5d35044 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,10 @@ 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 ac83dfb43aac26a256eb4d6d0a07d17914b0efc1 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 15:27:08 +0200 Subject: [PATCH 06/10] chore:detekt --- .../kalium/logic/data/conversation/ConversationMapper.kt | 2 +- .../logic/data/conversation/ConversationRepository.kt | 9 +++++---- .../logic/feature/call/usecase/StartCallUseCase.kt | 1 + .../persistence/dao/conversation/ConversationDAOImpl.kt | 7 ++++--- 4 files changed, 11 insertions(+), 8 deletions(-) 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 425af046bd9..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 @@ -457,7 +457,7 @@ internal class ConversationMapperImpl( ConversationEntity.LegalHoldStatus.DISABLED -> Conversation.LegalHoldStatus.DISABLED } - override fun fromConversationEntityType(type: ConversationEntity.Type): Conversation.Type { + override fun fromConversationEntityType(type: ConversationEntity.Type): Conversation.Type { return type.fromDaoModelToType() } } 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 fc27a686331..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 @@ -622,11 +622,12 @@ 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 getConversationTypeById(conversationId: ConversationId): Either = + wrapStorageRequest { + conversationDAO.getConversationTypeById(conversationId.toDao())?.let { + conversationMapper.fromConversationEntityType(it) + } } - } override suspend fun observeCacheDetailsById(conversationId: ConversationId): Either> = wrapStorageRequest { 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 db0f203416a..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,6 +36,7 @@ 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, 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 3c0b5d35044..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,9 +197,10 @@ internal class ConversationDAOImpl internal constructor( } } - override suspend fun getConversationTypeById(conversationId: QualifiedIDEntity): ConversationEntity.Type?= withContext(coroutineContext) { - conversationQueries.getConversationTypeById(conversationId).executeAsOneOrNull() - } + override suspend fun getConversationTypeById(conversationId: QualifiedIDEntity): ConversationEntity.Type? = + withContext(coroutineContext) { + conversationQueries.getConversationTypeById(conversationId).executeAsOneOrNull() + } override suspend fun getTeamConversationIdsReadyToCompleteMigration(teamId: String): List { return withContext(coroutineContext) { From b2b869ad3d8c193c083a249a1bef73b40af0b21b Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 23 Jul 2024 16:32:00 +0200 Subject: [PATCH 07/10] chore: Empty-Commit From 894d73eea9ed0c505a945bf9b67ae7a190717db0 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Thu, 25 Jul 2024 16:40:38 +0200 Subject: [PATCH 08/10] chore: unit test --- .../com/wire/kalium/logic/feature/call/CallManagerTest.kt | 5 +++++ ...UseCaseTest.kt => GetCallConversationTypeProviderTest.kt} | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) rename logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/{GetCallConversationTypeUseCaseTest.kt => GetCallConversationTypeProviderTest.kt} (99%) 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/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt similarity index 99% rename from logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeUseCaseTest.kt rename to logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt index efaa28ea5ea..068f0a21787 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetCallConversationTypeProviderTest.kt @@ -40,7 +40,7 @@ import kotlinx.datetime.Clock import kotlin.test.Test import kotlin.test.assertEquals -class GetCallConversationTypeUseCaseTest { +class GetCallConversationTypeProviderTest { @Test fun givenShouldUseSFTForOneOnOneCallsAndMLSEnabled_whenRunningUseCase_thenReturnConferenceMls() = From e4e9ff3eb0ae32fb128564d021b537e78c604d5c Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Thu, 25 Jul 2024 17:14:18 +0200 Subject: [PATCH 09/10] chore: make ConferenceCallingConfigDTO optional --- .../wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt | 2 +- .../api/authenticated/featureConfigs/FeatureConfigResponse.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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..62c3c3e9730 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 backend is ready ) : FeatureConfigData() @SerialName("conversationGuestLinks") From b344825882d087a421450670ad346db3265f1c2f Mon Sep 17 00:00:00 2001 From: Mohamad Jaara Date: Fri, 26 Jul 2024 09:40:28 +0200 Subject: [PATCH 10/10] edit comment --- .../api/authenticated/featureConfigs/FeatureConfigResponse.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 62c3c3e9730..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? // TODO make it non optional when the backend is ready + val config: ConferenceCallingConfigDTO? // TODO make it non optional when the minimum supported API is v6 ) : FeatureConfigData() @SerialName("conversationGuestLinks")