From 6a048fde08b58e4a1d9085413d84d4c6a281423e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 13 May 2025 11:27:55 +0200 Subject: [PATCH] improve/fix contents for picture in picture mode depending on amount of participants, voiceOnly call and enabled/disabled own video, the contents of PIP windows are updated. This will be further improved when speaker-view is implemented. Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/activities/CallActivity.kt | 68 ++++++++----------- .../call/components/AvatarWithFallback.kt | 3 - .../talk/call/components/ParticipantGrid.kt | 44 ++++++++---- .../talk/call/components/ParticipantTile.kt | 13 +++- 4 files changed, 70 insertions(+), 58 deletions(-) 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 c430ab050..d0d7084fe 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -938,35 +938,17 @@ class CallActivity : CallBaseActivity() { binding!!.pipSelfVideoRenderer.release() } - private fun initSelfVideoViewForPipMode() { - if (!isVoiceOnlyCall) { - binding!!.pipSelfVideoRenderer.visibility = View.VISIBLE - try { - binding!!.pipSelfVideoRenderer.init(rootEglBase!!.eglBaseContext, null) - } catch (e: IllegalStateException) { - Log.d(TAG, "pipGroupVideoRenderer already initialized", e) - } - binding!!.pipSelfVideoRenderer.setZOrderMediaOverlay(true) - // disabled because it causes some devices to crash - binding!!.pipSelfVideoRenderer.setEnableHardwareScaler(false) - binding!!.pipSelfVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) - - localVideoTrack?.addSink(binding?.pipSelfVideoRenderer) - } else { - binding!!.pipSelfVideoRenderer.visibility = View.GONE - } - } - private fun initGrid() { Log.d(TAG, "initGrid") - + binding!!.composeParticipantGrid.visibility = View.VISIBLE binding!!.composeParticipantGrid.setContent { MaterialTheme { val participantUiStates = participantItems.map { it.uiStateFlow.collectAsState().value } ParticipantGrid( participantUiStates = participantUiStates, eglBase = rootEglBase!!, - isVoiceOnlyCall = isVoiceOnlyCall + isVoiceOnlyCall = isVoiceOnlyCall, + isInPipMode = isInPipMode ) { animateCallControls(true, 0) } @@ -3206,33 +3188,38 @@ class CallActivity : CallBaseActivity() { override fun updateUiForPipMode() { Log.d(TAG, "updateUiForPipMode") - val params = RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - params.setMargins(0, 0, 0, 0) - binding!!.composeParticipantGrid.layoutParams = params binding!!.callControls.visibility = View.GONE binding!!.callInfosLinearLayout.visibility = View.GONE binding!!.selfVideoViewWrapper.visibility = View.GONE binding!!.callStates.callStateRelativeLayout.visibility = View.GONE binding!!.pipCallConversationNameTextView.text = conversationName - if (isVoiceOnlyCall) { - if (participantItems.size > 1) { + binding!!.selfVideoRenderer.clearImage() + binding!!.selfVideoRenderer.release() + + if (participantItems.size == 1) { + binding!!.pipOverlay.visibility = View.GONE + } else { + binding!!.composeParticipantGrid.visibility = View.GONE + + if (localVideoTrack?.enabled() == true) { + binding!!.pipOverlay.visibility = View.VISIBLE + binding!!.pipSelfVideoRenderer.visibility = View.VISIBLE + + try { + binding!!.pipSelfVideoRenderer.init(rootEglBase!!.eglBaseContext, null) + } catch (e: IllegalStateException) { + Log.d(TAG, "pipGroupVideoRenderer already initialized", e) + } + binding!!.pipSelfVideoRenderer.setZOrderMediaOverlay(true) + // disabled because it causes some devices to crash + binding!!.pipSelfVideoRenderer.setEnableHardwareScaler(false) + binding!!.pipSelfVideoRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) + + localVideoTrack?.addSink(binding?.pipSelfVideoRenderer) + } else { binding!!.pipOverlay.visibility = View.VISIBLE binding!!.pipSelfVideoRenderer.visibility = View.GONE - } else { - binding!!.pipOverlay.visibility = View.GONE - } - } else { - binding!!.selfVideoRenderer.clearImage() - binding!!.selfVideoRenderer.release() - if (participantItems.size > 1) { - binding!!.pipOverlay.visibility = View.VISIBLE - initSelfVideoViewForPipMode() - } else { - binding!!.pipOverlay.visibility = View.GONE } } } @@ -3240,6 +3227,7 @@ class CallActivity : CallBaseActivity() { override fun updateUiForNormalMode() { Log.d(TAG, "updateUiForNormalMode") binding!!.pipOverlay.visibility = View.GONE + binding!!.composeParticipantGrid.visibility = View.VISIBLE if (isVoiceOnlyCall) { binding!!.callControls.visibility = View.VISIBLE 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 5cfd29a81..fa96171f1 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 @@ -10,7 +10,6 @@ package com.nextcloud.talk.call.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -19,7 +18,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color 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.adapters.ParticipantUiState @@ -34,7 +32,6 @@ fun AvatarWithFallback(participant: ParticipantUiState, modifier: Modifier = Mod Box( modifier = modifier - .size(150.dp) .clip(CircleShape), contentAlignment = Alignment.Center ) { 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 d6baa6775..b812b5941 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 @@ -29,12 +29,14 @@ import com.nextcloud.talk.adapters.ParticipantUiState import org.webrtc.EglBase import kotlin.math.ceil +@Suppress("LongParameterList") @Composable fun ParticipantGrid( modifier: Modifier = Modifier, eglBase: EglBase?, participantUiStates: List, isVoiceOnlyCall: Boolean, + isInPipMode: Boolean, onClick: () -> Unit ) { val configuration = LocalConfiguration.current @@ -58,7 +60,7 @@ fun ParticipantGrid( val rows = ceil(participantUiStates.size / columns.toFloat()).toInt() - val heightForNonGridComponents = if (isVoiceOnlyCall) { + val heightForNonGridComponents = if (isVoiceOnlyCall && !isInPipMode) { // this is a workaround for now. It should ~summarize the height of callInfosLinearLayout and callControls // Once everything is migrated to jetpack, this workaround should be obsolete or solved in a better way 240.dp @@ -97,6 +99,7 @@ fun ParticipantGrid( .height(itemHeight) .fillMaxWidth(), eglBase = eglBase, + isInPipMode = isInPipMode, isVoiceOnlyCall = isVoiceOnlyCall ) } @@ -109,7 +112,8 @@ fun ParticipantGridPreview() { ParticipantGrid( participantUiStates = getTestParticipants(1), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -119,7 +123,8 @@ fun TwoParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(2), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -129,7 +134,8 @@ fun ThreeParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(3), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -139,7 +145,8 @@ fun FourParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(4), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -149,7 +156,8 @@ fun FiveParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(5), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -159,7 +167,8 @@ fun SevenParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(7), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -169,7 +178,8 @@ fun FiftyParticipants() { ParticipantGrid( participantUiStates = getTestParticipants(50), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -183,7 +193,8 @@ fun OneParticipantLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(1), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -197,7 +208,8 @@ fun TwoParticipantsLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(2), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -211,7 +223,8 @@ fun ThreeParticipantsLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(3), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -225,7 +238,8 @@ fun FourParticipantsLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(4), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -239,7 +253,8 @@ fun SevenParticipantsLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(7), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = false ) {} } @@ -253,7 +268,8 @@ fun FiftyParticipantsLandscape() { ParticipantGrid( participantUiStates = getTestParticipants(50), eglBase = null, - isVoiceOnlyCall = false + isVoiceOnlyCall = false, + isInPipMode = 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 4d0330c23..049126025 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 @@ -36,10 +36,12 @@ import org.webrtc.EglBase const val NICK_OFFSET = 4f const val NICK_BLUR_RADIUS = 4f +@Suppress("Detekt.LongMethod") @Composable fun ParticipantTile( participantUiState: ParticipantUiState, eglBase: EglBase?, + isInPipMode: Boolean, modifier: Modifier = Modifier, isVoiceOnlyCall: Boolean ) { @@ -53,9 +55,17 @@ fun ParticipantTile( if (!isVoiceOnlyCall && participantUiState.isStreamEnabled && participantUiState.mediaStream != null) { WebRTCVideoView(participantUiState, eglBase) } else { + val avatarSize = if (isInPipMode) { + 100.dp + } else { + 150.dp + } + AvatarWithFallback( participant = participantUiState, - modifier = Modifier.align(Alignment.Center) + modifier = Modifier + .align(Alignment.Center) + .size(avatarSize) ) } @@ -125,6 +135,7 @@ fun ParticipantTilePreview() { .fillMaxWidth() .height(300.dp), eglBase = null, + isInPipMode = false, isVoiceOnlyCall = false ) }