mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 00:29:11 +01:00
Resolve crash in blur container due to multiple measures.
This commit is contained in:
@@ -35,18 +35,6 @@ import org.thoughtcrime.securesms.events.CallParticipant
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
/**
|
||||
* Enum identifying each slot in the BlurrableContentLayer.
|
||||
* Used as subcomposition keys to ensure each slot is only composed once.
|
||||
*/
|
||||
private enum class BlurrableContentSlot {
|
||||
BARS,
|
||||
GRID,
|
||||
REACTIONS,
|
||||
OVERFLOW,
|
||||
AUDIO_INDICATOR
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallElementsLayout(
|
||||
callGridSlot: @Composable () -> Unit,
|
||||
@@ -84,8 +72,6 @@ fun CallElementsLayout(
|
||||
}
|
||||
|
||||
SubcomposeLayout(modifier = modifier) { constraints ->
|
||||
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
|
||||
// Holder to capture measurements from BlurrableContentLayer
|
||||
var measuredBarsHeightPx = 0
|
||||
var measuredBarsWidthPx = 0
|
||||
@@ -133,8 +119,8 @@ fun CallElementsLayout(
|
||||
|
||||
/**
|
||||
* A layer that contains content which can be blurred when the local participant video is focused.
|
||||
* All slots are subcomposed here ONCE to avoid duplicate subcomposition that would cause
|
||||
* IllegalArgumentException when slots contain SubcomposeLayout (like BoxWithConstraints).
|
||||
* Uses a single multi-content Layout pass to avoid re-subcomposing slots that contain
|
||||
* SubcomposeLayout (like BoxWithConstraints), which can trigger duplicate key crashes.
|
||||
*
|
||||
* @param onMeasured Callback invoked during measurement with (barsHeight, barsWidth) to report
|
||||
* dimensions needed by the parent layout for PipLayer positioning.
|
||||
@@ -156,17 +142,26 @@ private fun BlurrableContentLayer(
|
||||
isBlurred = isFocused,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
SubcomposeLayout(modifier = Modifier.fillMaxSize()) { constraints ->
|
||||
Layout(
|
||||
contents = listOf(
|
||||
{ callOverflowSlot() },
|
||||
{ callGridSlot() },
|
||||
{ barsSlot() },
|
||||
{ reactionsSlot() },
|
||||
{ audioIndicatorSlot() }
|
||||
),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { measurables, constraints ->
|
||||
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
|
||||
val overflowPlaceables = subcompose(BlurrableContentSlot.OVERFLOW, callOverflowSlot)
|
||||
.map { it.measure(looseConstraints) }
|
||||
val (overflowMeasurables, gridMeasurables, barsMeasurables, reactionsMeasurables, audioIndicatorMeasurables) = measurables
|
||||
|
||||
val overflowPlaceables = overflowMeasurables.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 gridPlaceables = subcompose(BlurrableContentSlot.GRID, callGridSlot)
|
||||
.map { it.measure(nonOverflowConstraints) }
|
||||
val gridPlaceables = gridMeasurables.map { it.measure(nonOverflowConstraints) }
|
||||
|
||||
val barConstraints = if (bottomInsetPx > constrainedHeightOffset) {
|
||||
looseConstraints.offset(-constrainedWidthOffset, -bottomInsetPx)
|
||||
@@ -178,9 +173,7 @@ private fun BlurrableContentLayer(
|
||||
val barsMaxWidth = minOf(barConstraints.maxWidth, bottomSheetWidthPx)
|
||||
val barsConstrainedToSheet = barConstraints.copy(maxWidth = barsMaxWidth)
|
||||
|
||||
val barsPlaceables = subcompose(BlurrableContentSlot.BARS, barsSlot)
|
||||
.map { it.measure(barsConstrainedToSheet) }
|
||||
|
||||
val barsPlaceables = barsMeasurables.map { it.measure(barsConstrainedToSheet) }
|
||||
val barsHeightPx = barsPlaceables.sumOf { it.height }
|
||||
val barsWidthPx = barsPlaceables.maxOfOrNull { it.width } ?: 0
|
||||
|
||||
@@ -188,17 +181,17 @@ private fun BlurrableContentLayer(
|
||||
onMeasured(barsHeightPx, barsWidthPx)
|
||||
|
||||
val reactionsConstraints = barConstraints.offset(vertical = -barsHeightPx)
|
||||
val reactionsPlaceables = subcompose(BlurrableContentSlot.REACTIONS, reactionsSlot)
|
||||
.map { it.measure(reactionsConstraints) }
|
||||
val reactionsPlaceables = reactionsMeasurables.map { it.measure(reactionsConstraints) }
|
||||
|
||||
val audioIndicatorPlaceables = subcompose(BlurrableContentSlot.AUDIO_INDICATOR, audioIndicatorSlot)
|
||||
.map { it.measure(looseConstraints) }
|
||||
val audioIndicatorPlaceables = audioIndicatorMeasurables.map { it.measure(looseConstraints) }
|
||||
|
||||
layout(looseConstraints.maxWidth, looseConstraints.maxHeight) {
|
||||
overflowPlaceables.forEach {
|
||||
if (isPortrait) {
|
||||
if (isPortrait) {
|
||||
overflowPlaceables.forEach {
|
||||
it.place(0, looseConstraints.maxHeight - it.height)
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
overflowPlaceables.forEach {
|
||||
it.place(looseConstraints.maxWidth - it.width, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,9 +383,11 @@ class RefreshOwnProfileJob private constructor(parameters: Parameters) : BaseJob
|
||||
if (userHasVisibleBadges && userHasInvisibleBadges) {
|
||||
val displayBadgesOnProfile = SignalStore.inAppPayments.getDisplayBadgesOnProfile()
|
||||
Log.d(
|
||||
TAG, "Detected mixed visibility of badges. Telling the server to mark them all " +
|
||||
TAG,
|
||||
"Detected mixed visibility of badges. Telling the server to mark them all " +
|
||||
(if (displayBadgesOnProfile) "" else "not") +
|
||||
" visible.", true
|
||||
" visible.",
|
||||
true
|
||||
)
|
||||
|
||||
val badgeRepository = BadgeRepository(context)
|
||||
|
||||
Reference in New Issue
Block a user