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] 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,