Add a11y handling for Carousel and update samples in playground app

It includes:
- Updated a11y behaviour for Carousel & CarouselItem
- Added a11y samples for LazyList, ImmersiveList, Carousel
& SideNavigation in the playground app

Test: N/A

Change-Id: If5903acaeda99e61c46d56d7819b0926add557cc
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
index c517d99..332cf06 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/Card.kt
@@ -17,7 +17,7 @@
 package androidx.tv.integration.playground
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.focusable
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.width
@@ -37,6 +37,6 @@
             .width(200.dp)
             .height(150.dp)
             .drawBorderOnFocus()
-            .focusable()
+            .clickable { }
     )
 }
\ No newline at end of file
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
index 64f92b9..705ef91 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/FeaturedCarousel.kt
@@ -16,7 +16,6 @@
 
 package androidx.tv.integration.playground
 
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -42,8 +41,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
-import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.material3.Carousel
 import androidx.tv.material3.CarouselDefaults
 import androidx.tv.material3.CarouselState
@@ -88,9 +88,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class,
-    ExperimentalTvFoundationApi::class
-)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun FeaturedCarousel(modifier: Modifier = Modifier) {
     val backgrounds = listOf(
@@ -105,41 +103,47 @@
     )
 
     val carouselState = remember { CarouselState() }
-    FocusGroup {
-        Carousel(
-            itemCount = backgrounds.size,
-            carouselState = carouselState,
-            modifier = modifier
-                .height(300.dp)
-                .fillMaxWidth(),
-            carouselIndicator = {
-                CarouselDefaults.IndicatorRow(
-                    itemCount = backgrounds.size,
-                    activeItemIndex = carouselState.activeItemIndex,
+    Carousel(
+        itemCount = backgrounds.size,
+        carouselState = carouselState,
+        modifier = modifier
+            .height(300.dp)
+            .fillMaxWidth(),
+        carouselIndicator = {
+            CarouselDefaults.IndicatorRow(
+                itemCount = backgrounds.size,
+                activeItemIndex = carouselState.activeItemIndex,
+                modifier = Modifier
+                    .align(Alignment.BottomEnd)
+                    .padding(16.dp),
+            )
+        }
+    ) { itemIndex ->
+        CarouselItem(
+            modifier = Modifier.semantics {
+                contentDescription = "Featured Content"
+            },
+            background = {
+                Box(
                     modifier = Modifier
-                        .align(Alignment.BottomEnd)
-                        .padding(16.dp),
+                        .background(backgrounds[itemIndex])
+                        .fillMaxSize()
                 )
-            }
-        ) { itemIndex ->
-            CarouselItem(
-                background = {
-                    Box(
-                        modifier = Modifier
-                            .background(backgrounds[itemIndex])
-                            .fillMaxSize()
-                    )
-                },
-                modifier =
-                if (itemIndex == 0)
-                    Modifier.initiallyFocused()
-                else
-                    Modifier.restorableFocus()
+            },
+        ) {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .padding(20.dp),
+                contentAlignment = Alignment.BottomStart
             ) {
-                Box(modifier = Modifier) {
-                    OverlayButton(
-                        modifier = Modifier
-                    )
+                Column {
+                    Text(text = "This is sample text content.", color = Color.Yellow)
+                    Text(text = "Sample description.", color = Color.Yellow)
+                    Row {
+                        OverlayButton(text = "Play")
+                        OverlayButton(text = "Add to Watchlist")
+                    }
                 }
             }
         }
@@ -147,14 +151,16 @@
 }
 
 @Composable
-private fun OverlayButton(modifier: Modifier = Modifier) {
+private fun OverlayButton(modifier: Modifier = Modifier, text: String = "Test Button") {
     var isFocused by remember { mutableStateOf(false) }
 
     Button(
         onClick = { },
         modifier = modifier
-            .onFocusChanged { isFocused = it.isFocused }
-            .padding(40.dp)
+            .onFocusChanged {
+                isFocused = it.isFocused
+            }
+            .padding(20.dp)
             .border(
                 width = 2.dp,
                 color = if (isFocused) Color.Red else Color.Transparent,
@@ -162,6 +168,6 @@
             )
             .padding(vertical = 2.dp, horizontal = 5.dp)
     ) {
-        Text(text = "Play")
+        Text(text = text)
     }
 }
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
index b8ed37f..ac3d021 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/ImmersiveList.kt
@@ -16,24 +16,19 @@
 
 package androidx.tv.integration.playground
 
-import android.util.Log
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.foundation.lazy.list.TvLazyColumn
@@ -54,7 +49,6 @@
 private fun SampleImmersiveList() {
     val immersiveListHeight = 300.dp
     val cardSpacing = 10.dp
-    val cardWidth = 200.dp
     val cardHeight = 150.dp
     val backgrounds = listOf(
         Color.Red,
@@ -76,27 +70,24 @@
                 )
             }
         ) {
-            Row(horizontalArrangement = Arrangement.spacedBy(cardSpacing)) {
-                backgrounds.forEachIndexed { index, backgroundColor ->
-                    var isFocused by remember { mutableStateOf(false) }
+            LazyRow(
+                horizontalArrangement = Arrangement.spacedBy(cardSpacing),
+                modifier = Modifier.lazyListSemantics(1, backgrounds.count())
+            ) {
+                itemsIndexed(backgrounds) { index, backgroundColor ->
+                    val cardModifier =
+                        if (index == 0)
+                            Modifier.initiallyFocused()
+                        else
+                            Modifier.restorableFocus()
 
-                    Box(
-                        modifier = Modifier
-                            .background(backgroundColor)
-                            .width(cardWidth)
-                            .height(cardHeight)
-                            .border(5.dp, Color.White.copy(alpha = if (isFocused) 1f else 0.3f))
-                            .then(
-                                if (index == 0)
-                                    Modifier.initiallyFocused()
-                                else
-                                    Modifier.restorableFocus()
-                            )
-                            .onFocusChanged { isFocused = it.isFocused }
-                            .immersiveListItem(index)
-                            .clickable {
-                                Log.d("ImmersiveList", "Item $index was clicked")
+                    Card(
+                        modifier = cardModifier
+                            .semantics {
+                                collectionItemInfo = CollectionItemInfo(0, 1, index, 1)
                             }
+                            .immersiveListItem(index),
+                        backgroundColor = backgroundColor
                     )
                 }
             }
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
index 7ecfec8..2fbddf4 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/LazyRowsAndColumns.kt
@@ -25,11 +25,17 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.foundation.PivotOffsets
 import androidx.tv.foundation.lazy.list.TvLazyColumn
 import androidx.tv.foundation.lazy.list.TvLazyRow
+import androidx.tv.foundation.lazy.list.itemsIndexed
 
 const val rowsCount = 20
 const val columnsCount = 100
@@ -58,19 +64,35 @@
     val backgroundColors = List(columnsCount) { colors.random() }
 
     FocusGroup {
-        TvLazyRow(modifier, horizontalArrangement = Arrangement.spacedBy(10.dp)) {
-            backgroundColors.forEachIndexed { index, backgroundColor ->
-                item {
-                    Card(
-                        backgroundColor = backgroundColor,
-                        modifier =
-                        if (index == 0)
-                            Modifier.initiallyFocused()
-                        else
-                            Modifier.restorableFocus()
-                    )
-                }
+        TvLazyRow(
+            modifier = modifier.lazyListSemantics(1, columnsCount),
+            horizontalArrangement = Arrangement.spacedBy(10.dp)
+        ) {
+            itemsIndexed(backgroundColors) { index, item ->
+                val cardModifier =
+                    if (index == 0)
+                        Modifier.initiallyFocused()
+                    else
+                        Modifier.restorableFocus()
+
+                Card(
+                    modifier = cardModifier.semantics {
+                        collectionItemInfo = CollectionItemInfo(0, 1, index, 1)
+                    },
+                    backgroundColor = item
+                )
             }
         }
     }
 }
+
+@Composable
+fun Modifier.lazyListSemantics(rowCount: Int = -1, columnCount: Int = -1): Modifier {
+    return this.then(
+        remember(rowCount, columnCount) {
+            Modifier.semantics {
+                collectionInfo = CollectionInfo(rowCount, columnCount)
+            }
+        }
+    )
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
index 86865b3..1257c1b 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/NavigationDrawer.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -28,11 +29,11 @@
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.KeyboardArrowLeft
 import androidx.compose.material.icons.filled.KeyboardArrowRight
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -43,14 +44,16 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.selected
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
-import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.material3.DrawerValue
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Icon
@@ -110,7 +113,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun Sidebar(
     drawerValue: DrawerValue,
@@ -128,8 +131,10 @@
     Column(
         modifier = Modifier
             .fillMaxHeight()
-            .background(pageColor),
+            .background(pageColor)
+            .selectableGroup(),
         horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.spacedBy(10.dp)
     ) {
         NavigationItem(
             imageVector = Icons.Default.KeyboardArrowRight,
@@ -160,15 +165,19 @@
 ) {
     var isFocused by remember { mutableStateOf(false) }
 
-    Button(
-        onClick = { selectedIndex.value = index },
+    Box(
         modifier = modifier
-            .onFocusChanged { isFocused = it.isFocused },
-        colors = ButtonDefaults.filledTonalButtonColors(
-            containerColor = if (isFocused) Color.White else Color.Transparent,
-        )
+            .clip(RoundedCornerShape(10.dp))
+            .onFocusChanged { isFocused = it.isFocused }
+            .background(if (isFocused) Color.White else Color.Transparent)
+            .semantics(mergeDescendants = true) {
+                selected = selectedIndex.value == index
+            }
+            .clickable {
+                selectedIndex.value = index
+            }
     ) {
-        Box(modifier = Modifier) {
+        Box(modifier = Modifier.padding(10.dp)) {
             Row(
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement = Arrangement.spacedBy(5.dp),
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
index 10080e0..2f80311 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselScopeTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.tv.material3
 
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -50,7 +49,7 @@
     @get:Rule
     val rule = createComposeRule()
 
-    @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+    @OptIn(ExperimentalTvMaterial3Api::class)
     @Test
     fun carouselItem_parentContainerGainsFocused_onBackPress() {
         val containerBoxTag = "container-box"
@@ -69,7 +68,8 @@
             ) {
                 CarouselScope(carouselState = carouselState)
                     .CarouselItem(
-                        modifier = Modifier.testTag(carouselItemTag),
+                        modifier = Modifier
+                            .testTag(carouselItemTag),
                         background = {
                             Box(
                                 modifier = Modifier
@@ -87,7 +87,8 @@
         rule.waitForIdle()
 
         // Check if overlay button in carousel item is focused
-        rule.onNodeWithTag(sampleButtonTag).assertIsFocused()
+        rule.onNodeWithTag(sampleButtonTag, useUnmergedTree = true)
+            .assertIsFocused()
 
         // Trigger back press
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
index 7582b2c..b8eb11f 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/CarouselTest.kt
@@ -275,7 +275,6 @@
         rule.onNodeWithText("Text 2").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_pagerIndicatorDisplayed() {
         rule.setContent {
@@ -287,7 +286,6 @@
         rule.onNodeWithTag("indicator").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_withAnimatedContent_successfulTransition() {
         rule.setContent {
@@ -308,7 +306,6 @@
         rule.onNodeWithText("PLAY").assertIsDisplayed()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_withAnimatedContent_successfulFocusIn() {
         rule.setContent {
@@ -326,8 +323,9 @@
         rule.mainClock.advanceTimeBy(animationTime, false)
         rule.mainClock.advanceTimeByFrame()
 
-        rule.onNodeWithText("Play 0").assertIsDisplayed()
-        rule.onNodeWithText("Play 0").assertIsFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true)
+            .assertIsDisplayed()
+            .assertIsFocused()
     }
 
     @Test
@@ -353,7 +351,7 @@
         rule.waitForIdle()
 
         // Check if the overlay button is focused
-        rule.onNodeWithText("Button-1").assertIsFocused()
+        rule.onNodeWithText("Button-1", useUnmergedTree = true).assertIsFocused()
 
         // Trigger back press event to exit focus
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
@@ -361,11 +359,10 @@
         rule.waitForIdle()
 
         // Check if carousel loses focus and parent container gains focus
-        rule.onNodeWithText("Button-1").assertIsNotFocused()
+        rule.onNodeWithText("Button-1", useUnmergedTree = true).assertIsNotFocused()
         rule.onNodeWithTag("box-container").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_withCarouselItem_parentContainerGainsFocus_onBackPress() {
         rule.setContent {
@@ -391,7 +388,7 @@
         rule.waitForIdle()
 
         // Check if the overlay button is focused
-        rule.onNodeWithText("Play 0").assertIsFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true).assertIsFocused()
 
         // Trigger back press event to exit focus
         performKeyPress(NativeKeyEvent.KEYCODE_BACK)
@@ -399,11 +396,10 @@
         rule.waitForIdle()
 
         // Check if carousel loses focus and parent container gains focus
-        rule.onNodeWithText("Play 0").assertIsNotFocused()
+        rule.onNodeWithText("Play 0", useUnmergedTree = true).assertIsNotFocused()
         rule.onNodeWithTag("box-container").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_scrollToRegainFocus_checkBringIntoView() {
         val focusRequester = FocusRequester()
@@ -491,7 +487,6 @@
         assertThat(checkNodeCompletelyVisible(rule, "featured-carousel")).isTrue()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_zeroItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
@@ -502,7 +497,6 @@
         rule.onNodeWithTag(testTag).assertExists()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_oneItemCount_shouldNotCrash() {
         val testTag = "emptyCarousel"
@@ -571,7 +565,6 @@
         rule.onNodeWithText("Button-1").assertIsFocused()
     }
 
-    @OptIn(ExperimentalAnimationApi::class)
     @Test
     fun carousel_manualScrolling_fastMultipleKeyPresses() {
         val carouselState = CarouselState()
@@ -615,13 +608,13 @@
         rule.mainClock.advanceTimeBy(animationTime)
 
         val finalItem = itemProgression.sum()
-        rule.onNodeWithText("Play $finalItem").assertIsFocused()
+        rule.onNodeWithText("Play $finalItem", useUnmergedTree = true).assertIsFocused()
 
         performKeyPress(NativeKeyEvent.KEYCODE_DPAD_RIGHT, 3)
 
         rule.mainClock.advanceTimeBy((animationTime) * 3)
 
-        rule.onNodeWithText("Play ${finalItem + 3}").assertIsFocused()
+        rule.onNodeWithText("Play ${finalItem + 3}", useUnmergedTree = true).assertIsFocused()
     }
 
     @Test
@@ -798,7 +791,7 @@
     }
 }
 
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun SampleCarousel(
     carouselState: CarouselState = remember { CarouselState() },
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index 50602d5..bf97f92 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -62,6 +62,9 @@
 import androidx.compose.ui.input.key.type
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.CollectionInfo
+import androidx.compose.ui.semantics.collectionInfo
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -127,6 +130,9 @@
         onAutoScrollChange = { isAutoScrollActive = it })
 
     Box(modifier = modifier
+        .semantics {
+            collectionInfo = CollectionInfo(rowCount = 1, columnCount = itemCount)
+        }
         .bringIntoViewIfChildrenAreFocused()
         .focusRequester(carouselOuterBoxFocusRequester)
         .onFocusChanged {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
index 2996e8d..bb10fd9 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CarouselItem.kt
@@ -16,12 +16,15 @@
 
 package androidx.tv.material3
 
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.ContentTransform
 import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.slideInHorizontally
 import androidx.compose.animation.slideOutHorizontally
 import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.clickable
 import androidx.compose.foundation.focusable
 import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
@@ -37,8 +40,13 @@
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.semantics.CollectionItemInfo
+import androidx.compose.ui.semantics.collectionItemInfo
+import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.tv.material3.KeyEventPropagation.ContinuePropagation
 
@@ -67,6 +75,10 @@
         CarouselItemDefaults.contentTransformStartToEnd,
     content: @Composable () -> Unit,
 ) {
+    val context = LocalContext.current
+    val accessibilityManager = remember {
+        context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+    }
     var containerBoxFocusState: FocusState? by remember { mutableStateOf(null) }
     val focusManager = LocalFocusManager.current
     var exitFocus by remember { mutableStateOf(false) }
@@ -81,6 +93,16 @@
     // This box holds the focus until the overlay animation completes
     Box(
         modifier = modifier
+            .semantics(mergeDescendants = true) {
+                isContainer = true
+                collectionItemInfo =
+                    CollectionItemInfo(
+                        rowIndex = 0,
+                        rowSpan = 1,
+                        columnIndex = itemIndex,
+                        columnSpan = 1
+                    )
+            }
             .onKeyEvent {
                 exitFocus = it.isBackPress() && it.isTypeKeyDown()
                 ContinuePropagation
@@ -92,7 +114,14 @@
                     exitFocus = false
                 }
             }
-            .focusable()
+            .then(
+                if (accessibilityManager.isEnabled)
+                    Modifier.clickable {
+                        focusManager.moveFocus(FocusDirection.Enter)
+                    }
+                else
+                    Modifier.focusable()
+            )
     ) {
         background()
 
@@ -102,7 +131,10 @@
             exit = contentTransform.initialContentExit,
         ) {
             LaunchedEffect(transition.isRunning, containerBoxFocusState?.isFocused) {
-                if (!transition.isRunning && containerBoxFocusState?.isFocused == true) {
+                if (!transition.isRunning &&
+                    containerBoxFocusState?.isFocused == true &&
+                    !accessibilityManager.isEnabled
+                ) {
                     focusManager.moveFocus(FocusDirection.Enter)
                 }
             }