diff options
| author | 2023-10-12 12:40:55 +0000 | |
|---|---|---|
| committer | 2023-10-12 12:40:55 +0000 | |
| commit | 1016e95f6eb624688277f8998fcd5674d4eaaf80 (patch) | |
| tree | cbd514bb24ada3abe68635990d9ee1ebc63c5a27 | |
| parent | f7f16510ff7e7adcc510c97aaeb70f2f8e483ab8 (diff) | |
| parent | 028dbf434343981d96ac65fe19f5268eea3fcb68 (diff) | |
Merge changes I6b4d256d,Ib62baf67,Ia9427c81 into main
* changes:
Rename Key.name to Key.debugName
Add support for movable elements
Revert "[flexiglass] Added isUserInputOngoing to transition model"
25 files changed, 537 insertions, 155 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt index 60c3fd3c4cdb..88944f10eab9 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -108,7 +108,7 @@ private fun CoroutineScope.animate( ) { val fromScene = layoutImpl.state.transitionState.currentScene val isUserInput = - (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput + (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven ?: false val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec @@ -119,23 +119,9 @@ private fun CoroutineScope.animate( val targetProgress = if (reversed) 0f else 1f val transition = if (reversed) { - OneOffTransition( - fromScene = target, - toScene = fromScene, - currentScene = target, - isUserInput, - isUserInputOngoing = false, - animatable, - ) + OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable) } else { - OneOffTransition( - fromScene = fromScene, - toScene = target, - currentScene = target, - isUserInput, - isUserInputOngoing = false, - animatable, - ) + OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable) } // Change the current layout state to use this new transition. @@ -156,8 +142,7 @@ private class OneOffTransition( override val fromScene: SceneKey, override val toScene: SceneKey, override val currentScene: SceneKey, - override val isInitiatedByUserInput: Boolean, - override val isUserInputOngoing: Boolean, + override val isUserInputDriven: Boolean, private val animatable: Animatable<Float, AnimationVector1D>, ) : TransitionState.Transition { override val progress: Float diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt index 3bcd920fb02b..ce96bbfc7976 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -59,6 +60,17 @@ internal class Element(val key: ElementKey) { /** The mapping between a scene and the values/state this element has in that scene, if any. */ val sceneValues = SnapshotStateMap<SceneKey, TargetValues>() + /** + * The movable content of this element, if this element is composed using + * [SceneScope.MovableElement]. + */ + val movableContent by + // This is only accessed from the composition (main) thread, so no need to use the default + // lock of lazy {} to synchronize. + lazy(mode = LazyThreadSafetyMode.NONE) { + movableContentOf { content: @Composable () -> Unit -> content() } + } + override fun toString(): String { return "Element(key=$key)" } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt index b7acc48e2865..bc015eedb1b4 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt @@ -22,7 +22,7 @@ import androidx.annotation.VisibleForTesting * A base class to create unique keys, associated to an [identity] that is used to check the * equality of two key instances. */ -sealed class Key(val name: String, val identity: Any) { +sealed class Key(val debugName: String, val identity: Any) { override fun equals(other: Any?): Boolean { if (this === other) return true if (this.javaClass != other?.javaClass) return false @@ -34,7 +34,7 @@ sealed class Key(val name: String, val identity: Any) { } override fun toString(): String { - return "Key(name=$name)" + return "Key(debugName=$debugName)" } } @@ -49,7 +49,7 @@ class SceneKey( val rootElementKey = ElementKey(name, identity) override fun toString(): String { - return "SceneKey(name=$name)" + return "SceneKey(debugName=$debugName)" } } @@ -71,7 +71,7 @@ class ElementKey( } override fun toString(): String { - return "ElementKey(name=$name)" + return "ElementKey(debugName=$debugName)" } companion object { @@ -89,6 +89,6 @@ class ElementKey( /** Key for a shared value of an element. */ class ValueKey(name: String, identity: Any = Object()) : Key(name, identity) { override fun toString(): String { - return "ValueKey(name=$name)" + return "ValueKey(debugName=$debugName)" } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt new file mode 100644 index 000000000000..11bbf2aa987e --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2023 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 com.android.compose.animation.scene + +import android.graphics.Picture +import android.util.Log +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshots.Snapshot +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.graphics.Canvas +import androidx.compose.ui.graphics.drawscope.draw +import androidx.compose.ui.graphics.drawscope.drawIntoCanvas +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.IntSize + +private const val TAG = "MovableElement" + +private object MovableElementScopeImpl : MovableElementScope + +@Composable +internal fun MovableElement( + layoutImpl: SceneTransitionLayoutImpl, + scene: Scene, + key: ElementKey, + modifier: Modifier, + content: @Composable MovableElementScope.() -> Unit, +) { + Box(modifier.element(layoutImpl, scene, key)) { + // Get the Element from the map. It will always be the same and we don't want to recompose + // every time an element is added/removed from SceneTransitionLayoutImpl.elements, so we + // disable read observation during the look-up in that map. + val element = Snapshot.withoutReadObservation { layoutImpl.elements.getValue(key) } + + // The [Picture] to which we save the last drawing commands of this element. This is + // necessary because the content of this element might not be composed in this scene, in + // which case we still need to draw it. + val picture = remember { Picture() } + + if (shouldComposeMovableElement(layoutImpl, scene.key, element)) { + Box( + Modifier.drawWithCache { + val width = size.width.toInt() + val height = size.height.toInt() + + onDrawWithContent { + // Save the draw commands into [picture] for later to draw the last content + // even when this movable content is not composed. + val pictureCanvas = Canvas(picture.beginRecording(width, height)) + draw(this, this.layoutDirection, pictureCanvas, this.size) { + this@onDrawWithContent.drawContent() + } + picture.endRecording() + + // Draw the content. + drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) } + } + } + ) { + element.movableContent { MovableElementScopeImpl.content() } + } + } else { + // If we are not composed, we draw the previous drawing commands at the same size as the + // movable content when it was composed in this scene. + val sceneValues = element.sceneValues.getValue(scene.key) + + Spacer( + Modifier.layout { measurable, _ -> + val size = + sceneValues.targetSize.takeIf { it != Element.SizeUnspecified } + ?: IntSize.Zero + val placeable = + measurable.measure(Constraints.fixed(size.width, size.height)) + layout(size.width, size.height) { placeable.place(0, 0) } + } + .drawBehind { + drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) } + } + ) + } + } +} + +private fun shouldComposeMovableElement( + layoutImpl: SceneTransitionLayoutImpl, + scene: SceneKey, + element: Element, +): Boolean { + val transitionState = layoutImpl.state.transitionState + + // If we are idle, there is only one [scene] that is composed so we can compose our movable + // content here. + if (transitionState is TransitionState.Idle) { + check(transitionState.currentScene == scene) + return true + } + + val fromScene = (transitionState as TransitionState.Transition).fromScene + val toScene = transitionState.toScene + if (fromScene == toScene) { + check(fromScene == scene) + return true + } + + val fromReady = layoutImpl.isSceneReady(fromScene) + val toReady = layoutImpl.isSceneReady(toScene) + + val otherScene = + when (scene) { + fromScene -> toScene + toScene -> fromScene + else -> + error( + "shouldComposeMovableElement(scene=$scene) called with fromScene=$fromScene " + + "and toScene=$toScene" + ) + } + + val isShared = otherScene in element.sceneValues + + if (isShared && !toReady && !fromReady) { + // This should usually not happen given that fromScene should be ready, but let's log a + // warning here in case it does so it helps debugging flicker issues caused by this part of + // the code. + Log.w( + TAG, + "MovableElement $element might have to be composed for the first time in both " + + "fromScene=$fromScene and toScene=$toScene. This will probably lead to a flicker " + + "where the size of the element will jump from IntSize.Zero to its actual size " + + "during the transition." + ) + } + + // Element is not shared in this transition. + if (!isShared) { + return true + } + + // toScene is not ready (because we are composing it for the first time), so we compose it there + // first. This is the most common scenario when starting a transition that has a shared movable + // element. + if (!toReady) { + return scene == toScene + } + + // This should usually not happen, but if we are also composing for the first time in fromScene + // then we should compose it there only. + if (!fromReady) { + return scene == fromScene + } + + // If we are ready in both scenes, then compose in the scene that has the highest zIndex (unless + // it is a background) given that this is the one that is going to be drawn. + val isHighestScene = layoutImpl.scene(scene).zIndex > layoutImpl.scene(otherScene).zIndex + return if (element.key.isBackground) { + !isHighestScene + } else { + isHighestScene + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 1b79dbdee510..ccdec6ea8c5e 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -52,14 +52,7 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isInitiatedByUserInput: Boolean, - - /** - * Whether user input is currently driving the transition. For example, if a user is - * dragging a pointer, this emits true. Once they lift their finger, this emits false while - * the transition completes/settles. - */ - val isUserInputOngoing: Flow<Boolean>, + val isUserInputDriven: Boolean, ) : ObservableTransitionState() } @@ -80,8 +73,7 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans fromScene = state.fromScene, toScene = state.toScene, progress = snapshotFlow { state.progress }, - isInitiatedByUserInput = state.isInitiatedByUserInput, - isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, + isUserInputDriven = state.isUserInputDriven, ) } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt index 3985233bd197..3fd6828fca6b 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt @@ -90,4 +90,13 @@ private class SceneScopeImpl( canOverflow, ) } + + @Composable + override fun MovableElement( + key: ElementKey, + modifier: Modifier, + content: @Composable MovableElementScope.() -> Unit, + ) { + MovableElement(layoutImpl, scene, key, modifier, content) + } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index 58c7bdbf3d71..4283c0e61df8 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -85,6 +85,13 @@ interface SceneTransitionLayoutScope { ) } +/** + * A DSL marker to prevent people from nesting calls to Modifier.element() inside a MovableElement, + * which is not supported. + */ +@DslMarker annotation class ElementDsl + +@ElementDsl interface SceneScope { /** * Tag an element identified by [key]. @@ -95,12 +102,37 @@ interface SceneScope { * Additionally, this [key] will be used to detect elements that are shared between scenes to * automatically interpolate their size, offset and [shared values][animateSharedValueAsState]. * + * Note that shared elements tagged using this function will be duplicated in each scene they + * are part of, so any **internal** state (e.g. state created using `remember { + * mutableStateOf(...) }`) will be lost. If you need to preserve internal state, you should use + * [MovableElement] instead. + * + * @see MovableElement + * * TODO(b/291566282): Migrate this to the new Modifier Node API and remove the @Composable * constraint. */ @Composable fun Modifier.element(key: ElementKey): Modifier /** + * Create a *movable* element identified by [key]. + * + * This creates an element that will be automatically shared when present in multiple scenes and + * that can be transformed during transitions, the same way that [element] does. The major + * difference with [element] is that elements created with [MovableElement] will be "moved" and + * composed only once during transitions (as opposed to [element] that duplicates shared + * elements) so that any internal state is preserved during and after the transition. + * + * @see element + */ + @Composable + fun MovableElement( + key: ElementKey, + modifier: Modifier, + content: @Composable MovableElementScope.() -> Unit, + ) + + /** * Animate some value of a shared element. * * @param value the value of this shared value in the current scene. @@ -126,6 +158,10 @@ interface SceneScope { ): State<T> } +// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey +// arguments to allow sharing values inside a movable element. +@ElementDsl interface MovableElementScope + /** An action performed by the user. */ sealed interface UserAction diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index b3a7a8e9f874..4952270cb5f2 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -199,4 +199,6 @@ internal class SceneTransitionLayoutImpl( return readyScenes.containsKey(transition.fromScene) && readyScenes.containsKey(transition.toScene) } + + internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene) } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index b9f83c545122..7a21211c3dde 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -70,9 +70,6 @@ sealed interface TransitionState { val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isInitiatedByUserInput: Boolean - - /** Whether user input is currently driving the transition. */ - val isUserInputOngoing: Boolean + val isUserInputDriven: Boolean } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt index e275fcaf4572..1cbfe3057ff0 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -66,7 +66,7 @@ internal fun Modifier.swipeToScene( // swipe in the other direction. val startDragImmediately = state == transition && - !transition.isUserInputOngoing && + transition.isAnimatingOffset && !currentScene.shouldEnableSwipes(orientation.opposite()) // The velocity threshold at which the intent of the user is to swipe up or down. It is the same @@ -126,7 +126,7 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition override val progress: Float get() { - val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value + val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset if (distance == 0f) { // This can happen only if fromScene == toScene. error( @@ -137,15 +137,16 @@ private class SwipeTransition(initialScene: Scene) : TransitionState.Transition return offset / distance } - override val isInitiatedByUserInput = true - - var _isUserInputOngoing by mutableStateOf(false) - override val isUserInputOngoing: Boolean - get() = _isUserInputOngoing + override val isUserInputDriven = true /** The current offset caused by the drag gesture. */ var dragOffset by mutableFloatStateOf(0f) + /** + * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture. + */ + var isAnimatingOffset by mutableStateOf(false) + /** The animatable used to animate the offset once the user lifted its finger. */ val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold) @@ -208,11 +209,9 @@ private fun onDragStarted( transition: SwipeTransition, orientation: Orientation, ) { - transition._isUserInputOngoing = true - if (layoutImpl.state.transitionState == transition) { // This [transition] was already driving the animation: simply take over it. - if (!transition.isUserInputOngoing) { + if (transition.isAnimatingOffset) { // Stop animating and start from where the current offset. Setting the animation job to // `null` will effectively cancel the animation. transition.stopOffsetAnimation() @@ -457,29 +456,30 @@ private fun CoroutineScope.animateOffset( ) { transition.startOffsetAnimation { launch { - if (transition.isUserInputOngoing) { - transition.offsetAnimatable.snapTo(transition.dragOffset) - } - transition._isUserInputOngoing = false - - transition.offsetAnimatable.animateTo( - targetOffset, - // TODO(b/290184746): Make this spring spec configurable. - spring( - stiffness = Spring.StiffnessMediumLow, - visibilityThreshold = OffsetVisibilityThreshold - ), - initialVelocity = initialVelocity, - ) + if (!transition.isAnimatingOffset) { + transition.offsetAnimatable.snapTo(transition.dragOffset) + } + transition.isAnimatingOffset = true + + transition.offsetAnimatable.animateTo( + targetOffset, + // TODO(b/290184746): Make this spring spec configurable. + spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = OffsetVisibilityThreshold + ), + initialVelocity = initialVelocity, + ) - // Now that the animation is done, the state should be idle. Note that if the state - // was changed since this animation started, some external code changed it and we - // shouldn't do anything here. Note also that this job will be cancelled in the case - // where the user intercepts this swipe. - if (layoutImpl.state.transitionState == transition) { - layoutImpl.state.transitionState = TransitionState.Idle(targetScene) + // Now that the animation is done, the state should be idle. Note that if the state + // was changed since this animation started, some external code changed it and we + // shouldn't do anything here. Note also that this job will be cancelled in the case + // where the user intercepts this swipe. + if (layoutImpl.state.transitionState == transition) { + layoutImpl.state.transitionState = TransitionState.Idle(targetScene) + } } - } + .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } } } } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt new file mode 100644 index 000000000000..4204cd5f0da0 --- /dev/null +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 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 com.android.compose.animation.scene + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.hasParent +import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.test.assertSizeIsEqualTo +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MovableElementTest { + @get:Rule val rule = createComposeRule() + + /** An element that displays a counter that is incremented whenever this element is clicked. */ + @Composable + private fun Counter(modifier: Modifier = Modifier) { + var count by remember { mutableIntStateOf(0) } + Box(modifier.fillMaxSize().clickable { count++ }) { Text("count: $count") } + } + + @Composable + private fun SceneScope.MovableCounter(key: ElementKey, modifier: Modifier) { + MovableElement(key, modifier) { Counter() } + } + + @Test + fun modifierElementIsDuplicatedDuringTransitions() { + rule.testTransition( + fromSceneContent = { + Box(Modifier.element(TestElements.Foo).size(50.dp)) { Counter() } + }, + toSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) { Counter() } }, + transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) }, + fromScene = TestScenes.SceneA, + toScene = TestScenes.SceneB, + ) { + before { + // Click 3 times on the counter. + rule.onNodeWithText("count: 0").assertIsDisplayed().performClick() + rule.onNodeWithText("count: 1").assertIsDisplayed().performClick() + rule.onNodeWithText("count: 2").assertIsDisplayed().performClick() + rule + .onNodeWithText("count: 3") + .assertIsDisplayed() + .assertSizeIsEqualTo(50.dp, 50.dp) + + // There are no other counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(1) + } + + at(32) { + // In the middle of the transition, there are 2 copies of the counter: the previous + // one from scene A (equal to 3) and the new one from scene B (equal to 0). + rule + .onNode( + hasText("count: 3") and + hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA)) + ) + .assertIsDisplayed() + .assertSizeIsEqualTo(75.dp, 75.dp) + + rule + .onNode( + hasText("count: 0") and + hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB)) + ) + .assertIsDisplayed() + .assertSizeIsEqualTo(75.dp, 75.dp) + + // There are exactly 2 counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(2) + } + + after { + // At the end of the transition, only the counter from scene B is composed. + rule + .onNodeWithText("count: 0") + .assertIsDisplayed() + .assertSizeIsEqualTo(100.dp, 100.dp) + + // There are no other counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(1) + } + } + } + + @Test + fun movableElementIsMovedAndComposedOnlyOnce() { + rule.testTransition( + fromSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(50.dp)) }, + toSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(100.dp)) }, + transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) }, + fromScene = TestScenes.SceneA, + toScene = TestScenes.SceneB, + ) { + before { + // Click 3 times on the counter. + rule.onNodeWithText("count: 0").assertIsDisplayed().performClick() + rule.onNodeWithText("count: 1").assertIsDisplayed().performClick() + rule.onNodeWithText("count: 2").assertIsDisplayed().performClick() + rule + .onNodeWithText("count: 3") + .assertIsDisplayed() + .assertSizeIsEqualTo(50.dp, 50.dp) + + // There are no other counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(1) + } + + at(32) { + // During the transition, there is a single counter that is moved, with the current + // value. + rule + .onNode(hasText("count: 3")) + .assertIsDisplayed() + .assertSizeIsEqualTo(75.dp, 75.dp) + + // There are no other counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(1) + } + + after { + // At the end of the transition, the counter still has the current value. + rule + .onNodeWithText("count: 3") + .assertIsDisplayed() + .assertSizeIsEqualTo(100.dp, 100.dp) + + // There are no other counters. + assertThat( + rule + .onAllNodesWithText("count: ", substring = true) + .fetchSemanticsNodes() + .size + ) + .isEqualTo(1) + } + } + } +} diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 328866ea76ca..5afd420a5e16 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -117,7 +117,7 @@ class SceneTransitionLayoutTest { .size(size) .background(Color.Red) .element(TestElements.Foo) - .testTag(TestElements.Foo.name) + .testTag(TestElements.Foo.debugName) ) { // Offset the single child of Foo by some animated shared offset. val offset by animateSharedDpAsState(childOffset, TestValues.Value1, TestElements.Foo) @@ -129,7 +129,7 @@ class SceneTransitionLayoutTest { } .size(30.dp) .background(Color.Blue) - .testTag(TestElements.Bar.name) + .testTag(TestElements.Bar.debugName) ) } } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 53ed2b5d3317..df3b72aa5533 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -63,7 +63,7 @@ class SwipeToSceneTest { { currentScene = it }, EmptyTestTransitions, state = layoutState, - modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.name), + modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), ) { scene( TestScenes.SceneA, @@ -122,8 +122,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isInitiatedByUserInput).isTrue() - assertThat(transition.isUserInputOngoing).isTrue() + assertThat(transition.isUserInputDriven).isTrue() // Release the finger. We should now be animating back to A (currentScene = SceneA) given // that 55dp < positional threshold. @@ -135,8 +134,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneB) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth) - assertThat(transition.isInitiatedByUserInput).isTrue() - assertThat(transition.isUserInputOngoing).isFalse() + assertThat(transition.isUserInputDriven).isTrue() // Wait for the animation to finish. We should now be in scene A. rule.waitForIdle() @@ -158,8 +156,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isInitiatedByUserInput).isTrue() - assertThat(transition.isUserInputOngoing).isTrue() + assertThat(transition.isUserInputDriven).isTrue() // Release the finger. We should now be animating to C (currentScene = SceneC) given // that 56dp >= positional threshold. @@ -171,8 +168,7 @@ class SwipeToSceneTest { assertThat(transition.toScene).isEqualTo(TestScenes.SceneC) assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC) assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight) - assertThat(transition.isInitiatedByUserInput).isTrue() - assertThat(transition.isUserInputOngoing).isFalse() + assertThat(transition.isUserInputDriven).isTrue() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt index 268057fd2f2c..e0ae1be69aaf 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt @@ -22,13 +22,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.SemanticsNodeInteractionCollection import androidx.compose.ui.test.hasParent import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.onAllNodesWithTag -import androidx.compose.ui.test.onNodeWithTag @DslMarker annotation class TransitionTestDsl @@ -63,6 +63,8 @@ interface TransitionTestBuilder { @TransitionTestDsl interface TransitionTestAssertionScope { + fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher + /** * Assert on [element]. * @@ -130,15 +132,19 @@ fun ComposeContentTestRule.testTransition( val test = transitionTest(builder) val assertionScope = object : TransitionTestAssertionScope { + override fun isElement(element: ElementKey, scene: SceneKey?): SemanticsMatcher { + return if (scene == null) { + hasTestTag(element.testTag) + } else { + hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)) + } + } + override fun onElement( element: ElementKey, scene: SceneKey? ): SemanticsNodeInteraction { - return if (scene == null) { - onNodeWithTag(element.testTag) - } else { - onNode(hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))) - } + return onNode(isElement(element, scene)) } override fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 0da562bcb3bb..2e93a09deb30 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -161,8 +161,7 @@ private fun SceneTransitionObservableTransitionState.toModel(): ObservableTransi fromScene = fromScene.toModel().key, toScene = toScene.toModel().key, progress = progress, - isInitiatedByUserInput = isInitiatedByUserInput, - isUserInputOngoing = isUserInputOngoing, + isUserInputDriven = isUserInputDriven, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt index f704894e56e2..3927873f8ba8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt @@ -42,13 +42,6 @@ sealed class ObservableTransitionState { * scene, this value will remain true after the pointer is no longer touching the screen and * will be true in any transition created to animate back to the original position. */ - val isInitiatedByUserInput: Boolean, - - /** - * Whether user input is currently driving the transition. For example, if a user is - * dragging a pointer, this emits true. Once they lift their finger, this emits false while - * the transition completes/settles. - */ - val isUserInputOngoing: Flow<Boolean>, + val isUserInputDriven: Boolean, ) : ObservableTransitionState() } diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index 6117f9f80e6c..e487a6fb9617 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -261,7 +261,7 @@ constructor( when (state) { is ObservableTransitionState.Idle -> false is ObservableTransitionState.Transition -> - state.isInitiatedByUserInput && + state.isUserInputDriven && (state.toScene == sceneKey || state.fromScene == sceneKey) } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt index 62f9a9dcce70..20d4eb907944 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -810,7 +810,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, flowOf(.5f), false, - isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -826,8 +825,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false, - isUserInputOngoing = flowOf(false), + false ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -844,8 +842,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f), - false, - isUserInputOngoing = flowOf(false), + false ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason") @@ -863,8 +860,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f), - false, - isUserInputOngoing = flowOf(false), + false ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") @@ -880,7 +876,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Lockscreen, flowOf(.5f), false, - isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason") @@ -898,7 +893,6 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { SceneKey.Gone, flowOf(.5f), false, - isUserInputOngoing = flowOf(false), ) runCurrent() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason") diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index 9ee22c89405d..b32905fd3b79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -204,8 +204,7 @@ class KeyguardInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) runCurrent() assertThat(isAnimate).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 2e16577bb24f..61dd69a8126b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -52,7 +52,6 @@ import com.google.common.truth.Truth.assertWithMessage import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -463,8 +462,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { fromScene = getCurrentSceneInUi(), toScene = to.key, progress = progressFlow, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index 740c6d9329df..432bd0f2a050 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -29,7 +29,6 @@ import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -120,8 +119,7 @@ class SceneContainerRepositoryTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 31d26c0d88f9..8b23d183f1a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -83,8 +83,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(reflectedTransitionState).isEqualTo(transitionState.value) @@ -122,8 +121,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = underTest.desiredScene.value.key, toScene = SceneKey.Shade, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(transitionTo).isEqualTo(SceneKey.Shade) @@ -160,8 +158,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) val transitioning by @@ -180,8 +177,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) underTest.setTransitionState(transitionState) @@ -198,8 +194,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) val transitioning by @@ -227,8 +222,7 @@ class SceneInteractorTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Lockscreen, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(transitioning).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index 3b9621e5c6e0..7b13de657657 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -109,8 +109,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason") @@ -123,8 +122,7 @@ class SceneContainerStartableTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.Gone, progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) assertThat(isVisible).isTrue() sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason") diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt index bcb060ddb417..81382a44def6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt @@ -60,7 +60,6 @@ import dagger.BindsInstance import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -595,8 +594,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) @@ -633,8 +631,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) @@ -670,8 +667,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = SceneKey.Shade, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) @@ -947,8 +943,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) @@ -985,8 +980,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = SceneKey.Lockscreen, toScene = key, progress = progress, - isInitiatedByUserInput = true, - isUserInputOngoing = flowOf(false), + isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) @@ -1023,8 +1017,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) sceneInteractor.setTransitionState(transitionState) @@ -1061,8 +1054,7 @@ class ShadeInteractorTest : SysuiTestCase() { fromScene = key, toScene = SceneKey.Lockscreen, progress = progress, - isInitiatedByUserInput = true, - isUserInputOngoing = flowOf(false), + isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) @@ -1097,9 +1089,8 @@ class ShadeInteractorTest : SysuiTestCase() { ObservableTransitionState.Transition( fromScene = SceneKey.Lockscreen, toScene = SceneKey.QuickSettings, - progress = MutableStateFlow(0f), - isInitiatedByUserInput = true, - isUserInputOngoing = flowOf(false), + progress = progress, + isUserInputDriven = true, ) ) sceneInteractor.setTransitionState(transitionState) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index bb20d94e7d39..607cdab12f56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -17,7 +17,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -85,8 +84,7 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Shade, toScene = SceneKey.QuickSettings, progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) ) @@ -104,8 +102,7 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.QuickSettings, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) ) @@ -123,8 +120,7 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { fromScene = SceneKey.Gone, toScene = SceneKey.Shade, progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), + isUserInputDriven = false, ) ) ) |