Send ancestor events for LiveRegion announcements

Prior to this CL, if an ancestor and its descendant both had layout changes, only the descendant would send accessibility events (this was done in aosp/2729277 to minimize number of events sent by Compose).

In the case of a liveRegion activated by a button, if the ancestor value change was triggered by a descendant button being clicked, only the `TYPE_VIEW_CLICKED` event from the descendant was being clicked, even though the ancestor value was also being changed. Without alerting TalkBack that the ancestor value was being changed, LiveRegions were not working properly.

With this fix, `CONTENT_CHANGE_TYPE_SUBTREE` is now sent in addition to `TYPE_VIEW_CLICKED`, fixing the scenario where liveRegion announcements were not being recognized by TalkBack if the change and its trigger were on ancestor/descendant components.

Fixes: 348590026
Test: sendClickedAndChangeSubtree_whenDescendantClicked() and verified with local repro provided in bug
(cherry picked from https://android-review.googlesource.com/q/commit:d2e708a4b92dd375abb47fead572b4308e72dd1e)
Merged-In: I308d5122947ecc3afbf0dbeb0d96f977d655be8a
Change-Id: I308d5122947ecc3afbf0dbeb0d96f977d655be8a
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 9f57a29..3599029 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -157,6 +157,7 @@
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertContentDescriptionEquals
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertIsNotSelected
 import androidx.compose.ui.test.assertIsOff
 import androidx.compose.ui.test.assertIsOn
@@ -2984,6 +2985,64 @@
     }
 
     @Test
+    fun sendClickedAndChangeSubtree_whenDescendantClicked() {
+        val textTag = "text_tag"
+        // Arrange.
+        setContent {
+            var selected by remember { mutableStateOf(true) }
+            Box {
+                Box(Modifier.testTag(textTag)) {
+                    if (selected) {
+                        BasicText(text = "DisappearingText")
+                    }
+                }
+                Box(Modifier.clickable(onClick = { selected = !selected }).testTag(tag)) {
+                    BasicText("ToggleableComponent")
+                }
+            }
+        }
+        val toggleableVirtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+
+        // Act.
+        val actionPerformed =
+            rule.runOnUiThread {
+                provider.performAction(toggleableVirtualViewId, ACTION_CLICK, null)
+            }
+
+        // Assert that `TYPE_VIEW_CLICKED` event was sent.
+        rule.runOnIdle {
+            assertThat(actionPerformed).isTrue()
+            verify(container, times(1))
+                .requestSendAccessibilityEvent(
+                    eq(androidComposeView),
+                    argThat(
+                        ArgumentMatcher {
+                            getAccessibilityEventSourceSemanticsNodeId(it) ==
+                                toggleableVirtualViewId && it.eventType == TYPE_VIEW_CLICKED
+                        }
+                    )
+                )
+        }
+
+        rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
+
+        // Assert that `TYPE_WINDOW_CONTENT_CHANGED` event was also sent.
+        rule.onNodeWithTag(textTag).assertIsNotDisplayed()
+        rule.runOnIdle {
+            verify(container, atLeastOnce())
+                .requestSendAccessibilityEvent(
+                    eq(androidComposeView),
+                    argThat(
+                        ArgumentMatcher {
+                            it.eventType == TYPE_WINDOW_CONTENT_CHANGED &&
+                                it.contentChangeTypes == CONTENT_CHANGE_TYPE_SUBTREE
+                        }
+                    )
+                )
+        }
+    }
+
+    @Test
     fun sendStateChangeEvent_whenSelectedChange() {
         // Arrange.
         setContent {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index c63d0c5..cf326bb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -2371,13 +2371,6 @@
             return
         }
 
-        // No need to send an event if an ancestor has also been changed
-        for (potentialAncestor in subtreeChangedLayoutNodes.indices) {
-            if (subtreeChangedLayoutNodes.valueAt(potentialAncestor).isAncestorOf(layoutNode)) {
-                return
-            }
-        }
-
         // When we finally send the event, make sure it is an accessibility-focusable node.
         val id = trace("GetSemanticsNode") {
             var semanticsNode = if (layoutNode.nodes.has(Nodes.Semantics))
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
index 9e94664..0c0e598 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/SemanticsUtils.android.kt
@@ -24,7 +24,6 @@
 import androidx.collection.mutableIntObjectMapOf
 import androidx.collection.mutableIntSetOf
 import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.node.OwnerScope
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.ScrollAxisRange
@@ -136,11 +135,6 @@
 internal fun AndroidViewsHandler.semanticsIdToView(id: Int): View? =
     layoutNodeToHolder.entries.firstOrNull { it.key.semanticsId == id }?.value
 
-internal fun LayoutNode.isAncestorOf(node: LayoutNode): Boolean {
-    val p = node.parent ?: return false
-    return (p == this) || isAncestorOf(p)
-}
-
 // TODO(mnuzen): refactor `currentSemanticsNodes` in the AccessibilityDelegate file to also use
 // IntObjectMap's. Then ACVADC can also call `getAllUncoveredSemanticsNodesToIntObjectMap` instead
 // of `getAllUncoveredSemanticsNodesToMap` as it does now.