Add call info treatment for unknown members.

This commit is contained in:
Alex Hart 2024-07-18 10:35:46 -03:00 committed by Greyson Parrelli
parent e7720640d1
commit c39739bcb4
2 changed files with 164 additions and 1 deletions

View file

@ -6,9 +6,12 @@
package org.thoughtcrime.securesms.components.webrtc.controls package org.thoughtcrime.securesms.components.webrtc.controls
import android.content.Context import android.content.Context
import android.content.res.Configuration
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
@ -29,8 +32,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rxjava3.subscribeAsState import androidx.compose.runtime.rxjava3.subscribeAsState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -46,13 +51,19 @@ import androidx.lifecycle.toLiveData
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.reactivex.rxjava3.core.BackpressureStrategy import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import org.signal.core.ui.Dialogs
import org.signal.core.ui.Dividers import org.signal.core.ui.Dividers
import org.signal.core.ui.Previews
import org.signal.core.ui.Rows import org.signal.core.ui.Rows
import org.signal.core.ui.theme.LocalExtendedColors
import org.signal.core.ui.theme.SignalTheme import org.signal.core.ui.theme.SignalTheme
import org.signal.ringrtc.CallLinkState import org.signal.ringrtc.CallLinkState
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarImage
import org.thoughtcrime.securesms.components.AvatarImageView import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.events.CallParticipant import org.thoughtcrime.securesms.events.CallParticipant
import org.thoughtcrime.securesms.events.GroupCallRaiseHandEvent import org.thoughtcrime.securesms.events.GroupCallRaiseHandEvent
@ -259,6 +270,15 @@ private fun CallInfo(
onBlockClicked = onBlock onBlockClicked = onBlock
) )
} }
if (participantsState.inCallLobby && participantsState.unknownParticipantCount > 0) {
item {
UnknownMembersRow(
unknownMemberCount = participantsState.unknownParticipantCount,
allCallMembersAreUnknown = participantsState.participantsForList.isEmpty()
)
}
}
} else if (participantsState.isGroupCall()) { } else if (participantsState.isGroupCall()) {
items( items(
items = participantsState.groupMembers, items = participantsState.groupMembers,
@ -516,6 +536,129 @@ private fun GroupMemberRow(
) {} ) {}
} }
@Composable
private fun UnknownMembersRow(
unknownMemberCount: Int,
allCallMembersAreUnknown: Boolean
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(Rows.defaultPadding())
) {
when (unknownMemberCount) {
1 -> SingleUnknownAvatar()
2 -> TwoUnknownAvatars()
else -> ThreeUnknownAvatars()
}
val textResId = if (allCallMembersAreUnknown) {
R.plurals.CallInfoView__d_people
} else {
R.plurals.CallInfoView__plus_d_people
}
Text(
text = pluralStringResource(
id = textResId,
count = unknownMemberCount,
unknownMemberCount
),
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)
.padding(horizontal = 24.dp)
)
var displayDialog by remember { mutableStateOf(false) }
Icon(
painter = painterResource(id = R.drawable.symbol_info_24),
contentDescription = stringResource(id = R.string.CallInfoView__more_information),
modifier = Modifier.clickable(onClick = {
displayDialog = true
})
)
if (displayDialog) {
Dialogs.SimpleMessageDialog(
message = stringResource(id = R.string.CallInfoView__before_joining_a_call),
dismiss = stringResource(id = R.string.CallInfoView__got_it),
onDismiss = { displayDialog = false }
)
}
}
}
@Composable
private fun SingleUnknownAvatar() {
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier.size(40.dp)
)
}
@Composable
private fun TwoUnknownAvatars() {
Box(modifier = Modifier.width(40.dp)) {
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier
.size(34.dp)
.align(Alignment.CenterStart)
)
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier
.size(38.dp)
.align(Alignment.CenterEnd)
.border(width = 2.dp, color = LocalExtendedColors.current.colorSurface1, shape = CircleShape)
)
}
}
@Composable
private fun ThreeUnknownAvatars() {
Box(modifier = Modifier.width(40.dp)) {
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier
.size(27.dp)
.align(Alignment.CenterStart)
)
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier
.size(31.dp)
.align(Alignment.Center)
.border(width = 2.dp, color = SignalTheme.colors.colorSurface1, shape = CircleShape)
)
FallbackAvatarImage(
fallbackAvatar = FallbackAvatar.Resource.Person(AvatarColor.random()),
modifier = Modifier
.size(31.dp)
.align(Alignment.CenterEnd)
.border(width = 2.dp, color = SignalTheme.colors.colorSurface1, shape = CircleShape)
)
}
}
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun UnknownMembersRowPreview() {
Previews.BottomSheetPreview {
Column {
UnknownMembersRow(unknownMemberCount = 1, allCallMembersAreUnknown = true)
UnknownMembersRow(unknownMemberCount = 1, allCallMembersAreUnknown = false)
UnknownMembersRow(unknownMemberCount = 2, allCallMembersAreUnknown = false)
UnknownMembersRow(unknownMemberCount = 3, allCallMembersAreUnknown = false)
}
}
}
private data class ParticipantsState( private data class ParticipantsState(
val inCallLobby: Boolean = false, val inCallLobby: Boolean = false,
val ringGroup: Boolean = true, val ringGroup: Boolean = true,
@ -532,7 +675,9 @@ private data class ParticipantsState(
listOf(localParticipant) + remoteParticipants listOf(localParticipant) + remoteParticipants
} else { } else {
remoteParticipants remoteParticipants
} }.filter { it.recipient.isProfileSharing }
val unknownParticipantCount = remoteParticipants.count { !it.recipient.isProfileSharing }
val participantCountForDisplay: Int = if (participantCount == 0) { val participantCountForDisplay: Int = if (participantCount == 0) {
participantsForList.size participantsForList.size

View file

@ -221,6 +221,24 @@
<string name="BucketedThreadMedia_Medium">Medium</string> <string name="BucketedThreadMedia_Medium">Medium</string>
<string name="BucketedThreadMedia_Small">Small</string> <string name="BucketedThreadMedia_Small">Small</string>
<!-- CallInfoView -->
<!-- Unknown people row text when the only people in the call are unknowns -->
<plurals name="CallInfoView__d_people">
<item quantity="one">%1$d person</item>
<item quantity="other">%1$d people</item>
</plurals>
<!-- Unknown people row, placeholder is the number of unknown people in the call -->
<plurals name="CallInfoView__plus_d_people">
<item quantity="one">+%1$d person</item>
<item quantity="other">+%1$d people</item>
</plurals>
<!-- Unknown people row icon content description -->
<string name="CallInfoView__more_information">More information</string>
<!-- Unknown people row info dialog message -->
<string name="CallInfoView__before_joining_a_call">Before joining a call you can only see the names of phone contacts, people you\'re in a group with, or people you\'ve chatted with 1:1. You\'ll see all names and photos once you\'ve joined the call.</string>
<!-- Unknown people row info dialog action to close the dialog -->
<string name="CallInfoView__got_it">Got it</string>
<!-- CameraFragment --> <!-- CameraFragment -->
<!-- Toasted when user device does not support video recording --> <!-- Toasted when user device does not support video recording -->
<string name="CameraFragment__video_recording_is_not_supported_on_your_device">Video recording is not supported on your device</string> <string name="CameraFragment__video_recording_is_not_supported_on_your_device">Video recording is not supported on your device</string>