Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af0ba3e8 authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge "Implement API to animate bubble bar position" into main

parents 2737373f d7fe32ab
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -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
    //
+78 −79
Original line number Diff line number Diff line
@@ -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.
     * <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 @@ 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;
+11 −2
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+5 −5
Original line number Diff line number Diff line
@@ -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<? 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();
                        }
+31 −6
Original line number Diff line number Diff line
@@ -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();