Skip to content

Commit

Permalink
chore: improve ongoing call performance [WPB-3959] (#3445)
Browse files Browse the repository at this point in the history
  • Loading branch information
Garzas authored Sep 16, 2024
1 parent 4992566 commit a36deb3
Showing 19 changed files with 107 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -76,6 +76,11 @@ class CallsModule {
fun provideObserveOngoingCallsUseCase(callsScope: CallsScope) =
callsScope.observeOngoingCalls

@ViewModelScoped
@Provides
fun provideObserveEstablishedCallWithSortedParticipantsUseCase(callsScope: CallsScope) =
callsScope.observeEstablishedCallWithSortedParticipants

@ViewModelScoped
@Provides
fun provideRejectCallUseCase(callsScope: CallsScope) =
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/wire/android/ui/calling/CallState.kt
Original file line number Diff line number Diff line change
@@ -18,22 +18,22 @@

package com.wire.android.ui.calling

import androidx.compose.runtime.Stable
import com.wire.android.model.ImageAsset.UserAvatarAsset
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.home.conversationslist.model.Membership
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.ConversationTypeForCall
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId

@Stable
data class CallState(
val conversationId: ConversationId,
val conversationName: ConversationName? = null,
val callerName: String? = null,
val accentId: Int = -1,
val callStatus: CallStatus = CallStatus.CLOSED,
val avatarAssetId: UserAvatarAsset? = null,
val participants: List<UICallParticipant> = listOf(),
val isMuted: Boolean? = null,
val isCameraOn: Boolean = false,
val isOnFrontCamera: Boolean = true,
Original file line number Diff line number Diff line change
@@ -29,10 +29,10 @@ import com.wire.android.mapper.UICallParticipantMapper
import com.wire.android.mapper.UserTypeMapper
import com.wire.android.media.CallRinger
import com.wire.android.model.ImageAsset
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.android.util.ui.WireSessionImageLoader
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.ConversationTypeForCall
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.conversation.Conversation
@@ -41,8 +41,8 @@ import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.feature.call.usecase.EndCallUseCase
import com.wire.kalium.logic.feature.call.usecase.FlipToBackCameraUseCase
import com.wire.kalium.logic.feature.call.usecase.FlipToFrontCameraUseCase
import com.wire.kalium.logic.feature.call.usecase.GetAllCallsWithSortedParticipantsUseCase
import com.wire.kalium.logic.feature.call.usecase.MuteCallUseCase
import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallWithSortedParticipantsUseCase
import com.wire.kalium.logic.feature.call.usecase.ObserveSpeakerUseCase
import com.wire.kalium.logic.feature.call.usecase.SetVideoPreviewUseCase
import com.wire.kalium.logic.feature.call.usecase.TurnLoudSpeakerOffUseCase
@@ -55,11 +55,12 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
@@ -72,7 +73,7 @@ import kotlinx.coroutines.launch
class SharedCallingViewModel @AssistedInject constructor(
@Assisted val conversationId: ConversationId,
private val conversationDetails: ObserveConversationDetailsUseCase,
private val allCalls: GetAllCallsWithSortedParticipantsUseCase,
private val observeEstablishedCallWithSortedParticipants: ObserveEstablishedCallWithSortedParticipantsUseCase,
private val endCall: EndCallUseCase,
private val muteCall: MuteCallUseCase,
private val unMuteCall: UnMuteCallUseCase,
@@ -92,15 +93,12 @@ class SharedCallingViewModel @AssistedInject constructor(

var callState by mutableStateOf(CallState(conversationId))

var participantsState by mutableStateOf(persistentListOf<UICallParticipant>())

init {
viewModelScope.launch {
val allCallsSharedFlow = allCalls().map {
it.find { call ->
call.conversationId == conversationId &&
call.status != CallStatus.CLOSED &&
call.status != CallStatus.MISSED
}
}.flowOn(dispatchers.default()).shareIn(this, started = SharingStarted.Lazily)
val allCallsSharedFlow = observeEstablishedCallWithSortedParticipants(conversationId)
.flowOn(dispatchers.default()).shareIn(this, started = SharingStarted.Lazily)

launch {
observeConversationDetails(this)
@@ -176,20 +174,16 @@ class SharedCallingViewModel @AssistedInject constructor(
}

private suspend fun observeParticipants(sharedFlow: SharedFlow<Call?>) {
sharedFlow.distinctUntilChanged().collectLatest { call ->
sharedFlow.collectLatest { call ->
call?.let {
callState = callState.copy(
isMuted = it.isMuted,
callStatus = it.status,
isCameraOn = it.isCameraOn,
isCbrEnabled = it.isCbrEnabled && call.conversationType == Conversation.Type.ONE_ON_ONE,
callerName = it.callerName,
participants = it.participants.map { participant ->
uiCallParticipantMapper.toUICallParticipant(
participant
)
}
)
participantsState = call.participants.map { uiCallParticipantMapper.toUICallParticipant(it) }.toPersistentList()
}
}
}
Original file line number Diff line number Diff line change
@@ -28,10 +28,10 @@ import com.wire.android.util.ui.PreviewMultipleThemes

@Composable
fun CameraFlipButton(
isOnFrontCamera: Boolean = false,
onCameraFlipButtonClicked: () -> Unit,
size: Dp = dimensions().defaultCallingControlsSize,
modifier: Modifier = Modifier,
isOnFrontCamera: Boolean = false,
size: Dp = dimensions().defaultCallingControlsSize
) {
WireCallControlButton(
isSelected = !isOnFrontCamera,
Original file line number Diff line number Diff line change
@@ -49,7 +49,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -58,6 +57,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.wire.android.BuildConfig
import com.wire.android.R
import com.wire.android.ui.LocalActivity
@@ -96,6 +96,9 @@ 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.QualifiedID
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import java.util.Locale

@Suppress("ParameterWrapping")
@@ -127,18 +130,14 @@ fun OngoingCallScreen(
}
}
}
val hangUpCall = remember {
{
sharedCallingViewModel.hangUpCall { activity.finishAndRemoveTask() }
}
}

OngoingCallContent(
callState = sharedCallingViewModel.callState,
shouldShowDoubleTapToast = ongoingCallViewModel.shouldShowDoubleTapToast,
toggleSpeaker = sharedCallingViewModel::toggleSpeaker,
toggleMute = sharedCallingViewModel::toggleMute,
hangUpCall = { sharedCallingViewModel.hangUpCall { activity.finishAndRemoveTask() } },
toggleVideo = sharedCallingViewModel::toggleVideo,
flipCamera = sharedCallingViewModel::flipCamera,
setVideoPreview = sharedCallingViewModel::setVideoPreview,
clearVideoPreview = sharedCallingViewModel::clearVideoPreview,
onCollapse = {
val onCollapse = remember {
{
if (shouldUsePiPMode) {
(activity as OngoingCallActivity).enterPiPMode(
conversationId,
@@ -147,17 +146,36 @@ fun OngoingCallScreen(
} else {
activity.moveTaskToBack(true)
}
},
requestVideoStreams = ongoingCallViewModel::requestVideoStreams,
hideDoubleTapToast = ongoingCallViewModel::hideDoubleTapToast,
onCameraPermissionPermanentlyDenied = {
Unit
}
}

val onCameraPermissionPermanentlyDenied = remember {
{
permissionPermanentlyDeniedDialogState.show(
PermissionPermanentlyDeniedDialogState.Visible(
title = R.string.app_permission_dialog_title,
description = R.string.camera_permission_dialog_description
)
)
}
}

OngoingCallContent(
callState = sharedCallingViewModel.callState,
shouldShowDoubleTapToast = ongoingCallViewModel.shouldShowDoubleTapToast,
toggleSpeaker = sharedCallingViewModel::toggleSpeaker,
toggleMute = sharedCallingViewModel::toggleMute,
hangUpCall = hangUpCall,
toggleVideo = sharedCallingViewModel::toggleVideo,
flipCamera = sharedCallingViewModel::flipCamera,
setVideoPreview = sharedCallingViewModel::setVideoPreview,
clearVideoPreview = sharedCallingViewModel::clearVideoPreview,
onCollapse = onCollapse,
requestVideoStreams = ongoingCallViewModel::requestVideoStreams,
hideDoubleTapToast = ongoingCallViewModel::hideDoubleTapToast,
onCameraPermissionPermanentlyDenied = onCameraPermissionPermanentlyDenied,
participants = sharedCallingViewModel.participantsState
)

BackHandler {
@@ -264,9 +282,9 @@ private fun OngoingCallContent(
onCollapse: () -> Unit,
hideDoubleTapToast: () -> Unit,
onCameraPermissionPermanentlyDenied: () -> Unit,
requestVideoStreams: (participants: List<UICallParticipant>) -> Unit
) = with(callState) {

requestVideoStreams: (participants: List<UICallParticipant>) -> Unit,
participants: PersistentList<UICallParticipant>
) {
val activity = LocalActivity.current

val sheetInitialValue = SheetValue.PartiallyExpanded
@@ -288,16 +306,16 @@ private fun OngoingCallContent(
} else {
{
OngoingCallTopBar(
conversationName = when (conversationName) {
is ConversationName.Known -> conversationName.name
is ConversationName.Unknown -> stringResource(id = conversationName.resourceId)
conversationName = when (callState.conversationName) {
is ConversationName.Known -> callState.conversationName.name
is ConversationName.Unknown -> stringResource(id = callState.conversationName.resourceId)
else -> ""
},
isCbrEnabled = isCbrEnabled,
isCbrEnabled = callState.isCbrEnabled,
onCollapse = onCollapse,
protocolInfo = protocolInfo,
mlsVerificationStatus = mlsVerificationStatus,
proteusVerificationStatus = proteusVerificationStatus
protocolInfo = callState.protocolInfo,
mlsVerificationStatus = callState.mlsVerificationStatus,
proteusVerificationStatus = callState.proteusVerificationStatus
)
}
},
@@ -306,11 +324,11 @@ private fun OngoingCallContent(
sheetContent = {
if (!activity.isInPictureInPictureMode) {
CallingControls(
conversationId = conversationId,
isMuted = isMuted ?: true,
isCameraOn = isCameraOn,
isOnFrontCamera = isOnFrontCamera,
isSpeakerOn = isSpeakerOn,
conversationId = callState.conversationId,
isMuted = callState.isMuted ?: true,
isCameraOn = callState.isCameraOn,
isOnFrontCamera = callState.isOnFrontCamera,
isSpeakerOn = callState.isSpeakerOn,
toggleSpeaker = toggleSpeaker,
toggleMute = toggleMute,
onHangUpCall = hangUpCall,
@@ -374,12 +392,13 @@ private fun OngoingCallContent(
},
setVideoPreview = setVideoPreview,
clearVideoPreview = clearVideoPreview,
participants = participants
)
} else {
VerticalCallingPager(
participants = participants,
isSelfUserCameraOn = isCameraOn,
isSelfUserMuted = isMuted ?: true,
isSelfUserCameraOn = callState.isCameraOn,
isSelfUserMuted = callState.isMuted ?: true,
contentHeight = this@BoxWithConstraints.maxHeight,
onSelfVideoPreviewCreated = setVideoPreview,
onSelfClearVideoPreview = clearVideoPreview,
@@ -503,7 +522,10 @@ private fun CallingControls(
)

if (isCameraOn) {
CameraFlipButton(isOnFrontCamera, flipCamera)
CameraFlipButton(
isOnFrontCamera = isOnFrontCamera,
onCameraFlipButtonClicked = flipCamera
)
}

HangUpButton(
@@ -517,12 +539,11 @@ private fun CallingControls(
}

@Composable
fun PreviewOngoingCallContent(participants: List<UICallParticipant>) {
fun PreviewOngoingCallContent(participants: PersistentList<UICallParticipant>) {
OngoingCallContent(
callState = CallState(
conversationId = ConversationId("conversationId", "domain"),
conversationName = ConversationName.Known("Conversation Name"),
participants = participants,
isMuted = false,
isCameraOn = false,
isOnFrontCamera = false,
@@ -544,13 +565,14 @@ fun PreviewOngoingCallContent(participants: List<UICallParticipant>) {
hideDoubleTapToast = {},
onCameraPermissionPermanentlyDenied = {},
requestVideoStreams = {},
participants = participants
)
}

@PreviewMultipleThemes
@Composable
fun PreviewOngoingCallScreenConnecting() = WireTheme {
PreviewOngoingCallContent(participants = emptyList())
PreviewOngoingCallContent(participants = persistentListOf())
}

@PreviewMultipleThemes
@@ -589,4 +611,4 @@ fun buildPreviewParticipantsList(count: Int = 10) = buildList {
)
)
}
}
}.toPersistentList()
Original file line number Diff line number Diff line change
@@ -40,18 +40,21 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.wire.android.R
import com.wire.android.ui.calling.CallState
import com.wire.android.ui.calling.model.UICallParticipant
import com.wire.android.ui.calling.ongoing.OngoingCallViewModel.Companion.DOUBLE_TAP_TOAST_DISPLAY_TIME
import com.wire.android.ui.calling.ongoing.buildPreviewParticipantsList
import com.wire.android.ui.calling.ongoing.participantsview.ParticipantTile
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.kalium.logic.data.id.ConversationId
import kotlinx.collections.immutable.PersistentList
import kotlinx.coroutines.delay

@Composable
fun FullScreenTile(
callState: CallState,
participants: PersistentList<UICallParticipant>,
selectedParticipant: SelectedParticipant,
height: Dp,
closeFullScreen: (offset: Offset) -> Unit,
@@ -67,7 +70,7 @@ fun FullScreenTile(
onBackButtonClicked()
}

callState.participants.find {
participants.find {
it.id == selectedParticipant.userId && it.clientId == selectedParticipant.clientId
}?.let {
Box(modifier = modifier) {
@@ -125,7 +128,6 @@ fun PreviewFullScreenTile() = WireTheme {
FullScreenTile(
callState = CallState(
conversationId = ConversationId("id", "domain"),
participants = participants,
),
selectedParticipant = SelectedParticipant(
userId = participants.first().id,
@@ -137,5 +139,6 @@ fun PreviewFullScreenTile() = WireTheme {
onBackButtonClicked = {},
setVideoPreview = {},
clearVideoPreview = {},
participants = participants,
)
}
Loading

0 comments on commit a36deb3

Please sign in to comment.