Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,9 @@ class CallsModule {
@Provides
fun provideObserveInCallReactionsUseCase(callsScope: CallsScope) =
callsScope.observeInCallReactions

@ViewModelScoped
@Provides
fun provideObserveCallQualityDataUseCase(callsScope: CallsScope) =
callsScope.observeCallQualityData
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Wire
* Copyright (C) 2026 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.android.ui.calling.ongoing

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.wire.android.R
import com.wire.android.ui.common.OverlapDirection
import com.wire.android.ui.common.OverlappingCirclesRow
import com.wire.android.ui.common.colorsScheme
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.call.CallQuality

@Composable
fun CallDetailsButton(callQuality: CallQuality, modifier: Modifier = Modifier) {
IconButton(
modifier = modifier,
onClick = { /* TODO */ },
content = {
OverlappingCirclesRow(
overlapSize = dimensions().spacing4x,
overlapCutoutSize = dimensions().spacing1x,
overlapDirection = OverlapDirection.StartOnTop,
items = listOf(
{ LowNetworkItem(callQuality = callQuality) },
{ InfoItem() }
)
)
},
)
}

@Composable
private fun LowNetworkItem(callQuality: CallQuality) {
AnimatedVisibility(
visible = callQuality.isLowQuality,
enter = fadeIn() + scaleIn() + expandIn(expandFrom = Alignment.Center),
exit = fadeOut() + scaleOut() + shrinkOut(shrinkTowards = Alignment.Center)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(dimensions().wireIconButtonSize)
.clip(CircleShape)
.background(color = colorsScheme().warning, shape = CircleShape)
.padding(horizontal = dimensions().spacing2x, vertical = dimensions().spacing4x),
) {
Icon(
painter = painterResource(id = R.drawable.ic_network),
contentDescription = stringResource(R.string.content_description_call_low_network_quality),
tint = colorsScheme().onWarning,
)
}
}
}

@Composable
private fun InfoItem() {
Icon(
painter = painterResource(id = R.drawable.ic_info),
contentDescription = stringResource(R.string.content_description_call_open_calling_details),
tint = colorsScheme().onBackground,
modifier = Modifier.size(dimensions().wireIconButtonSize)
)
}

@PreviewMultipleThemes
@Composable
fun CallDetailsButtonPreview() = WireTheme {
CallDetailsButton(callQuality = CallQuality.NORMAL)
}

