-
Notifications
You must be signed in to change notification settings - Fork 26
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: show connecting indicator if user has bad/lost connection during a call (WPB-1125) #2101
Changes from 8 commits
4b45048
787a65b
2838af2
f6ddfcd
af627a5
6b807f7
736ceba
2f7ff2f
6e2d94f
69694f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,12 +30,14 @@ import androidx.compose.foundation.layout.Column | |
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.foundation.layout.widthIn | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material.ContentAlpha | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.DisposableEffect | ||
import androidx.compose.runtime.getValue | ||
|
@@ -44,6 +46,7 @@ import androidx.compose.runtime.remember | |
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.alpha | ||
import androidx.compose.ui.geometry.CornerRadius | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.graphics.drawscope.Stroke | ||
|
@@ -90,10 +93,11 @@ fun ParticipantTile( | |
onSelfUserVideoPreviewCreated: (view: View) -> Unit, | ||
onClearSelfUserVideoPreview: () -> Unit | ||
) { | ||
val alpha = if (participantTitleState.hasEstablishedAudio) ContentAlpha.high else ContentAlpha.medium | ||
Surface( | ||
modifier = modifier, | ||
color = colorsScheme().callingParticipantTileBackgroundColor, | ||
shape = RoundedCornerShape(dimensions().corner6x) | ||
shape = RoundedCornerShape(dimensions().corner6x), | ||
) { | ||
var size by remember { mutableStateOf(IntSize.Zero) } | ||
var zoom by remember { mutableStateOf(1f) } | ||
|
@@ -106,6 +110,7 @@ fun ParticipantTile( | |
AvatarTile( | ||
modifier = Modifier | ||
.fillMaxSize() | ||
.alpha(alpha) | ||
.constrainAs(avatar) { }, | ||
avatar = UserAvatarData(participantTitleState.avatar), | ||
avatarSize = avatarSize | ||
|
@@ -178,7 +183,8 @@ fun ParticipantTile( | |
bottom.linkTo(parent.bottom) | ||
start.linkTo(parent.start) | ||
}, | ||
isMuted = participantTitleState.isMuted | ||
isMuted = participantTitleState.isMuted, | ||
hasEstablishedAudio = participantTitleState.hasEstablishedAudio | ||
) | ||
|
||
UsernameTile( | ||
|
@@ -191,7 +197,8 @@ fun ParticipantTile( | |
} | ||
.widthIn(max = onGoingCallTileUsernameMaxWidth), | ||
name = participantTitleState.name, | ||
isSpeaking = participantTitleState.isSpeaking | ||
isSpeaking = participantTitleState.isSpeaking, | ||
hasEstablishedAudio = participantTitleState.hasEstablishedAudio | ||
) | ||
} | ||
TileBorder(participantTitleState.isSpeaking) | ||
|
@@ -277,32 +284,74 @@ private fun AvatarTile( | |
private fun UsernameTile( | ||
modifier: Modifier, | ||
name: String, | ||
isSpeaking: Boolean | ||
isSpeaking: Boolean, | ||
hasEstablishedAudio: Boolean | ||
) { | ||
val color = if (isSpeaking) MaterialTheme.wireColorScheme.primary else Color.Black | ||
val nameLabelColor = if (hasEstablishedAudio) Color.White else colorsScheme().secondaryText | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: maybe we should move all these hard-coded to the theme colors as well, they would just be the same for both light and dark mode, but it would ensure we have all colors in one place so if we decide to change one of them we wouldn't need to look for them in the whole project There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, not that simple by just changing the color in the file.. we need to check the whole project in all cases I would say especially for white and black |
||
|
||
Surface( | ||
modifier = modifier, | ||
shape = RoundedCornerShape(dimensions().corner4x), | ||
color = color | ||
) { | ||
Text( | ||
color = Color.White, | ||
style = MaterialTheme.wireTypography.label01, | ||
modifier = Modifier.padding(dimensions().spacing4x), | ||
text = name, | ||
maxLines = 1, | ||
overflow = TextOverflow.Ellipsis | ||
) | ||
ConstraintLayout(modifier = modifier) { | ||
val (nameLabel, connectingLabel) = createRefs() | ||
|
||
Surface( | ||
modifier = Modifier.constrainAs(nameLabel) { | ||
bottom.linkTo(parent.bottom) | ||
start.linkTo(parent.start) | ||
end.linkTo(connectingLabel.start) | ||
}, | ||
shape = RoundedCornerShape( | ||
topStart = dimensions().corner4x, | ||
bottomStart = dimensions().corner4x, | ||
topEnd = if (hasEstablishedAudio) dimensions().corner4x else 0.dp, | ||
bottomEnd = if (hasEstablishedAudio) dimensions().corner4x else 0.dp, | ||
), | ||
color = color | ||
) { | ||
Text( | ||
color = nameLabelColor, | ||
style = MaterialTheme.wireTypography.label01, | ||
modifier = Modifier.padding(dimensions().spacing4x), | ||
text = name, | ||
maxLines = 1, | ||
overflow = TextOverflow.Ellipsis | ||
) | ||
} | ||
if (!hasEstablishedAudio) { | ||
Surface( | ||
saleniuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
modifier = Modifier.constrainAs(connectingLabel) { | ||
start.linkTo(nameLabel.end) | ||
top.linkTo(nameLabel.top) | ||
bottom.linkTo(nameLabel.bottom) | ||
}, | ||
shape = RoundedCornerShape( | ||
topEnd = dimensions().corner4x, | ||
bottomEnd = dimensions().corner4x | ||
), | ||
color = color | ||
) { | ||
Text( | ||
color = colorsScheme().error, | ||
style = MaterialTheme.wireTypography.label01, | ||
modifier = Modifier.padding( | ||
top = dimensions().spacing4x, | ||
bottom = dimensions().spacing4x, | ||
end = dimensions().spacing4x | ||
), | ||
text = stringResource(id = R.string.participant_tile_call_connecting_label), | ||
maxLines = 1, | ||
) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
private fun MicrophoneTile( | ||
modifier: Modifier, | ||
isMuted: Boolean, | ||
hasEstablishedAudio: Boolean | ||
) { | ||
if (isMuted) { | ||
if (isMuted && hasEstablishedAudio) { | ||
Surface( | ||
modifier = modifier, | ||
color = Color.Black, | ||
|
@@ -319,21 +368,70 @@ private fun MicrophoneTile( | |
} | ||
} | ||
|
||
@Preview | ||
@Preview("Default view") | ||
@Composable | ||
fun PreviewParticipantTile() { | ||
ParticipantTile( | ||
modifier = Modifier.height(300.dp), | ||
participantTitleState = UICallParticipant( | ||
id = QualifiedID("", ""), | ||
clientId = "client-id", | ||
name = "name", | ||
name = "user name", | ||
isMuted = true, | ||
isSpeaking = false, | ||
isCameraOn = false, | ||
isSharingScreen = false, | ||
avatar = null, | ||
membership = Membership.Admin, | ||
hasEstablishedAudio = true | ||
), | ||
onClearSelfUserVideoPreview = {}, | ||
onSelfUserVideoPreviewCreated = {}, | ||
isSelfUser = false | ||
) | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun PreviewParticipantTalking() { | ||
ParticipantTile( | ||
modifier = Modifier.height(300.dp), | ||
participantTitleState = UICallParticipant( | ||
id = QualifiedID("", ""), | ||
clientId = "client-id", | ||
name = "long user name to be displayed in participant tile during a call", | ||
isMuted = false, | ||
isSpeaking = true, | ||
isCameraOn = true, | ||
isCameraOn = false, | ||
isSharingScreen = false, | ||
avatar = null, | ||
membership = Membership.Admin, | ||
hasEstablishedAudio = true | ||
), | ||
onClearSelfUserVideoPreview = {}, | ||
onSelfUserVideoPreviewCreated = {}, | ||
isSelfUser = false | ||
) | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun PreviewParticipantConnecting() { | ||
ParticipantTile( | ||
modifier = Modifier | ||
.height(350.dp) | ||
.width(200.dp), | ||
participantTitleState = UICallParticipant( | ||
id = QualifiedID("", ""), | ||
clientId = "client-id", | ||
name = "Oussama2", | ||
isMuted = true, | ||
isSpeaking = false, | ||
isCameraOn = false, | ||
isSharingScreen = false, | ||
avatar = null, | ||
membership = Membership.Admin | ||
membership = Membership.Admin, | ||
hasEstablishedAudio = false | ||
), | ||
onClearSelfUserVideoPreview = {}, | ||
onSelfUserVideoPreviewCreated = {}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we write some test for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's already tested in
UICallParticipantMapperTest.kt