Skip to content

Commit

Permalink
feat: Recently ended call metadata [WPB-14256] (#3112)
Browse files Browse the repository at this point in the history
* feat: Recently ended call metadata [WPB-14256]

* Small test changes

* Make use case internal constructor

* feat: Recently ended call metadata compact [WPB-14256]  (#3113)

* Code review

* Include screen sharing metadata

* Code review

---------

Co-authored-by: Yamil Medina <[email protected]>
  • Loading branch information
m-zagorski and yamilmedina authored Dec 17, 2024
1 parent ad91db8 commit 8efbb07
Show file tree
Hide file tree
Showing 17 changed files with 592 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 screenShareDurationInSeconds: Long,
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
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -132,7 +136,8 @@ class CallManagerTest {
networkStateObserver = networkStateObserver,
kaliumConfigs = kaliumConfigs,
mediaManagerService = mediaManagerService,
flowManagerService = flowManagerService
flowManagerService = flowManagerService,
createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -56,7 +57,8 @@ actual class GlobalCallManager {
conversationClientsInCallUpdater: ConversationClientsInCallUpdater,
getCallConversationType: GetCallConversationTypeProvider,
networkStateObserver: NetworkStateObserver,
kaliumConfigs: KaliumConfigs
kaliumConfigs: KaliumConfigs,
createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase
): CallManager {
return CallManagerImpl()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -116,7 +118,8 @@ actual class GlobalCallManager(
mediaManagerService = mediaManager,
flowManagerService = flowManager,
userConfigRepository = userConfigRepository,
kaliumConfigs = kaliumConfigs
kaliumConfigs = kaliumConfigs,
createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata
)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -62,6 +64,7 @@ class OnCloseCall(
val conversationIdWithDomain = qualifiedIdMapper.fromStringToQualifiedID(conversationId)

scope.launch {
val callMetadata = callRepository.getCallMetadataProfile()[conversationIdWithDomain]

val isConnectedToInternet =
networkStateObserver.observeNetworkState().value == NetworkState.ConnectedWithInternet
Expand All @@ -78,11 +81,15 @@ class OnCloseCall(
status = callStatus
)

if (callRepository.getCallMetadataProfile()[conversationIdWithDomain]?.protocol is Conversation.ProtocolInfo.MLS) {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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
Expand Down Expand Up @@ -136,6 +137,9 @@ interface CallRepository {
suspend fun advanceEpoch(conversationId: ConversationId)
fun currentCallProtocol(conversationId: ConversationId): Conversation.ProtocolInfo?
suspend fun observeCurrentCall(conversationId: ConversationId): Flow<Call?>

suspend fun updateRecentlyEndedCallMetadata(recentlyEndedCallMetadata: RecentlyEndedCallMetadata)
suspend fun observeRecentlyEndedCallMetadata(): Flow<RecentlyEndedCallMetadata>
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -164,25 +168,38 @@ internal class CallDataSource(
private val scope = CoroutineScope(job + kaliumDispatchers.io)
private val callJobs = ConcurrentMutableMap<ConversationId, Job>()
private val staleParticipantJobs = ConcurrentMutableMap<QualifiedClientID, Job>()
private val _recentlyEndedCallFlow = MutableSharedFlow<RecentlyEndedCallMetadata>(
extraBufferCapacity = 1
)

override suspend fun observeCurrentCall(conversationId: ConversationId): Flow<Call?> = _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 updateRecentlyEndedCallMetadata(recentlyEndedCallMetadata: RecentlyEndedCallMetadata) {
_recentlyEndedCallFlow.emit(recentlyEndedCallMetadata)
}

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 observeRecentlyEndedCallMetadata(): Flow<RecentlyEndedCallMetadata> {
return _recentlyEndedCallFlow
}

override suspend fun getCallConfigResponse(limit: Int?): Either<CoreFailure, String> = wrapApiRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ import com.wire.kalium.logic.feature.call.CallsScope
import com.wire.kalium.logic.feature.call.GlobalCallManager
import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdater
import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdaterImpl
import com.wire.kalium.logic.feature.call.usecase.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
Expand Down Expand Up @@ -1273,7 +1275,8 @@ class UserSessionScope internal constructor(
conversationClientsInCallUpdater = conversationClientsInCallUpdater,
getCallConversationType = getCallConversationType,
networkStateObserver = networkStateObserver,
kaliumConfigs = kaliumConfigs
kaliumConfigs = kaliumConfigs,
createAndPersistRecentlyEndedCallMetadata = createAndPersistRecentlyEndedCallMetadata
)
}

Expand Down Expand Up @@ -2108,6 +2111,13 @@ class UserSessionScope internal constructor(
userScopedLogger
)

private val createAndPersistRecentlyEndedCallMetadata: CreateAndPersistRecentlyEndedCallMetadataUseCase
get() = CreateAndPersistRecentlyEndedCallMetadataUseCaseImpl(
callRepository = callRepository,
observeConversationMembers = conversations.observeConversationMembers,
selfTeamIdProvider = selfTeamId
)

val migrateFromPersonalToTeam: MigrateFromPersonalToTeamUseCase
get() = MigrateFromPersonalToTeamUseCaseImpl(userId, userRepository, invalidateTeamId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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.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
Expand Down Expand Up @@ -232,4 +234,9 @@ class CallsScope internal constructor(
val updateNextTimeCallFeedback: UpdateNextTimeCallFeedbackUseCase by lazy {
UpdateNextTimeCallFeedbackUseCase(userConfigRepository)
}

val observeRecentlyEndedCallMetadata: ObserveRecentlyEndedCallMetadataUseCase
get() = ObserveRecentlyEndedCallMetadataUseCaseImpl(
callRepository = callRepository
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 8efbb07

Please sign in to comment.