diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java index 61a2e2222d..76d86de4e2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java @@ -23,6 +23,7 @@ import android.animation.AnimatorSet; import android.annotation.Nullable; import android.view.InsetsController; import android.view.MotionEvent; +import android.view.View; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.taskbar.StashedHandleViewController; @@ -32,6 +33,7 @@ import com.android.launcher3.taskbar.TaskbarInsetsController; import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.animation.PhysicsAnimator; /** * Coordinates between controllers such as BubbleBarView and BubbleHandleViewController to @@ -51,15 +53,6 @@ public class BubbleStashController { */ private static final float STASHED_BAR_SCALE = 0.5f; - /** The duration of hiding and showing the stashed handle as part of a new bubble animation. */ - private static final long NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS = 200; - - /** The translation Y value the handle animates to when hiding it for a new bubble. */ - private static final int NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y = -20; - - /** The alpha value the handle animates to when hiding it for a new bubble. */ - public static final float NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA = 0.5f; - protected final TaskbarActivityContext mActivity; // Initialized in init. @@ -73,7 +66,6 @@ public class BubbleStashController { private AnimatedFloat mIconScaleForStash; private AnimatedFloat mIconTranslationYForStash; private MultiPropertyFactory.MultiProperty mBubbleStashedHandleAlpha; - private AnimatedFloat mBubbleStashedHandleTranslationY; private boolean mRequestedStashState; private boolean mRequestedExpandedState; @@ -105,7 +97,6 @@ public class BubbleStashController { mBubbleStashedHandleAlpha = mHandleViewController.getStashedHandleAlpha().get( StashedHandleViewController.ALPHA_INDEX_STASHED); - mBubbleStashedHandleTranslationY = mHandleViewController.getStashedHandleTranslationY(); mStashedHeight = mHandleViewController.getStashedHeight(); mUnstashedHeight = mHandleViewController.getUnstashedHeight(); @@ -379,29 +370,8 @@ public class BubbleStashController { return mHandleViewController.getStashedHandleCenterX(); } - /** Returns the animation for hiding the handle before a new bubble animates in. */ - public AnimatorSet buildHideHandleAnimationForNewBubble() { - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether( - mBubbleStashedHandleTranslationY.animateToValue( - NEW_BUBBLE_HIDE_HANDLE_ANIMATION_TRANSLATION_Y), - mBubbleStashedHandleAlpha.animateToValue(NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA)); - animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS); - return animatorSet; - } - - /** Sets the alpha value of the stashed handle. */ - public void setStashAlpha(float alpha) { - mBubbleStashedHandleAlpha.setValue(alpha); - } - - /** Returns the animation for showing the handle after a new bubble animated in. */ - public AnimatorSet buildShowHandleAnimationForNewBubble() { - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether( - mBubbleStashedHandleTranslationY.animateToValue(0), - mBubbleStashedHandleAlpha.animateToValue(1)); - animatorSet.setDuration(NEW_BUBBLE_HANDLE_ANIMATION_DURATION_MS); - return animatorSet; + /** Returns the [PhysicsAnimator] for the stashed handle view. */ + public PhysicsAnimator getStashedHandlePhysicsAnimator() { + return mHandleViewController.getPhysicsAnimator(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index 2a5912a5c2..6f1a0934a7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -29,7 +29,6 @@ import android.view.View; import android.view.ViewOutlineProvider; import com.android.launcher3.R; -import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.RevealOutlineAnimation; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.taskbar.StashedHandleView; @@ -40,6 +39,7 @@ import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.animation.PhysicsAnimator; /** * Handles properties/data collection, then passes the results to our stashed handle View to render. @@ -59,12 +59,6 @@ public class BubbleStashedHandleViewController { private int mStashedHandleWidth; private int mStashedHandleHeight; - private final AnimatedFloat mStashedHandleTranslationY = - new AnimatedFloat(this::updateTranslationY); - - // Modified when swipe up is happening on the stashed handle or task bar. - private float mSwipeUpTranslationY; - // The bounds we want to clip to in the settled state when showing the stashed handle. private final Rect mStashedHandleBounds = new Rect(); @@ -129,6 +123,11 @@ public class BubbleStashedHandleViewController { updateBounds(mBarViewController.getBubbleBarLocation())); } + /** Returns the [PhysicsAnimator] for the stashed handle view. */ + public PhysicsAnimator getPhysicsAnimator() { + return PhysicsAnimator.getInstance(mStashedHandleView); + } + private void updateBounds(BubbleBarLocation bubbleBarLocation) { // As more bubbles get added, the icon bounds become larger. To ensure a consistent // handle bar position, we pin it to the edge of the screen. @@ -238,21 +237,11 @@ public class BubbleStashedHandleViewController { } } - /** Returns an animator for translation Y. */ - public AnimatedFloat getStashedHandleTranslationY() { - return mStashedHandleTranslationY; - } - /** * Sets the translation of the stashed handle during the swipe up gesture. */ public void setTranslationYForSwipe(float transY) { - mSwipeUpTranslationY = transY; - updateTranslationY(); - } - - private void updateTranslationY() { - mStashedHandleView.setTranslationY(mStashedHandleTranslationY.value + mSwipeUpTranslationY); + mStashedHandleView.setTranslationY(transY); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt index 1db51037de..2d8983fca7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt @@ -18,16 +18,12 @@ package com.android.launcher3.taskbar.bubbles.animation import android.view.View import android.view.View.VISIBLE -import androidx.core.animation.AnimatorSet -import androidx.core.animation.ObjectAnimator -import androidx.core.animation.doOnEnd import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.launcher3.taskbar.bubbles.BubbleBarBubble import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleStashController import com.android.launcher3.taskbar.bubbles.BubbleView -import com.android.systemui.util.doOnEnd import com.android.wm.shell.shared.animation.PhysicsAnimator /** Handles animations for bubble bar bubbles. */ @@ -43,17 +39,19 @@ constructor( /** The time to show the flyout. */ const val FLYOUT_DELAY_MS: Long = 2500 /** The translation Y the new bubble will animate to. */ - const val BUBBLE_ANIMATION_FINAL_TRANSLATION_Y = -50f + const val BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y = -50f /** The initial translation Y value the new bubble is set to before the animation starts. */ // TODO(liranb): get rid of this and calculate this based on the y-distance between the // bubble and the stash handle. - const val BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y = 50f + const val BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET = 50f /** The initial scale Y value that the new bubble is set to before the animation starts. */ const val BUBBLE_ANIMATION_INITIAL_SCALE_Y = 0.3f - /** The initial alpha value that the new bubble is set to before the animation starts. */ - const val BUBBLE_ANIMATION_INITIAL_ALPHA = 0.5f - /** The duration of the hide bubble animation. */ - const val HIDE_BUBBLE_ANIMATION_DURATION_MS = 250L + /** + * The distance the stashed handle will travel as it gets hidden as part of the new bubble + * animation. + */ + // TODO(liranb): calculate this based on the position of the views + const val BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y = -20f } /** An interface for scheduling jobs. */ @@ -91,7 +89,7 @@ constructor( if (animator.isRunning()) animator.cancel() // the animation of a new bubble is divided into 2 parts. The first part shows the bubble // and the second part hides it after a delay. - val showAnimation = buildShowAnimation(bubbleView, b.key, animator) + val showAnimation = buildShowAnimation(bubbleView, b.key) val hideAnimation = buildHideAnimation(bubbleView) scheduler.post(showAnimation) scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation) @@ -100,71 +98,139 @@ constructor( /** * Returns a lambda that starts the animation that shows the new bubble. * - * The animation is divided into 2 parts. First the stash handle starts animating up and fades - * out. When it ends the bubble starts fading in. The bubble and stashed handle are aligned to - * give the impression of the stash handle morphing into the bubble. + * Visually, the animation is divided into 2 parts. The stash handle starts animating up and + * fading out and then the bubble starts animating up and fading in. + * + * To make the transition from the handle to the bubble smooth, the positions and movement of + * the 2 views must be synchronized. To do that we use a single spring path along the Y axis, + * starting from the handle's position to the eventual bubble's position. The path is split into + * 3 parts. + * 1. In the first part, we only animate the handle. + * 1. In the second part the handle is fully hidden, and the bubble is animating in. + * 1. The third part is the overshoot of the spring animation, where we make the bubble fully + * visible which helps avoiding further updates when we re-enter the second part. */ private fun buildShowAnimation( bubbleView: BubbleView, key: String, - bubbleAnimator: PhysicsAnimator ): () -> Unit = { + bubbleBarView.prepareForAnimatingBubbleWhileStashed(key) // calculate the initial translation x the bubble should have in order to align it with the // stash handle. val initialTranslationX = bubbleStashController.stashedHandleCenterX - bubbleView.centerXOnScreen - bubbleBarView.prepareForAnimatingBubbleWhileStashed(key) - bubbleAnimator.setDefaultSpringConfig(springConfig) - bubbleAnimator - .spring(DynamicAnimation.ALPHA, 1f) - .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_FINAL_TRANSLATION_Y) - .spring(DynamicAnimation.SCALE_Y, 1f) // prepare the bubble for the animation bubbleView.alpha = 0f bubbleView.translationX = initialTranslationX - bubbleView.translationY = BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y bubbleView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y bubbleView.visibility = VISIBLE - // start the stashed handle animation. when it ends, start the bubble animation. - val stashedHandleAnimation = bubbleStashController.buildHideHandleAnimationForNewBubble() - stashedHandleAnimation.doOnEnd { - bubbleView.alpha = BUBBLE_ANIMATION_INITIAL_ALPHA - bubbleAnimator.start() - bubbleStashController.setStashAlpha(0f) + + // this is the total distance that both the stashed handle and the bubble will be traveling + val totalTranslationY = + BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y + val animator = bubbleStashController.stashedHandlePhysicsAnimator + animator.setDefaultSpringConfig(springConfig) + animator.spring(DynamicAnimation.TRANSLATION_Y, totalTranslationY) + animator.addUpdateListener { target, values -> + val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener + when { + ty >= BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y -> { + // we're in the first leg of the animation. only animate the handle. the bubble + // remains hidden during this part of the animation + + // map the path [0, BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y] to [0,1] + val fraction = ty / BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y + target.alpha = 1 - fraction / 2 + } + ty >= totalTranslationY -> { + // this is the second leg of the animation. the handle should be completely + // hidden and the bubble should start animating in. + // it's possible that we're re-entering this leg because this is a spring + // animation, so only set the alpha and scale for the bubble if we didn't + // already fully animate in. + target.alpha = 0f + bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET + if (bubbleView.alpha != 1f) { + // map the path + // [BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y, totalTranslationY] + // to [0, 1] + val fraction = + (ty - BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y) / + BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + bubbleView.alpha = fraction + bubbleView.scaleY = + BUBBLE_ANIMATION_INITIAL_SCALE_Y + + (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction + } + } + else -> { + // we're past the target animated value, set the alpha and scale for the bubble + // so that it's fully visible and no longer changing, but keep moving it along + // the animation path + bubbleView.alpha = 1f + bubbleView.scaleY = 1f + bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET + } + } } - stashedHandleAnimation.start() + animator.start() } /** * Returns a lambda that starts the animation that hides the new bubble. * - * Similarly to the show animation, this is divided into 2 parts. We first animate the bubble - * out, and then animate the stash handle in. At the end of the animation we reset the values of - * the bubble. + * Similarly to the show animation, this is visually divided into 2 parts. We first animate the + * bubble out, and then animate the stash handle in. At the end of the animation we reset the + * values of the bubble. + * + * This is a spring animation that goes along the same path of the show animation in the + * opposite order, and is split into 3 parts: + * 1. In the first part the bubble animates out. + * 1. In the second part the bubble is fully hidden and the handle animates in. + * 1. The third part is the overshoot. The handle is made fully visible. */ private fun buildHideAnimation(bubbleView: BubbleView): () -> Unit = { - val stashAnimation = bubbleStashController.buildShowHandleAnimationForNewBubble() - val alphaAnimator = - ObjectAnimator.ofFloat(bubbleView, View.ALPHA, BUBBLE_ANIMATION_INITIAL_ALPHA) - val translationYAnimator = - ObjectAnimator.ofFloat( - bubbleView, - View.TRANSLATION_Y, - BUBBLE_ANIMATION_INITIAL_TRANSLATION_Y - ) - val scaleYAnimator = - ObjectAnimator.ofFloat(bubbleView, View.SCALE_Y, BUBBLE_ANIMATION_INITIAL_SCALE_Y) - val hideBubbleAnimation = AnimatorSet() - hideBubbleAnimation.playTogether(alphaAnimator, translationYAnimator, scaleYAnimator) - hideBubbleAnimation.duration = HIDE_BUBBLE_ANIMATION_DURATION_MS - hideBubbleAnimation.doOnEnd { - // the bubble is now hidden, start the stash handle animation and reset bubble - // properties - bubbleStashController.setStashAlpha( - BubbleStashController.NEW_BUBBLE_HIDE_HANDLE_ANIMATION_ALPHA - ) + // this is the total distance that both the stashed handle and the bubble will be traveling + val totalTranslationY = + BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y + val animator = bubbleStashController.stashedHandlePhysicsAnimator + animator.setDefaultSpringConfig(springConfig) + animator.spring(DynamicAnimation.TRANSLATION_Y, 0f) + animator.addUpdateListener { target, values -> + val ty = values[DynamicAnimation.TRANSLATION_Y]?.value ?: return@addUpdateListener + when { + ty <= BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y -> { + // this is the first leg of the animation. only animate the bubble. the handle + // is hidden during this part + bubbleView.translationY = ty + BUBBLE_ANIMATION_TRANSLATION_Y_OFFSET + // map the path + // [totalTranslationY, BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y] + // to [0, 1] + val fraction = (totalTranslationY - ty) / BUBBLE_ANIMATION_BUBBLE_TRANSLATION_Y + bubbleView.alpha = 1 - fraction / 2 + bubbleView.scaleY = 1 - (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction + } + ty <= 0 -> { + // this is the second part of the animation. make the bubble invisible and + // start fading in the handle, but don't update the alpha if it's already fully + // visible + bubbleView.alpha = 0f + if (target.alpha != 1f) { + // map the path [BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y, 0] to [0, 1] + val fraction = + (BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y - ty) / + BUBBLE_ANIMATION_STASH_HANDLE_TRANSLATION_Y + target.alpha = fraction + } + } + else -> { + // we reached the target value. set the alpha of the handle to 1 + target.alpha = 1f + } + } + } + animator.addEndListener { _, _, _, _, _, _, _ -> bubbleView.alpha = 0f - stashAnimation.start() bubbleView.translationY = 0f bubbleView.scaleY = 1f if (bubbleStashController.isStashed) { @@ -172,7 +238,7 @@ constructor( } bubbleBarView.onAnimatingBubbleCompleted() } - hideBubbleAnimation.start() + animator.start() } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt index b478efac93..d90e0484e5 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt @@ -16,19 +16,15 @@ package com.android.launcher3.taskbar.bubbles.animation -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.AnimatorSet import android.content.Context import android.graphics.Color import android.graphics.Path import android.graphics.drawable.ColorDrawable import android.view.LayoutInflater +import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.widget.FrameLayout -import androidx.core.animation.AnimatorTestRule -import androidx.core.animation.doOnEnd import androidx.core.graphics.drawable.toBitmap import androidx.dynamicanimation.animation.DynamicAnimation import androidx.test.core.app.ApplicationProvider @@ -42,16 +38,13 @@ import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleStashController import com.android.launcher3.taskbar.bubbles.BubbleView import com.android.wm.shell.common.bubbles.BubbleInfo +import com.android.wm.shell.shared.animation.PhysicsAnimator import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils import com.google.common.truth.Truth.assertThat -import java.util.concurrent.Semaphore -import java.util.concurrent.TimeUnit import org.junit.Before -import org.junit.ClassRule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @SmallTest @@ -61,10 +54,6 @@ class BubbleBarViewAnimatorTest { private val context = ApplicationProvider.getApplicationContext() private val animatorScheduler = TestBubbleBarViewAnimatorScheduler() - companion object { - @JvmField @ClassRule val animatorTestRule = AnimatorTestRule() - } - @Before fun setUp() { PhysicsAnimatorTestUtils.prepareForTest() @@ -99,14 +88,9 @@ class BubbleBarViewAnimatorTest { val bubbleStashController = mock() whenever(bubbleStashController.isStashed).thenReturn(true) - val semaphore = Semaphore(0) - val hideHandleAnimator = AnimatorSet() - hideHandleAnimator.duration = 0 - whenever(bubbleStashController.buildHideHandleAnimationForNewBubble()) - .thenReturn(hideHandleAnimator) - // add an end listener to the hide handle animation. we add it when the animation starts - // to ensure that it gets called after all other end listeners. - hideHandleAnimator.doOnStart { hideHandleAnimator.doOnEnd { semaphore.release() } } + val handle = View(context) + val handleAnimator = PhysicsAnimator.getInstance(handle) + whenever(bubbleStashController.stashedHandlePhysicsAnimator).thenReturn(handleAnimator) val animator = BubbleBarViewAnimator(bubbleBarView, bubbleStashController, animatorScheduler) @@ -115,44 +99,26 @@ class BubbleBarViewAnimatorTest { animator.animateBubbleInForStashed(bubble) } - // wait for the stash handle animation to complete - assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue() - // stash handle animation finished. verify that the stash handle is now hidden - verify(bubbleStashController).setStashAlpha(0f) - + // let the animation start and wait for it to complete InstrumentationRegistry.getInstrumentation().waitForIdleSync() + PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y) + assertThat(handle.alpha).isEqualTo(0) + assertThat(handle.translationY).isEqualTo(-70) assertThat(overflowView.visibility).isEqualTo(INVISIBLE) assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE) assertThat(bubbleView.visibility).isEqualTo(VISIBLE) - - // wait for the show bubble animation to complete - PhysicsAnimatorTestUtils.blockUntilAnimationsEnd( - DynamicAnimation.ALPHA, - DynamicAnimation.TRANSLATION_Y, - DynamicAnimation.SCALE_Y, - ) - assertThat(bubbleView.alpha).isEqualTo(1) - assertThat(bubbleView.translationY).isEqualTo(-50) + assertThat(bubbleView.translationY).isEqualTo(-20) assertThat(bubbleView.scaleY).isEqualTo(1) - val showHandleAnimator = AnimatorSet() - showHandleAnimator.duration = 0 - whenever(bubbleStashController.buildShowHandleAnimationForNewBubble()) - .thenReturn(showHandleAnimator) - var showHandleAnimationStarted = false - showHandleAnimator.doOnStart { showHandleAnimationStarted = true } - // execute the hide bubble animation assertThat(animatorScheduler.delayedBlock).isNotNull() InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!) - // finish the hide bubble animation - InstrumentationRegistry.getInstrumentation().runOnMainSync { - animatorTestRule.advanceTimeBy(250) - } - assertThat(showHandleAnimationStarted).isTrue() + // let the animation start and wait for it to complete + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y) assertThat(bubbleView.alpha).isEqualTo(1) assertThat(bubbleView.visibility).isEqualTo(VISIBLE) @@ -160,16 +126,8 @@ class BubbleBarViewAnimatorTest { assertThat(bubbleBarView.alpha).isEqualTo(0) assertThat(overflowView.alpha).isEqualTo(1) assertThat(overflowView.visibility).isEqualTo(VISIBLE) - } - - private fun AnimatorSet.doOnStart(onStart: () -> Unit) { - addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationStart(animator: Animator) { - onStart() - } - } - ) + assertThat(handle.alpha).isEqualTo(1) + assertThat(handle.translationY).isEqualTo(0) } private class TestBubbleBarViewAnimatorScheduler : BubbleBarViewAnimator.Scheduler {