aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-03-16 22:49:51 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2023-03-16 22:49:51 +0000
commit497e3d8cb03a99657c4f51c6b9f7317f57b5f15a (patch)
tree69e9be0dcd410934ff158809d5173a73bdce1b97
parent8b6d82fa81cf938a709c55cc97db95c16f0d5715 (diff)
parent9a1247639be7542ead7263f92de3a54a064817fb (diff)
downloadsupport-497e3d8cb03a99657c4f51c6b9f7317f57b5f15a.tar.gz
Merge "Merge cherrypicks of ['android-review.googlesource.com/2472949', 'android-review.googlesource.com/2494919'] into androidx-compose-integration-release." into androidx-compose-integration-release
-rw-r--r--compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt37
-rw-r--r--compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt82
-rw-r--r--compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt13
-rw-r--r--compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt2
4 files changed, 132 insertions, 2 deletions
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index 71f542b250b..925bd713b52 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -18,6 +18,7 @@ package androidx.compose.ui.layout
import android.annotation.SuppressLint
import android.os.Build
+import android.view.View
import android.widget.FrameLayout
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
@@ -52,6 +53,7 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.layout.RootMeasurePolicy.measure
import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalDensity
@@ -71,6 +73,7 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
@@ -2351,6 +2354,40 @@ class SubcomposeLayoutTest {
.assertExists()
}
+ // Regression test of b/271156218
+ @Test
+ fun deactivatingDeeplyNestedAndroidViewDoesNotCauseRemeasure() {
+ var showContent by mutableStateOf(true)
+ val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(1))
+ rule.setContent {
+ SubcomposeLayout(
+ state = state,
+ modifier = Modifier.fillMaxSize()
+ ) { constraints ->
+ val content = if (showContent) {
+ subcompose(0) {
+ Box {
+ AndroidView(::View, Modifier.fillMaxSize().testTag("AndroidView"))
+ }
+ }
+ } else emptyList()
+
+ val placeables = measure(content, constraints)
+ layout(100, 100) {
+ placeables.placeChildren()
+ }
+ }
+ }
+
+ rule.onNodeWithTag("AndroidView").assertExists()
+
+ rule.runOnIdle { showContent = false }
+ rule.onNodeWithTag("AndroidView").assertIsNotDisplayed()
+
+ rule.runOnIdle { showContent = true }
+ rule.onNodeWithTag("AndroidView").assertExists()
+ }
+
private fun composeItems(
state: SubcomposeLayoutState,
items: MutableState<List<Int>>
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
index 2084e711f38..0ba55a45e78 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt
@@ -37,7 +37,10 @@ import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverNode
import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.Constraints
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -499,6 +502,47 @@ class ModifierNodeReuseAndDeactivationTest {
assertThat(receivedValue).isEqualTo(1)
}
}
+
+ @Test
+ fun placingChildWithReusedUnchangedModifier() {
+ // regression test for b/271430143
+ var active by mutableStateOf(true)
+ var modifier by mutableStateOf(StatelessLayoutElement1.then(StatelessLayoutElement2))
+ var childX by mutableStateOf(0)
+
+ rule.setContent {
+ ReusableContentHost(active) {
+ ReusableContent(0) {
+ Layout(content = {
+ Layout(
+ modifier = modifier.testTag("child"),
+ measurePolicy = MeasurePolicy
+ )
+ }) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ layout(placeable.width, placeable.height) {
+ childX.toString()
+ placeable.place(childX, 0)
+ }
+ }
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ active = false
+ }
+
+ rule.runOnIdle {
+ active = true
+ modifier = StatelessLayoutElement1
+ // force relayout parent
+ childX = 10
+ }
+
+ rule.onNodeWithTag("child")
+ .assertLeftPositionInRootIsEqualTo(with(rule.density) { 10.toDp() })
+ }
}
@Composable
@@ -657,3 +701,41 @@ private data class ObserverModifierElement(
}
}
}
+
+private object StatelessLayoutElement1 : ModifierNodeElement<StatelessLayoutModifier1>() {
+ override fun create() = StatelessLayoutModifier1()
+ override fun update(node: StatelessLayoutModifier1) = node
+ override fun hashCode(): Int = 241
+ override fun equals(other: Any?) = other === this
+}
+
+private class StatelessLayoutModifier1 : Modifier.Node(), LayoutModifierNode {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+}
+
+private object StatelessLayoutElement2 : ModifierNodeElement<StatelessLayoutModifier2>() {
+ override fun create() = StatelessLayoutModifier2()
+ override fun update(node: StatelessLayoutModifier2) = node
+ override fun hashCode(): Int = 242
+ override fun equals(other: Any?) = other === this
+}
+
+private class StatelessLayoutModifier2 : Modifier.Node(), LayoutModifierNode {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(placeable.width, placeable.height) {
+ placeable.place(0, 0)
+ }
+ }
+} \ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
index 258b7171ea5..bd6bf08f5f6 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
@@ -204,7 +204,7 @@ internal open class AndroidViewHolder(
override fun onDeactivate() {
reset()
- removeView(view)
+ removeAllViewsInLayout()
}
override fun onRelease() {
@@ -212,6 +212,13 @@ internal open class AndroidViewHolder(
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (view?.parent !== this) {
+ setMeasuredDimension(
+ MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec)
+ )
+ return
+ }
view?.measure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(view?.measuredWidth ?: 0, view?.measuredHeight ?: 0)
lastWidthMeasureSpec = widthMeasureSpec
@@ -339,6 +346,10 @@ internal open class AndroidViewHolder(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
+ if (childCount == 0) {
+ return layout(constraints.minWidth, constraints.minHeight) {}
+ }
+
if (constraints.minWidth != 0) {
getChildAt(0).minimumWidth = constraints.minWidth
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
index a01e089bd03..e6eeda92dc1 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt
@@ -241,7 +241,7 @@ internal class NodeChain(val layoutNode: LayoutNode) {
var node: Modifier.Node? = tail.parent
while (node != null) {
if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) {
- val next = if (node.isAttached) {
+ val next = if (node.coordinator != null) {
val c = node.coordinator as LayoutModifierNodeCoordinator
val prevNode = c.layoutModifierNode
c.layoutModifierNode = node