diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index d08015e0ca..f3ac1e44ba 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -174,6 +174,8 @@ public class BubbleBarView extends FrameLayout { private BubbleView mDraggedBubbleView; private float mAlphaDuringDrag = 1f; + private Controller mController; + private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED; public BubbleBarView(Context context) { @@ -366,22 +368,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()); } /** @@ -791,6 +794,10 @@ public class BubbleBarView extends FrameLayout { mUpdateSelectedBubbleAfterCollapse = updateSelectedBubbleAfterCollapse; } + void setController(Controller controller) { + mController = controller; + } + /** * Sets which bubble view should be shown as selected. */ @@ -963,7 +970,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; } @@ -974,4 +984,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 cd8eaf9a5e..fbdb2ed4e3 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 15de1b8e51..f4b393add3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -124,7 +124,7 @@ public class BubbleDragController { @Override void onDragEnd() { mBubbleBarController.updateBubbleBarLocation(mReleasedLocation); - mBubbleBarViewController.onDragEnd(); + mBubbleBarViewController.onDragBubbleEnded(); mBubblePinController.setListener(null); } @@ -192,6 +192,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)