From 20016adef1ef68ac2d7027bc109b84611338d2e8 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 21 Jan 2026 12:38:34 -0400 Subject: [PATCH] Display self-pip above raise hands bar if it is present. --- .../webrtc/v2/CallElementsLayout.kt | 84 ++++++++++++++----- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallElementsLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallElementsLayout.kt index 096301201c..dddedf04ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallElementsLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallElementsLayout.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.SubcomposeLayout import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Constraints @@ -69,25 +70,62 @@ fun CallElementsLayout( bottomSheetWidth.roundToPx() } - Box(modifier = modifier) { - BlurrableContentLayer( - isFocused = isFocused, - isPortrait = isPortrait, - bottomInsetPx = bottomInsetPx, - bottomSheetWidthPx = bottomSheetWidthPx, - barsSlot = { Bars() }, - callGridSlot = callGridSlot, - reactionsSlot = reactionsSlot, - callOverflowSlot = callOverflowSlot - ) + SubcomposeLayout(modifier = modifier) { constraints -> + // First, measure the bars to get their height + val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0) - PipLayer( - pictureInPictureSlot = pictureInPictureSlot, - localRenderState = localRenderState, - bottomInsetPx = bottomInsetPx, - pipSizePx = pipSizePx, - bottomSheetWidthPx = bottomSheetWidthPx - ) + // Measure overflow to determine constraints for bars + val overflowPlaceables = subcompose("overflow_measure", callOverflowSlot).map { it.measure(looseConstraints) } + val constrainedHeightOffset = if (isPortrait) overflowPlaceables.maxOfOrNull { it.height } ?: 0 else 0 + val constrainedWidthOffset = if (isPortrait) { 0 } else overflowPlaceables.maxOfOrNull { it.width } ?: 0 + + val nonOverflowConstraints = looseConstraints.offset(horizontal = -constrainedWidthOffset, vertical = -constrainedHeightOffset) + + val barConstraints = if (bottomInsetPx > constrainedHeightOffset) { + looseConstraints.offset(-constrainedWidthOffset, -bottomInsetPx) + } else { + nonOverflowConstraints + } + + val barsMaxWidth = minOf(barConstraints.maxWidth, bottomSheetWidthPx) + val barsConstrainedToSheet = barConstraints.copy(maxWidth = barsMaxWidth) + + val barsPlaceables = subcompose("bars_measure") { Bars() }.map { it.measure(barsConstrainedToSheet) } + val barsHeightPx = barsPlaceables.sumOf { it.height } + val barsWidthPx = barsPlaceables.maxOfOrNull { it.width } ?: 0 + + // Now compose and measure the actual layers with the bars height available + val blurrableLayerPlaceable = subcompose("blurrable") { + BlurrableContentLayer( + isFocused = isFocused, + isPortrait = isPortrait, + bottomInsetPx = bottomInsetPx, + bottomSheetWidthPx = bottomSheetWidthPx, + barsSlot = { Bars() }, + callGridSlot = callGridSlot, + reactionsSlot = reactionsSlot, + callOverflowSlot = callOverflowSlot + ) + }.map { it.measure(constraints) } + + // Use the wider of bars or bottom sheet for space calculation + val centeredContentWidthPx = maxOf(barsWidthPx, bottomSheetWidthPx) + + val pipLayerPlaceable = subcompose("pip") { + PipLayer( + pictureInPictureSlot = pictureInPictureSlot, + localRenderState = localRenderState, + bottomInsetPx = bottomInsetPx, + barsHeightPx = barsHeightPx, + pipSizePx = pipSizePx, + centeredContentWidthPx = centeredContentWidthPx + ) + }.map { it.measure(constraints) } + + layout(constraints.maxWidth, constraints.maxHeight) { + blurrableLayerPlaceable.forEach { it.place(0, 0) } + pipLayerPlaceable.forEach { it.place(0, 0) } + } } } @@ -165,8 +203,9 @@ private fun PipLayer( pictureInPictureSlot: @Composable () -> Unit, localRenderState: WebRtcLocalRenderState, bottomInsetPx: Int, + barsHeightPx: Int, pipSizePx: Size, - bottomSheetWidthPx: Int + centeredContentWidthPx: Int ) { Layout( content = pictureInPictureSlot, @@ -177,8 +216,11 @@ private fun PipLayer( val pictureInPictureConstraints: Constraints = when (localRenderState) { WebRtcLocalRenderState.GONE, WebRtcLocalRenderState.SMALLER_RECTANGLE, WebRtcLocalRenderState.LARGE, WebRtcLocalRenderState.LARGE_NO_VIDEO, WebRtcLocalRenderState.FOCUSED -> constraints WebRtcLocalRenderState.SMALL_RECTANGLE, WebRtcLocalRenderState.EXPANDED -> { - val shouldOffset = bottomInsetPx > 0 && looseConstraints.maxWidth - pipSizePx.width - pipSizePx.width - bottomSheetWidthPx < 0 - looseConstraints.offset(vertical = if (shouldOffset) -bottomInsetPx else 0) + // Check if there's enough space on either side of the centered content (bars/sheet) + val spaceOnEachSide = (looseConstraints.maxWidth - centeredContentWidthPx) / 2 + val shouldOffset = centeredContentWidthPx > 0 && spaceOnEachSide < pipSizePx.width + val offsetAmount = bottomInsetPx + barsHeightPx + looseConstraints.offset(vertical = if (shouldOffset) -offsetAmount else 0) } }