Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Recently ended call metadata [WPB-14256] #3112

Merged
merged 10 commits into from
Dec 17, 2024
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 @@ -2107,6 +2110,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
Loading