Skip to content

Commit

Permalink
feat: conference simulcast support (WPB-11480) (#3744)
Browse files Browse the repository at this point in the history
  • Loading branch information
yamilmedina authored Dec 17, 2024
1 parent 21ee4fc commit e0e5c8b
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ fun OngoingCallScreen(
clearVideoPreview = sharedCallingViewModel::clearVideoPreview,
onCollapse = onCollapse,
requestVideoStreams = ongoingCallViewModel::requestVideoStreams,
onSelectedParticipant = ongoingCallViewModel::onSelectedParticipant,
selectedParticipantForFullScreen = ongoingCallViewModel.selectedParticipant,
hideDoubleTapToast = ongoingCallViewModel::hideDoubleTapToast,
onCameraPermissionPermanentlyDenied = onCameraPermissionPermanentlyDenied,
participants = sharedCallingViewModel.participantsState,
Expand Down Expand Up @@ -289,6 +291,8 @@ private fun OngoingCallContent(
hideDoubleTapToast: () -> Unit,
onCameraPermissionPermanentlyDenied: () -> Unit,
requestVideoStreams: (participants: List<UICallParticipant>) -> Unit,
onSelectedParticipant: (selectedParticipant: SelectedParticipant) -> Unit,
selectedParticipantForFullScreen: SelectedParticipant,
participants: PersistentList<UICallParticipant>,
inPictureInPictureMode: Boolean,
currentUserId: UserId,
Expand All @@ -303,7 +307,6 @@ private fun OngoingCallContent(
)

var shouldOpenFullScreen by remember { mutableStateOf(false) }
var selectedParticipantForFullScreen by remember { mutableStateOf(SelectedParticipant()) }

WireBottomSheetScaffold(
sheetDragHandle = null,
Expand Down Expand Up @@ -391,11 +394,14 @@ private fun OngoingCallContent(
selectedParticipant = selectedParticipantForFullScreen,
height = this@BoxWithConstraints.maxHeight - dimensions().spacing4x,
closeFullScreen = {
onSelectedParticipant(SelectedParticipant())
shouldOpenFullScreen = !shouldOpenFullScreen
},
onBackButtonClicked = {
onSelectedParticipant(SelectedParticipant())
shouldOpenFullScreen = !shouldOpenFullScreen
},
requestVideoStreams = requestVideoStreams,
setVideoPreview = setVideoPreview,
clearVideoPreview = clearVideoPreview,
participants = participants
Expand All @@ -412,7 +418,7 @@ private fun OngoingCallContent(
requestVideoStreams = requestVideoStreams,
currentUserId = currentUserId,
onDoubleTap = { selectedParticipant ->
selectedParticipantForFullScreen = selectedParticipant
onSelectedParticipant(selectedParticipant)
shouldOpenFullScreen = !shouldOpenFullScreen
},
)
Expand Down Expand Up @@ -580,6 +586,8 @@ fun PreviewOngoingCallContent(participants: PersistentList<UICallParticipant>) {
participants = participants,
inPictureInPictureMode = false,
currentUserId = UserId("userId", "domain"),
onSelectedParticipant = {},
selectedParticipantForFullScreen = SelectedParticipant(),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import com.wire.android.appLogger
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.di.CurrentAccount
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.calling.ongoing.fullscreen.SelectedParticipant
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId
Expand Down Expand Up @@ -63,6 +65,8 @@ class OngoingCallViewModel @AssistedInject constructor(

var state by mutableStateOf(OngoingCallState())
private set
var selectedParticipant by mutableStateOf(SelectedParticipant())
private set

init {
viewModelScope.launch {
Expand Down Expand Up @@ -124,20 +128,32 @@ class OngoingCallViewModel @AssistedInject constructor(
.also {
if (it.isNotEmpty()) {
val clients: List<CallClient> = it.map { uiParticipant ->
CallClient(uiParticipant.id.toString(), uiParticipant.clientId)
CallClient(
userId = uiParticipant.id.toString(),
clientId = uiParticipant.clientId,
quality = mapQualityStream(uiParticipant)
)
}
requestVideoStreams(conversationId, clients)
}
}
}
}

private fun mapQualityStream(uiParticipant: UICallParticipant): CallQuality {
return if (uiParticipant.clientId == selectedParticipant.clientId) {
CallQuality.HIGH
} else {
CallQuality.LOW
}
}

private fun startDoubleTapToastDisplayCountDown() {
doubleTapIndicatorCountDownTimer?.cancel()
doubleTapIndicatorCountDownTimer =
object : CountDownTimer(DOUBLE_TAP_TOAST_DISPLAY_TIME, COUNT_DOWN_INTERVAL) {
override fun onTick(p0: Long) {
appLogger.i("startDoubleTapToastDisplayCountDown: $p0")
appLogger.d("$TAG - startDoubleTapToastDisplayCountDown: $p0")
}

override fun onFinish() {
Expand Down Expand Up @@ -171,10 +187,16 @@ class OngoingCallViewModel @AssistedInject constructor(
}
}

fun onSelectedParticipant(selectedParticipant: SelectedParticipant) {
appLogger.d("$TAG - Selected participant: ${selectedParticipant.toLogString()}")
this.selectedParticipant = selectedParticipant
}

companion object {
const val DOUBLE_TAP_TOAST_DISPLAY_TIME = 7000L
const val COUNT_DOWN_INTERVAL = 1000L
const val DELAY_TO_SHOW_DOUBLE_TAP_TOAST = 500L
const val TAG = "OngoingCallViewModel"
}

@AssistedFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ fun FullScreenTile(
closeFullScreen: (offset: Offset) -> Unit,
onBackButtonClicked: () -> Unit,
setVideoPreview: (View) -> Unit,
requestVideoStreams: (participants: List<UICallParticipant>) -> Unit,
clearVideoPreview: () -> Unit,
modifier: Modifier = Modifier,
contentPadding: Dp = dimensions().spacing4x,
Expand Down Expand Up @@ -119,6 +120,10 @@ fun FullScreenTile(
}
)
}

LaunchedEffect(selectedParticipant.userId) {
requestVideoStreams(listOf(it))
}
}
}

Expand All @@ -139,6 +144,7 @@ fun PreviewFullScreenTile() = WireTheme {
closeFullScreen = {},
onBackButtonClicked = {},
setVideoPreview = {},
requestVideoStreams = {},
clearVideoPreview = {},
participants = participants,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@
*/
package com.wire.android.ui.calling.ongoing.fullscreen

import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.user.UserId

data class SelectedParticipant(
val userId: UserId = UserId("", ""),
val clientId: String = "",
val isSelfUser: Boolean = false
)
) {

fun toLogString(): String {
return "SelectedParticipant(userId=${userId.toLogString()}, clientId=${clientId.obfuscateId()}, isSelfUser=$isSelfUser)"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import com.wire.android.config.NavigationTestExtension
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.calling.ongoing.OngoingCallViewModel
import com.wire.android.ui.calling.ongoing.fullscreen.SelectedParticipant
import com.wire.android.ui.home.conversationslist.model.Membership
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallClient
import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.conversation.Conversation
Expand Down Expand Up @@ -170,6 +172,72 @@ class OngoingCallViewModelTest {
}
}

@Test
fun givenAUserIsSelected_whenRequestedFullScreen_thenSetTheUserAsSelected() =
runTest {
val (_, ongoingCallViewModel) = Arrangement()
.withCall(provideCall().copy(isCameraOn = true))
.withShouldShowDoubleTapToastReturning(false)
.withSetVideoSendState()
.arrange()

ongoingCallViewModel.onSelectedParticipant(selectedParticipant3)

assertEquals(selectedParticipant3, ongoingCallViewModel.selectedParticipant)
}

@Test
fun givenParticipantsList_WhenRequestingVideoStreamForFullScreenParticipant_ThenRequestItInHighQuality() =
runTest {
val expectedClients = listOf(
CallClient(participant1.id.toString(), participant1.clientId, false, CallQuality.LOW),
CallClient(participant3.id.toString(), participant3.clientId, false, CallQuality.HIGH)
)

val (arrangement, ongoingCallViewModel) = Arrangement()
.withCall(provideCall())
.withShouldShowDoubleTapToastReturning(false)
.withSetVideoSendState()
.withRequestVideoStreams(conversationId, expectedClients)
.arrange()

ongoingCallViewModel.onSelectedParticipant(selectedParticipant3)
ongoingCallViewModel.requestVideoStreams(participants)

coVerify(exactly = 1) {
arrangement.requestVideoStreams(
conversationId,
expectedClients
)
}
}

@Test
fun givenParticipantsList_WhenRequestingVideoStreamForAllParticipant_ThenRequestItInLowQuality() =
runTest {
val expectedClients = listOf(
CallClient(participant1.id.toString(), participant1.clientId, false, CallQuality.LOW),
CallClient(participant3.id.toString(), participant3.clientId, false, CallQuality.LOW)
)

val (arrangement, ongoingCallViewModel) = Arrangement()
.withCall(provideCall())
.withShouldShowDoubleTapToastReturning(false)
.withSetVideoSendState()
.withRequestVideoStreams(conversationId, expectedClients)
.arrange()

ongoingCallViewModel.onSelectedParticipant(SelectedParticipant())
ongoingCallViewModel.requestVideoStreams(participants)

coVerify(exactly = 1) {
arrangement.requestVideoStreams(
conversationId,
expectedClients
)
}
}

private class Arrangement {

@MockK
Expand Down Expand Up @@ -268,6 +336,7 @@ class OngoingCallViewModelTest {
accentId = -1
)
val participants = listOf(participant1, participant2, participant3)
val selectedParticipant3 = SelectedParticipant(participant3.id, participant3.clientId, false)
}

private fun provideCall(
Expand Down
2 changes: 1 addition & 1 deletion kalium
Submodule kalium updated 20 files
+38 βˆ’4 data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallClient.kt
+46 βˆ’0 data/src/commonMain/kotlin/com/wire/kalium/logic/data/call/RecentlyEndedCallMetadata.kt
+10 βˆ’10 gradle/libs.versions.toml
+6 βˆ’1 logic/src/androidInstrumentedTest/kotlin/com/wire/kalium/logic/feature/call/CallManagerTest.kt
+3 βˆ’1 logic/src/appleMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt
+12 βˆ’4 logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/CallManagerImpl.kt
+5 βˆ’2 logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt
+9 βˆ’2 logic/src/commonJvmAndroid/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCall.kt
+34 βˆ’17 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/call/CallRepository.kt
+11 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt
+7 βˆ’0 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/CallsScope.kt
+3 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/call/GlobalCallManager.kt
+99 βˆ’0 ...nMain/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCase.kt
+39 βˆ’0 ...src/commonMain/kotlin/com/wire/kalium/logic/feature/call/usecase/ObserveRecentlyEndedCallMetadataUseCase.kt
+1 βˆ’1 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchScope.kt
+16 βˆ’6 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/search/SearchUsersUseCase.kt
+281 βˆ’0 ...t/kotlin/com/wire/kalium/logic/feature/call/usecase/CreateAndPersistRecentlyEndedCallMetadataUseCaseTest.kt
+1 βˆ’1 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/search/SearchUseCaseTest.kt
+27 βˆ’2 logic/src/jvmTest/kotlin/com/wire/kalium/logic/feature/call/scenario/OnCloseCallTest.kt
+32 βˆ’0 util/src/commonMain/kotlin/com.wire.kalium.util/serialization/LenientJsonSerializer.kt

0 comments on commit e0e5c8b

Please sign in to comment.