Merge "feat: supress Experimental Carousel features until we launch RC candidate" into androidx-main
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 c6569aa..d240598 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
@@ -76,7 +76,7 @@
import kotlin.math.abs
import kotlinx.coroutines.delay
import org.junit.Rule
-import org.junit.Test
+// import org.junit.Test
private const val delayBetweenItems = 2500L
private const val animationTime = 900L
@@ -86,7 +86,7 @@
@get:Rule
val rule = createComposeRule()
- @Test
+ // @Test
fun carousel_autoScrolls() {
rule.setContent {
SampleCarousel {
@@ -103,7 +103,7 @@
rule.onNodeWithText("Text 3").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_onFocus_stopsScroll() {
rule.setContent {
SampleCarousel {
@@ -124,7 +124,7 @@
rule.onNodeWithText("Text 1").onParent().assertIsFocused()
}
- @Test
+ // @Test
fun carousel_onUserTriggeredPause_stopsScroll() {
rule.setContent {
val carouselState = rememberCarouselState()
@@ -143,7 +143,7 @@
rule.onNodeWithText("Text 1").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_onUserTriggeredPauseAndResume_resumeScroll() {
var pauseHandle: ScrollPauseHandle? = null
rule.setContent {
@@ -176,7 +176,7 @@
rule.onNodeWithText("Text 2").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_onMultipleUserTriggeredPauseAndResume_resumesScroll() {
var pauseHandle1: ScrollPauseHandle? = null
var pauseHandle2: ScrollPauseHandle? = null
@@ -220,7 +220,7 @@
rule.onNodeWithText("Text 2").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_onRepeatedResumesOnSamePauseHandle_ignoresSubsequentResumeCalls() {
var pauseHandle1: ScrollPauseHandle? = null
rule.setContent {
@@ -259,7 +259,7 @@
rule.onNodeWithText("Text 1").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_outOfFocus_resumesScroll() {
rule.setContent {
Column {
@@ -282,7 +282,7 @@
rule.onNodeWithText("Text 2").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_pagerIndicatorDisplayed() {
rule.setContent {
SampleCarousel {
@@ -293,7 +293,7 @@
rule.onNodeWithTag("indicator").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_withAnimatedContent_successfulTransition() {
rule.setContent {
SampleCarousel {
@@ -313,7 +313,7 @@
rule.onNodeWithText("PLAY").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_withAnimatedContent_successfulFocusIn() {
rule.setContent {
SampleCarousel {
@@ -335,7 +335,7 @@
.assertIsFocused()
}
- @Test
+ // @Test
fun carousel_parentContainerGainsFocus_onBackPress() {
rule.setContent {
Box(modifier = Modifier
@@ -370,7 +370,7 @@
rule.onNodeWithTag("box-container").assertIsFocused()
}
- @Test
+ // @Test
fun carousel_withCarouselItem_parentContainerGainsFocusOnBackPress() {
rule.setContent {
Box(modifier = Modifier
@@ -407,7 +407,7 @@
rule.onNodeWithTag("box-container").assertIsFocused()
}
- @Test
+ // @Test
fun carousel_scrollToRegainFocus_checkBringIntoView() {
val focusRequester = FocusRequester()
rule.setContent {
@@ -500,7 +500,7 @@
assertThat(checkNodeCompletelyVisible(rule, "featured-carousel")).isTrue()
}
- @Test
+ // @Test
fun carousel_zeroItemCount_shouldNotCrash() {
val testTag = "emptyCarousel"
rule.setContent {
@@ -510,7 +510,7 @@
rule.onNodeWithTag(testTag).assertExists()
}
- @Test
+ // @Test
fun carousel_oneItemCount_shouldNotCrash() {
val testTag = "emptyCarousel"
rule.setContent {
@@ -520,7 +520,7 @@
rule.onNodeWithTag(testTag).assertExists()
}
- @Test
+ // @Test
fun carousel_manualScrollingWithFocusableItemsOnTop_focusStaysWithinCarousel() {
rule.setContent {
Column {
@@ -577,7 +577,7 @@
rule.onNodeWithText("Button-1").assertIsFocused()
}
- @Test
+ // @Test
fun carousel_manualScrollingFastMultipleKeyPresses_focusStaysWithinCarousel() {
val carouselState = CarouselState()
val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
@@ -640,7 +640,7 @@
rule.onNodeWithText("Play ${finalItem + 3}", useUnmergedTree = true).assertIsFocused()
}
- @Test
+ // @Test
fun carousel_manualScrollingDpadLongPress_moveOnlyOneSlide() {
rule.setContent {
SampleCarousel(itemCount = 6) { index ->
@@ -685,7 +685,7 @@
rule.onNodeWithText("Button 1").assertIsFocused()
}
- @Test
+ // @Test
fun carousel_manualScrollingLtr_RightMovesToNextSlideLeftMovesToPrevSlide() {
rule.setContent {
SampleCarousel { index ->
@@ -731,7 +731,7 @@
rule.onNodeWithText("Button 1").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_manualScrollingRtl_LeftMovesToNextSlideRightMovesToPrevSlide() {
rule.setContent {
CompositionLocalProvider(
@@ -780,7 +780,7 @@
rule.onNodeWithText("Button 1").assertIsDisplayed()
}
- @Test
+ // @Test
fun carousel_itemCountChangesDuringAnimation_shouldNotCrash() {
val itemDisplayDurationMs: Long = 100
var itemChanges = 0
@@ -813,7 +813,7 @@
rule.waitUntil(timeoutMillis = 5000) { itemChanges > minSuccessfulItemChanges }
}
- @Test
+ // @Test
fun carousel_slideWithTwoButtonsInARow_focusMovesWithinSlideAndChangesSlideOnlyOnFocusExit() {
rule.setContent {
// No AutoScrolling
@@ -834,7 +834,7 @@
rule.onNodeWithText("Left Button 2").assertIsFocused()
}
- @Test
+ // @Test
fun carousel_manualScrollingLtr_loopsAroundWhenNoAdjacentFocusableItemsArePresent() {
rule.setContent {
// No AutoScrolling
@@ -860,7 +860,7 @@
}
@OptIn(ExperimentalComposeUiApi::class)
- @Test
+ // @Test
fun carousel_manualScrollingLtr_focusMovesToAdjacentItemsOutsideCarousel() {
rule.setContent {
val focusRequester = remember { FocusRequester() }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/BringIntoViewIfChildrenAreFocused.kt b/tv/tv-material/src/main/java/androidx/tv/material3/BringIntoViewIfChildrenAreFocused.kt
index b9decd5..572fbfe 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/BringIntoViewIfChildrenAreFocused.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/BringIntoViewIfChildrenAreFocused.kt
@@ -16,39 +16,28 @@
package androidx.tv.material3
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.relocation.BringIntoViewResponder
-import androidx.compose.foundation.relocation.bringIntoViewResponder
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.debugInspectorInfo
-
-@OptIn(ExperimentalFoundationApi::class)
-internal fun Modifier.bringIntoViewIfChildrenAreFocused(): Modifier = composed(
- inspectorInfo = debugInspectorInfo { name = "bringIntoViewIfChildrenAreFocused" },
- factory = {
- var myRect: Rect = Rect.Zero
- this
- .onSizeChanged {
- myRect = Rect(Offset.Zero, Offset(it.width.toFloat(), it.height.toFloat()))
- }
- .bringIntoViewResponder(
- remember {
- object : BringIntoViewResponder {
- // return the current rectangle and ignoring the child rectangle received.
- @ExperimentalFoundationApi
- override fun calculateRectForParent(localRect: Rect): Rect = myRect
-
- // The container is not expected to be scrollable. Hence the child is
- // already in view with respect to the container.
- @ExperimentalFoundationApi
- override suspend fun bringChildIntoView(localRect: () -> Rect?) {}
- }
- }
- )
- }
-)
+// @OptIn(ExperimentalFoundationApi::class)
+// internal fun Modifier.bringIntoViewIfChildrenAreFocused(): Modifier = composed(
+// inspectorInfo = debugInspectorInfo { name = "bringIntoViewIfChildrenAreFocused" },
+// factory = {
+// var myRect: Rect = Rect.Zero
+// this
+// .onSizeChanged {
+// myRect = Rect(Offset.Zero, Offset(it.width.toFloat(), it.height.toFloat()))
+// }
+// .bringIntoViewResponder(
+// remember {
+// object : BringIntoViewResponder {
+// // return the current rectangle and ignoring the child rectangle received.
+// @ExperimentalFoundationApi
+// override fun calculateRectForParent(localRect: Rect): Rect = myRect
+//
+// // The container is not expected to be scrollable. Hence the child is
+// // already in view with respect to the container.
+// @ExperimentalFoundationApi
+// override suspend fun bringChildIntoView(localRect: () -> Rect?) {}
+// }
+// }
+// )
+// }
+// )
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 1cc9e95..9aec264 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
@@ -22,7 +22,6 @@
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ContentTransform
-import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -49,7 +48,6 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusDirection.Companion.Left
@@ -86,6 +84,10 @@
/**
* Composes a hero card rotator to highlight a piece of content.
*
+ * Note: The animations and focus management features have been dropped temporarily due to
+ * some technical challenges. If you need them, consider using the previous version of the
+ * library (1.0.0-alpha10) or kindly wait until the next alpha version (1.1.0-alpha01).
+ *
* Examples:
* @sample androidx.tv.samples.SimpleCarousel
* @sample androidx.tv.samples.CarouselIndicatorWithRectangleShape
@@ -102,7 +104,7 @@
* @param carouselIndicator indicator showing the position of the current item among all items.
* @param content defines the items for a given index.
*/
-@OptIn(ExperimentalComposeUiApi::class)
+// @OptIn(ExperimentalComposeUiApi::class)
@ExperimentalTvMaterial3Api
@Composable
fun Carousel(
@@ -144,14 +146,14 @@
Box(modifier = modifier
.carouselSemantics(itemCount = itemCount, state = carouselState)
- .bringIntoViewIfChildrenAreFocused()
+ // .bringIntoViewIfChildrenAreFocused()
.focusRequester(carouselOuterBoxFocusRequester)
.onFocusChanged {
focusState = it
// When the carousel gains focus for the first time
- if (it.isFocused && isAutoScrollActive) {
- focusManager.moveFocus(FocusDirection.Enter)
- }
+// if (it.isFocused && isAutoScrollActive) {
+// focusManager.moveFocus(FocusDirection.Enter)
+// }
}
.handleKeyEvents(
carouselState = carouselState,
@@ -181,7 +183,7 @@
// Outer box is focused
if (!isAutoScrollActive && focusState?.isFocused == true) {
carouselOuterBoxFocusRequester.requestFocus()
- focusManager.moveFocus(FocusDirection.Enter)
+// focusManager.moveFocus(FocusDirection.Enter)
}
}
}
@@ -209,9 +211,9 @@
return !accessibilityManager.isEnabled && !(carouselIsFocused || carouselHasFocus)
}
-@OptIn(ExperimentalAnimationApi::class)
+// @OptIn(ExperimentalAnimationApi::class)
private suspend fun AnimatedVisibilityScope.onAnimationCompletion(action: suspend () -> Unit) {
- snapshotFlow { transition.currentState == transition.targetState }.first { it }
+// snapshotFlow { transition.currentState == transition.targetState }.first { it }
action.invoke()
}
@@ -246,7 +248,10 @@
onAutoScrollChange(doAutoScroll)
}
-@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
+@OptIn(
+ ExperimentalTvMaterial3Api::class,
+// ExperimentalComposeUiApi::class
+)
private fun Modifier.handleKeyEvents(
carouselState: CarouselState,
outerBoxFocusRequester: FocusRequester,
@@ -289,7 +294,7 @@
}
!focusManager.moveFocus(direction) &&
- currentCarouselBoxFocusState()?.hasFocus == true -> {
+ currentCarouselBoxFocusState()?.hasFocus == true -> {
// if focus search was unsuccessful, interpret as input for slide change
updateItemBasedOnLayout(direction, isLtr)
KeyEventPropagation.StopPropagation
@@ -302,7 +307,7 @@
// Ignore KeyUp action type
it.type == KeyUp -> KeyEventPropagation.ContinuePropagation
it.key == Key.Back -> {
- focusManager.moveFocus(FocusDirection.Exit)
+// focusManager.moveFocus(FocusDirection.Exit)
KeyEventPropagation.ContinuePropagation
}
@@ -313,14 +318,14 @@
}
}.focusProperties {
// allow exit along horizontal axis only for first and last slide.
- exit = {
- when {
- shouldFocusExitCarousel(it, carouselState, itemCount, isLtr) ->
- FocusRequester.Default
-
- else -> FocusRequester.Cancel
- }
- }
+// exit = {
+// when {
+// shouldFocusExitCarousel(it, carouselState, itemCount, isLtr) ->
+// FocusRequester.Default
+//
+// else -> FocusRequester.Cancel
+// }
+// }
}
@OptIn(ExperimentalTvMaterial3Api::class)
@@ -490,9 +495,9 @@
* Transition applied when bringing it into view and removing it from the view
*/
val contentTransform: ContentTransform
- @Composable get() =
- fadeIn(animationSpec = tween(100))
- .togetherWith(fadeOut(animationSpec = tween(100)))
+ @Composable get() =
+ fadeIn(animationSpec = tween(100))
+ .togetherWith(fadeOut(animationSpec = tween(100)))
/**
* An indicator showing the position of the current active item among the items of the