Merge "Deprecate DefaultMonotonicFrameClock" into androidx-main
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/AutoTestFrameClock.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/AutoTestFrameClock.kt
new file mode 100644
index 0000000..b889918
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/AutoTestFrameClock.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation
+
+import androidx.compose.runtime.MonotonicFrameClock
+import java.util.concurrent.atomic.AtomicLong
+
+class AutoTestFrameClock : MonotonicFrameClock {
+ private val time = AtomicLong(0)
+
+ override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
+ return onFrame(time.getAndAdd(16_000_000))
+ }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
index 85aab9a..7e2327c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollTest.kt
@@ -522,7 +522,7 @@
@OptIn(ExperimentalTestApi::class)
@Test
- fun scroller_coerce_whenScrollSmoothTo() = runBlocking {
+ fun scroller_coerce_whenScrollSmoothTo() = runBlocking(AutoTestFrameClock()) {
val scrollState = ScrollState(initial = 0)
createScrollableContent(isVertical = true, scrollState = scrollState)
@@ -571,7 +571,7 @@
@OptIn(ExperimentalTestApi::class)
@Test
- fun scroller_restoresScrollerPosition() = runBlocking {
+ fun scroller_restoresScrollerPosition() = runBlocking(AutoTestFrameClock()) {
val restorationTester = StateRestorationTester(rule)
var scrollState: ScrollState? = null
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index bcc20f7..80001cb 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -392,7 +392,7 @@
@Test
@OptIn(ExperimentalTestApi::class)
- fun scrollable_snappingScrolling() = runBlocking {
+ fun scrollable_snappingScrolling() = runBlocking(AutoTestFrameClock()) {
var total = 0f
val controller = ScrollableState(
consumeScrollDelta = {
@@ -735,7 +735,7 @@
@Test
@OptIn(ExperimentalTestApi::class)
- fun scrollable_nestedScrollBelow_listensDispatches() = runBlocking {
+ fun scrollable_nestedScrollBelow_listensDispatches() = runBlocking(AutoTestFrameClock()) {
var value = 0f
var expectedConsumed = 0f
val controller = ScrollableState(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
index 247f9e1..f12af2d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
@@ -364,7 +364,7 @@
}
@Test
- fun transformable_animateTo_zoom() = runBlocking {
+ fun transformable_animateTo_zoom() = runBlocking(AutoTestFrameClock()) {
rule.mainClock.autoAdvance = false
var cumulativeScale = 1.0f
var callbackCount = 0
@@ -400,7 +400,7 @@
}
@Test
- fun transformable_animateTo_rotate() = runBlocking {
+ fun transformable_animateTo_rotate() = runBlocking(AutoTestFrameClock()) {
rule.mainClock.autoAdvance = false
var totalRotation = 0f
var callbackCount = 0
@@ -436,7 +436,7 @@
}
@Test
- fun transformable_animateTo_pan() = runBlocking {
+ fun transformable_animateTo_pan() = runBlocking(AutoTestFrameClock()) {
rule.mainClock.autoAdvance = false
var totalPan = Offset.Zero
var callbackCount = 0
@@ -531,7 +531,7 @@
}
@Test
- fun transformable_stopTransformations() = runBlocking {
+ fun transformable_stopTransformations() = runBlocking(AutoTestFrameClock()) {
rule.mainClock.autoAdvance = false
var totalRotation = 0f
var callbackCount = 0
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
index e9a4d58..3dfc426 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
@@ -18,6 +18,7 @@
import androidx.compose.animation.core.advanceClockMillis
import androidx.compose.animation.core.snap
+import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
@@ -77,6 +78,7 @@
import com.google.common.truth.IntegerSubject
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
@@ -1308,7 +1310,7 @@
}
private fun LazyListState.scrollBy(offset: Dp) {
- runBlocking {
+ runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
index dd64524..f319f73 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
@@ -17,13 +17,14 @@
package androidx.compose.foundation.lazy
import androidx.compose.animation.core.snap
+import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.width
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertHeightIsEqualTo
@@ -37,6 +38,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
@@ -611,7 +613,7 @@
}
private fun LazyListState.scrollBy(offset: Dp) {
- runBlocking {
+ runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
index c5403a7..99c95cb 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.foundation.lazy
import androidx.compose.animation.core.snap
+import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -54,6 +55,7 @@
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
@@ -1038,7 +1040,7 @@
}
private fun LazyListState.scrollBy(offset: Dp) {
- runBlocking {
+ runBlocking(Dispatchers.Main + AutoTestFrameClock()) {
animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap())
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
index 18f6153..689da8c 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.lazy
+import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.layout.Spacer
@@ -66,7 +67,7 @@
@Test
fun snapToItemTest() = runBlocking {
- withContext(Dispatchers.Main) {
+ withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToItem(3)
}
assertThat(state.firstVisibleItemIndex).isEqualTo(3)
@@ -83,7 +84,7 @@
val expectedIndex = scrollDistance / itemSize // resolves to 3
val expectedOffset = scrollDistance % itemSize // resolves to ~17.dp.toIntPx()
- withContext(Dispatchers.Main) {
+ withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollBy(scrollDistance.toFloat())
}
assertThat(state.firstVisibleItemIndex).isEqualTo(expectedIndex)
@@ -92,7 +93,7 @@
@Test
fun smoothScrollToItemTest() = runBlocking {
- withContext(Dispatchers.Main) {
+ withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.animateScrollToItem(5, 10)
}
assertThat(state.firstVisibleItemIndex).isEqualTo(5)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AutoTestFrameClock.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AutoTestFrameClock.kt
new file mode 100644
index 0000000..5691a74
--- /dev/null
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/AutoTestFrameClock.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material
+
+import androidx.compose.runtime.MonotonicFrameClock
+import java.util.concurrent.atomic.AtomicLong
+
+class AutoTestFrameClock : MonotonicFrameClock {
+ private val time = AtomicLong(0)
+
+ override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
+ return onFrame(time.getAndAdd(16_000_000))
+ }
+}
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
index 9e194c0..d204bf8 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BackdropScaffoldTest.kt
@@ -215,7 +215,7 @@
@Test
@LargeTest
- fun backdropScaffold_revealAndConceal_manually(): Unit = runBlocking {
+ fun backdropScaffold_revealAndConceal_manually(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var scaffoldState: BackdropScaffoldState
rule.setContent {
scaffoldState = rememberBackdropScaffoldState(Concealed)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
index 9516feb..c955f22 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/BottomSheetScaffoldTest.kt
@@ -198,7 +198,7 @@
}
@Test
- fun backdropScaffold_revealAndConceal_manually(): Unit = runBlocking {
+ fun backdropScaffold_revealAndConceal_manually(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var bottomSheetState: BottomSheetState
rule.setContent {
bottomSheetState = rememberBottomSheetState(BottomSheetValue.Collapsed)
@@ -382,7 +382,7 @@
}
@Test
- fun bottomSheetScaffold_fab_position(): Unit = runBlocking {
+ fun bottomSheetScaffold_fab_position(): Unit = runBlocking(AutoTestFrameClock()) {
val fabTag = "fab"
var fabSize: IntSize = IntSize.Zero
lateinit var scaffoldState: BottomSheetScaffoldState
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
index 6946763..6f885b0 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/DrawerTest.kt
@@ -243,7 +243,7 @@
@Test
@LargeTest
- fun modalDrawer_openAndClose(): Unit = runBlocking {
+ fun modalDrawer_openAndClose(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var drawerState: DrawerState
rule.setMaterialContent {
drawerState = rememberDrawerState(DrawerValue.Closed)
@@ -274,7 +274,7 @@
@Test
@LargeTest
- fun modalDrawer_bodyContent_clickable(): Unit = runBlocking {
+ fun modalDrawer_bodyContent_clickable(): Unit = runBlocking(AutoTestFrameClock()) {
var drawerClicks = 0
var bodyClicks = 0
lateinit var drawerState: DrawerState
@@ -314,7 +314,9 @@
@Test
@LargeTest
- fun modalDrawer_drawerContent_doesntPropagateClicksWhenOpen(): Unit = runBlocking {
+ fun modalDrawer_drawerContent_doesntPropagateClicksWhenOpen(): Unit = runBlocking(
+ AutoTestFrameClock()
+ ) {
var bodyClicks = 0
lateinit var drawerState: DrawerState
rule.setMaterialContent {
@@ -421,7 +423,9 @@
@Test
@LargeTest
- fun modalDrawer_noDismissActionWhenClosed_hasDissmissActionWhenOpen(): Unit = runBlocking {
+ fun modalDrawer_noDismissActionWhenClosed_hasDissmissActionWhenOpen(): Unit = runBlocking(
+ AutoTestFrameClock()
+ ) {
lateinit var drawerState: DrawerState
rule.setMaterialContent {
drawerState = rememberDrawerState(DrawerValue.Closed)
@@ -457,7 +461,7 @@
}
@Test
- fun bottomDrawer_bodyContent_clickable(): Unit = runBlocking {
+ fun bottomDrawer_bodyContent_clickable(): Unit = runBlocking(AutoTestFrameClock()) {
var drawerClicks = 0
var bodyClicks = 0
lateinit var drawerState: BottomDrawerState
@@ -502,7 +506,9 @@
@Test
@LargeTest
- fun bottomDrawer_drawerContent_doesntPropagateClicksWhenOpen(): Unit = runBlocking {
+ fun bottomDrawer_drawerContent_doesntPropagateClicksWhenOpen(): Unit = runBlocking(
+ AutoTestFrameClock()
+ ) {
var bodyClicks = 0
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
@@ -548,7 +554,7 @@
@Test
@LargeTest
- fun bottomDrawer_openBySwipe_shortDrawer(): Unit = runBlocking {
+ fun bottomDrawer_openBySwipe_shortDrawer(): Unit = runBlocking(AutoTestFrameClock()) {
val contentTag = "contentTestTag"
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
@@ -589,7 +595,7 @@
@Test
@LargeTest
- fun bottomDrawer_expandBySwipe_tallDrawer(): Unit = runBlocking {
+ fun bottomDrawer_expandBySwipe_tallDrawer(): Unit = runBlocking(AutoTestFrameClock()) {
val contentTag = "contentTestTag"
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
@@ -656,7 +662,7 @@
}
@Test
- fun bottomDrawer_openBySwipe_onBodyContent(): Unit = runBlocking {
+ fun bottomDrawer_openBySwipe_onBodyContent(): Unit = runBlocking(AutoTestFrameClock()) {
val contentTag = "contentTestTag"
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
@@ -683,7 +689,7 @@
}
@Test
- fun bottomDrawer_hasDismissAction_whenExpanded(): Unit = runBlocking {
+ fun bottomDrawer_hasDismissAction_whenExpanded(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
drawerState = rememberBottomDrawerState(BottomDrawerValue.Expanded)
@@ -709,7 +715,9 @@
@Test
@LargeTest
- fun bottomDrawer_noDismissActionWhenClosed_hasDissmissActionWhenOpen(): Unit = runBlocking {
+ fun bottomDrawer_noDismissActionWhenClosed_hasDissmissActionWhenOpen(): Unit = runBlocking(
+ AutoTestFrameClock()
+ ) {
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
@@ -749,7 +757,7 @@
@Test
@LargeTest
- fun bottomDrawer_openAndClose_shortDrawer(): Unit = runBlocking {
+ fun bottomDrawer_openAndClose_shortDrawer(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
@@ -788,7 +796,7 @@
@Test
@LargeTest
- fun bottomDrawer_openAndClose_tallDrawer(): Unit = runBlocking {
+ fun bottomDrawer_openAndClose_tallDrawer(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var drawerState: BottomDrawerState
rule.setMaterialContent {
drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
index e41fd75..c73ec0f 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ModalBottomSheetTest.kt
@@ -309,7 +309,7 @@
}
@Test
- fun modalBottomSheet_showAndHide_manually(): Unit = runBlocking {
+ fun modalBottomSheet_showAndHide_manually(): Unit = runBlocking(AutoTestFrameClock()) {
lateinit var sheetState: ModalBottomSheetState
rule.setMaterialContent {
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
@@ -348,7 +348,9 @@
}
@Test
- fun modalBottomSheet_showAndHide_manually_tallBottomSheet(): Unit = runBlocking {
+ fun modalBottomSheet_showAndHide_manually_tallBottomSheet(): Unit = runBlocking(
+ AutoTestFrameClock()
+ ) {
lateinit var sheetState: ModalBottomSheetState
rule.setMaterialContent {
sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
index a3a0cfb..e44274b 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwipeableTest.kt
@@ -403,7 +403,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_fixed_small() = runBlocking {
+ fun swipeable_thresholds_fixed_small() = runBlocking(AutoTestFrameClock()) {
rule.mainClock.autoAdvance = false
lateinit var state: SwipeableState<String>
val offsetDp = with(rule.density) { 35.toDp() }
@@ -459,7 +459,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_fixed_large() = runBlocking {
+ fun swipeable_thresholds_fixed_large() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
val offsetDp = with(rule.density) { 65.toDp() }
setSwipeableContent {
@@ -514,7 +514,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_fractional_half() = runBlocking {
+ fun swipeable_thresholds_fractional_half() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -568,7 +568,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_fractional_quarter() = runBlocking {
+ fun swipeable_thresholds_fractional_quarter() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -622,7 +622,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_fractional_threeQuarters() = runBlocking {
+ fun swipeable_thresholds_fractional_threeQuarters() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -676,7 +676,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_mixed() = runBlocking {
+ fun swipeable_thresholds_mixed() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
val offsetDp = with(rule.density) { 35.toDp() }
setSwipeableContent {
@@ -737,7 +737,7 @@
*/
@Test
@LargeTest
- fun swipeable_thresholds_custom() = runBlocking {
+ fun swipeable_thresholds_custom() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -997,7 +997,7 @@
*/
@Test
@LargeTest
- fun swipeable_targetValue() = runBlocking {
+ fun swipeable_targetValue() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -1297,7 +1297,7 @@
* Tests that 'snapTo' updates the state and offset immediately.
*/
@Test
- fun swipeable_snapTo() = runBlocking {
+ fun swipeable_snapTo() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -1326,7 +1326,7 @@
* Tests that 'animateTo' starts an animation which updates the state and offset.
*/
@Test
- fun swipeable_animateTo() = runBlocking {
+ fun swipeable_animateTo() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -1358,7 +1358,7 @@
* Tests that the 'onEnd' callback of 'animateTo' is invoked and with the correct end value.
*/
@Test
- fun swipeable_animateTo_onEnd() = runBlocking {
+ fun swipeable_animateTo_onEnd() = runBlocking(AutoTestFrameClock()) {
lateinit var state: SwipeableState<String>
setSwipeableContent {
state = rememberSwipeableState("A")
@@ -1415,7 +1415,7 @@
* Tests that the [SwipeableState] is restored, when created with [rememberSwipeableState].
*/
@Test
- fun swipeable_restoreSwipeableState() = runBlocking {
+ fun swipeable_restoreSwipeableState() = runBlocking(AutoTestFrameClock()) {
val restorationTester = StateRestorationTester(rule)
var state: SwipeableState<String>? = null
@@ -1690,7 +1690,7 @@
}
@Test
- fun swipeable_nestedScroll_postFlings() = runBlocking {
+ fun swipeable_nestedScroll_postFlings() = runBlocking(AutoTestFrameClock()) {
lateinit var swipeableState: SwipeableState<String>
lateinit var anchors: MutableState<Map<Float, String>>
lateinit var scrollState: ScrollState
diff --git a/compose/runtime/runtime/api/1.0.0-beta02.txt b/compose/runtime/runtime/api/1.0.0-beta02.txt
index d73a573..329d523 100644
--- a/compose/runtime/runtime/api/1.0.0-beta02.txt
+++ b/compose/runtime/runtime/api/1.0.0-beta02.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -190,6 +190,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index d73a573..329d523 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -190,6 +190,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta02.txt b/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta02.txt
index c803652..c51803e 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta02.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_1.0.0-beta02.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -243,6 +243,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index c803652..c51803e 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -243,6 +243,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/api/restricted_1.0.0-beta02.txt b/compose/runtime/runtime/api/restricted_1.0.0-beta02.txt
index d80e38d..552fff5 100644
--- a/compose/runtime/runtime/api/restricted_1.0.0-beta02.txt
+++ b/compose/runtime/runtime/api/restricted_1.0.0-beta02.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -215,6 +215,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index d80e38d..552fff5 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -17,7 +17,7 @@
}
public final class ActualAndroid_androidKt {
- method public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
+ method @Deprecated public static androidx.compose.runtime.MonotonicFrameClock getDefaultMonotonicFrameClock();
}
public final class ActualJvm_jvmKt {
@@ -215,6 +215,7 @@
}
public final class MonotonicFrameClockKt {
+ method public static androidx.compose.runtime.MonotonicFrameClock getMonotonicFrameClock(kotlin.coroutines.CoroutineContext);
method public static suspend inline <R> Object? withFrameMillis(androidx.compose.runtime.MonotonicFrameClock, kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameMillis(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
method public static suspend <R> Object? withFrameNanos(kotlin.jvm.functions.Function1<? super java.lang.Long,? extends R> onFrame, kotlin.coroutines.Continuation<? super R> p);
diff --git a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
index f94428c..8c95361 100644
--- a/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
+++ b/compose/runtime/runtime/integration-tests/src/androidAndroidTest/kotlin/androidx/compose/runtime/RecomposerTests.kt
@@ -19,6 +19,7 @@
import android.view.View
import android.widget.TextView
import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.test.TestMonotonicFrameClock
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import junit.framework.TestCase.assertEquals
@@ -126,7 +127,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun runningRecomposerFlow() = runBlockingTest {
lateinit var recomposer: RecomposerInfo
- val recomposerJob = launch {
+ val recomposerJob = launch(TestMonotonicFrameClock(this)) {
withRunningRecomposer {
recomposer = it.asRecomposerInfo()
suspendCancellableCoroutine<Unit> { }
diff --git a/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.android.kt b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.android.kt
index 7b49ff7..1c270d3 100644
--- a/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.android.kt
+++ b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/ActualAndroid.android.kt
@@ -72,6 +72,10 @@
// For local testing
private const val DisallowDefaultMonotonicFrameClock = false
+@Deprecated(
+ "MonotonicFrameClocks are not globally applicable across platforms. " +
+ "Use an appropriate local clock."
+)
actual val DefaultMonotonicFrameClock: MonotonicFrameClock by lazy {
if (DisallowDefaultMonotonicFrameClock) error("Disallowed use of DefaultMonotonicFrameClock")
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
index 91193ee..585fe15 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Expect.kt
@@ -76,9 +76,11 @@
/**
* The [MonotonicFrameClock] used by [withFrameNanos] and [withFrameMillis] if one is not present
* in the calling [kotlin.coroutines.CoroutineContext].
+ *
+ * This value is no longer used by compose runtime.
*/
-// Implementor's note:
-// This frame clock implementation should try to synchronize with the vsync rate of the device's
-// default display. Without this synchronization, any usage of this default clock will result
-// in inconsistent animation frame timing and associated visual artifacts.
+@Deprecated(
+ "MonotonicFrameClocks are not globally applicable across platforms. " +
+ "Use an appropriate local clock."
+)
expect val DefaultMonotonicFrameClock: MonotonicFrameClock
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt
index 17d6bff..dcc1c59 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MonotonicFrameClock.kt
@@ -81,8 +81,9 @@
* [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
* in the [CoroutineContext].
*/
+@OptIn(ExperimentalComposeApi::class)
suspend fun <R> withFrameNanos(onFrame: (frameTimeMillis: Long) -> R): R =
- (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameNanos(onFrame)
+ coroutineContext.monotonicFrameClock.withFrameNanos(onFrame)
/**
* Suspends until a new frame is requested, immediately invokes [onFrame] with the frame time
@@ -102,5 +103,17 @@
* [CoroutineContext]'s [MonotonicFrameClock] or a default frame clock if one is not present
* in the [CoroutineContext].
*/
+@OptIn(ExperimentalComposeApi::class)
suspend fun <R> withFrameMillis(onFrame: (frameTimeMillis: Long) -> R): R =
- (coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock).withFrameMillis(onFrame)
+ coroutineContext.monotonicFrameClock.withFrameMillis(onFrame)
+
+/**
+ * Returns the [MonotonicFrameClock] for this [CoroutineContext] or throws [IllegalStateException]
+ * if one is not present.
+ */
+@ExperimentalComposeApi
+val CoroutineContext.monotonicFrameClock: MonotonicFrameClock
+ get() = this[MonotonicFrameClock] ?: error(
+ "A MonotonicFrameClock is not available in this CoroutineContext. Callers should supply " +
+ "an appropriate MonotonicFrameClock using withContext."
+ )
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index 0dfc7c5..3da5ece 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -608,10 +608,11 @@
}
}
+ @OptIn(ExperimentalComposeApi::class)
private suspend fun recompositionRunner(
block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
) {
- val parentFrameClock = coroutineContext[MonotonicFrameClock] ?: DefaultMonotonicFrameClock
+ val parentFrameClock = coroutineContext.monotonicFrameClock
withContext(broadcastFrameClock) {
// Enforce mutual exclusion of callers; register self as current runner
val callingJob = coroutineContext.job
diff --git a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.desktop.kt b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.desktop.kt
index b704879..6214d94 100644
--- a/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.desktop.kt
+++ b/compose/runtime/runtime/src/desktopMain/kotlin/androidx/compose/runtime/ActualDesktop.desktop.kt
@@ -60,6 +60,10 @@
* obtained using [LaunchedEffect] or [rememberCoroutineScope] they also use
* [MonotonicFrameClock] which is bound to the current window.
*/
+@Deprecated(
+ "MonotonicFrameClocks are not globally applicable across platforms. " +
+ "Use an appropriate local clock."
+)
actual val DefaultMonotonicFrameClock: MonotonicFrameClock get() = SixtyFpsMonotonicFrameClock
private object SixtyFpsMonotonicFrameClock : MonotonicFrameClock {
diff --git a/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
new file mode 100644
index 0000000..b555bea
--- /dev/null
+++ b/compose/runtime/runtime/src/test/kotlin/androidx/compose/runtime/MonotonicFrameClockTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime
+
+import kotlinx.coroutines.runBlocking
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+import kotlin.test.assertSame
+
+class MonotonicFrameClockTest {
+ @ExperimentalComposeApi
+ @Test
+ fun monotonicFrameClockThrowsWhenAbsent() {
+ assertFailsWith<IllegalStateException> {
+ runBlocking {
+ coroutineContext.monotonicFrameClock
+ }
+ }
+ }
+
+ @ExperimentalComposeApi
+ @Test
+ fun monotonicFrameClockReturnsContextClock() {
+ val clock = object : MonotonicFrameClock {
+ override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
+ error("not implemented")
+ }
+ }
+
+ val result = runBlocking(clock) {
+ coroutineContext.monotonicFrameClock
+ }
+
+ assertSame(clock, result)
+ }
+
+ @Test
+ fun withFrameNanosThrowsWithNoClock() {
+ assertFailsWith<IllegalStateException> {
+ runBlocking {
+ withFrameNanos {
+ throw RuntimeException("withFrameNanos block should not be called")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun withFrameNanosCallsPresentClock() {
+ val clock = object : MonotonicFrameClock {
+ var callCount = 0
+ override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
+ callCount++
+ return onFrame(0)
+ }
+ }
+ val expected = Any()
+ val result = runBlocking(clock) {
+ withFrameNanos { expected }
+ }
+ assertSame(expected, result, "expected value not returned from withFrameNanos")
+ assertEquals(1, clock.callCount, "withFrameNanos did not use supplied clock")
+ }
+}