Merge changes I8abf7c0c,I260a6279 into androidx-main

* changes:
  Fix tests broken after DEBUG was left sat to true
  The ViewFactoryHolder should return itself as viewRoot
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/AndroidViewTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/AndroidViewTest.kt
index 3f51e6b..282b65d 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/AndroidViewTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/AndroidViewTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.ui.inspection
 
+import android.view.View
 import android.view.ViewGroup
 import androidx.compose.ui.inspection.rules.ComposeInspectionRule
 import androidx.compose.ui.inspection.rules.sendCommand
@@ -44,7 +45,11 @@
             it.viewId != 0L
         }.single()
         assertThat(strings[composeNode.name]).isEqualTo("ComposeNode")
-        val viewHolder = (rule.rootsForTest.single() as ViewGroup).getChildAt(0)
-        assertThat(composeNode.viewId).isEqualTo(viewHolder.uniqueDrawingId)
+        val androidViewsHandler = rule.rootsForTest.single().view.childAt(0)
+        val viewFactoryHolder = androidViewsHandler.childAt(0)
+        assertThat(composeNode.viewId).isEqualTo(viewFactoryHolder.uniqueDrawingId)
     }
+
+    private fun View.childAt(index: Int): View =
+        (this as ViewGroup).getChildAt(index)
 }
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
index 09a5db6..6904996 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTreeTest.kt
@@ -56,6 +56,7 @@
 import androidx.compose.ui.R
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.inspection.compose.flatten
 import androidx.compose.ui.inspection.testdata.TestActivity
 import androidx.compose.ui.layout.GraphicLayerInfo
 import androidx.compose.ui.platform.LocalDensity
@@ -93,7 +94,7 @@
 import java.util.WeakHashMap
 import kotlin.math.roundToInt
 
-private const val DEBUG = true
+private const val DEBUG = false
 private const val ROOT_ID = 3L
 private const val MAX_RECURSIONS = 2
 private const val MAX_ITERABLE_SIZE = 5
@@ -120,23 +121,26 @@
         return findAllAndroidComposeViews().single()
     }
 
