diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt index 4a85d2fc9..c430ab050 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -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() - private val participantUiStates = mutableStateListOf() - 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() diff --git a/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.kt index 9b0fa75e6..299505afd 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.kt @@ -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, diff --git a/app/src/main/java/com/nextcloud/talk/call/ParticipantUiState.kt b/app/src/main/java/com/nextcloud/talk/call/ParticipantUiState.kt deleted file mode 100644 index 71b93e85f..000000000 --- a/app/src/main/java/com/nextcloud/talk/call/ParticipantUiState.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2025 Marcel Hibbe - * 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? -) diff --git a/app/src/main/java/com/nextcloud/talk/call/components/AvatarWithFallback.kt b/app/src/main/java/com/nextcloud/talk/call/components/AvatarWithFallback.kt index 49a09cd6c..5cfd29a81 100644 --- a/app/src/main/java/com/nextcloud/talk/call/components/AvatarWithFallback.kt +++ b/app/src/main/java/com/nextcloud/talk/call/components/AvatarWithFallback.kt @@ -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) { diff --git a/app/src/main/java/com/nextcloud/talk/call/components/ParticipantGrid.kt b/app/src/main/java/com/nextcloud/talk/call/components/ParticipantGrid.kt index b9c4e0ee6..d6baa6775 100644 --- a/app/src/main/java/com/nextcloud/talk/call/components/ParticipantGrid.kt +++ b/app/src/main/java/com/nextcloud/talk/call/components/ParticipantGrid.kt @@ -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, + participantUiStates: List, 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 ) {} diff --git a/app/src/main/java/com/nextcloud/talk/call/components/ParticipantTile.kt b/app/src/main/java/com/nextcloud/talk/call/components/ParticipantTile.kt index dc6828adf..4d0330c23 100644 --- a/app/src/main/java/com/nextcloud/talk/call/components/ParticipantTile.kt +++ b/app/src/main/java/com/nextcloud/talk/call/components/ParticipantTile.kt @@ -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), diff --git a/app/src/main/java/com/nextcloud/talk/call/components/WebRTCVideoView.kt b/app/src/main/java/com/nextcloud/talk/call/components/WebRTCVideoView.kt index 1b3c2be0f..375627481 100644 --- a/app/src/main/java/com/nextcloud/talk/call/components/WebRTCVideoView.kt +++ b/app/src/main/java/com/nextcloud/talk/call/components/WebRTCVideoView.kt @@ -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