From 6097a291583428c86dd50ba2b0c2840c2e02d52d Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Thu, 9 May 2024 14:18:42 -0400 Subject: [PATCH] Handle touch on bubble bar while animating This change handles touches on the bubble bar while the bubble bar is animating. We now update the taskbar touch region during the animation to make the bubble bar touchable. When the bubble bar is touched during the animation, we now cancel the animation and allow dragging or clicking to expand. Demo: http://recall/-/bJtug1HhvXkkeA4MQvIaiP/gtGS6FCrygN5RKueFtPs5E BubbleBarTest 100x run: https://android-build.corp.google.com/abtd/run/L42600030003675041 Note that in the abtd run above there's a flake but that's for a different test which I'll look into separately. Flag: ACONFIG com.android.wm.shell.enable_bubble_bar DEVELOPMENT Bug: 280605790 Bug: 280605846 Bug: 339066271 Fixes: 339244038 Test: See demo Test: atest BubbleBarViewAnimatorTest Change-Id: Icb8f4fa46ae4bb8c94ca931193d4a3cc49c5b718 --- .../taskbar/bubbles/BubbleBarView.java | 52 +++++++++++++------ .../bubbles/BubbleBarViewController.java | 40 +++++++++----- .../taskbar/bubbles/BubbleDragController.java | 3 +- .../animation/BubbleBarViewAnimator.kt | 21 +++++++- .../animation/BubbleBarViewAnimatorTest.kt | 5 +- 5 files changed, 88 insertions(+), 33 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index de93ba5859..ac97fac7e0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -192,6 +192,8 @@ public class BubbleBarView extends FrameLayout { private float mTranslationXDuringDrag = 0f; private float mAlphaDuringDrag = 0f; + private Controller mController; + private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED; public BubbleBarView(Context context) { @@ -376,22 +378,23 @@ public class BubbleBarView extends FrameLayout { */ public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation, BubbleBarLocation location) { - // Start with the initial translation. Value on y-axis can be reused. - final PointF dragEndTranslation = new PointF(initialTranslation); - // Bubble bar is laid out on left or right side of the screen. And the desired new - // location is on the other side. Calculate x translation value required to shift - // bubble bar from one side to the other. - final float shift = getDistanceFromOtherSide(); - if (location.isOnLeft(isLayoutRtl())) { - // New location is on the left, shift left - // before -> |......ooo.| after -> |.ooo......| - dragEndTranslation.x = -shift; - } else { - // New location is on the right, shift right - // before -> |.ooo......| after -> |......ooo.| - dragEndTranslation.x = shift; + float dragEndTranslationX = initialTranslation.x; + if (getBubbleBarLocation().isOnLeft(isLayoutRtl()) != location.isOnLeft(isLayoutRtl())) { + // Bubble bar is laid out on left or right side of the screen. And the desired new + // location is on the other side. Calculate x translation value required to shift + // bubble bar from one side to the other. + final float shift = getDistanceFromOtherSide(); + if (location.isOnLeft(isLayoutRtl())) { + // New location is on the left, shift left + // before -> |......ooo.| after -> |.ooo......| + dragEndTranslationX = -shift; + } else { + // New location is on the right, shift right + // before -> |.ooo......| after -> |......ooo.| + dragEndTranslationX = shift; + } } - return dragEndTranslation; + return new PointF(dragEndTranslationX, mController.getBubbleBarTranslationY()); } /** @@ -833,6 +836,10 @@ public class BubbleBarView extends FrameLayout { mUpdateSelectedBubbleAfterCollapse = updateSelectedBubbleAfterCollapse; } + void setController(Controller controller) { + mController = controller; + } + /** * Sets which bubble view should be shown as selected. */ @@ -1009,7 +1016,10 @@ public class BubbleBarView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mIsBarExpanded && !mIsAnimatingNewBubble) { + if (mIsAnimatingNewBubble) { + mController.onBubbleBarTouchedWhileAnimating(); + } + if (!mIsBarExpanded) { // When the bar is collapsed, all taps on it should expand it. return true; } @@ -1020,4 +1030,14 @@ public class BubbleBarView extends FrameLayout { public boolean isAnimatingNewBubble() { return mIsAnimatingNewBubble; } + + /** Interface for BubbleBarView to communicate with its controller. */ + interface Controller { + + /** Returns the translation Y that the bubble bar should have. */ + float getBubbleBarTranslationY(); + + /** Notifies the controller that the bubble bar was touched while it was animating. */ + void onBubbleBarTouchedWhileAnimating(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index ac02f1fe5e..7dbe8089d8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -132,6 +132,17 @@ public class BubbleBarViewController { }); mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); + mBarView.setController(new BubbleBarView.Controller() { + @Override + public float getBubbleBarTranslationY() { + return mBubbleStashController.getBubbleBarTranslationY(); + } + + @Override + public void onBubbleBarTouchedWhileAnimating() { + BubbleBarViewController.this.onBubbleBarTouchedWhileAnimating(); + } + }); } private void onBubbleClicked(View v) { @@ -140,14 +151,6 @@ public class BubbleBarViewController { Log.e(TAG, "bubble click listener, bubble was null"); } - if (mBarView.isAnimatingNewBubble()) { - mBubbleBarViewAnimator.onBubbleClickedWhileAnimating(); - mBubbleStashController.showBubbleBarImmediate(); - setExpanded(true); - mBubbleBarController.showAndSelectBubble(bubble); - return; - } - final String currentlySelected = mBubbleBarController.getSelectedBubbleKey(); if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) { // Tapping the currently selected bubble while expanded collapses the view. @@ -158,6 +161,11 @@ public class BubbleBarViewController { } } + private void onBubbleBarTouchedWhileAnimating() { + mBubbleBarViewAnimator.onBubbleBarTouchedWhileAnimating(); + mBubbleStashController.onNewBubbleAnimationInterrupted(false, mBarView.getTranslationY()); + } + private void onBubbleBarClicked() { if (mShouldShowEducation) { mShouldShowEducation = false; @@ -169,6 +177,10 @@ public class BubbleBarViewController { // Show user education relative to the reference point mSystemUiProxy.showUserEducation(position); } else { + // ensure that the bubble bar has the correct translation. we may have just interrupted + // the animation by touching the bubble bar. + mBubbleBarTranslationY.animateToValue(mBubbleStashController.getBubbleBarTranslationY()) + .start(); setExpanded(true); } } @@ -505,10 +517,17 @@ public class BubbleBarViewController { /** * Notifies {@link BubbleBarView} that drag and all animations are finished. */ - public void onDragEnd() { + public void onDragBubbleEnded() { mBarView.setDraggedBubble(null); } + /** Notifies that dragging the bubble bar ended. */ + public void onDragBubbleBarEnded() { + // we may have changed the bubble bar translation Y value from the value it had at the + // beginning of the drag, so update the translation Y animator state + mBubbleBarTranslationY.updateValue(mBarView.getTranslationY()); + } + /** * Get translation for bubble bar when drag is released. * @@ -516,9 +535,6 @@ public class BubbleBarViewController { */ public PointF getBubbleBarDragReleaseTranslation(PointF initialTranslation, BubbleBarLocation location) { - if (location == mBarView.getBubbleBarLocation()) { - return initialTranslation; - } return mBarView.getBubbleBarDragReleaseTranslation(initialTranslation, location); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java index 1764f757cc..7386a967f7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -122,7 +122,7 @@ public class BubbleDragController { @Override void onDragEnd() { mBubbleBarController.updateBubbleBarLocation(mReleasedLocation); - mBubbleBarViewController.onDragEnd(); + mBubbleBarViewController.onDragBubbleEnded(); mBubblePinController.setListener(null); } @@ -193,6 +193,7 @@ public class BubbleDragController { bubbleBarView.setIsDragging(false); // Restoring the initial pivot for the bubble bar view bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y); + mBubbleBarViewController.onDragBubbleBarEnded(); mBubbleBarPinController.setListener(null); } 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 be935d8b54..d88e272332 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt @@ -42,6 +42,8 @@ constructor( const val FLYOUT_DELAY_MS: Long = 2500 /** 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 minimum alpha value to make the bubble bar touchable. */ + const val MIN_ALPHA_FOR_TOUCHABLE = 0.5f } /** Wrapper around the animating bubble with its show and hide animations. */ @@ -167,6 +169,9 @@ constructor( bubbleBarView.scaleY = BUBBLE_ANIMATION_INITIAL_SCALE_Y + (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction + if (bubbleBarView.alpha > MIN_ALPHA_FOR_TOUCHABLE) { + bubbleStashController.updateTaskbarTouchRegion() + } } } else -> { @@ -176,6 +181,7 @@ constructor( bubbleBarView.alpha = 1f bubbleBarView.scaleY = 1f bubbleBarView.translationY = ty - offset + bubbleStashController.updateTaskbarTouchRegion() } } } @@ -233,6 +239,9 @@ constructor( (totalTranslationY - ty) / (totalTranslationY - stashedHandleTranslationY) bubbleBarView.alpha = 1 - fraction bubbleBarView.scaleY = 1 - (1 - BUBBLE_ANIMATION_INITIAL_SCALE_Y) * fraction + if (bubbleBarView.alpha > MIN_ALPHA_FOR_TOUCHABLE) { + bubbleStashController.updateTaskbarTouchRegion() + } } ty <= 0 -> { // this is the second part of the animation. make the bubble bar invisible and @@ -279,6 +288,7 @@ constructor( animatingBubble = null bubbleStashController.showBubbleBarImmediate() bubbleBarView.onAnimatingBubbleCompleted() + bubbleStashController.updateTaskbarTouchRegion() } } animatingBubble = AnimatingBubble(bubbleView, showAnimation, hideAnimation) @@ -298,6 +308,7 @@ constructor( val animator = PhysicsAnimator.getInstance(bubbleBarView) animator.setDefaultSpringConfig(springConfig) animator.spring(DynamicAnimation.TRANSLATION_Y, bubbleStashController.bubbleBarTranslationY) + animator.addUpdateListener { _, _ -> bubbleStashController.updateTaskbarTouchRegion() } animator.addEndListener { _, _, _, _, _, _, _ -> // the bubble bar is now fully settled in. update taskbar touch region so it's touchable bubbleStashController.updateTaskbarTouchRegion() @@ -305,8 +316,10 @@ constructor( animator.start() } - /** Handles clicking on the animating bubble while the animation is still playing. */ - fun onBubbleClickedWhileAnimating() { + /** Handles touching the animating bubble bar. */ + fun onBubbleBarTouchedWhileAnimating() { + PhysicsAnimator.getInstance(bubbleBarView).cancelIfRunning() + bubbleStashController.stashedHandlePhysicsAnimator.cancelIfRunning() val hideAnimation = animatingBubble?.hideAnimation ?: return scheduler.cancel(hideAnimation) bubbleBarView.onAnimatingBubbleCompleted() @@ -327,4 +340,8 @@ constructor( bubbleBarView.translationY ) } + + private fun PhysicsAnimator.cancelIfRunning() { + if (isRunning()) cancel() + } } 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 2bcfa3f224..7ad432b071 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 @@ -44,6 +44,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any +import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -139,12 +140,12 @@ class BubbleBarViewAnimatorTest { assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR) assertThat(bubbleBarView.isAnimatingNewBubble).isTrue() - verify(bubbleStashController).updateTaskbarTouchRegion() + verify(bubbleStashController, atLeastOnce()).updateTaskbarTouchRegion() // verify the hide bubble animation is pending assertThat(animatorScheduler.delayedBlock).isNotNull() - animator.onBubbleClickedWhileAnimating() + animator.onBubbleBarTouchedWhileAnimating() assertThat(animatorScheduler.delayedBlock).isNull() assertThat(bubbleBarView.alpha).isEqualTo(1)