mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 03:29:28 +01:00
simplify participant data structure
move ParticipantUiState into ParticipantDisplayItem Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
962972dce4
commit
eaed93087b
@ -48,12 +48,12 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@ -73,7 +73,6 @@ import com.nextcloud.talk.call.MessageSender
|
||||
import com.nextcloud.talk.call.MessageSenderMcu
|
||||
import com.nextcloud.talk.call.MessageSenderNoMcu
|
||||
import com.nextcloud.talk.call.MutableLocalCallParticipantModel
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.call.ReactionAnimator
|
||||
import com.nextcloud.talk.call.components.ParticipantGrid
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
@ -154,7 +153,6 @@ import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Cache
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -308,8 +306,6 @@ class CallActivity : CallBaseActivity() {
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
|
||||
private val participantItems = mutableStateListOf<ParticipantDisplayItem>()
|
||||
private val participantUiStates = mutableStateListOf<ParticipantUiState>()
|
||||
|
||||
private var binding: CallActivityBinding? = null
|
||||
private var audioOutputDialog: AudioOutputDialog? = null
|
||||
private var moreCallActionsDialog: MoreCallActionsDialog? = null
|
||||
@ -966,8 +962,9 @@ class CallActivity : CallBaseActivity() {
|
||||
|
||||
binding!!.composeParticipantGrid.setContent {
|
||||
MaterialTheme {
|
||||
val participantUiStates = participantItems.map { it.uiStateFlow.collectAsState().value }
|
||||
ParticipantGrid(
|
||||
participants = participantUiStates.toList(),
|
||||
participantUiStates = participantUiStates,
|
||||
eglBase = rootEglBase!!,
|
||||
isVoiceOnlyCall = isVoiceOnlyCall
|
||||
) {
|
||||
@ -2555,7 +2552,6 @@ class CallActivity : CallBaseActivity() {
|
||||
val participant = participantItems.find { it.sessionKey == key }
|
||||
participant?.destroy()
|
||||
participantItems.removeAll { it.sessionKey == key }
|
||||
participantUiStates.removeAll { it.sessionKey == key }
|
||||
initGrid()
|
||||
}
|
||||
|
||||
@ -2674,17 +2670,6 @@ class CallActivity : CallBaseActivity() {
|
||||
|
||||
if (participantItems.none { it.sessionKey == sessionKey }) {
|
||||
participantItems.add(participantDisplayItem)
|
||||
|
||||
lifecycleScope.launch {
|
||||
participantDisplayItem.uiStateFlow.collect { uiState ->
|
||||
val index = participantUiStates.indexOfFirst { it.sessionKey == sessionKey }
|
||||
if (index >= 0) {
|
||||
participantUiStates[index] = uiState
|
||||
} else {
|
||||
participantUiStates.add(uiState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initGrid()
|
||||
|
@ -15,7 +15,6 @@ import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import com.nextcloud.talk.call.CallParticipantModel
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.call.RaisedHand
|
||||
import com.nextcloud.talk.models.json.participants.Participant.ActorType
|
||||
import com.nextcloud.talk.utils.ApiUtils.getUrlForAvatar
|
||||
@ -30,6 +29,17 @@ import org.webrtc.MediaStream
|
||||
import org.webrtc.PeerConnection.IceConnectionState
|
||||
import org.webrtc.SurfaceViewRenderer
|
||||
|
||||
data class ParticipantUiState(
|
||||
val sessionKey: String,
|
||||
val nick: String,
|
||||
val isConnected: Boolean,
|
||||
val isAudioEnabled: Boolean,
|
||||
val isStreamEnabled: Boolean,
|
||||
val raisedHand: Boolean,
|
||||
val avatarUrl: String?,
|
||||
val mediaStream: MediaStream?
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class ParticipantDisplayItem(
|
||||
private val context: Context,
|
||||
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.call
|
||||
|
||||
import org.webrtc.MediaStream
|
||||
|
||||
data class ParticipantUiState(
|
||||
val sessionKey: String,
|
||||
val nick: String,
|
||||
val isConnected: Boolean,
|
||||
val isAudioEnabled: Boolean,
|
||||
val isStreamEnabled: Boolean,
|
||||
val raisedHand: Boolean,
|
||||
val avatarUrl: String?,
|
||||
val mediaStream: MediaStream?
|
||||
)
|
@ -22,7 +22,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.adapters.ParticipantUiState
|
||||
|
||||
@Composable
|
||||
fun AvatarWithFallback(participant: ParticipantUiState, modifier: Modifier = Modifier) {
|
||||
|
@ -5,6 +5,8 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
@file:Suppress("MagicNumber", "TooManyFunctions")
|
||||
|
||||
package com.nextcloud.talk.call.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
@ -23,16 +25,15 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.adapters.ParticipantUiState
|
||||
import org.webrtc.EglBase
|
||||
import kotlin.math.ceil
|
||||
|
||||
@Suppress("MagicNumber", "TooManyFunctions")
|
||||
@Composable
|
||||
fun ParticipantGrid(
|
||||
modifier: Modifier = Modifier,
|
||||
eglBase: EglBase?,
|
||||
participants: List<ParticipantUiState>,
|
||||
participantUiStates: List<ParticipantUiState>,
|
||||
isVoiceOnlyCall: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@ -43,19 +44,19 @@ fun ParticipantGrid(
|
||||
|
||||
val columns =
|
||||
if (isPortrait) {
|
||||
when (participants.size) {
|
||||
when (participantUiStates.size) {
|
||||
1, 2, 3 -> 1
|
||||
else -> 2
|
||||
}
|
||||
} else {
|
||||
when (participants.size) {
|
||||
when (participantUiStates.size) {
|
||||
1 -> 1
|
||||
2, 4 -> 2
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
|
||||
val rows = ceil(participants.size / columns.toFloat()).toInt()
|
||||
val rows = ceil(participantUiStates.size / columns.toFloat()).toInt()
|
||||
|
||||
val heightForNonGridComponents = if (isVoiceOnlyCall) {
|
||||
// this is a workaround for now. It should ~summarize the height of callInfosLinearLayout and callControls
|
||||
@ -87,11 +88,11 @@ fun ParticipantGrid(
|
||||
contentPadding = PaddingValues(vertical = edgePadding)
|
||||
) {
|
||||
items(
|
||||
participants,
|
||||
participantUiStates,
|
||||
key = { it.sessionKey }
|
||||
) { participant ->
|
||||
ParticipantTile(
|
||||
participant = participant,
|
||||
participantUiState = participant,
|
||||
modifier = Modifier
|
||||
.height(itemHeight)
|
||||
.fillMaxWidth(),
|
||||
@ -102,84 +103,76 @@ fun ParticipantGrid(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun ParticipantGridPreview() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(1),
|
||||
participantUiStates = getTestParticipants(1),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun TwoParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(2),
|
||||
participantUiStates = getTestParticipants(2),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun ThreeParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(3),
|
||||
participantUiStates = getTestParticipants(3),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun FourParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(4),
|
||||
participantUiStates = getTestParticipants(4),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun FiveParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(5),
|
||||
participantUiStates = getTestParticipants(5),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun SevenParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(7),
|
||||
participantUiStates = getTestParticipants(7),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview
|
||||
@Composable
|
||||
fun FiftyParticipants() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(50),
|
||||
participantUiStates = getTestParticipants(50),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -188,13 +181,12 @@ fun FiftyParticipants() {
|
||||
@Composable
|
||||
fun OneParticipantLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(1),
|
||||
participantUiStates = getTestParticipants(1),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -203,13 +195,12 @@ fun OneParticipantLandscape() {
|
||||
@Composable
|
||||
fun TwoParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(2),
|
||||
participantUiStates = getTestParticipants(2),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -218,13 +209,12 @@ fun TwoParticipantsLandscape() {
|
||||
@Composable
|
||||
fun ThreeParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(3),
|
||||
participantUiStates = getTestParticipants(3),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -233,13 +223,12 @@ fun ThreeParticipantsLandscape() {
|
||||
@Composable
|
||||
fun FourParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(4),
|
||||
participantUiStates = getTestParticipants(4),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -248,13 +237,12 @@ fun FourParticipantsLandscape() {
|
||||
@Composable
|
||||
fun SevenParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(7),
|
||||
participantUiStates = getTestParticipants(7),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(
|
||||
showBackground = false,
|
||||
heightDp = 360,
|
||||
@ -263,7 +251,7 @@ fun SevenParticipantsLandscape() {
|
||||
@Composable
|
||||
fun FiftyParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participants = getTestParticipants(50),
|
||||
participantUiStates = getTestParticipants(50),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
) {}
|
||||
|
@ -29,7 +29,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.adapters.ParticipantUiState
|
||||
import com.nextcloud.talk.utils.ColorGenerator
|
||||
import org.webrtc.EglBase
|
||||
|
||||
@ -38,28 +38,28 @@ const val NICK_BLUR_RADIUS = 4f
|
||||
|
||||
@Composable
|
||||
fun ParticipantTile(
|
||||
participant: ParticipantUiState,
|
||||
participantUiState: ParticipantUiState,
|
||||
eglBase: EglBase?,
|
||||
modifier: Modifier = Modifier,
|
||||
isVoiceOnlyCall: Boolean
|
||||
) {
|
||||
val colorInt = ColorGenerator.shared.usernameToColor(participant.nick)
|
||||
val colorInt = ColorGenerator.shared.usernameToColor(participantUiState.nick)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color(colorInt))
|
||||
) {
|
||||
if (!isVoiceOnlyCall && participant.isStreamEnabled && participant.mediaStream != null) {
|
||||
WebRTCVideoView(participant, eglBase)
|
||||
if (!isVoiceOnlyCall && participantUiState.isStreamEnabled && participantUiState.mediaStream != null) {
|
||||
WebRTCVideoView(participantUiState, eglBase)
|
||||
} else {
|
||||
AvatarWithFallback(
|
||||
participant = participant,
|
||||
participant = participantUiState,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
|
||||
if (participant.raisedHand) {
|
||||
if (participantUiState.raisedHand) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_hand_back_left),
|
||||
contentDescription = "Raised Hand",
|
||||
@ -71,7 +71,7 @@ fun ParticipantTile(
|
||||
)
|
||||
}
|
||||
|
||||
if (!participant.isAudioEnabled) {
|
||||
if (!participantUiState.isAudioEnabled) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_mic_off_white_24px),
|
||||
contentDescription = "Mic Off",
|
||||
@ -84,7 +84,7 @@ fun ParticipantTile(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = participant.nick,
|
||||
text = participantUiState.nick,
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
@ -98,7 +98,7 @@ fun ParticipantTile(
|
||||
)
|
||||
)
|
||||
|
||||
if (!participant.isConnected) {
|
||||
if (!participantUiState.isConnected) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
@ -120,7 +120,7 @@ fun ParticipantTilePreview() {
|
||||
mediaStream = null
|
||||
)
|
||||
ParticipantTile(
|
||||
participant = participant,
|
||||
participantUiState = participant,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp),
|
||||
|
@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.nextcloud.talk.call.ParticipantUiState
|
||||
import com.nextcloud.talk.adapters.ParticipantUiState
|
||||
import org.webrtc.EglBase
|
||||
import org.webrtc.SurfaceViewRenderer
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user