mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
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 <dev@mhibbe.de>
This commit is contained in:
parent
eaed93087b
commit
6a048fde08
@ -938,35 +938,17 @@ class CallActivity : CallBaseActivity() {
|
|||||||
binding!!.pipSelfVideoRenderer.release()
|
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() {
|
private fun initGrid() {
|
||||||
Log.d(TAG, "initGrid")
|
Log.d(TAG, "initGrid")
|
||||||
|
binding!!.composeParticipantGrid.visibility = View.VISIBLE
|
||||||
binding!!.composeParticipantGrid.setContent {
|
binding!!.composeParticipantGrid.setContent {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
val participantUiStates = participantItems.map { it.uiStateFlow.collectAsState().value }
|
val participantUiStates = participantItems.map { it.uiStateFlow.collectAsState().value }
|
||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = participantUiStates,
|
participantUiStates = participantUiStates,
|
||||||
eglBase = rootEglBase!!,
|
eglBase = rootEglBase!!,
|
||||||
isVoiceOnlyCall = isVoiceOnlyCall
|
isVoiceOnlyCall = isVoiceOnlyCall,
|
||||||
|
isInPipMode = isInPipMode
|
||||||
) {
|
) {
|
||||||
animateCallControls(true, 0)
|
animateCallControls(true, 0)
|
||||||
}
|
}
|
||||||
@ -3206,33 +3188,38 @@ class CallActivity : CallBaseActivity() {
|
|||||||
|
|
||||||
override fun updateUiForPipMode() {
|
override fun updateUiForPipMode() {
|
||||||
Log.d(TAG, "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!!.callControls.visibility = View.GONE
|
||||||
binding!!.callInfosLinearLayout.visibility = View.GONE
|
binding!!.callInfosLinearLayout.visibility = View.GONE
|
||||||
binding!!.selfVideoViewWrapper.visibility = View.GONE
|
binding!!.selfVideoViewWrapper.visibility = View.GONE
|
||||||
binding!!.callStates.callStateRelativeLayout.visibility = View.GONE
|
binding!!.callStates.callStateRelativeLayout.visibility = View.GONE
|
||||||
binding!!.pipCallConversationNameTextView.text = conversationName
|
binding!!.pipCallConversationNameTextView.text = conversationName
|
||||||
|
|
||||||
if (isVoiceOnlyCall) {
|
binding!!.selfVideoRenderer.clearImage()
|
||||||
if (participantItems.size > 1) {
|
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!!.pipOverlay.visibility = View.VISIBLE
|
||||||
binding!!.pipSelfVideoRenderer.visibility = View.GONE
|
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() {
|
override fun updateUiForNormalMode() {
|
||||||
Log.d(TAG, "updateUiForNormalMode")
|
Log.d(TAG, "updateUiForNormalMode")
|
||||||
binding!!.pipOverlay.visibility = View.GONE
|
binding!!.pipOverlay.visibility = View.GONE
|
||||||
|
binding!!.composeParticipantGrid.visibility = View.VISIBLE
|
||||||
|
|
||||||
if (isVoiceOnlyCall) {
|
if (isVoiceOnlyCall) {
|
||||||
binding!!.callControls.visibility = View.VISIBLE
|
binding!!.callControls.visibility = View.VISIBLE
|
||||||
|
@ -10,7 +10,6 @@ package com.nextcloud.talk.call.components
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -19,7 +18,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.nextcloud.talk.adapters.ParticipantUiState
|
import com.nextcloud.talk.adapters.ParticipantUiState
|
||||||
@ -34,7 +32,6 @@ fun AvatarWithFallback(participant: ParticipantUiState, modifier: Modifier = Mod
|
|||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(150.dp)
|
|
||||||
.clip(CircleShape),
|
.clip(CircleShape),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
|
@ -29,12 +29,14 @@ import com.nextcloud.talk.adapters.ParticipantUiState
|
|||||||
import org.webrtc.EglBase
|
import org.webrtc.EglBase
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
@Composable
|
@Composable
|
||||||
fun ParticipantGrid(
|
fun ParticipantGrid(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
eglBase: EglBase?,
|
eglBase: EglBase?,
|
||||||
participantUiStates: List<ParticipantUiState>,
|
participantUiStates: List<ParticipantUiState>,
|
||||||
isVoiceOnlyCall: Boolean,
|
isVoiceOnlyCall: Boolean,
|
||||||
|
isInPipMode: Boolean,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
@ -58,7 +60,7 @@ fun ParticipantGrid(
|
|||||||
|
|
||||||
val rows = ceil(participantUiStates.size / columns.toFloat()).toInt()
|
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
|
// 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
|
// Once everything is migrated to jetpack, this workaround should be obsolete or solved in a better way
|
||||||
240.dp
|
240.dp
|
||||||
@ -97,6 +99,7 @@ fun ParticipantGrid(
|
|||||||
.height(itemHeight)
|
.height(itemHeight)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
eglBase = eglBase,
|
eglBase = eglBase,
|
||||||
|
isInPipMode = isInPipMode,
|
||||||
isVoiceOnlyCall = isVoiceOnlyCall
|
isVoiceOnlyCall = isVoiceOnlyCall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -109,7 +112,8 @@ fun ParticipantGridPreview() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(1),
|
participantUiStates = getTestParticipants(1),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +123,8 @@ fun TwoParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(2),
|
participantUiStates = getTestParticipants(2),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +134,8 @@ fun ThreeParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(3),
|
participantUiStates = getTestParticipants(3),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +145,8 @@ fun FourParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(4),
|
participantUiStates = getTestParticipants(4),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +156,8 @@ fun FiveParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(5),
|
participantUiStates = getTestParticipants(5),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +167,8 @@ fun SevenParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(7),
|
participantUiStates = getTestParticipants(7),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +178,8 @@ fun FiftyParticipants() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(50),
|
participantUiStates = getTestParticipants(50),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +193,8 @@ fun OneParticipantLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(1),
|
participantUiStates = getTestParticipants(1),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +208,8 @@ fun TwoParticipantsLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(2),
|
participantUiStates = getTestParticipants(2),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +223,8 @@ fun ThreeParticipantsLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(3),
|
participantUiStates = getTestParticipants(3),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +238,8 @@ fun FourParticipantsLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(4),
|
participantUiStates = getTestParticipants(4),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +253,8 @@ fun SevenParticipantsLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(7),
|
participantUiStates = getTestParticipants(7),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +268,8 @@ fun FiftyParticipantsLandscape() {
|
|||||||
ParticipantGrid(
|
ParticipantGrid(
|
||||||
participantUiStates = getTestParticipants(50),
|
participantUiStates = getTestParticipants(50),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false,
|
||||||
|
isInPipMode = false
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,10 +36,12 @@ import org.webrtc.EglBase
|
|||||||
const val NICK_OFFSET = 4f
|
const val NICK_OFFSET = 4f
|
||||||
const val NICK_BLUR_RADIUS = 4f
|
const val NICK_BLUR_RADIUS = 4f
|
||||||
|
|
||||||
|
@Suppress("Detekt.LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
fun ParticipantTile(
|
fun ParticipantTile(
|
||||||
participantUiState: ParticipantUiState,
|
participantUiState: ParticipantUiState,
|
||||||
eglBase: EglBase?,
|
eglBase: EglBase?,
|
||||||
|
isInPipMode: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isVoiceOnlyCall: Boolean
|
isVoiceOnlyCall: Boolean
|
||||||
) {
|
) {
|
||||||
@ -53,9 +55,17 @@ fun ParticipantTile(
|
|||||||
if (!isVoiceOnlyCall && participantUiState.isStreamEnabled && participantUiState.mediaStream != null) {
|
if (!isVoiceOnlyCall && participantUiState.isStreamEnabled && participantUiState.mediaStream != null) {
|
||||||
WebRTCVideoView(participantUiState, eglBase)
|
WebRTCVideoView(participantUiState, eglBase)
|
||||||
} else {
|
} else {
|
||||||
|
val avatarSize = if (isInPipMode) {
|
||||||
|
100.dp
|
||||||
|
} else {
|
||||||
|
150.dp
|
||||||
|
}
|
||||||
|
|
||||||
AvatarWithFallback(
|
AvatarWithFallback(
|
||||||
participant = participantUiState,
|
participant = participantUiState,
|
||||||
modifier = Modifier.align(Alignment.Center)
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.size(avatarSize)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +135,7 @@ fun ParticipantTilePreview() {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(300.dp),
|
.height(300.dp),
|
||||||
eglBase = null,
|
eglBase = null,
|
||||||
|
isInPipMode = false,
|
||||||
isVoiceOnlyCall = false
|
isVoiceOnlyCall = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user