@PreviewMultipleThemes
@Composable
fun CallDetailsButtonWithLowNetworkPreview() = WireTheme {
CallDetailsButton(callQuality = CallQuality.POOR)
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.PreviewMultipleThemesForLandscape
import com.wire.android.util.ui.PreviewMultipleThemesForPortrait
import com.wire.android.util.ui.PreviewMultipleThemesForSquare
import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
Expand Down Expand Up @@ -211,6 +212,7 @@ fun OngoingCallScreen(
participants = sharedCallingViewModel.participantsState,
inPictureInPictureMode = inPictureInPictureMode,
recentReactions = sharedCallingViewModel.recentReactions,
callQuality = ongoingCallViewModel.state.callQualityData.quality
)
ObserveRotation(sharedCallingViewModel::setUIRotation)

Expand Down Expand Up @@ -324,6 +326,7 @@ private fun OngoingCallContent(
participants: PersistentList<UICallParticipant>,
recentReactions: Map<UserId, String>,
inPictureInPictureMode: Boolean,
callQuality: CallQuality,
initialShowInCallReactionsPanel: Boolean = false, // for preview purposes
) {
var shouldOpenFullScreen by remember { mutableStateOf(false) }
Expand All @@ -345,7 +348,8 @@ private fun OngoingCallContent(
onCollapse = onCollapse,
protocolInfo = callState.protocolInfo,
mlsVerificationStatus = callState.mlsVerificationStatus,
proteusVerificationStatus = callState.proteusVerificationStatus
proteusVerificationStatus = callState.proteusVerificationStatus,
callQuality = callQuality
)
}
}
Expand Down Expand Up @@ -507,6 +511,7 @@ private fun OngoingCallTopBar(
protocolInfo: Conversation.ProtocolInfo?,
mlsVerificationStatus: Conversation.VerificationStatus?,
proteusVerificationStatus: Conversation.VerificationStatus?,
callQuality: CallQuality,
onCollapse: () -> Unit
) {
Column {
Expand Down Expand Up @@ -534,7 +539,9 @@ private fun OngoingCallTopBar(
},
navigationIconType = NavigationIconType.Collapse,
elevation = 0.dp,
actions = {}
actions = {
CallDetailsButton(callQuality = callQuality)
}
)
if (isCbrEnabled) {
Text(
Expand Down Expand Up @@ -644,6 +651,7 @@ fun PreviewOngoingCallContent(participants: PersistentList<UICallParticipant>, i
selectedParticipantForFullScreen = SelectedParticipant(),
recentReactions = emptyMap(),
initialShowInCallReactionsPanel = inCallReactionsPanelVisible,
callQuality = CallQuality.NORMAL
)
}

Expand Down Expand Up @@ -685,7 +693,13 @@ fun PreviewOngoingCallScreenConnecting() = WireTheme {
@PreviewMultipleThemes
@Composable
fun PreviewOngoingCallTopBar() = WireTheme {
OngoingCallTopBar("Default", true, null, null, null) { }
OngoingCallTopBar("Default", true, null, null, null, CallQuality.NORMAL) { }
}

@PreviewMultipleThemes
@Composable
fun PreviewOngoingCallTopBarWithPoorQuality() = WireTheme {
OngoingCallTopBar("Default", true, null, null, null, CallQuality.POOR) { }
}

fun buildPreviewParticipantsList(count: Int = 10) = buildList {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
*/
package com.wire.android.ui.calling.ongoing

import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.CallQualityData

data class OngoingCallState(
val flowState: FlowState = FlowState.Default
val flowState: FlowState = FlowState.Default,
val callQualityData: CallQualityData = CallQualityData(
quality = CallQuality.UNKNOWN,
roundTripTimeInMilliseconds = -1,
upstreamPacketLossPercentage = -1,
downstreamPacketLossPercentage = -1,
),
) {
sealed interface FlowState {
object Default : FlowState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ 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.CallClient
import com.wire.kalium.logic.data.call.CallQuality
import com.wire.kalium.logic.data.call.CallResolutionQuality
import com.wire.kalium.logic.data.call.VideoState
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.call.usecase.ObserveCallQualityDataUseCase
import com.wire.kalium.logic.feature.call.usecase.ObserveLastActiveCallWithSortedParticipantsUseCase
import com.wire.kalium.logic.feature.call.usecase.RequestVideoStreamsUseCase
import com.wire.kalium.logic.feature.call.usecase.video.SetVideoSendStateUseCase
Expand All @@ -58,6 +59,7 @@ class OngoingCallViewModel @AssistedInject constructor(
private val observeLastActiveCall: ObserveLastActiveCallWithSortedParticipantsUseCase,
private val requestVideoStreams: RequestVideoStreamsUseCase,
private val setVideoSendState: SetVideoSendStateUseCase,
private val observeCallQualityData: ObserveCallQualityDataUseCase,
) : ViewModel() {
var shouldShowDoubleTapToast: Boolean by mutableStateOf(false)
private set
Expand All @@ -70,6 +72,7 @@ class OngoingCallViewModel @AssistedInject constructor(

init {
observeCurrentCallFlowState()
observeCallQuality()
showDoubleTapToast()
}

Expand Down Expand Up @@ -108,6 +111,14 @@ class OngoingCallViewModel @AssistedInject constructor(
}
}

fun observeCallQuality() {
viewModelScope.launch {
observeCallQualityData(conversationId).collectLatest { callQualityData ->
state = state.copy(callQualityData = callQualityData)
}
}
}

fun requestVideoStreams(participants: List<UICallParticipant>) {
viewModelScope.launch {
participants
Expand All @@ -129,11 +140,11 @@ class OngoingCallViewModel @AssistedInject constructor(
}
}

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

Expand Down
26 changes: 26 additions & 0 deletions app/src/main/res/drawable/ic_network.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
~ Wire
~ Copyright (C) 2026 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/.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11dp"
android:height="8dp"
android:viewportWidth="11"
android:viewportHeight="8">
<path
android:pathData="M1.025,3.362L0,2.3C0.74,1.558 1.584,0.99 2.533,0.594C3.482,0.198 4.471,0 5.5,0C6.529,0 7.518,0.198 8.467,0.594C9.416,0.99 10.26,1.558 11,2.3L9.975,3.362C9.372,2.763 8.684,2.302 7.912,1.981C7.14,1.66 6.336,1.5 5.5,1.5C4.664,1.5 3.86,1.66 3.088,1.981C2.316,2.302 1.628,2.763 1.025,3.362ZM3.064,5.475L2.05,4.425C2.525,3.967 3.058,3.615 3.649,3.369C4.24,3.123 4.857,3 5.5,3C6.143,3 6.76,3.123 7.351,3.369C7.942,3.615 8.475,3.967 8.95,4.425L7.936,5.475C7.599,5.158 7.223,4.917 6.809,4.75C6.395,4.583 5.958,4.5 5.5,4.5C5.042,4.5 4.605,4.583 4.191,4.75C3.777,4.917 3.401,5.158 3.064,5.475ZM5.5,8C5.235,8 5.007,7.902 4.819,7.706C4.63,7.51 4.535,7.275 4.535,7C4.535,6.725 4.63,6.49 4.819,6.294C5.007,6.098 5.235,6 5.5,6C5.765,6 5.993,6.098 6.181,6.294C6.37,6.49 6.465,6.725 6.465,7C6.465,7.275 6.37,7.51 6.181,7.706C5.993,7.902 5.765,8 5.5,8Z"
android:fillColor="#000000"/>
</vector>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@
<string name="content_description_calling_turn_speaker_off">Turn speaker off</string>
<string name="content_description_calling_in_call_reactions_show">Show in call reactions panel</string>
<string name="content_description_calling_in_call_reactions_hide">Hide in call reactions panel</string>
<string name="content_description_call_open_calling_details">Open calling details</string>
<string name="content_description_call_low_network_quality">Low network quality</string>
<string name="content_description_reply_to_messge">Reply to the message</string>
<string name="content_description_reply_cancel">Cancel message reply</string>
<string name="content_description_ping_message">Ping message</string>
Expand Down
Loading
Loading