Skip to content

Commit

Permalink
feat: End SFT oneOnOne call on proteus protocol (WPB-7153) 🍒 (#2936)
Browse files Browse the repository at this point in the history
* Commit with unresolved merge conflicts

* chore: resolve conflicts

---------

Co-authored-by: Oussama Hassine <[email protected]>
  • Loading branch information
github-actions[bot] and ohassine authored Aug 13, 2024
1 parent 133b5e7 commit 58d7ee8
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ class CallManagerImpl internal constructor(
qualifiedIdMapper = qualifiedIdMapper,
participantMapper = ParticipantMapperImpl(videoStateChecker, callMapper),
userRepository = userRepository,
userConfigRepository = userConfigRepository,
mlsCallHelper = MLSCallHelperImpl(
callRepository = callRepository,
subconversationRepository = subconversationRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.sun.jna.Pointer
import com.wire.kalium.calling.callbacks.ParticipantChangedHandler
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.callingLogger
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.call.CallParticipants
import com.wire.kalium.logic.data.call.CallRepository
import com.wire.kalium.logic.data.call.MLSCallHelper
Expand All @@ -30,6 +31,7 @@ import com.wire.kalium.logic.data.call.mapper.ParticipantMapper
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.functional.getOrElse
import com.wire.kalium.logic.functional.onFailure
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.logic.kaliumLogger
Expand All @@ -38,21 +40,22 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json

// TODO: add tests for this class
@Suppress("LongParameterList")
class OnParticipantListChanged internal constructor(
private val callRepository: CallRepository,
private val qualifiedIdMapper: QualifiedIdMapper,
private val participantMapper: ParticipantMapper,
private val userRepository: UserRepository,
private val userConfigRepository: UserConfigRepository,
private val mlsCallHelper: MLSCallHelper,
private val endCall: suspend (conversationId: ConversationId) -> Unit,
private val callingScope: CoroutineScope
private val callingScope: CoroutineScope,
private val jsonDecoder: Json = Json
) : ParticipantChangedHandler {

override fun onParticipantChanged(remoteConversationId: String, data: String, arg: Pointer?) {

val participantsChange = Json.decodeFromString<CallParticipants>(data)
val participantsChange = jsonDecoder.decodeFromString<CallParticipants>(data)

callingScope.launch {
val participants = mutableListOf<Participant>()
Expand All @@ -72,20 +75,23 @@ class OnParticipantListChanged internal constructor(
participants.add(participant)
}
}
val callProtocol = callRepository.currentCallProtocol(conversationIdWithDomain)

val currentCall = callRepository.establishedCallsFlow().first().firstOrNull()
currentCall?.let {
val shouldEndSFTOneOnOneCall = mlsCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationIdWithDomain,
callProtocol = callProtocol,
conversationType = it.conversationType,
newCallParticipants = participants,
previousCallParticipants = it.participants
)
if (shouldEndSFTOneOnOneCall) {
kaliumLogger.i("[onParticipantChanged] - Ending MLS call due to participant leaving")
endCall(conversationIdWithDomain)
if (userConfigRepository.shouldUseSFTForOneOnOneCalls().getOrElse(false)) {
val callProtocol = callRepository.currentCallProtocol(conversationIdWithDomain)

val currentCall = callRepository.establishedCallsFlow().first().firstOrNull()
currentCall?.let {
val shouldEndSFTOneOnOneCall = mlsCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationIdWithDomain,
callProtocol = callProtocol,
conversationType = it.conversationType,
newCallParticipants = participants,
previousCallParticipants = it.participants
)
if (shouldEndSFTOneOnOneCall) {
kaliumLogger.i("[onParticipantChanged] - Ending SFT one on one call due to participant leaving")
endCall(conversationIdWithDomain)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,18 @@ class MLSCallHelperImpl(
conversationType: Conversation.Type,
newCallParticipants: List<Participant>,
previousCallParticipants: List<Participant>
) = callProtocol is Conversation.ProtocolInfo.MLS &&
userConfigRepository.shouldUseSFTForOneOnOneCalls().getOrElse(false) &&
): Boolean {
return if (callProtocol is Conversation.ProtocolInfo.Proteus) {
conversationType == Conversation.Type.ONE_ON_ONE &&
newCallParticipants.size == TWO_PARTICIPANTS &&
previousCallParticipants.size == TWO_PARTICIPANTS &&
previousCallParticipants[1].hasEstablishedAudio && !newCallParticipants[1].hasEstablishedAudio
newCallParticipants.size == ONE_PARTICIPANTS &&
previousCallParticipants.size == TWO_PARTICIPANTS
} else {
conversationType == Conversation.Type.ONE_ON_ONE &&
newCallParticipants.size == TWO_PARTICIPANTS &&
previousCallParticipants.size == TWO_PARTICIPANTS &&
previousCallParticipants[1].hasEstablishedAudio && !newCallParticipants[1].hasEstablishedAudio
}
}

override suspend fun handleCallTermination(
conversationId: ConversationId,
Expand Down Expand Up @@ -119,5 +125,6 @@ class MLSCallHelperImpl(

companion object {
const val TWO_PARTICIPANTS = 2
const val ONE_PARTICIPANTS = 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,44 +47,34 @@ import kotlin.test.assertTrue
class MLSCallHelperTest {

@Test
fun givenMlsConferenceCall_whenShouldEndSFTOneOnOneCallIsCalled_thenReturnCorrectValue() =
fun givenMlsProtocol_whenShouldEndSFTOneOnOneCallIsCalled_thenReturnCorrectValue() =
runTest {
val (_, mLSCallHelper) = Arrangement()
.withShouldUseSFTForOneOnOneCallsReturning(Either.Right(true))
.arrange()

// Proteus protocol
val shouldEndSFTOneOnOneCall1 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = Conversation.ProtocolInfo.Proteus,
conversationType = Conversation.Type.ONE_ON_ONE,
newCallParticipants = listOf(participant1, participant2),
previousCallParticipants = listOf(participant1, participant2)
)
assertFalse { shouldEndSFTOneOnOneCall1 }

// one participant in the call
val shouldEndSFTOneOnOneCall2 = mLSCallHelper.shouldEndSFTOneOnOneCall(
val shouldEndSFTOneOnOneCall1 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = CONVERSATION_MLS_PROTOCOL_INFO,
conversationType = Conversation.Type.ONE_ON_ONE,
newCallParticipants = listOf(participant1),
previousCallParticipants = listOf(participant1)
)
assertFalse { shouldEndSFTOneOnOneCall2 }
assertFalse { shouldEndSFTOneOnOneCall1 }

// Audi not lost for the second participant
val shouldEndSFTOneOnOneCall3 = mLSCallHelper.shouldEndSFTOneOnOneCall(
// Audio not lost for the second participant
val shouldEndSFTOneOnOneCall2 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = CONVERSATION_MLS_PROTOCOL_INFO,
conversationType = Conversation.Type.GROUP,
newCallParticipants = listOf(participant1, participant2),
previousCallParticipants = listOf(participant1, participant2)
)
assertFalse { shouldEndSFTOneOnOneCall3 }
assertFalse { shouldEndSFTOneOnOneCall2 }

// Audio lost for the second participant
val shouldEndSFTOneOnOneCall4 = mLSCallHelper.shouldEndSFTOneOnOneCall(
val shouldEndSFTOneOnOneCall3 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = CONVERSATION_MLS_PROTOCOL_INFO,
conversationType = Conversation.Type.ONE_ON_ONE,
Expand All @@ -94,24 +84,36 @@ class MLSCallHelperTest {
participant2.copy(hasEstablishedAudio = false)
)
)
assertTrue { shouldEndSFTOneOnOneCall4 }
assertTrue { shouldEndSFTOneOnOneCall3 }
}

val (_, mLSCallHelper2) = Arrangement()
.withShouldUseSFTForOneOnOneCallsReturning(Either.Left(StorageFailure.DataNotFound))
@Test
fun givenProteusProtocol_whenShouldEndSFTOneOnOneCallIsCalled_thenReturnCorrectValue() =
runTest {

val (_, mLSCallHelper) = Arrangement()
.withShouldUseSFTForOneOnOneCallsReturning(Either.Right(true))
.arrange()

// ShouldUseSFTForOneOnOneCalls user config is not found
val shouldEndSFTOneOnOneCall5 = mLSCallHelper2.shouldEndSFTOneOnOneCall(
// participants list has 2 items for the new list and the previous list
val shouldEndSFTOneOnOneCall1 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = CONVERSATION_MLS_PROTOCOL_INFO,
callProtocol = Conversation.ProtocolInfo.Proteus,
conversationType = Conversation.Type.ONE_ON_ONE,
previousCallParticipants = listOf(participant1, participant2),
newCallParticipants = listOf(
participant1,
participant2.copy(hasEstablishedAudio = false)
)
newCallParticipants = listOf(participant1, participant2),
previousCallParticipants = listOf(participant1, participant2)
)
assertFalse { shouldEndSFTOneOnOneCall5 }
assertFalse { shouldEndSFTOneOnOneCall1 }

// new participants list has 1 participant
val shouldEndSFTOneOnOneCall2 = mLSCallHelper.shouldEndSFTOneOnOneCall(
conversationId = conversationId,
callProtocol = Conversation.ProtocolInfo.Proteus,
conversationType = Conversation.Type.ONE_ON_ONE,
newCallParticipants = listOf(participant1),
previousCallParticipants = listOf(participant1, participant2)
)
assertTrue { shouldEndSFTOneOnOneCall2 }
}

@Test
Expand All @@ -129,7 +131,7 @@ class MLSCallHelperTest {

coVerify {
arrangement.subconversationRepository.deleteRemoteSubConversation(any(), any(), any())
}.wasInvoked(once)
}.wasInvoked(exactly = once)
}

@Test
Expand All @@ -149,11 +151,7 @@ class MLSCallHelperTest {
mLSCallHelper.handleCallTermination(conversationId, Conversation.Type.ONE_ON_ONE)

coVerify {
arrangement.subconversationRepository.deleteRemoteSubConversation(
eq(conversationId),
any(),
any()
)
arrangement.subconversationRepository.deleteRemoteSubConversation(any(), any(), any())
}.wasNotInvoked()
}

Expand All @@ -167,8 +165,8 @@ class MLSCallHelperTest {
mLSCallHelper.handleCallTermination(conversationId, Conversation.Type.GROUP)

coVerify {
arrangement.callRepository.leaveMlsConference(eq(conversationId))
}.wasInvoked(once)
arrangement.callRepository.leaveMlsConference(any())
}.wasInvoked(exactly = once)
}

@Test
Expand All @@ -182,7 +180,7 @@ class MLSCallHelperTest {

coVerify {
arrangement.callRepository.leaveMlsConference(eq(conversationId))
}.wasInvoked(once)
}.wasInvoked(exactly = once)
}

private class Arrangement {
Expand All @@ -206,18 +204,13 @@ class MLSCallHelperTest {

fun withShouldUseSFTForOneOnOneCallsReturning(result: Either<StorageFailure, Boolean>) =
apply {
every {
userConfigRepository.shouldUseSFTForOneOnOneCalls()
}.returns(result)
every { userConfigRepository.shouldUseSFTForOneOnOneCalls() }.returns(result)
}

suspend fun withFetchRemoteSubConversationDetailsReturning(result: Either<NetworkFailure, SubconversationResponse>) =
apply {
coEvery {
subconversationRepository.fetchRemoteSubConversationDetails(
conversationId,
CALL_SUBCONVERSATION_ID
)
subconversationRepository.fetchRemoteSubConversationDetails(eq(conversationId), eq(CALL_SUBCONVERSATION_ID))
}.returns(result)
}

Expand Down
Loading

0 comments on commit 58d7ee8

Please sign in to comment.