From d7fe32ab8e04ecb76394a1e168a9dad2fca7fd29 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 9 Apr 2024 16:24:41 -0700 Subject: [PATCH] Implement API to animate bubble bar position Create a new API to animate bubble bar position. Animating it will not update its laid out location. To update bubble bar laid out location, BubbleBarUpdate can be used. Applying a location change from BubbleBarUpdate no longer animates the change. Bug: 330585402 Flag: ACONFIG com.android.wm.shell.enable_bubble_bar DEVELOPMENT Test: long press bubble bar and drag it to left and right Change-Id: I2572da4c063fc8e07cf07c4303778d343baa4ec4 --- .../taskbar/bubbles/BubbleBarController.java | 16 +- .../taskbar/bubbles/BubbleBarView.java | 157 +++++++++--------- .../bubbles/BubbleBarViewController.java | 13 +- .../taskbar/bubbles/BubbleDragAnimator.java | 10 +- .../taskbar/bubbles/BubbleDragController.java | 37 ++++- 5 files changed, 135 insertions(+), 98 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index 981c9f929c..66e530294c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -419,9 +419,7 @@ public class BubbleBarController extends IBubblesListener.Stub { } if (update.bubbleBarLocation != null) { if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) { - // Animate when receiving updates. Skip it if we received the initial state. - boolean animate = !update.initialState; - updateBubbleBarLocationInternal(update.bubbleBarLocation, animate); + updateBubbleBarLocationInternal(update.bubbleBarLocation); } } } @@ -483,15 +481,21 @@ public class BubbleBarController extends IBubblesListener.Stub { * Updates the value locally in Launcher and in WMShell. */ public void updateBubbleBarLocation(BubbleBarLocation location) { - updateBubbleBarLocationInternal(location, false /* animate */); + updateBubbleBarLocationInternal(location); mSystemUiProxy.setBubbleBarLocation(location); } - private void updateBubbleBarLocationInternal(BubbleBarLocation location, boolean animate) { - mBubbleBarViewController.setBubbleBarLocation(location, animate); + private void updateBubbleBarLocationInternal(BubbleBarLocation location) { + mBubbleBarViewController.setBubbleBarLocation(location); mBubbleStashController.setBubbleBarLocation(location); } + @Override + public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + mMainExecutor.execute( + () -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation)); + } + // // Loading data for the bubbles // diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 8c6cbc9230..60e8abe9ac 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -153,8 +153,6 @@ public class BubbleBarView extends FrameLayout { private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED; - private boolean mLocationChangePending; - public BubbleBarView(Context context) { this(context, null); } @@ -188,7 +186,7 @@ public class BubbleBarView extends FrameLayout { mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS); mWidthAnimator.addUpdateListener(animation -> { - updateChildrenRenderNodeProperties(); + updateChildrenRenderNodeProperties(mBubbleBarLocation); invalidate(); }); mWidthAnimator.addListener(new Animator.AnimatorListener() { @@ -262,7 +260,7 @@ public class BubbleBarView extends FrameLayout { setPivotY(mRelativePivotY * getHeight()); // Position the views - updateChildrenRenderNodeProperties(); + updateChildrenRenderNodeProperties(mBubbleBarLocation); } @Override @@ -278,7 +276,6 @@ public class BubbleBarView extends FrameLayout { @SuppressLint("RtlHardcoded") private void onBubbleBarLocationChanged() { - mLocationChangePending = false; final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl()); mBubbleBarBackground.setAnchorLeft(onLeft); mRelativePivotX = onLeft ? 0f : 1f; @@ -299,11 +296,18 @@ public class BubbleBarView extends FrameLayout { /** * Update {@link BubbleBarLocation} */ - public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) { - if (animate) { - animateToBubbleBarLocation(bubbleBarLocation); - } else { - setBubbleBarLocationInternal(bubbleBarLocation); + public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + if (mBubbleBarLocationAnimator != null) { + mBubbleBarLocationAnimator.removeAllListeners(); + mBubbleBarLocationAnimator.cancel(); + mBubbleBarLocationAnimator = null; + } + setTranslationX(0f); + setAlpha(1f); + if (bubbleBarLocation != mBubbleBarLocation) { + mBubbleBarLocation = bubbleBarLocation; + onBubbleBarLocationChanged(); + invalidate(); } } @@ -316,51 +320,37 @@ public class BubbleBarView extends FrameLayout { } mDragging = dragging; setElevation(dragging ? mDragElevation : mBubbleElevation); - if (!dragging && mLocationChangePending) { - // During drag finish animation we may update the translation x value to shift the - // bubble to the new drop target. Clear the translation here. - setTranslationX(0f); - onBubbleBarLocationChanged(); - } } /** - * Adjust resting position for the bubble bar while it is being dragged. - *

- * Bubble bar is laid out on left or right side of the screen. When it is being dragged to - * the opposite side, the resting position should be on that side. Calculate any additional - * translation that may be required to move the bubble bar to the new side. + * Get translation for bubble bar when drag is released and it needs to animate back to the + * resting position. + * Resting position is based on the supplied location. If the supplied location is different + * from the internal location that was used to lay out the bubble bar, translation values are + * calculated to position the bar at the desired location. * - * @param restingPosition relative resting position of the bubble bar from the laid out position + * @param initialTranslation initial bubble bar translation at the start of drag + * @param location desired location of the bubble bar when drag is released + * @return point with x and y values representing translation on x and y-axis */ - @SuppressLint("RtlHardcoded") - void adjustRelativeRestingPosition(PointF restingPosition) { - final boolean locationOnLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl()); - // Bubble bar is placed left or right with gravity. Check where it is currently. - final int absoluteGravity = Gravity.getAbsoluteGravity( - ((LayoutParams) getLayoutParams()).gravity, getLayoutDirection()); - final boolean gravityOnLeft = - (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT; - - // Bubble bar is pinned to the same side per gravity and the desired location. - // Resting translation does not need to be adjusted. - if (locationOnLeft == gravityOnLeft) { - return; - } - + 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 the + // location is on the other side. Calculate x translation value required to shift // bubble bar from one side to the other. - float x = getDistanceFromOtherSide(); - if (locationOnLeft) { + final float shift = getDistanceFromOtherSide(); + if (location.isOnLeft(isLayoutRtl())) { // New location is on the left, shift left // before -> |......ooo.| after -> |.ooo......| - restingPosition.x = -x; + dragEndTranslation.x = -shift; } else { // New location is on the right, shift right // before -> |.ooo......| after -> |......ooo.| - restingPosition.x = x; + dragEndTranslation.x = shift; } + return dragEndTranslation; } private float getDistanceFromOtherSide() { @@ -374,49 +364,40 @@ public class BubbleBarView extends FrameLayout { return (float) (displayWidth - getWidth() - margin); } - private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) { - if (bubbleBarLocation != mBubbleBarLocation) { - mBubbleBarLocation = bubbleBarLocation; - if (mDragging) { - mLocationChangePending = true; - } else { - onBubbleBarLocationChanged(); - invalidate(); - } - } - } - - private void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { - if (bubbleBarLocation == mBubbleBarLocation) { - // nothing to do, already at expected location - return; - } + /** + * Animate bubble bar to the given location transiently. Does not modify the layout or the value + * returned by {@link #getBubbleBarLocation()}. + */ + public void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { if (mBubbleBarLocationAnimator != null && mBubbleBarLocationAnimator.isRunning()) { + mBubbleBarLocationAnimator.removeAllListeners(); mBubbleBarLocationAnimator.cancel(); } // Location animation uses two separate animators. // First animator hides the bar. - // After it completes, location update is sent to layout the bar in the new location. + // After it completes, bubble positions in the bar and arrow position is updated. // Second animator is started to show the bar. - mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator(); + mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator(bubbleBarLocation); mBubbleBarLocationAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - // Bubble bar is not visible, update the location - setBubbleBarLocationInternal(bubbleBarLocation); + updateChildrenRenderNodeProperties(bubbleBarLocation); + mBubbleBarBackground.setAnchorLeft(bubbleBarLocation.isOnLeft(isLayoutRtl())); + // Animate it in - mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator(); + mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator(bubbleBarLocation); mBubbleBarLocationAnimator.start(); } }); mBubbleBarLocationAnimator.start(); } - private AnimatorSet getLocationUpdateFadeOutAnimator() { + private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation bubbleBarLocation) { final float shift = getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT; - final float tx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift; + final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); + final float tx = getTranslationX() + (onLeft ? shift : -shift); ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X, tx) .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS); @@ -431,14 +412,31 @@ public class BubbleBarView extends FrameLayout { return animatorSet; } - private Animator getLocationUpdateFadeInAnimator() { + private Animator getLocationUpdateFadeInAnimator(BubbleBarLocation animatedLocation) { final float shift = getResources().getDisplayMetrics().widthPixels * FADE_IN_ANIM_POSITION_SHIFT; - final float startTx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift; + + final boolean onLeft = animatedLocation.isOnLeft(isLayoutRtl()); + final float startTx; + final float finalTx; + if (animatedLocation == mBubbleBarLocation) { + // Animated location matches layout location. + finalTx = 0; + } else { + // We are animating in to a transient location, need to move the bar accordingly. + finalTx = getDistanceFromOtherSide() * (onLeft ? -1 : 1); + } + if (onLeft) { + // Bar will be shown on the left side. Start point is shifted right. + startTx = finalTx + shift; + } else { + // Bar will be shown on the right side. Start point is shifted left. + startTx = finalTx - shift; + } ValueAnimator positionAnim = new SpringAnimationBuilder(getContext()) .setStartValue(startTx) - .setEndValue(0) + .setEndValue(finalTx) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS) .build(this, VIEW_TRANSLATE_X); @@ -547,7 +545,7 @@ public class BubbleBarView extends FrameLayout { * Updates the z order, positions, and badge visibility of the bubble views in the bar based * on the expanded state. */ - private void updateChildrenRenderNodeProperties() { + private void updateChildrenRenderNodeProperties(BubbleBarLocation bubbleBarLocation) { final float widthState = (float) mWidthAnimator.getAnimatedValue(); final float currentWidth = getWidth(); final float expandedWidth = expandedWidth(); @@ -555,7 +553,7 @@ public class BubbleBarView extends FrameLayout { int bubbleCount = getChildCount(); final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; final boolean animate = getVisibility() == VISIBLE; - final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl()); + final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl()); // elevation state is opposite to widthState - when expanded all icons are flat float elevationState = (1 - widthState); for (int i = 0; i < bubbleCount; i++) { @@ -613,8 +611,9 @@ public class BubbleBarView extends FrameLayout { } // update the arrow position - final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed(); - final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded(); + final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed( + bubbleBarLocation); + final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded(bubbleBarLocation); final float interpolatedWidth = widthState * (expandedWidth - collapsedWidth) + collapsedWidth; final float arrowPosition; @@ -661,7 +660,7 @@ public class BubbleBarView extends FrameLayout { addViewInLayout(child, i, child.getLayoutParams()); } } - updateChildrenRenderNodeProperties(); + updateChildrenRenderNodeProperties(mBubbleBarLocation); } } @@ -702,7 +701,7 @@ public class BubbleBarView extends FrameLayout { return; } // Find the center of the bubble when it's expanded, set the arrow position to it. - final float tx = arrowPositionForSelectedWhenExpanded(); + final float tx = arrowPositionForSelectedWhenExpanded(mBubbleBarLocation); final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX(); if (tx == currentArrowPosition) { // arrow position remains unchanged @@ -727,10 +726,10 @@ public class BubbleBarView extends FrameLayout { } } - private float arrowPositionForSelectedWhenExpanded() { + private float arrowPositionForSelectedWhenExpanded(BubbleBarLocation bubbleBarLocation) { final int index = indexOfChild(mSelectedBubbleView); final int bubblePosition; - if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) { + if (bubbleBarLocation.isOnLeft(isLayoutRtl())) { // Bubble positions are reversed. First bubble is on the right. bubblePosition = getChildCount() - index - 1; } else { @@ -740,10 +739,10 @@ public class BubbleBarView extends FrameLayout { + mIconSize / 2f; } - private float arrowPositionForSelectedWhenCollapsed() { + private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) { final int index = indexOfChild(mSelectedBubbleView); final int bubblePosition; - if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) { + if (bubbleBarLocation.isOnLeft(isLayoutRtl())) { // Bubble positions are reversed. First bubble may be shifted, if there are more // bubbles than the current bubble and overflow. bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0; diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 0b927488f3..dc48a66cd0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -215,8 +215,17 @@ public class BubbleBarViewController { /** * Update bar {@link BubbleBarLocation} */ - public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) { - mBarView.setBubbleBarLocation(bubbleBarLocation, animate); + public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + mBarView.setBubbleBarLocation(bubbleBarLocation); + } + + /** + * Animate bubble bar to the given location. The location change is transient. It does not + * update the state of the bubble bar. + * To update bubble bar pinned location, use {@link #setBubbleBarLocation(BubbleBarLocation)}. + */ + public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + mBarView.animateToBubbleBarLocation(bubbleBarLocation); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java index 8b811d9579..49f114a6e2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java @@ -110,25 +110,25 @@ public class BubbleDragAnimator { /** * Animates the dragged bubble movement back to the initial position. * - * @param initialPosition the position to animate to + * @param restingPosition the position to animate to * @param velocity the initial velocity to use for the spring animation * @param endActions gets called when the animation completes or gets cancelled */ - public void animateToInitialState(@NonNull PointF initialPosition, @NonNull PointF velocity, + public void animateToRestingState(@NonNull PointF restingPosition, @NonNull PointF velocity, @Nullable Runnable endActions) { mBubbleAnimator.cancel(); mBubbleAnimator .spring(DynamicAnimation.SCALE_X, 1f) .spring(DynamicAnimation.SCALE_Y, 1f) - .spring(DynamicAnimation.TRANSLATION_X, initialPosition.x, velocity.x, + .spring(DynamicAnimation.TRANSLATION_X, restingPosition.x, velocity.x, mTranslationConfig) - .spring(DynamicAnimation.TRANSLATION_Y, initialPosition.y, velocity.y, + .spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y, mTranslationConfig) .addEndListener((View target, @NonNull FloatPropertyCompat property, boolean wasFling, boolean canceled, float finalValue, float finalVelocity, boolean allRelevantPropertyAnimationsEnded) -> { if (canceled || allRelevantPropertyAnimationsEnded) { - resetAnimatedViews(initialPosition); + resetAnimatedViews(restingPosition); if (endActions != null) { endActions.run(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java index 5ffc6d830a..d1c9da7655 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java @@ -26,6 +26,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.taskbar.TaskbarActivityContext; +import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener; +import com.android.wm.shell.common.bubbles.BubbleBarLocation; /** * Controls bubble bar drag interactions. @@ -37,6 +39,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext; */ public class BubbleDragController { private final TaskbarActivityContext mActivity; + private BubbleBarController mBubbleBarController; private BubbleBarViewController mBubbleBarViewController; private BubbleDismissController mBubbleDismissController; private BubbleBarPinController mBubbleBarPinController; @@ -51,11 +54,10 @@ public class BubbleDragController { * controllers may still be waiting for init(). */ public void init(@NonNull BubbleControllers bubbleControllers) { + mBubbleBarController = bubbleControllers.bubbleBarController; mBubbleBarViewController = bubbleControllers.bubbleBarViewController; mBubbleDismissController = bubbleControllers.bubbleDismissController; mBubbleBarPinController = bubbleControllers.bubbleBarPinController; - mBubbleBarPinController.setListener( - bubbleControllers.bubbleBarController::updateBubbleBarLocation); mBubbleDismissController.setListener( stuck -> mBubbleBarPinController.setDropTargetHidden(stuck)); } @@ -96,6 +98,17 @@ public class BubbleDragController { PointF initialRelativePivot = new PointF(); bubbleBarView.setOnTouchListener(new BubbleTouchListener() { + @Nullable + private BubbleBarLocation mReleasedLocation; + + private final LocationChangeListener mLocationChangeListener = + new LocationChangeListener() { + @Override + public void onRelease(@NonNull BubbleBarLocation location) { + mReleasedLocation = location; + } + }; + @Override protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) { if (bubbleBarView.isExpanded()) return false; @@ -104,6 +117,7 @@ public class BubbleDragController { @Override void onDragStart() { + mBubbleBarPinController.setListener(mLocationChangeListener); initialRelativePivot.set(bubbleBarView.getRelativePivotX(), bubbleBarView.getRelativePivotY()); // By default the bubble bar view pivot is in bottom right corner, while dragging @@ -134,13 +148,17 @@ public class BubbleDragController { // Restoring the initial pivot for the bubble bar view bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y); bubbleBarView.setIsDragging(false); + mBubbleBarController.updateBubbleBarLocation(mReleasedLocation); } @Override protected PointF getRestingPosition() { - PointF restingPosition = super.getRestingPosition(); - bubbleBarView.adjustRelativeRestingPosition(restingPosition); - return restingPosition; + if (mReleasedLocation == null + || mReleasedLocation == bubbleBarView.getBubbleBarLocation()) { + return getInitialPosition(); + } + return bubbleBarView.getBubbleBarDragReleaseTranslation(getInitialPosition(), + mReleasedLocation); } }); } @@ -228,6 +246,13 @@ public class BubbleDragController { protected void onDragDismiss() { } + /** + * Get the initial position of the view when drag started + */ + protected PointF getInitialPosition() { + return mViewInitialPosition; + } + /** * Get the resting position of the view when drag is released */ @@ -362,7 +387,7 @@ public class BubbleDragController { mAnimator.animateDismiss(mViewInitialPosition, onComplete); } else { onDragRelease(); - mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(), + mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(), onComplete); } mBubbleDismissController.hideDismissView();