-    private fun findAllAndroidComposeViews(): List<View> {
-        val composeViews = mutableListOf<View>()
+    private fun findAllAndroidComposeViews(): List<View> =
+        findAllViews("AndroidComposeView")
+
+    private fun findAllViews(className: String): List<View> {
+        val views = mutableListOf<View>()
         WindowInspector.getGlobalWindowViews().forEach {
-            collectAllAndroidComposeView(it.rootView, composeViews)
+            collectAllViews(it.rootView, className, views)
         }
-        return composeViews
+        return views
     }
 
-    private fun collectAllAndroidComposeView(view: View, composeViews: MutableList<View>) {
-        if (view.javaClass.simpleName == "AndroidComposeView") {
-            composeViews.add(view)
+    private fun collectAllViews(view: View, className: String, views: MutableList<View>) {
+        if (view.javaClass.simpleName == className) {
+            views.add(view)
         }
         if (view !is ViewGroup) {
             return
         }
         for (i in 0 until view.childCount) {
-            collectAllAndroidComposeView(view.getChildAt(i), composeViews)
+            collectAllViews(view.getChildAt(i), className, views)
         }
     }
 
@@ -146,6 +150,11 @@
     }
 
     @Test
+    fun doNotCommitWithDebugSetToTrue() {
+        assertThat(DEBUG).isFalse()
+    }
+
+    @Test
     fun buildTree() {
         val slotTableRecord = CompositionDataRecord.create()
 
@@ -495,6 +504,7 @@
         val builder = LayoutInspectorTree()
 
         val appNodes = builder.convert(appView)
+        dumpSlotTableSet(slotTableRecord)
         dumpNodes(appNodes, appView, builder)
 
         // Verify that the main app does not contain the Popup
@@ -516,7 +526,7 @@
         dumpNodes(dialogNodes, dialogView, builder)
 
         // Verify that the AlertDialog is captured with content
-        validate(dialogNodes, builder, ignoreElementsFromShow = false) {
+        validate(dialogNodes, builder) {
             node(
                 name = "AlertDialog",
                 fileName = "LayoutInspectorTreeTest.kt",
@@ -564,6 +574,7 @@
         validate(appNodes, builder) {
             node(
                 name = "Column",
+                isRenderNode = true,
                 fileName = "LayoutInspectorTreeTest.kt",
                 children = listOf("Text")
             )
@@ -579,7 +590,7 @@
         dumpNodes(popupNodes, popupView, builder)
 
         // Verify that the Popup is captured with content
-        validate(popupNodes, builder, ignoreElementsFromShow = false) {
+        validate(popupNodes, builder) {
             node(
                 name = "Popup",
                 fileName = "LayoutInspectorTreeTest.kt",
@@ -617,7 +628,7 @@
         dumpNodes(nodes, composeView, builder)
         val androidView = nodes.flatMap { flatten(it) }.single { it.name == "AndroidView" }
 
-        validate(listOf(androidView), builder, ignoreElementsFromShow = false) {
+        validate(listOf(androidView), builder) {
             node(
                 name = "AndroidView",
                 fileName = "LayoutInspectorTreeTest.kt",
@@ -631,6 +642,47 @@
         }
     }
 
+    @Test
+    fun testDoubleAndroidView() {
+        val slotTableRecord = CompositionDataRecord.create()
+
+        show {
+            Inspectable(slotTableRecord) {
+                Column {
+                    Text("Compose Text1")
+                    AndroidView({ context ->
+                        TextView(context).apply {
+                            text = "first"
+                        }
+                    })
+                    Text("Compose Text2")
+                    AndroidView({ context ->
+                        TextView(context).apply {
+                            text = "second"
+                        }
+                    })
+                }
+            }
+        }
+        val composeView = findAndroidComposeView() as ViewGroup
+        composeView.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
+        val builder = LayoutInspectorTree()
+        builder.hideSystemNodes = false
+        val nodes = builder.convert(composeView)
+        dumpSlotTableSet(slotTableRecord)
+        dumpNodes(nodes, composeView, builder)
+        val textViews = findAllViews("TextView")
+        val firstTextView = textViews
+            .filterIsInstance<TextView>()
+            .first { it.text == "first" }
+        val secondTextView = textViews
+            .filterIsInstance<TextView>()
+            .first { it.text == "second" }
+        val composeNodes = nodes.flatMap { it.flatten() }.filter { it.name == "ComposeNode" }
+        assertThat(composeNodes[0].viewId).isEqualTo(viewParent(secondTextView)?.uniqueDrawingId)
+        assertThat(composeNodes[1].viewId).isEqualTo(viewParent(firstTextView)?.uniqueDrawingId)
+    }
+
     // WARNING: The formatting of the lines below here affect test results.
     val titleLine = Throwable().stackTrace[0].lineNumber + 3
 
@@ -782,16 +834,12 @@
         checkSemantics: Boolean = false,
         checkLineNumbers: Boolean = false,
         checkRenderNodes: Boolean = true,
-        ignoreElementsFromShow: Boolean = true,
         block: TreeValidationReceiver.() -> Unit = {}
     ) {
         if (DEBUG) {
             return
         }
         val nodes = result.flatMap { flatten(it) }.listIterator()
-        if (ignoreElementsFromShow) {
-            ignoreStart(nodes, "Box")
-        }
         val tree = TreeValidationReceiver(
             nodes,
             density,
@@ -804,12 +852,6 @@
         tree.block()
     }
 
-    private fun ignoreStart(nodes: ListIterator<InspectorNode>, vararg names: String) {
-        for (name in names) {
-            assertThat(nodes.next().name).isEqualTo(name)
-        }
-    }
-
     private class TreeValidationReceiver(
         val nodeIterator: Iterator<InspectorNode>,
         val density: Density,
@@ -899,11 +941,14 @@
         }
 
         private fun View.hasChild(id: Long): Boolean {
+            if (uniqueDrawingId == id) {
+                return true
+            }
             if (this !is ViewGroup) {
                 return false
             }
             for (index in 0..childCount) {
-                if (getChildAt(index).uniqueDrawingId == id) {
+                if (getChildAt(index).hasChild(id)) {
                     return true
                 }
             }
@@ -914,7 +959,11 @@
     private fun flatten(node: InspectorNode): List<InspectorNode> =
         listOf(node).plus(node.children.flatMap { flatten(it) })
 
-    fun show(composable: @Composable () -> Unit) = composeTestRule.setContent(composable)
+    private fun viewParent(view: View): View? =
+        view.parent as? View
+
+    private fun show(composable: @Composable () -> Unit) =
+        composeTestRule.setContent(composable)
 
     // region DEBUG print methods
     private fun dumpNodes(nodes: List<InspectorNode>, view: View, builder: LayoutInspectorTree) {
@@ -1022,12 +1071,14 @@
     }
 
     private fun dumpGroup(group: Group, indent: Int) {
+        val location = group.location
         val position = group.position?.let { "\"$it\"" } ?: "null"
         val box = group.box
         val id = group.modifierInfo.mapNotNull { (it.extra as? GraphicLayerInfo)?.layerId }
             .singleOrNull() ?: 0
         println(
             "\"${"  ".repeat(indent)}\", ${group.javaClass.simpleName}, \"${group.name}\", " +
+                "file: ${location?.sourceFile}  hash: ${location?.packageHash}, " +
                 "params: ${group.parameters.size}, children: ${group.children.size}, " +
                 "$id, $position, " +
                 "${box.left}, ${box.right}, ${box.right - box.left}, ${box.bottom - box.top}"
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
index 3a917f6..74b6615 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt
@@ -57,6 +57,7 @@
     packageNameHash("androidx.compose.material"),
     packageNameHash("androidx.compose.material.ripple"),
     packageNameHash("androidx.compose.runtime"),
+    packageNameHash("androidx.compose.runtime.saveable"),
     packageNameHash("androidx.compose.ui"),
     packageNameHash("androidx.compose.ui.graphics.vector"),
     packageNameHash("androidx.compose.ui.layout"),
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
index 85ad0bd..994bf78 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
@@ -135,7 +135,7 @@
 
     internal var typedView: T? = null
 
-    override val viewRoot: View? get() = parent as? View
+    override val viewRoot: View get() = this
 
     var factory: ((Context) -> T)? = null
         set(value) {