From 65ab78ab84ba7ec570d82a620bbe4d0f2f50f49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Tue, 19 Nov 2024 07:47:15 +0100 Subject: [PATCH 1/7] feat: Recently ended call metadata [WPB-14256] --- .../logic/data/call/RecentlyEndedCall.kt | 23 +++ .../data/call/RecentlyEndedCallMetadata.kt | 45 ++++++ .../feature/call/scenario/OnCloseCall.kt | 11 +- .../kalium/logic/data/call/CallRepository.kt | 58 +++++-- .../kalium/logic/feature/UserSessionScope.kt | 8 + .../kalium/logic/feature/call/CallsScope.kt | 7 + .../GetRecentlyEndedCallMetadataUseCase.kt | 79 ++++++++++ .../ObserveRecentlyEndedCallUseCase.kt | 39 +++++ .../logic/feature/search/SearchScope.kt | 2 +- .../feature/search/SearchUsersUseCase.kt | 22 ++- ...GetRecentlyEndedCallMetadataUseCaseTest.kt | 144 ++++++++++++++++++ .../logic/feature/search/SearchUseCaseTest.kt | 2 +- .../feature/call/scenario/OnCloseCallTest.kt | 45 ++++++ 13 files changed, 460 insertions(+), 25 deletions(-) create mode 100644 data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt create mode 100644 data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt new file mode 100644 index 00000000000..fa5e23169b1 --- /dev/null +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt @@ -0,0 +1,23 @@ +/* + * 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.data.call + +data class RecentlyEndedCall( + val call: Call, + val callEndedCause: Int +) diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt new file mode 100644 index 00000000000..d64d5109f14 --- /dev/null +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt @@ -0,0 +1,45 @@ +/* + * 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.data.call + +import com.wire.kalium.logic.data.conversation.Conversation + +data class RecentlyEndedCallMetadata( + val callEndReason: Int, + val callDetails: CallDetails, + val conversationDetails: ConversationDetails, + val isTeamMember: Boolean +) { + data class CallDetails( + val isCallScreenShare: Boolean, + val callScreenShareUniques: Int, + val isOutgoingCall: Boolean, + val callDurationInSeconds: Long, + val callParticipantsCount: Int, + val conversationServices: Int, + val callAVSwitchToggle: Boolean, + val callVideoEnabled: Boolean + ) + + data class ConversationDetails( + val conversationType: Conversation.Type, + val conversationSize: Int, + val conversationGuests: Int, + val conversationGuestsPro: Int + ) +} diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt index 26d8c0b8194..9a1191f3570 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt @@ -62,6 +62,7 @@ class OnCloseCall( val conversationIdWithDomain = qualifiedIdMapper.fromStringToQualifiedID(conversationId) scope.launch { + val callMetadata = callRepository.getCallMetadataProfile()[conversationIdWithDomain] val isConnectedToInternet = networkStateObserver.observeNetworkState().value == NetworkState.ConnectedWithInternet @@ -78,7 +79,15 @@ class OnCloseCall( status = callStatus ) - if (callRepository.getCallMetadataProfile()[conversationIdWithDomain]?.protocol is Conversation.ProtocolInfo.MLS) { + callMetadata?.let { + callRepository.updateRecentlyEndedCall( + conversationId = conversationIdWithDomain, + callMetadata = callMetadata, + callEndReason = reason + ) + } + + if (callMetadata?.protocol is Conversation.ProtocolInfo.MLS) { callRepository.leaveMlsConference(conversationIdWithDomain) } callingLogger.i("[OnCloseCall] -> ConversationId: ${conversationId.obfuscateId()} | callStatus: $callStatus") 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 e5f8bc07ec4..6c1d2e306d0 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 @@ -74,6 +74,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -135,6 +136,9 @@ interface CallRepository { suspend fun advanceEpoch(conversationId: ConversationId) fun currentCallProtocol(conversationId: ConversationId): Conversation.ProtocolInfo? suspend fun observeCurrentCall(conversationId: ConversationId): Flow + + suspend fun updateRecentlyEndedCall(conversationId: ConversationId, callMetadata: CallMetadata, callEndReason: Int) + suspend fun observeRecentlyEndedCall(): Flow } @Suppress("LongParameterList", "TooManyFunctions") @@ -163,25 +167,47 @@ internal class CallDataSource( private val scope = CoroutineScope(job + kaliumDispatchers.io) private val callJobs = ConcurrentMutableMap() private val staleParticipantJobs = ConcurrentMutableMap() + private val _recentlyEndedCallFlow = MutableSharedFlow( + extraBufferCapacity = 1 + ) override suspend fun observeCurrentCall(conversationId: ConversationId): Flow = _callMetadataProfile.map { - it[conversationId]?.let { currentCall -> - Call( - conversationId = conversationId, - status = currentCall.callStatus, - isMuted = currentCall.isMuted, - isCameraOn = currentCall.isCameraOn, - isCbrEnabled = currentCall.isCbrEnabled, - callerId = currentCall.callerId, - conversationName = currentCall.conversationName, - conversationType = currentCall.conversationType, - callerName = currentCall.callerName, - callerTeamName = currentCall.callerTeamName, - establishedTime = currentCall.establishedTime, - participants = currentCall.getFullParticipants(), - maxParticipants = currentCall.maxParticipants + it[conversationId]?.mapCallMetadataToCall(conversationId) + } + + override suspend fun updateRecentlyEndedCall( + conversationId: ConversationId, + callMetadata: CallMetadata, + callEndReason: Int + ) { + _recentlyEndedCallFlow.tryEmit( + RecentlyEndedCall( + call = callMetadata.mapCallMetadataToCall(conversationId), + callEndedCause = callEndReason ) - } + ) + } + + private fun CallMetadata.mapCallMetadataToCall(conversationId: ConversationId): Call { + return Call( + conversationId = conversationId, + status = callStatus, + isMuted = isMuted, + isCameraOn = isCameraOn, + isCbrEnabled = isCbrEnabled, + callerId = callerId, + conversationName = conversationName, + conversationType = conversationType, + callerName = callerName, + callerTeamName = callerTeamName, + establishedTime = establishedTime, + participants = getFullParticipants(), + maxParticipants = maxParticipants + ) + } + + override suspend fun observeRecentlyEndedCall(): Flow { + return _recentlyEndedCallFlow } override suspend fun getCallConfigResponse(limit: Int?): Either = wrapApiRequest { 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 53cf5806b89..415f6de58a2 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 @@ -178,6 +178,8 @@ import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdat 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.GetRecentlyEndedCallMetadataUseCase +import com.wire.kalium.logic.feature.call.usecase.GetRecentlyEndedCallMetadataUseCaseImpl 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 @@ -2091,6 +2093,12 @@ class UserSessionScope internal constructor( userScopedLogger ) + val getRecentlyEndedCallMetadata: GetRecentlyEndedCallMetadataUseCase + get() = GetRecentlyEndedCallMetadataUseCaseImpl( + observeConversationMembers = conversations.observeConversationMembers, + getSelf = users.getSelfUser + ) + internal val getProxyCredentials: GetProxyCredentialsUseCase get() = GetProxyCredentialsUseCaseImpl(sessionManager) 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 5d401de2afc..15a8b320406 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 @@ -61,6 +61,8 @@ 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.ObserveRecentlyEndedCallUseCase +import com.wire.kalium.logic.feature.call.usecase.ObserveRecentlyEndedCallUseCaseImpl 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 @@ -227,4 +229,9 @@ class CallsScope internal constructor( val updateNextTimeCallFeedback: UpdateNextTimeCallFeedbackUseCase by lazy { UpdateNextTimeCallFeedbackUseCase(userConfigRepository) } + + val observeRecentlyEndedCall: ObserveRecentlyEndedCallUseCase + get() = ObserveRecentlyEndedCallUseCaseImpl( + callRepository = callRepository + ) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt new file mode 100644 index 00000000000..b5ef44af925 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt @@ -0,0 +1,79 @@ +/* + * 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.logic.data.call.Call +import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata +import com.wire.kalium.logic.data.user.type.UserType +import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase +import com.wire.kalium.logic.feature.user.GetSelfUserUseCase +import com.wire.kalium.util.DateTimeUtil +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull + +/** + * Given a call and raw call end reason create metadata containing all information regarding + * a call. + */ +interface GetRecentlyEndedCallMetadataUseCase { + suspend operator fun invoke(call: Call, callEndedReason: Int): RecentlyEndedCallMetadata +} + +class GetRecentlyEndedCallMetadataUseCaseImpl( + private val observeConversationMembers: ObserveConversationMembersUseCase, + private val getSelf: GetSelfUserUseCase, +) : GetRecentlyEndedCallMetadataUseCase { + override suspend fun invoke(call: Call, callEndedReason: Int): RecentlyEndedCallMetadata { + val selfUser = getSelf().firstOrNull() + val selfCallUser = call.participants.firstOrNull { participant -> participant.userType == UserType.OWNER } + val conversationMembers = observeConversationMembers(call.conversationId).first() + val conversationServicesCount = conversationMembers.count { member -> member.user.userType == UserType.SERVICE } + val guestsCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST } + val guestsProCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST && member.user.teamId != null } + val uniqueScreenShares = call.participants.count { participant -> participant.isSharingScreen } + val isOutgoingCall = call.callerId.value == selfCallUser?.id?.value + val callDurationInSeconds = call.establishedTime?.let { + DateTimeUtil.calculateMillisDifference(it, DateTimeUtil.currentIsoDateTimeString()) / MILLIS_IN_SECOND + } ?: 0L + + return RecentlyEndedCallMetadata( + callEndReason = callEndedReason, + callDetails = RecentlyEndedCallMetadata.CallDetails( + isCallScreenShare = selfCallUser?.isSharingScreen ?: false, + callScreenShareUniques = uniqueScreenShares, + isOutgoingCall = isOutgoingCall, + callDurationInSeconds = callDurationInSeconds, + callParticipantsCount = call.participants.size, + conversationServices = conversationServicesCount, + callAVSwitchToggle = selfCallUser?.isCameraOn ?: false, + callVideoEnabled = call.isCameraOn + ), + conversationDetails = RecentlyEndedCallMetadata.ConversationDetails( + conversationType = call.conversationType, + conversationSize = conversationMembers.size, + conversationGuests = guestsCount, + conversationGuestsPro = guestsProCount + ), + isTeamMember = selfUser?.teamId != null + ) + } + + private companion object { + const val MILLIS_IN_SECOND = 1_000L + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt new file mode 100644 index 00000000000..a87a72dd5cd --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt @@ -0,0 +1,39 @@ +/* + * 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.logic.data.call.CallRepository +import com.wire.kalium.logic.data.call.RecentlyEndedCall +import kotlinx.coroutines.flow.Flow +import com.wire.kalium.logic.data.id.ConversationId + +/** + * Use case to observe recently ended call. This gives us the call that has been ended and also + * raw cause of the end. + */ +interface ObserveRecentlyEndedCallUseCase { + suspend operator fun invoke(conversationId: ConversationId): Flow +} + +class ObserveRecentlyEndedCallUseCaseImpl internal constructor( + private val callRepository: CallRepository, +) : ObserveRecentlyEndedCallUseCase { + override suspend fun invoke(conversationId: ConversationId): Flow { + return callRepository.observeRecentlyEndedCall() + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchScope.kt index 662514c9447..6eb97fadcfd 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchScope.kt @@ -29,7 +29,7 @@ class SearchScope internal constructor( private val kaliumConfigs: KaliumConfigs ) { val searchUsers: SearchUsersUseCase - get() = SearchUsersUseCase( + get() = SearchUsersUseCaseImpl( searchUserRepository, selfUserId, kaliumConfigs.maxRemoteSearchResultCount diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchUsersUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchUsersUseCase.kt index 74b27af6a65..b9cd963b41b 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchUsersUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchUsersUseCase.kt @@ -30,16 +30,26 @@ import kotlinx.coroutines.coroutineScope /** * Use case for searching users. - * @param searchQuery The search query. - * @param excludingMembersOfConversation The conversation to exclude its members from the search. - * @param customDomain The custom domain to search in if null the search will be on the self user domain. */ -class SearchUsersUseCase internal constructor( +interface SearchUsersUseCase { + /** + * @param searchQuery The search query. + * @param excludingMembersOfConversation The conversation to exclude its members from the search. + * @param customDomain The custom domain to search in if null the search will be on the self user domain. + */ + suspend operator fun invoke( + searchQuery: String, + excludingMembersOfConversation: ConversationId?, + customDomain: String? + ): SearchUserResult +} + +class SearchUsersUseCaseImpl internal constructor( private val searchUserRepository: SearchUserRepository, private val selfUserId: UserId, private val maxRemoteSearchResultCount: Int -) { - suspend operator fun invoke( +) : SearchUsersUseCase { + override suspend operator fun invoke( searchQuery: String, excludingMembersOfConversation: ConversationId?, customDomain: String? diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt new file mode 100644 index 00000000000..b5045ee8638 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt @@ -0,0 +1,144 @@ +/* + * 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.logic.data.call.RecentlyEndedCallMetadata +import com.wire.kalium.logic.data.conversation.Conversation +import com.wire.kalium.logic.data.conversation.MemberDetails +import com.wire.kalium.logic.data.user.type.UserType +import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase +import com.wire.kalium.logic.feature.user.GetSelfUserUseCase +import com.wire.kalium.logic.framework.TestCall +import com.wire.kalium.logic.framework.TestCall.CALLER_ID +import com.wire.kalium.logic.framework.TestUser +import io.mockative.Mock +import io.mockative.any +import io.mockative.coEvery +import io.mockative.mock +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class GetRecentlyEndedCallMetadataUseCaseTest { + + @Test + fun givenCallAndEndCallReaction_whenUseCaseInvoked_thenCreatesProperMetadata() = runTest { + // given + val (_, useCase) = Arrangement() + .withSelfUser() + .withConversationMembers() + .arrange() + + // when + val metadata = useCase(TestCall.oneOnOneEstablishedCall(), callEndedReason = 2) + + // then + assertEquals(2, metadata.callEndReason) + assertEquals( + RecentlyEndedCallMetadata.CallDetails( + isCallScreenShare = false, + callScreenShareUniques = 0, + isOutgoingCall = false, + callDurationInSeconds = 0L, + callParticipantsCount = 1, + conversationServices = 0, + callAVSwitchToggle = false, + callVideoEnabled = false + ), metadata.callDetails + ) + assertEquals( + RecentlyEndedCallMetadata.ConversationDetails( + conversationType = Conversation.Type.ONE_ON_ONE, + conversationSize = 2, + conversationGuests = 0, + conversationGuestsPro = 0 + ), metadata.conversationDetails + ) + assertEquals(true, metadata.isTeamMember) + } + + @Test + fun givenCallDetailsWithinConversationWithGuests_whenUseCaseInvoked_thenReturnCorrectMetadataGuestsCount() = runTest { + // given + val (_, useCase) = Arrangement() + .withSelfUser() + .withConversationGuests() + .arrange() + + // when + val metadata = useCase(TestCall.oneOnOneEstablishedCall(), callEndedReason = 2) + + // then + assertEquals(1, metadata.conversationDetails.conversationGuests) + } + + @Test + fun givenIncomingCallDetails_whenUseCaseInvoked_thenReturnCorrectMetadataIncomingCall() = runTest { + // given + val (_, useCase) = Arrangement() + .withSelfUser() + .withConversationGuests() + .arrange() + + // when + val metadata = useCase(TestCall.oneOnOneEstablishedCall().copy(callerId = CALLER_ID.copy(value = "external")), callEndedReason = 2) + + // then + assertEquals(false, metadata.callDetails.isOutgoingCall) + } + + private class Arrangement { + @Mock + val observeConversationMembers = mock(ObserveConversationMembersUseCase::class) + + @Mock + val getSelf = mock(GetSelfUserUseCase::class) + + suspend fun withConversationMembers() = apply { + coEvery { observeConversationMembers(any()) }.returns( + flowOf( + listOf( + MemberDetails(TestUser.SELF, Conversation.Member.Role.Admin), + MemberDetails(TestUser.OTHER, Conversation.Member.Role.Member) + ) + ) + ) + } + + suspend fun withConversationGuests() = apply { + coEvery { observeConversationMembers(any()) }.returns( + flowOf( + listOf( + MemberDetails(TestUser.SELF, Conversation.Member.Role.Admin), + MemberDetails(TestUser.OTHER.copy(userType = UserType.GUEST), Conversation.Member.Role.Member) + ) + ) + ) + } + + suspend fun withSelfUser() = apply { + coEvery { getSelf() }.returns(flowOf(TestUser.SELF)) + } + + fun arrange(): Pair = this to GetRecentlyEndedCallMetadataUseCaseImpl( + observeConversationMembers = observeConversationMembers, + getSelf = getSelf + ) + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/search/SearchUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/search/SearchUseCaseTest.kt index 821f32c3a2b..aafedb5a00c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/search/SearchUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/search/SearchUseCaseTest.kt @@ -243,7 +243,7 @@ class SearchUseCaseTest { private class Arrangement : SearchRepositoryArrangement by SearchRepositoryArrangementImpl() { - private val searchUseCase: SearchUsersUseCase = SearchUsersUseCase( + private val searchUseCase: SearchUsersUseCase = SearchUsersUseCaseImpl( searchUserRepository = searchUserRepository, selfUserId = selfUserID, maxRemoteSearchResultCount = 30 diff --git a/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt b/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt index 56fb0aec4e2..6b0c6dbafb0 100644 --- a/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt +++ b/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt @@ -34,6 +34,7 @@ import com.wire.kalium.logic.framework.TestUser import com.wire.kalium.network.NetworkState import com.wire.kalium.network.NetworkStateObserver import io.mockative.Mock +import io.mockative.any import io.mockative.coVerify import io.mockative.eq import io.mockative.every @@ -338,6 +339,50 @@ class OnCloseCallTest { }.wasNotInvoked() } + @Test + fun givenCloseCallInvoked_whenClosedCallMetadataIsPresent_thenUpdateRecentEndedCallInvoked() = + testScope.runTest { + val reason = CallClosedReason.CANCELLED.avsValue + + onCloseCall.onClosedCall( + reason, + conversationIdString, + time, + userIdString, + clientId, + null + ) + yield() + + coVerify { + callRepository.updateRecentlyEndedCall(any(), any(), any()) + }.wasInvoked(once) + } + + @Test + fun givenCloseCallInvoked_whenClosedCallMetadataIsAbsent_thenUpdateRecentEndedCallNotInvoked() = + testScope.runTest { + val reason = CallClosedReason.CANCELLED.avsValue + + every { + callRepository.getCallMetadataProfile() + }.returns(CallMetadataProfile(emptyMap())) + + onCloseCall.onClosedCall( + reason, + conversationIdString, + time, + userIdString, + clientId, + null + ) + yield() + + coVerify { + callRepository.updateRecentlyEndedCall(any(), any(), any()) + }.wasNotInvoked() + } + companion object { private val conversationId = ConversationId("conversationId", "wire.com") private const val conversationIdString = "conversationId@wire.com" From 081395d879f29215628954a4ab4c3ca9868908f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Tue, 19 Nov 2024 22:06:37 +0100 Subject: [PATCH 2/7] Small test changes --- ...GetRecentlyEndedCallMetadataUseCaseTest.kt | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt index b5045ee8638..d39bbe7b651 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt @@ -17,6 +17,8 @@ */ package com.wire.kalium.logic.feature.call.usecase +import com.wire.kalium.logic.data.call.Call +import com.wire.kalium.logic.data.call.Participant import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.MemberDetails @@ -46,7 +48,7 @@ class GetRecentlyEndedCallMetadataUseCaseTest { .arrange() // when - val metadata = useCase(TestCall.oneOnOneEstablishedCall(), callEndedReason = 2) + val metadata = useCase(callWithOwner(), callEndedReason = 2) // then assertEquals(2, metadata.callEndReason) @@ -54,9 +56,9 @@ class GetRecentlyEndedCallMetadataUseCaseTest { RecentlyEndedCallMetadata.CallDetails( isCallScreenShare = false, callScreenShareUniques = 0, - isOutgoingCall = false, + isOutgoingCall = true, callDurationInSeconds = 0L, - callParticipantsCount = 1, + callParticipantsCount = 2, conversationServices = 0, callAVSwitchToggle = false, callVideoEnabled = false @@ -82,7 +84,7 @@ class GetRecentlyEndedCallMetadataUseCaseTest { .arrange() // when - val metadata = useCase(TestCall.oneOnOneEstablishedCall(), callEndedReason = 2) + val metadata = useCase(callWithOwner(), callEndedReason = 2) // then assertEquals(1, metadata.conversationDetails.conversationGuests) @@ -97,7 +99,7 @@ class GetRecentlyEndedCallMetadataUseCaseTest { .arrange() // when - val metadata = useCase(TestCall.oneOnOneEstablishedCall().copy(callerId = CALLER_ID.copy(value = "external")), callEndedReason = 2) + val metadata = useCase(callWithOwner().copy(callerId = CALLER_ID.copy(value = "external")), callEndedReason = 2) // then assertEquals(false, metadata.callDetails.isOutgoingCall) @@ -141,4 +143,28 @@ class GetRecentlyEndedCallMetadataUseCaseTest { getSelf = getSelf ) } + + private companion object { + fun callWithOwner(): Call { + return TestCall.oneOnOneEstablishedCall() + .copy( + callerId = CALLER_ID.copy(value = "ownerId"), + participants = TestCall.oneOnOneEstablishedCall().participants.plus( + Participant( + id = CALLER_ID.copy(value = "ownerId"), + clientId = "abcd", + isMuted = true, + isCameraOn = false, + isSharingScreen = false, + hasEstablishedAudio = true, + name = "User Name", + avatarAssetId = null, + userType = UserType.OWNER, + isSpeaking = false, + accentId = 0 + ) + ) + ) + } + } } From 5c89646b486e348379564bbff0e561b7fe9a577b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Wed, 20 Nov 2024 09:58:15 +0100 Subject: [PATCH 3/7] Make use case internal constructor --- .../feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt index b5ef44af925..f9b68bc58f6 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt @@ -34,7 +34,7 @@ interface GetRecentlyEndedCallMetadataUseCase { suspend operator fun invoke(call: Call, callEndedReason: Int): RecentlyEndedCallMetadata } -class GetRecentlyEndedCallMetadataUseCaseImpl( +class GetRecentlyEndedCallMetadataUseCaseImpl internal constructor( private val observeConversationMembers: ObserveConversationMembersUseCase, private val getSelf: GetSelfUserUseCase, ) : GetRecentlyEndedCallMetadataUseCase { From be6083ebc6425a0c2226b4b9ce6a94a6d86b13a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Wed, 20 Nov 2024 14:45:30 +0100 Subject: [PATCH 4/7] feat: Recently ended call metadata compact [WPB-14256] (#3113) --- .../logic/data/call/RecentlyEndedCall.kt | 23 --- .../logic/feature/call/CallManagerTest.kt | 7 +- .../logic/feature/call/GlobalCallManager.kt | 4 +- .../logic/feature/call/CallManagerImpl.kt | 5 +- .../logic/feature/call/GlobalCallManager.kt | 7 +- .../feature/call/scenario/OnCloseCall.kt | 16 +- .../kalium/logic/data/call/CallRepository.kt | 21 +-- .../kalium/logic/feature/UserSessionScope.kt | 12 +- .../kalium/logic/feature/call/CallsScope.kt | 8 +- .../logic/feature/call/GlobalCallManager.kt | 4 +- ...ersistRecentlyEndedCallMetadataUseCase.kt} | 36 ++-- ...bserveRecentlyEndedCallMetadataUseCase.kt} | 18 +- ...stRecentlyEndedCallMetadataUseCaseTest.kt} | 171 +++++++++++++----- .../feature/call/scenario/OnCloseCallTest.kt | 36 +--- 14 files changed, 214 insertions(+), 154 deletions(-) delete mode 100644 data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt rename logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/{GetRecentlyEndedCallMetadataUseCase.kt => CreateAndPersistRecentlyEndedCallMetadataUseCase.kt} (67%) rename logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/{ObserveRecentlyEndedCallUseCase.kt => ObserveRecentlyEndedCallMetadataUseCase.kt} (68%) rename logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/{GetRecentlyEndedCallMetadataUseCaseTest.kt => CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt} (51%) diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt deleted file mode 100644 index fa5e23169b1..00000000000 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCall.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.data.call - -data class RecentlyEndedCall( - val call: Call, - val callEndedCause: Int -) 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 2e89948aefd..7435be07478 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 @@ -36,6 +36,7 @@ import com.wire.kalium.logic.data.message.MessageContent 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.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.call.usecase.GetCallConversationTypeProvider import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs @@ -103,6 +104,9 @@ class CallManagerTest { @Mock private val getCallConversationType = mock(GetCallConversationTypeProvider::class) + @Mock + private val createAndPersistRecentlyEndedCallMetadata = mock(CreateAndPersistRecentlyEndedCallMetadataUseCase::class) + private val dispatcher = TestKaliumDispatcher private lateinit var callManagerImpl: CallManagerImpl @@ -132,7 +136,8 @@ class CallManagerTest { networkStateObserver = networkStateObserver, kaliumConfigs = kaliumConfigs, mediaManagerService = mediaManagerService, - flowManagerService = flowManagerService + flowManagerService = flowManagerService, + createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata ) } 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 1382845bf18..648c3d755ee 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 @@ -34,6 +34,7 @@ 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.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.network.NetworkStateObserver @@ -56,7 +57,8 @@ actual class GlobalCallManager { conversationClientsInCallUpdater: ConversationClientsInCallUpdater, getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, - kaliumConfigs: KaliumConfigs + kaliumConfigs: KaliumConfigs, + createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase ): CallManager { return CallManagerImpl() } diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt index 693c76d98f9..14ce1d51f68 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 @@ -75,6 +75,7 @@ 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.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.functional.fold @@ -119,6 +120,7 @@ class CallManagerImpl internal constructor( private val kaliumConfigs: KaliumConfigs, private val mediaManagerService: MediaManagerService, private val flowManagerService: FlowManagerService, + private val createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase, private val json: Json = Json { ignoreUnknownKeys = true }, private val shouldRemoteMuteChecker: ShouldRemoteMuteChecker = ShouldRemoteMuteCheckerImpl(), private val serverTimeHandler: ServerTimeHandler = ServerTimeHandlerImpl(), @@ -219,7 +221,8 @@ class CallManagerImpl internal constructor( callRepository = callRepository, networkStateObserver = networkStateObserver, scope = scope, - qualifiedIdMapper = qualifiedIdMapper + qualifiedIdMapper = qualifiedIdMapper, + createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata ).keepingStrongReference(), metricsHandler = metricsHandler, callConfigRequestHandler = OnConfigRequest(calling, callRepository, scope) 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 277eadc1662..1471ec8dd5e 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 @@ -39,6 +39,7 @@ 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.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.util.CurrentPlatform @@ -94,7 +95,8 @@ actual class GlobalCallManager( conversationClientsInCallUpdater: ConversationClientsInCallUpdater, getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, - kaliumConfigs: KaliumConfigs + kaliumConfigs: KaliumConfigs, + createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase ): CallManager { if (kaliumConfigs.enableCalling) { return callManagerHolder.computeIfAbsent(userId) { @@ -116,7 +118,8 @@ actual class GlobalCallManager( mediaManagerService = mediaManager, flowManagerService = flowManager, userConfigRepository = userConfigRepository, - kaliumConfigs = kaliumConfigs + kaliumConfigs = kaliumConfigs, + createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata ) } } else { diff --git a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt index 9a1191f3570..cafbc99d38b 100644 --- a/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt +++ b/logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt @@ -31,6 +31,7 @@ import com.wire.kalium.logic.data.call.CallStatus import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.QualifiedIdMapper +import com.wire.kalium.logic.feature.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.network.NetworkState import com.wire.kalium.network.NetworkStateObserver import kotlinx.coroutines.CoroutineScope @@ -41,7 +42,8 @@ class OnCloseCall( private val callRepository: CallRepository, private val scope: CoroutineScope, private val qualifiedIdMapper: QualifiedIdMapper, - private val networkStateObserver: NetworkStateObserver + private val networkStateObserver: NetworkStateObserver, + private val createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase ) : CloseCallHandler { override fun onClosedCall( reason: Int, @@ -79,19 +81,15 @@ class OnCloseCall( status = callStatus ) - callMetadata?.let { - callRepository.updateRecentlyEndedCall( - conversationId = conversationIdWithDomain, - callMetadata = callMetadata, - callEndReason = reason - ) - } - if (callMetadata?.protocol is Conversation.ProtocolInfo.MLS) { callRepository.leaveMlsConference(conversationIdWithDomain) } callingLogger.i("[OnCloseCall] -> ConversationId: ${conversationId.obfuscateId()} | callStatus: $callStatus") } + + scope.launch { + createAndPersistRecentlyEndedCallMetadata(conversationIdWithDomain, reason) + } } private fun shouldPersistMissedCall( 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 6c1d2e306d0..852315927f5 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 @@ -137,8 +137,8 @@ interface CallRepository { fun currentCallProtocol(conversationId: ConversationId): Conversation.ProtocolInfo? suspend fun observeCurrentCall(conversationId: ConversationId): Flow - suspend fun updateRecentlyEndedCall(conversationId: ConversationId, callMetadata: CallMetadata, callEndReason: Int) - suspend fun observeRecentlyEndedCall(): Flow + suspend fun updateRecentlyEndedCallMetadata(recentlyEndedCallMetadata: RecentlyEndedCallMetadata) + suspend fun observeRecentlyEndedCallMetadata(): Flow } @Suppress("LongParameterList", "TooManyFunctions") @@ -167,7 +167,7 @@ internal class CallDataSource( private val scope = CoroutineScope(job + kaliumDispatchers.io) private val callJobs = ConcurrentMutableMap() private val staleParticipantJobs = ConcurrentMutableMap() - private val _recentlyEndedCallFlow = MutableSharedFlow( + private val _recentlyEndedCallFlow = MutableSharedFlow( extraBufferCapacity = 1 ) @@ -175,17 +175,8 @@ internal class CallDataSource( it[conversationId]?.mapCallMetadataToCall(conversationId) } - override suspend fun updateRecentlyEndedCall( - conversationId: ConversationId, - callMetadata: CallMetadata, - callEndReason: Int - ) { - _recentlyEndedCallFlow.tryEmit( - RecentlyEndedCall( - call = callMetadata.mapCallMetadataToCall(conversationId), - callEndedCause = callEndReason - ) - ) + override suspend fun updateRecentlyEndedCallMetadata(recentlyEndedCallMetadata: RecentlyEndedCallMetadata) { + _recentlyEndedCallFlow.tryEmit(recentlyEndedCallMetadata) } private fun CallMetadata.mapCallMetadataToCall(conversationId: ConversationId): Call { @@ -206,7 +197,7 @@ internal class CallDataSource( ) } - override suspend fun observeRecentlyEndedCall(): Flow { + override suspend fun observeRecentlyEndedCallMetadata(): Flow { return _recentlyEndedCallFlow } 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 415f6de58a2..307bc1a1ae5 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 @@ -178,8 +178,8 @@ import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdat 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.GetRecentlyEndedCallMetadataUseCase -import com.wire.kalium.logic.feature.call.usecase.GetRecentlyEndedCallMetadataUseCaseImpl +import com.wire.kalium.logic.feature.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase +import com.wire.kalium.logic.feature.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl 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 @@ -1265,7 +1265,8 @@ class UserSessionScope internal constructor( conversationClientsInCallUpdater = conversationClientsInCallUpdater, getCallConversationType = getCallConversationType, networkStateObserver = networkStateObserver, - kaliumConfigs = kaliumConfigs + kaliumConfigs = kaliumConfigs, + createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata ) } @@ -2093,8 +2094,9 @@ class UserSessionScope internal constructor( userScopedLogger ) - val getRecentlyEndedCallMetadata: GetRecentlyEndedCallMetadataUseCase - get() = GetRecentlyEndedCallMetadataUseCaseImpl( + private val createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase + get() = CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl( + callRepository = callRepository, observeConversationMembers = conversations.observeConversationMembers, getSelf = users.getSelfUser ) 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 15a8b320406..d5356d7391c 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 @@ -61,8 +61,8 @@ 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.ObserveRecentlyEndedCallUseCase -import com.wire.kalium.logic.feature.call.usecase.ObserveRecentlyEndedCallUseCaseImpl +import com.wire.kalium.logic.feature.call.usecase.ObserveRecentlyEndedCallMetadataUseCase +import com.wire.kalium.logic.feature.call.usecase.ObserveRecentlyEndedCallMetadataUseCaseImpl 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 @@ -230,8 +230,8 @@ class CallsScope internal constructor( UpdateNextTimeCallFeedbackUseCase(userConfigRepository) } - val observeRecentlyEndedCall: ObserveRecentlyEndedCallUseCase - get() = ObserveRecentlyEndedCallUseCaseImpl( + val observeRecentlyEndedCallMetadata: ObserveRecentlyEndedCallMetadataUseCase + get() = ObserveRecentlyEndedCallMetadataUseCaseImpl( callRepository = callRepository ) } 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 eb7bca57c8b..12f8ee9dadf 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 @@ -34,6 +34,7 @@ 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.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.message.MessageSender import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.network.NetworkStateObserver @@ -57,7 +58,8 @@ expect class GlobalCallManager { conversationClientsInCallUpdater: ConversationClientsInCallUpdater, getCallConversationType: GetCallConversationTypeProvider, networkStateObserver: NetworkStateObserver, - kaliumConfigs: KaliumConfigs + kaliumConfigs: KaliumConfigs, + createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase ): CallManager suspend fun removeInMemoryCallingManagerForUser(userId: UserId) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt similarity index 67% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt index f9b68bc58f6..f01dd1e5a7f 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt @@ -18,7 +18,9 @@ package com.wire.kalium.logic.feature.call.usecase import com.wire.kalium.logic.data.call.Call +import com.wire.kalium.logic.data.call.CallRepository import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata +import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase import com.wire.kalium.logic.feature.user.GetSelfUserUseCase @@ -30,24 +32,32 @@ import kotlinx.coroutines.flow.firstOrNull * Given a call and raw call end reason create metadata containing all information regarding * a call. */ -interface GetRecentlyEndedCallMetadataUseCase { - suspend operator fun invoke(call: Call, callEndedReason: Int): RecentlyEndedCallMetadata +interface CreateAndPersistRecentlyEndedCallMetadataUseCase { + suspend operator fun invoke(conversationId: ConversationId, callEndedReason: Int) } -class GetRecentlyEndedCallMetadataUseCaseImpl internal constructor( +class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( + private val callRepository: CallRepository, private val observeConversationMembers: ObserveConversationMembersUseCase, private val getSelf: GetSelfUserUseCase, -) : GetRecentlyEndedCallMetadataUseCase { - override suspend fun invoke(call: Call, callEndedReason: Int): RecentlyEndedCallMetadata { +) : CreateAndPersistRecentlyEndedCallMetadataUseCase { + override suspend fun invoke(conversationId: ConversationId, callEndedReason: Int) { + val call = callRepository.observeCurrentCall(conversationId).first() + call?.createMetadata(callEndedReason = callEndedReason)?.let { metadata -> + callRepository.updateRecentlyEndedCallMetadata(metadata) + } + } + + private suspend fun Call.createMetadata(callEndedReason: Int): RecentlyEndedCallMetadata { val selfUser = getSelf().firstOrNull() - val selfCallUser = call.participants.firstOrNull { participant -> participant.userType == UserType.OWNER } - val conversationMembers = observeConversationMembers(call.conversationId).first() + val selfCallUser = participants.firstOrNull { participant -> participant.userType == UserType.OWNER } + val conversationMembers = observeConversationMembers(conversationId).first() val conversationServicesCount = conversationMembers.count { member -> member.user.userType == UserType.SERVICE } val guestsCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST } val guestsProCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST && member.user.teamId != null } - val uniqueScreenShares = call.participants.count { participant -> participant.isSharingScreen } - val isOutgoingCall = call.callerId.value == selfCallUser?.id?.value - val callDurationInSeconds = call.establishedTime?.let { + val uniqueScreenShares = participants.count { participant -> participant.isSharingScreen } + val isOutgoingCall = callerId.value == selfCallUser?.id?.value + val callDurationInSeconds = establishedTime?.let { DateTimeUtil.calculateMillisDifference(it, DateTimeUtil.currentIsoDateTimeString()) / MILLIS_IN_SECOND } ?: 0L @@ -58,13 +68,13 @@ class GetRecentlyEndedCallMetadataUseCaseImpl internal constructor( callScreenShareUniques = uniqueScreenShares, isOutgoingCall = isOutgoingCall, callDurationInSeconds = callDurationInSeconds, - callParticipantsCount = call.participants.size, + callParticipantsCount = participants.size, conversationServices = conversationServicesCount, callAVSwitchToggle = selfCallUser?.isCameraOn ?: false, - callVideoEnabled = call.isCameraOn + callVideoEnabled = isCameraOn ), conversationDetails = RecentlyEndedCallMetadata.ConversationDetails( - conversationType = call.conversationType, + conversationType = conversationType, conversationSize = conversationMembers.size, conversationGuests = guestsCount, conversationGuestsPro = guestsProCount diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallMetadataUseCase.kt similarity index 68% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallMetadataUseCase.kt index a87a72dd5cd..d1a9de46bcf 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallMetadataUseCase.kt @@ -18,22 +18,22 @@ package com.wire.kalium.logic.feature.call.usecase import com.wire.kalium.logic.data.call.CallRepository -import com.wire.kalium.logic.data.call.RecentlyEndedCall +import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import kotlinx.coroutines.flow.Flow import com.wire.kalium.logic.data.id.ConversationId /** - * Use case to observe recently ended call. This gives us the call that has been ended and also - * raw cause of the end. + * Use case to observe recently ended call metadata. This gives us all metadata assigned to a call. + * Used mainly for analytics. */ -interface ObserveRecentlyEndedCallUseCase { - suspend operator fun invoke(conversationId: ConversationId): Flow +interface ObserveRecentlyEndedCallMetadataUseCase { + suspend operator fun invoke(conversationId: ConversationId): Flow } -class ObserveRecentlyEndedCallUseCaseImpl internal constructor( +class ObserveRecentlyEndedCallMetadataUseCaseImpl internal constructor( private val callRepository: CallRepository, -) : ObserveRecentlyEndedCallUseCase { - override suspend fun invoke(conversationId: ConversationId): Flow { - return callRepository.observeRecentlyEndedCall() +) : ObserveRecentlyEndedCallMetadataUseCase { + override suspend fun invoke(conversationId: ConversationId): Flow { + return callRepository.observeRecentlyEndedCallMetadata() } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt similarity index 51% rename from logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt rename to logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt index d39bbe7b651..d6a22c094b5 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/GetRecentlyEndedCallMetadataUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt @@ -18,10 +18,12 @@ package com.wire.kalium.logic.feature.call.usecase import com.wire.kalium.logic.data.call.Call +import com.wire.kalium.logic.data.call.CallRepository import com.wire.kalium.logic.data.call.Participant import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.MemberDetails +import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase import com.wire.kalium.logic.feature.user.GetSelfUserUseCase @@ -31,78 +33,116 @@ import com.wire.kalium.logic.framework.TestUser import io.mockative.Mock import io.mockative.any import io.mockative.coEvery +import io.mockative.coVerify import io.mockative.mock +import io.mockative.once import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import kotlin.test.Test -import kotlin.test.assertEquals -class GetRecentlyEndedCallMetadataUseCaseTest { +class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { @Test - fun givenCallAndEndCallReaction_whenUseCaseInvoked_thenCreatesProperMetadata() = runTest { + fun givenCallAndEndCallReaction_whenUseCaseInvoked_thenRecentlyCallMetadataIsProperlyUpdated() = runTest { // given - val (_, useCase) = Arrangement() + val (arrangement, useCase) = Arrangement() + .withOutgoingCall() .withSelfUser() .withConversationMembers() .arrange() // when - val metadata = useCase(callWithOwner(), callEndedReason = 2) + useCase( + conversationId = ConversationId(value = "value", domain = "domain"), + callEndedReason = 2 + ) // then - assertEquals(2, metadata.callEndReason) - assertEquals( - RecentlyEndedCallMetadata.CallDetails( - isCallScreenShare = false, - callScreenShareUniques = 0, - isOutgoingCall = true, - callDurationInSeconds = 0L, - callParticipantsCount = 2, - conversationServices = 0, - callAVSwitchToggle = false, - callVideoEnabled = false - ), metadata.callDetails - ) - assertEquals( - RecentlyEndedCallMetadata.ConversationDetails( - conversationType = Conversation.Type.ONE_ON_ONE, - conversationSize = 2, - conversationGuests = 0, - conversationGuestsPro = 0 - ), metadata.conversationDetails - ) - assertEquals(true, metadata.isTeamMember) + coVerify { + arrangement.callRepository.updateRecentlyEndedCallMetadata(DEFAULT_ENDED_CALL_METADATA) + }.wasInvoked(exactly = once) } @Test - fun givenCallDetailsWithinConversationWithGuests_whenUseCaseInvoked_thenReturnCorrectMetadataGuestsCount() = runTest { + fun givenCallDetailsWithinConversationWithGuests_whenUseCaseInvoked_thenRecentlyEndedCallMetadataHasProperGuestsCount() = runTest { // given - val (_, useCase) = Arrangement() + val (arrangement, useCase) = Arrangement() + .withOutgoingCall() .withSelfUser() .withConversationGuests() .arrange() // when - val metadata = useCase(callWithOwner(), callEndedReason = 2) + useCase( + conversationId = ConversationId(value = "value", domain = "domain"), + callEndedReason = 2 + ) + + // then + coVerify { + arrangement.callRepository.updateRecentlyEndedCallMetadata( + DEFAULT_ENDED_CALL_METADATA.copy( + conversationDetails = DEFAULT_ENDED_CALL_METADATA.conversationDetails.copy( + conversationGuests = 1 + ) + ) + ) + }.wasInvoked(exactly = once) + } + + @Test + fun givenCallDetailsWithinConversationWithGuests_whenUseCaseInvoked_thenRecentlyEndedCallMetadataHasProperGuestsProCount() = runTest { + // given + val (arrangement, useCase) = Arrangement() + .withOutgoingCall() + .withSelfUser() + .withConversationGuestsPro() + .arrange() + + // when + useCase( + conversationId = ConversationId(value = "value", domain = "domain"), + callEndedReason = 2 + ) // then - assertEquals(1, metadata.conversationDetails.conversationGuests) + coVerify { + arrangement.callRepository.updateRecentlyEndedCallMetadata( + DEFAULT_ENDED_CALL_METADATA.copy( + conversationDetails = DEFAULT_ENDED_CALL_METADATA.conversationDetails.copy( + conversationGuests = 1, + conversationGuestsPro = 1 + ) + ) + ) + }.wasInvoked(exactly = once) } @Test fun givenIncomingCallDetails_whenUseCaseInvoked_thenReturnCorrectMetadataIncomingCall() = runTest { // given - val (_, useCase) = Arrangement() + val (arrangement, useCase) = Arrangement() + .withIncomingCall() .withSelfUser() - .withConversationGuests() + .withConversationMembers() .arrange() // when - val metadata = useCase(callWithOwner().copy(callerId = CALLER_ID.copy(value = "external")), callEndedReason = 2) + useCase( + conversationId = ConversationId(value = "value", domain = "domain"), + callEndedReason = 2 + ) // then - assertEquals(false, metadata.callDetails.isOutgoingCall) + coVerify { + arrangement.callRepository.updateRecentlyEndedCallMetadata( + DEFAULT_ENDED_CALL_METADATA.copy( + callDetails = DEFAULT_ENDED_CALL_METADATA.callDetails.copy( + isOutgoingCall = false + ) + ) + ) + }.wasInvoked(exactly = once) } private class Arrangement { @@ -112,6 +152,19 @@ class GetRecentlyEndedCallMetadataUseCaseTest { @Mock val getSelf = mock(GetSelfUserUseCase::class) + @Mock + val callRepository = mock(CallRepository::class) + + suspend fun withOutgoingCall() = apply { + coEvery { callRepository.observeCurrentCall(any()) } + .returns(flowOf(callWithOwner())) + } + + suspend fun withIncomingCall() = apply { + coEvery { callRepository.observeCurrentCall(any()) } + .returns(flowOf(callWithOwner().copy(callerId = CALLER_ID.copy(value = "external")))) + } + suspend fun withConversationMembers() = apply { coEvery { observeConversationMembers(any()) }.returns( flowOf( @@ -124,6 +177,17 @@ class GetRecentlyEndedCallMetadataUseCaseTest { } suspend fun withConversationGuests() = apply { + coEvery { observeConversationMembers(any()) }.returns( + flowOf( + listOf( + MemberDetails(TestUser.SELF, Conversation.Member.Role.Admin), + MemberDetails(TestUser.OTHER.copy(userType = UserType.GUEST, teamId = null), Conversation.Member.Role.Member) + ) + ) + ) + } + + suspend fun withConversationGuestsPro() = apply { coEvery { observeConversationMembers(any()) }.returns( flowOf( listOf( @@ -138,14 +202,14 @@ class GetRecentlyEndedCallMetadataUseCaseTest { coEvery { getSelf() }.returns(flowOf(TestUser.SELF)) } - fun arrange(): Pair = this to GetRecentlyEndedCallMetadataUseCaseImpl( - observeConversationMembers = observeConversationMembers, - getSelf = getSelf - ) - } + fun arrange(): Pair = + this to CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl( + callRepository = callRepository, + observeConversationMembers = observeConversationMembers, + getSelf = getSelf + ) - private companion object { - fun callWithOwner(): Call { + private fun callWithOwner(): Call { return TestCall.oneOnOneEstablishedCall() .copy( callerId = CALLER_ID.copy(value = "ownerId"), @@ -167,4 +231,27 @@ class GetRecentlyEndedCallMetadataUseCaseTest { ) } } + + private companion object { + val DEFAULT_ENDED_CALL_METADATA = RecentlyEndedCallMetadata( + callEndReason = 2, + isTeamMember = true, + callDetails = RecentlyEndedCallMetadata.CallDetails( + isCallScreenShare = false, + callScreenShareUniques = 0, + isOutgoingCall = true, + callDurationInSeconds = 0L, + callParticipantsCount = 2, + conversationServices = 0, + callAVSwitchToggle = false, + callVideoEnabled = false + ), + conversationDetails = RecentlyEndedCallMetadata.ConversationDetails( + conversationType = Conversation.Type.ONE_ON_ONE, + conversationSize = 2, + conversationGuests = 0, + conversationGuestsPro = 0 + ) + ) + } } diff --git a/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt b/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt index 6b0c6dbafb0..aa34f1b8a9a 100644 --- a/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt +++ b/logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt @@ -19,7 +19,6 @@ package com.wire.kalium.logic.feature.call.scenario import com.wire.kalium.calling.CallClosedReason import com.wire.kalium.calling.types.Uint32_t -import com.wire.kalium.logic.data.call.CallHelper import com.wire.kalium.logic.data.call.CallMetadata import com.wire.kalium.logic.data.call.CallMetadataProfile import com.wire.kalium.logic.data.call.CallRepository @@ -29,6 +28,7 @@ import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.GroupID import com.wire.kalium.logic.data.id.QualifiedIdMapperImpl import com.wire.kalium.logic.data.mls.CipherSuite +import com.wire.kalium.logic.feature.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.framework.TestCall import com.wire.kalium.logic.framework.TestUser import com.wire.kalium.network.NetworkState @@ -56,6 +56,9 @@ class OnCloseCallTest { @Mock val networkStateObserver = mock(NetworkStateObserver::class) + @Mock + val createAndPersistRecentlyEndedCallMetadata = mock(CreateAndPersistRecentlyEndedCallMetadataUseCase::class) + val qualifiedIdMapper = QualifiedIdMapperImpl(TestUser.SELF.id) private lateinit var onCloseCall: OnCloseCall @@ -70,7 +73,8 @@ class OnCloseCallTest { callRepository, testScope, qualifiedIdMapper, - networkStateObserver + networkStateObserver, + createAndPersistRecentlyEndedCallMetadata ) every { @@ -340,7 +344,7 @@ class OnCloseCallTest { } @Test - fun givenCloseCallInvoked_whenClosedCallMetadataIsPresent_thenUpdateRecentEndedCallInvoked() = + fun givenClosedCall_whenOnCloseCallInvoked_thenCreateAndPersistRecentlyEndedCallIsInvoked() = testScope.runTest { val reason = CallClosedReason.CANCELLED.avsValue @@ -355,34 +359,10 @@ class OnCloseCallTest { yield() coVerify { - callRepository.updateRecentlyEndedCall(any(), any(), any()) + createAndPersistRecentlyEndedCallMetadata(any(), any()) }.wasInvoked(once) } - @Test - fun givenCloseCallInvoked_whenClosedCallMetadataIsAbsent_thenUpdateRecentEndedCallNotInvoked() = - testScope.runTest { - val reason = CallClosedReason.CANCELLED.avsValue - - every { - callRepository.getCallMetadataProfile() - }.returns(CallMetadataProfile(emptyMap())) - - onCloseCall.onClosedCall( - reason, - conversationIdString, - time, - userIdString, - clientId, - null - ) - yield() - - coVerify { - callRepository.updateRecentlyEndedCall(any(), any(), any()) - }.wasNotInvoked() - } - companion object { private val conversationId = ConversationId("conversationId", "wire.com") private const val conversationIdString = "conversationId@wire.com" From 35b8116696f1031f3ad9367192134fd94b6cacfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Thu, 28 Nov 2024 08:33:58 +0100 Subject: [PATCH 5/7] Code review --- .../kalium/logic/feature/UserSessionScope.kt | 6 +++--- ...PersistRecentlyEndedCallMetadataUseCase.kt | 9 ++++----- ...istRecentlyEndedCallMetadataUseCaseTest.kt | 20 ++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) 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 123053c23d3..817a04e219d 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 @@ -177,10 +177,10 @@ 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.CreateAndPersistRecentlyEndedCallMetadataUseCase import com.wire.kalium.logic.feature.call.usecase.CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl +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 @@ -2115,7 +2115,7 @@ class UserSessionScope internal constructor( get() = CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl( callRepository = callRepository, observeConversationMembers = conversations.observeConversationMembers, - getSelf = users.getSelfUser + selfTeamIdProvider = selfTeamId ) val migrateFromPersonalToTeam: MigrateFromPersonalToTeamUseCase diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt index f01dd1e5a7f..14674eae244 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt @@ -21,12 +21,12 @@ import com.wire.kalium.logic.data.call.Call import com.wire.kalium.logic.data.call.CallRepository import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.id.ConversationId +import com.wire.kalium.logic.data.id.SelfTeamIdProvider import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase -import com.wire.kalium.logic.feature.user.GetSelfUserUseCase +import com.wire.kalium.logic.functional.getOrNull import com.wire.kalium.util.DateTimeUtil import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.firstOrNull /** * Given a call and raw call end reason create metadata containing all information regarding @@ -39,7 +39,7 @@ interface CreateAndPersistRecentlyEndedCallMetadataUseCase { class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( private val callRepository: CallRepository, private val observeConversationMembers: ObserveConversationMembersUseCase, - private val getSelf: GetSelfUserUseCase, + private val selfTeamIdProvider: SelfTeamIdProvider, ) : CreateAndPersistRecentlyEndedCallMetadataUseCase { override suspend fun invoke(conversationId: ConversationId, callEndedReason: Int) { val call = callRepository.observeCurrentCall(conversationId).first() @@ -49,7 +49,6 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( } private suspend fun Call.createMetadata(callEndedReason: Int): RecentlyEndedCallMetadata { - val selfUser = getSelf().firstOrNull() val selfCallUser = participants.firstOrNull { participant -> participant.userType == UserType.OWNER } val conversationMembers = observeConversationMembers(conversationId).first() val conversationServicesCount = conversationMembers.count { member -> member.user.userType == UserType.SERVICE } @@ -79,7 +78,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( conversationGuests = guestsCount, conversationGuestsPro = guestsProCount ), - isTeamMember = selfUser?.teamId != null + isTeamMember = selfTeamIdProvider().getOrNull() != null ) } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt index d6a22c094b5..d01f3e1deef 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt @@ -24,16 +24,18 @@ import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.MemberDetails import com.wire.kalium.logic.data.id.ConversationId +import com.wire.kalium.logic.data.id.SelfTeamIdProvider import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase -import com.wire.kalium.logic.feature.user.GetSelfUserUseCase import com.wire.kalium.logic.framework.TestCall import com.wire.kalium.logic.framework.TestCall.CALLER_ID import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.functional.Either import io.mockative.Mock import io.mockative.any import io.mockative.coEvery import io.mockative.coVerify +import io.mockative.every import io.mockative.mock import io.mockative.once import kotlinx.coroutines.flow.flowOf @@ -47,7 +49,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // given val (arrangement, useCase) = Arrangement() .withOutgoingCall() - .withSelfUser() + .withSelfTeamIdPresent() .withConversationMembers() .arrange() @@ -68,7 +70,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // given val (arrangement, useCase) = Arrangement() .withOutgoingCall() - .withSelfUser() + .withSelfTeamIdPresent() .withConversationGuests() .arrange() @@ -95,7 +97,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // given val (arrangement, useCase) = Arrangement() .withOutgoingCall() - .withSelfUser() + .withSelfTeamIdPresent() .withConversationGuestsPro() .arrange() @@ -123,7 +125,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // given val (arrangement, useCase) = Arrangement() .withIncomingCall() - .withSelfUser() + .withSelfTeamIdPresent() .withConversationMembers() .arrange() @@ -150,7 +152,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { val observeConversationMembers = mock(ObserveConversationMembersUseCase::class) @Mock - val getSelf = mock(GetSelfUserUseCase::class) + val selfTeamIdProvider = mock(SelfTeamIdProvider::class) @Mock val callRepository = mock(CallRepository::class) @@ -198,15 +200,15 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { ) } - suspend fun withSelfUser() = apply { - coEvery { getSelf() }.returns(flowOf(TestUser.SELF)) + suspend fun withSelfTeamIdPresent() = apply { + coEvery { selfTeamIdProvider() }.returns(Either.Right(TestUser.SELF.teamId)) } fun arrange(): Pair = this to CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl( callRepository = callRepository, observeConversationMembers = observeConversationMembers, - getSelf = getSelf + selfTeamIdProvider = selfTeamIdProvider ) private fun callWithOwner(): Call { From 6ab6e3d814818b803c8608445e0382b559a85a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Thu, 5 Dec 2024 10:46:50 +0100 Subject: [PATCH 6/7] Include screen sharing metadata --- .../data/call/RecentlyEndedCallMetadata.kt | 1 + ...PersistRecentlyEndedCallMetadataUseCase.kt | 25 ++++-- ...istRecentlyEndedCallMetadataUseCaseTest.kt | 86 ++++++++++++------- 3 files changed, 73 insertions(+), 39 deletions(-) diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt index d64d5109f14..2d6468356f7 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt @@ -27,6 +27,7 @@ data class RecentlyEndedCallMetadata( ) { data class CallDetails( val isCallScreenShare: Boolean, + val screenShareDurationInSeconds: Long, val callScreenShareUniques: Int, val isOutgoingCall: Boolean, val callDurationInSeconds: Long, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt index 14674eae244..8c5f88e2691 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt @@ -17,8 +17,9 @@ */ package com.wire.kalium.logic.feature.call.usecase -import com.wire.kalium.logic.data.call.Call +import com.wire.kalium.logic.data.call.CallMetadata import com.wire.kalium.logic.data.call.CallRepository +import com.wire.kalium.logic.data.call.CallScreenSharingMetadata import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.SelfTeamIdProvider @@ -42,19 +43,20 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( private val selfTeamIdProvider: SelfTeamIdProvider, ) : CreateAndPersistRecentlyEndedCallMetadataUseCase { override suspend fun invoke(conversationId: ConversationId, callEndedReason: Int) { - val call = callRepository.observeCurrentCall(conversationId).first() - call?.createMetadata(callEndedReason = callEndedReason)?.let { metadata -> + callRepository.getCallMetadataProfile()[conversationId]?.createMetadata( + conversationId = conversationId, + callEndedReason = callEndedReason + )?.let { metadata -> callRepository.updateRecentlyEndedCallMetadata(metadata) } } - private suspend fun Call.createMetadata(callEndedReason: Int): RecentlyEndedCallMetadata { - val selfCallUser = participants.firstOrNull { participant -> participant.userType == UserType.OWNER } + private suspend fun CallMetadata.createMetadata(conversationId: ConversationId, callEndedReason: Int): RecentlyEndedCallMetadata { + val selfCallUser = getFullParticipants().firstOrNull { participant -> participant.userType == UserType.OWNER } val conversationMembers = observeConversationMembers(conversationId).first() val conversationServicesCount = conversationMembers.count { member -> member.user.userType == UserType.SERVICE } val guestsCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST } val guestsProCount = conversationMembers.count { member -> member.user.userType == UserType.GUEST && member.user.teamId != null } - val uniqueScreenShares = participants.count { participant -> participant.isSharingScreen } val isOutgoingCall = callerId.value == selfCallUser?.id?.value val callDurationInSeconds = establishedTime?.let { DateTimeUtil.calculateMillisDifference(it, DateTimeUtil.currentIsoDateTimeString()) / MILLIS_IN_SECOND @@ -64,7 +66,8 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( callEndReason = callEndedReason, callDetails = RecentlyEndedCallMetadata.CallDetails( isCallScreenShare = selfCallUser?.isSharingScreen ?: false, - callScreenShareUniques = uniqueScreenShares, + screenShareDurationInSeconds = screenShareMetadata.totalDurationInSeconds(), + callScreenShareUniques = screenShareMetadata.uniqueSharingUsers.size, isOutgoingCall = isOutgoingCall, callDurationInSeconds = callDurationInSeconds, callParticipantsCount = participants.size, @@ -82,6 +85,14 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl internal constructor( ) } + private fun CallScreenSharingMetadata.totalDurationInSeconds(): Long { + val now = DateTimeUtil.currentInstant() + val activeScreenSharesDurationInSeconds = + activeScreenShares.values.sumOf { startTime -> DateTimeUtil.calculateMillisDifference(startTime, now) } + + return (activeScreenSharesDurationInSeconds + completedScreenShareDurationInMillis) / MILLIS_IN_SECOND + } + private companion object { const val MILLIS_IN_SECOND = 1_000L } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt index d01f3e1deef..8a75f6c8c0c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt @@ -17,9 +17,11 @@ */ package com.wire.kalium.logic.feature.call.usecase -import com.wire.kalium.logic.data.call.Call +import com.wire.kalium.logic.data.call.CallMetadata +import com.wire.kalium.logic.data.call.CallMetadataProfile import com.wire.kalium.logic.data.call.CallRepository -import com.wire.kalium.logic.data.call.Participant +import com.wire.kalium.logic.data.call.CallStatus +import com.wire.kalium.logic.data.call.ParticipantMinimized import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.MemberDetails @@ -27,9 +29,9 @@ import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.SelfTeamIdProvider import com.wire.kalium.logic.data.user.type.UserType import com.wire.kalium.logic.feature.conversation.ObserveConversationMembersUseCase -import com.wire.kalium.logic.framework.TestCall import com.wire.kalium.logic.framework.TestCall.CALLER_ID import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.framework.TestUser.OTHER_MINIMIZED import com.wire.kalium.logic.functional.Either import io.mockative.Mock import io.mockative.any @@ -55,7 +57,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // when useCase( - conversationId = ConversationId(value = "value", domain = "domain"), + conversationId = CONVERSATION_ID, callEndedReason = 2 ) @@ -76,7 +78,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // when useCase( - conversationId = ConversationId(value = "value", domain = "domain"), + conversationId = CONVERSATION_ID, callEndedReason = 2 ) @@ -103,7 +105,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // when useCase( - conversationId = ConversationId(value = "value", domain = "domain"), + conversationId = CONVERSATION_ID, callEndedReason = 2 ) @@ -131,7 +133,7 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { // when useCase( - conversationId = ConversationId(value = "value", domain = "domain"), + conversationId = CONVERSATION_ID, callEndedReason = 2 ) @@ -157,14 +159,14 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { @Mock val callRepository = mock(CallRepository::class) - suspend fun withOutgoingCall() = apply { - coEvery { callRepository.observeCurrentCall(any()) } - .returns(flowOf(callWithOwner())) + fun withOutgoingCall() = apply { + every { callRepository.getCallMetadataProfile() } + .returns(CallMetadataProfile(mapOf(CONVERSATION_ID to callMetadata()))) } - suspend fun withIncomingCall() = apply { - coEvery { callRepository.observeCurrentCall(any()) } - .returns(flowOf(callWithOwner().copy(callerId = CALLER_ID.copy(value = "external")))) + fun withIncomingCall() = apply { + every { callRepository.getCallMetadataProfile() } + .returns(CallMetadataProfile(mapOf(CONVERSATION_ID to callMetadata().copy(callerId = CALLER_ID.copy(value = "external"))))) } suspend fun withConversationMembers() = apply { @@ -211,35 +213,55 @@ class CreateAndPersistRecentlyEndedCallMetadataUseCaseTest { selfTeamIdProvider = selfTeamIdProvider ) - private fun callWithOwner(): Call { - return TestCall.oneOnOneEstablishedCall() - .copy( - callerId = CALLER_ID.copy(value = "ownerId"), - participants = TestCall.oneOnOneEstablishedCall().participants.plus( - Participant( - id = CALLER_ID.copy(value = "ownerId"), - clientId = "abcd", - isMuted = true, - isCameraOn = false, - isSharingScreen = false, - hasEstablishedAudio = true, - name = "User Name", - avatarAssetId = null, - userType = UserType.OWNER, - isSpeaking = false, - accentId = 0 - ) + private fun callMetadata(): CallMetadata { + return CallMetadata( + callerId = CALLER_ID.copy(value = "ownerId"), + isMuted = true, + isCameraOn = false, + isCbrEnabled = false, + conversationName = null, + users = listOf( + OTHER_MINIMIZED.copy(id = CALLER_ID.copy(value = "ownerId"), userType = UserType.OWNER), + OTHER_MINIMIZED + ), + participants = listOf( + ParticipantMinimized( + id = CALLER_ID.copy(value = "ownerId"), + userId = CALLER_ID.copy(value = "ownerId"), + clientId = "abcd", + isMuted = true, + isCameraOn = false, + isSharingScreen = false, + hasEstablishedAudio = true + ), + ParticipantMinimized( + id = CALLER_ID, + userId = CALLER_ID, + clientId = "abcd", + isMuted = true, + isCameraOn = false, + isSharingScreen = false, + hasEstablishedAudio = true ) - ) + ), + conversationType = Conversation.Type.ONE_ON_ONE, + callerName = "User Name", + callerTeamName = null, + callStatus = CallStatus.ESTABLISHED, + protocol = Conversation.ProtocolInfo.Proteus, + activeSpeakers = mapOf() + ) } } private companion object { + val CONVERSATION_ID = ConversationId(value = "value", domain = "domain") val DEFAULT_ENDED_CALL_METADATA = RecentlyEndedCallMetadata( callEndReason = 2, isTeamMember = true, callDetails = RecentlyEndedCallMetadata.CallDetails( isCallScreenShare = false, + screenShareDurationInSeconds = 0L, callScreenShareUniques = 0, isOutgoingCall = true, callDurationInSeconds = 0L, From 07e01fba3d1baeac892adb74e2a20091065b470e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Zag=C3=B3rski?= Date: Fri, 13 Dec 2024 09:28:17 +0100 Subject: [PATCH 7/7] Code review --- .../kotlin/com/wire/kalium/logic/data/call/CallRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28054323a1f..4cc7ea71551 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 @@ -177,7 +177,7 @@ internal class CallDataSource( } override suspend fun updateRecentlyEndedCallMetadata(recentlyEndedCallMetadata: RecentlyEndedCallMetadata) { - _recentlyEndedCallFlow.tryEmit(recentlyEndedCallMetadata) + _recentlyEndedCallFlow.emit(recentlyEndedCallMetadata) } private fun CallMetadata.mapCallMetadataToCall(conversationId: ConversationId): Call {