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
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 981c9f9..66e5302 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 @@
}
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 @@
* 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 8c6cbc9..60e8abe 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 @@
private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
- private boolean mLocationChangePending;
-
public BubbleBarView(Context context) {
this(context, null);
}
@@ -188,7 +186,7 @@
mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
mWidthAnimator.addUpdateListener(animation -> {
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
invalidate();
});
mWidthAnimator.addListener(new Animator.AnimatorListener() {
@@ -262,7 +260,7 @@
setPivotY(mRelativePivotY * getHeight());
// Position the views
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
}
@Override
@@ -278,7 +276,6 @@
@SuppressLint("RtlHardcoded")
private void onBubbleBarLocationChanged() {
- mLocationChangePending = false;
final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
mBubbleBarBackground.setAnchorLeft(onLeft);
mRelativePivotX = onLeft ? 0f : 1f;
@@ -299,11 +296,18 @@
/**
* 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 @@
}
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.
- * <p>
- * 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 @@
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 @@
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 @@
* 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 @@
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 @@
}
// 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 @@
addViewInLayout(child, i, child.getLayoutParams());
}
}
- updateChildrenRenderNodeProperties();
+ updateChildrenRenderNodeProperties(mBubbleBarLocation);
}
}
@@ -702,7 +701,7 @@
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 @@
}
}
- 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 @@
+ 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 0b92748..dc48a66 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 @@
/**
* 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 8b811d9..49f114a 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 @@
/**
* 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<? super View> 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 5ffc6d8..d1c9da7 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.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 @@
*/
public class BubbleDragController {
private final TaskbarActivityContext mActivity;
+ private BubbleBarController mBubbleBarController;
private BubbleBarViewController mBubbleBarViewController;
private BubbleDismissController mBubbleDismissController;
private BubbleBarPinController mBubbleBarPinController;
@@ -51,11 +54,10 @@
* 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 @@
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 @@
@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 @@
// 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);
}
});
}
@@ -229,6 +247,13 @@
}
/**
+ * 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
*/
protected PointF getRestingPosition() {
@@ -362,7 +387,7 @@
mAnimator.animateDismiss(mViewInitialPosition, onComplete);
} else {
onDragRelease();
- mAnimator.animateToInitialState(getRestingPosition(), getCurrentVelocity(),
+ mAnimator.animateToRestingState(getRestingPosition(), getCurrentVelocity(),
onComplete);
}
mBubbleDismissController.hideDismissView();