mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-02 08:23:00 +01:00
Eliminate unnecessary utilization of SubcomposeLayout which was causing a calling crash.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
committed by
Cody Henthorne
parent
911d7f3be8
commit
cfc1c35203
@@ -16,11 +16,11 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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
|
||||
@@ -35,6 +35,31 @@ import org.thoughtcrime.securesms.events.CallParticipant
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
/**
|
||||
* Mutable holder for bar dimensions, used to pass measurement results from
|
||||
* BlurrableContentLayer to PipLayer during the same Layout measurement pass
|
||||
* without requiring SubcomposeLayout.
|
||||
*/
|
||||
private class BarDimensions {
|
||||
var heightPx: Int = 0
|
||||
var widthPx: Int = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Arranges call screen content in coordinated layers so the local PiP can avoid centered bars.
|
||||
*
|
||||
* @param callGridSlot Main call grid content.
|
||||
* @param pictureInPictureSlot Local participant PiP content.
|
||||
* @param reactionsSlot Reactions overlay content.
|
||||
* @param raiseHandSlot Slot for the raised-hand bar.
|
||||
* @param callLinkBarSlot Slot for the call-link bar.
|
||||
* @param callOverflowSlot Overflow participants strip.
|
||||
* @param audioIndicatorSlot Participant audio indicator content.
|
||||
* @param bottomInset Bottom inset used to keep content clear of anchored UI.
|
||||
* @param bottomSheetWidth Maximum width of centered bottom content.
|
||||
* @param localRenderState Current local renderer mode.
|
||||
* @param modifier Modifier applied to the root layout.
|
||||
*/
|
||||
@Composable
|
||||
fun CallElementsLayout(
|
||||
callGridSlot: @Composable () -> Unit,
|
||||
@@ -71,48 +96,46 @@ fun CallElementsLayout(
|
||||
bottomSheetWidth.roundToPx()
|
||||
}
|
||||
|
||||
SubcomposeLayout(modifier = modifier) { constraints ->
|
||||
// Holder to capture measurements from BlurrableContentLayer
|
||||
var measuredBarsHeightPx = 0
|
||||
var measuredBarsWidthPx = 0
|
||||
val barDimensions = remember { BarDimensions() }
|
||||
|
||||
// Subcompose and measure the blurrable layer first - it will measure all content internally
|
||||
// and report back the bars dimensions via the onMeasured callback
|
||||
val blurrableLayerPlaceable = subcompose("blurrable") {
|
||||
BlurrableContentLayer(
|
||||
isFocused = isFocused,
|
||||
isPortrait = isPortrait,
|
||||
bottomInsetPx = bottomInsetPx,
|
||||
bottomSheetWidthPx = bottomSheetWidthPx,
|
||||
barsSlot = { Bars() },
|
||||
callGridSlot = callGridSlot,
|
||||
reactionsSlot = reactionsSlot,
|
||||
callOverflowSlot = callOverflowSlot,
|
||||
audioIndicatorSlot = audioIndicatorSlot,
|
||||
onMeasured = { barsHeight, barsWidth ->
|
||||
measuredBarsHeightPx = barsHeight
|
||||
measuredBarsWidthPx = barsWidth
|
||||
}
|
||||
)
|
||||
}.map { it.measure(constraints) }
|
||||
|
||||
// Use the wider of bars or bottom sheet for space calculation
|
||||
val centeredContentWidthPx = maxOf(measuredBarsWidthPx, bottomSheetWidthPx)
|
||||
|
||||
val pipLayerPlaceable = subcompose("pip") {
|
||||
PipLayer(
|
||||
pictureInPictureSlot = pictureInPictureSlot,
|
||||
localRenderState = localRenderState,
|
||||
bottomInsetPx = bottomInsetPx,
|
||||
barsHeightPx = measuredBarsHeightPx,
|
||||
pipSizePx = pipSizePx,
|
||||
centeredContentWidthPx = centeredContentWidthPx
|
||||
)
|
||||
}.map { it.measure(constraints) }
|
||||
Layout(
|
||||
contents = listOf(
|
||||
{
|
||||
BlurrableContentLayer(
|
||||
isFocused = isFocused,
|
||||
isPortrait = isPortrait,
|
||||
bottomInsetPx = bottomInsetPx,
|
||||
bottomSheetWidthPx = bottomSheetWidthPx,
|
||||
barsSlot = { Bars() },
|
||||
callGridSlot = callGridSlot,
|
||||
reactionsSlot = reactionsSlot,
|
||||
callOverflowSlot = callOverflowSlot,
|
||||
audioIndicatorSlot = audioIndicatorSlot,
|
||||
onMeasured = { barsHeight, barsWidth ->
|
||||
barDimensions.heightPx = barsHeight
|
||||
barDimensions.widthPx = barsWidth
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
PipLayer(
|
||||
pictureInPictureSlot = pictureInPictureSlot,
|
||||
localRenderState = localRenderState,
|
||||
bottomInsetPx = bottomInsetPx,
|
||||
barDimensions = barDimensions,
|
||||
pipSizePx = pipSizePx,
|
||||
bottomSheetWidthPx = bottomSheetWidthPx
|
||||
)
|
||||
}
|
||||
),
|
||||
modifier = modifier
|
||||
) { (blurrableMeasurables, pipMeasurables), constraints ->
|
||||
val blurrablePlaceables = blurrableMeasurables.map { it.measure(constraints) }
|
||||
val pipPlaceables = pipMeasurables.map { it.measure(constraints) }
|
||||
|
||||
layout(constraints.maxWidth, constraints.maxHeight) {
|
||||
blurrableLayerPlaceable.forEach { it.place(0, 0) }
|
||||
pipLayerPlaceable.forEach { it.place(0, 0) }
|
||||
blurrablePlaceables.forEach { it.place(0, 0) }
|
||||
pipPlaceables.forEach { it.place(0, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +192,6 @@ private fun BlurrableContentLayer(
|
||||
nonOverflowConstraints
|
||||
}
|
||||
|
||||
// Cap bars width to sheet max width (bars can be narrower if content doesn't fill)
|
||||
val barsMaxWidth = minOf(barConstraints.maxWidth, bottomSheetWidthPx)
|
||||
val barsConstrainedToSheet = barConstraints.copy(maxWidth = barsMaxWidth)
|
||||
|
||||
@@ -177,7 +199,6 @@ private fun BlurrableContentLayer(
|
||||
val barsHeightPx = barsPlaceables.sumOf { it.height }
|
||||
val barsWidthPx = barsPlaceables.maxOfOrNull { it.width } ?: 0
|
||||
|
||||
// Report measurements to parent for PipLayer positioning
|
||||
onMeasured(barsHeightPx, barsWidthPx)
|
||||
|
||||
val reactionsConstraints = barConstraints.offset(vertical = -barsHeightPx)
|
||||
@@ -229,9 +250,9 @@ private fun PipLayer(
|
||||
pictureInPictureSlot: @Composable () -> Unit,
|
||||
localRenderState: WebRtcLocalRenderState,
|
||||
bottomInsetPx: Int,
|
||||
barsHeightPx: Int,
|
||||
barDimensions: BarDimensions,
|
||||
pipSizePx: Size,
|
||||
centeredContentWidthPx: Int
|
||||
bottomSheetWidthPx: Int
|
||||
) {
|
||||
Layout(
|
||||
content = pictureInPictureSlot,
|
||||
@@ -239,13 +260,14 @@ private fun PipLayer(
|
||||
) { measurables, constraints ->
|
||||
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
|
||||
val centeredContentWidthPx = maxOf(barDimensions.widthPx, bottomSheetWidthPx)
|
||||
|
||||
val pictureInPictureConstraints: Constraints = when (localRenderState) {
|
||||
WebRtcLocalRenderState.GONE, WebRtcLocalRenderState.SMALLER_RECTANGLE, WebRtcLocalRenderState.LARGE, WebRtcLocalRenderState.LARGE_NO_VIDEO, WebRtcLocalRenderState.FOCUSED -> constraints
|
||||
WebRtcLocalRenderState.SMALL_RECTANGLE, WebRtcLocalRenderState.EXPANDED -> {
|
||||
// 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
|
||||
val offsetAmount = bottomInsetPx + barDimensions.heightPx
|
||||
looseConstraints.offset(vertical = if (shouldOffset) -offsetAmount else 0)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user