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

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

Merge "Update expanded view "drag to dismiss" animation" into main

parents 58404632 07a19bf7
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -266,6 +266,10 @@
    <dimen name="bubble_bar_manage_menu_item_height">52dp</dimen>
    <!-- Size of the icons in the bubble bar manage menu. -->
    <dimen name="bubble_bar_manage_menu_item_icon_size">20dp</dimen>
    <!-- Corner radius for expanded view when bubble bar is used -->
    <dimen name="bubble_bar_expanded_view_corner_radius">16dp</dimen>
    <!-- Corner radius for expanded view while it is being dragged -->
    <dimen name="bubble_bar_expanded_view_corner_radius_dragged">28dp</dimen>

    <!-- Bottom and end margin for compat buttons. -->
    <dimen name="compat_button_margin">24dp</dimen>
+138 −68
Original line number Diff line number Diff line
@@ -15,17 +15,28 @@
 */
package com.android.wm.shell.bubbles.bar;

import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_X;
import static android.view.View.TRANSLATION_Y;
import static android.view.View.VISIBLE;
import static android.view.View.X;
import static android.view.View.Y;

import static com.android.wm.shell.animation.Interpolators.EMPHASIZED;
import static com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.CORNER_RADIUS;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;
@@ -48,15 +59,16 @@ public class BubbleBarAnimationHelper {
    private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
    private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f;
    private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
    private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100;
    private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
    private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 400;
    private static final int EXPANDED_VIEW_ANIMATE_TO_REST_DURATION = 400;
    private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;
    private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 150;
    private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 400;
    /**
     * Additional scale applied to expanded view when it is positioned inside a magnetic target.
     */
    private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.6f;
    private static final float EXPANDED_VIEW_DRAG_SCALE = 0.5f;
    private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.2f;
    private static final float EXPANDED_VIEW_DRAG_SCALE = 0.4f;
    private static final float DISMISS_VIEW_SCALE = 1.25f;

    /** Spring config for the expanded view scale-in animation. */
    private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -72,6 +84,9 @@ public class BubbleBarAnimationHelper {
    /** Animator for animating the expanded view's alpha (including the TaskView inside it). */
    private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);

    @Nullable
    private Animator mRunningDragAnimator;

    private final Context mContext;
    private final BubbleBarLayerView mLayerView;
    private final BubblePositioner mPositioner;
@@ -232,14 +247,18 @@ public class BubbleBarAnimationHelper {
            Log.w(TAG, "Trying to animate start drag without a bubble");
            return;
        }
        bbev.setPivotX(bbev.getWidth() / 2f);
        bbev.setPivotY(0f);
        bbev.animate()
                .scaleX(EXPANDED_VIEW_DRAG_SCALE)
                .scaleY(EXPANDED_VIEW_DRAG_SCALE)
                .setInterpolator(Interpolators.EMPHASIZED)
                .setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION)
                .start();
        setDragPivot(bbev);
        AnimatorSet animatorSet = new AnimatorSet();
        // Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
        final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(bbev, SCALE_X, EXPANDED_VIEW_DRAG_SCALE),
                ObjectAnimator.ofFloat(bbev, SCALE_Y, EXPANDED_VIEW_DRAG_SCALE),
                ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, cornerRadius)
        );
        animatorSet.setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION).setInterpolator(EMPHASIZED);
        animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
        startNewDragAnimation(animatorSet);
    }

    /**
@@ -258,6 +277,7 @@ public class BubbleBarAnimationHelper {
        int[] location = bbev.getLocationOnScreen();
        int diffFromBottom = mPositioner.getScreenRect().bottom - location[1];

        cancelAnimations();
        bbev.animate()
                // 2x distance from bottom so the view flies out
                .translationYBy(diffFromBottom * 2)
@@ -276,19 +296,24 @@ public class BubbleBarAnimationHelper {
            return;
        }
        Point restPoint = getExpandedViewRestPosition(getExpandedViewSize());
        bbev.animate()
                .x(restPoint.x)
                .y(restPoint.y)
                .scaleX(1f)
                .scaleY(1f)
                .setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION)
                .setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
                .withStartAction(() -> bbev.setAnimating(true))
                .withEndAction(() -> {
                    bbev.setAnimating(false);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(bbev, X, restPoint.x),
                ObjectAnimator.ofFloat(bbev, Y, restPoint.y),
                ObjectAnimator.ofFloat(bbev, SCALE_X, 1f),
                ObjectAnimator.ofFloat(bbev, SCALE_Y, 1f),
                ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, bbev.getRestingCornerRadius())
        );
        animatorSet.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION).setInterpolator(EMPHASIZED);
        animatorSet.addListener(new DragAnimatorListenerAdapter(bbev) {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                bbev.resetPivot();
                })
                .start();
            }
        });
        startNewDragAnimation(animatorSet);
    }

    /**
@@ -304,17 +329,7 @@ public class BubbleBarAnimationHelper {
            return;
        }

        // Calculate scale of expanded view so it fits inside the magnetic target
        float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
        View targetView = target.getTargetView();
        float targetMaxSide = Math.max(targetView.getWidth(), targetView.getHeight());
        // Reduce target size to have some padding between the target and expanded view
        targetMaxSide *= EXPANDED_VIEW_IN_TARGET_SCALE;
        float scaleInTarget = targetMaxSide / bbevMaxSide;

        // Scale around the top center of the expanded view. Same as when dragging.
        bbev.setPivotX(bbev.getWidth() / 2f);
        bbev.setPivotY(0);
        setDragPivot(bbev);

        // When the view animates into the target, it is scaled down with the pivot at center top.
        // Find the point on the view that would be the center of the view at its final scale.
@@ -330,13 +345,13 @@ public class BubbleBarAnimationHelper {
        // Get scaled width of the view and adjust mTmpLocation so that point on x-axis is at the
        // center of the view at its current size.
        float currentWidth = bbev.getWidth() * bbev.getScaleX();
        mTmpLocation[0] += currentWidth / 2;
        mTmpLocation[0] += (int) (currentWidth / 2f);
        // Since pivotY is at the top of the view, at final scale, top coordinate of the view
        // remains the same.
        // Get height of the view at final scale and adjust mTmpLocation so that point on y-axis is
        // moved down by half of the height at final scale.
        float targetHeight = bbev.getHeight() * scaleInTarget;
        mTmpLocation[1] += targetHeight / 2;
        float targetHeight = bbev.getHeight() * EXPANDED_VIEW_IN_TARGET_SCALE;
        mTmpLocation[1] += (int) (targetHeight / 2f);
        // mTmpLocation is now set to the point on the view that will be the center of the view once
        // scale is applied.

@@ -344,41 +359,61 @@ public class BubbleBarAnimationHelper {
        float xDiff = target.getCenterOnScreen().x - mTmpLocation[0];
        float yDiff = target.getCenterOnScreen().y - mTmpLocation[1];

        bbev.animate()
                .translationX(bbev.getTranslationX() + xDiff)
                .translationY(bbev.getTranslationY() + yDiff)
                .scaleX(scaleInTarget)
                .scaleY(scaleInTarget)
                .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
                .setInterpolator(Interpolators.EMPHASIZED)
                .withStartAction(() -> bbev.setAnimating(true))
                .withEndAction(() -> {
                    bbev.setAnimating(false);
        // Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
        final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_IN_TARGET_SCALE;

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                // Move expanded view to the center of dismiss view
                ObjectAnimator.ofFloat(bbev, TRANSLATION_X, bbev.getTranslationX() + xDiff),
                ObjectAnimator.ofFloat(bbev, TRANSLATION_Y, bbev.getTranslationY() + yDiff),
                // Scale expanded view down
                ObjectAnimator.ofFloat(bbev, SCALE_X, EXPANDED_VIEW_IN_TARGET_SCALE),
                ObjectAnimator.ofFloat(bbev, SCALE_Y, EXPANDED_VIEW_IN_TARGET_SCALE),
                // Update corner radius for expanded view
                ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, cornerRadius),
                // Scale dismiss view up
                ObjectAnimator.ofFloat(target.getTargetView(), SCALE_X, DISMISS_VIEW_SCALE),
                ObjectAnimator.ofFloat(target.getTargetView(), SCALE_Y, DISMISS_VIEW_SCALE)
        );
        animatorSet.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION).setInterpolator(
                EMPHASIZED_DECELERATE);
        animatorSet.addListener(new DragAnimatorListenerAdapter(bbev) {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (endRunnable != null) {
                    endRunnable.run();
                }
                })
                .start();
            }
        });
        startNewDragAnimation(animatorSet);
    }

    /**
     * Animate currently expanded view when it is released from dismiss view
     */
    public void animateUnstuckFromDismissView() {
        BubbleBarExpandedView expandedView = getExpandedView();
        if (expandedView == null) {
    public void animateUnstuckFromDismissView(MagneticTarget target) {
        BubbleBarExpandedView bbev = getExpandedView();
        if (bbev == null) {
            Log.w(TAG, "Trying to unsnap the expanded view from dismiss without a bubble");
            return;
        }
        expandedView
                .animate()
                .scaleX(EXPANDED_VIEW_DRAG_SCALE)
                .scaleY(EXPANDED_VIEW_DRAG_SCALE)
                .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
                .setInterpolator(Interpolators.EMPHASIZED)
                .withStartAction(() -> expandedView.setAnimating(true))
                .withEndAction(() -> expandedView.setAnimating(false))
                .start();
        setDragPivot(bbev);
        // Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
        final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(bbev, SCALE_X, EXPANDED_VIEW_DRAG_SCALE),
                ObjectAnimator.ofFloat(bbev, SCALE_Y, EXPANDED_VIEW_DRAG_SCALE),
                ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, cornerRadius),
                ObjectAnimator.ofFloat(target.getTargetView(), SCALE_X, 1f),
                ObjectAnimator.ofFloat(target.getTargetView(), SCALE_Y, 1f)
        );
        animatorSet.setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION).setInterpolator(
                EMPHASIZED_DECELERATE);
        animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
        startNewDragAnimation(animatorSet);
    }

    /**
@@ -391,6 +426,10 @@ public class BubbleBarAnimationHelper {
        if (bbev != null) {
            bbev.animate().cancel();
        }
        if (mRunningDragAnimator != null) {
            mRunningDragAnimator.cancel();
            mRunningDragAnimator = null;
        }
    }

    private @Nullable BubbleBarExpandedView getExpandedView() {
@@ -438,4 +477,35 @@ public class BubbleBarAnimationHelper {
        final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
        return new Size(width, height);
    }

    private void startNewDragAnimation(Animator animator) {
        cancelAnimations();
        mRunningDragAnimator = animator;
        animator.start();
    }

    private static void setDragPivot(BubbleBarExpandedView bbev) {
        bbev.setPivotX(bbev.getWidth() / 2f);
        bbev.setPivotY(0f);
    }

    private class DragAnimatorListenerAdapter extends AnimatorListenerAdapter {

        private final BubbleBarExpandedView mBubbleBarExpandedView;

        DragAnimatorListenerAdapter(BubbleBarExpandedView bbev) {
            mBubbleBarExpandedView = bbev;
        }

        @Override
        public void onAnimationStart(Animator animation) {
            mBubbleBarExpandedView.setAnimating(true);
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            mBubbleBarExpandedView.setAnimating(false);
            mRunningDragAnimator = null;
        }
    }
}
+63 −13
Original line number Diff line number Diff line
@@ -27,13 +27,13 @@ import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;

import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
@@ -61,6 +61,23 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
        void onBackPressed();
    }

    /**
     * A property wrapper around corner radius for the expanded view, handled by
     * {@link #setCornerRadius(float)} and {@link #getCornerRadius()} methods.
     */
    public static final FloatProperty<BubbleBarExpandedView> CORNER_RADIUS = new FloatProperty<>(
            "cornerRadius") {
        @Override
        public void setValue(BubbleBarExpandedView bbev, float radius) {
            bbev.setCornerRadius(radius);
        }

        @Override
        public Float get(BubbleBarExpandedView bbev) {
            return bbev.getCornerRadius();
        }
    };

    private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
    private static final int INVALID_TASK_ID = -1;

@@ -78,7 +95,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
    private int mCaptionHeight;

    private int mBackgroundColor;
    private float mCornerRadius = 0f;
    /** Corner radius used when view is resting */
    private float mRestingCornerRadius = 0f;
    /** Corner radius applied while dragging */
    private float mDraggedCornerRadius = 0f;
    /** Current corner radius */
    private float mCurrentCornerRadius = 0f;

    /**
     * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If
@@ -118,7 +140,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
        setOutlineProvider(new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius);
            }
        });
    }
@@ -155,7 +177,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
                    new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
            addView(mTaskView, lp);
            mTaskView.setEnableSurfaceClipping(true);
            mTaskView.setCornerRadius(mCornerRadius);
            mTaskView.setCornerRadius(mCurrentCornerRadius);
            mTaskView.setVisibility(VISIBLE);

            // Handle view needs to draw on top of task view.
@@ -198,22 +220,24 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
    // TODO (b/275087636): call this when theme/config changes
    /** Updates the view based on the current theme. */
    public void applyThemeAttrs() {
        boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
                mContext.getResources());
        mRestingCornerRadius = getResources().getDimensionPixelSize(
                R.dimen.bubble_bar_expanded_view_corner_radius
        );
        mDraggedCornerRadius = getResources().getDimensionPixelSize(
                R.dimen.bubble_bar_expanded_view_corner_radius_dragged
        );

        mCurrentCornerRadius = mRestingCornerRadius;

        final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
                android.R.attr.dialogCornerRadius,
                android.R.attr.colorBackgroundFloating});
        mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
        mCornerRadius = mCornerRadius / 2f;
        mBackgroundColor = ta.getColor(1, Color.WHITE);

        mBackgroundColor = ta.getColor(0, Color.WHITE);
        ta.recycle();

        mCaptionHeight = getResources().getDimensionPixelSize(
                R.dimen.bubble_bar_expanded_view_caption_height);

        if (mTaskView != null) {
            mTaskView.setCornerRadius(mCornerRadius);
            mTaskView.setCornerRadius(mCurrentCornerRadius);
            updateHandleColor(true /* animated */);
        }
    }
@@ -396,4 +420,30 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
    public boolean isAnimating() {
        return mIsAnimating;
    }

    /** @return corner radius that should be applied while view is in rest */
    public float getRestingCornerRadius() {
        return mRestingCornerRadius;
    }

    /** @return corner radius that should be applied while view is being dragged */
    public float getDraggedCornerRadius() {
        return mDraggedCornerRadius;
    }

    /** @return current corner radius */
    public float getCornerRadius() {
        return mCurrentCornerRadius;
    }

    /** Update corner radius */
    public void setCornerRadius(float cornerRadius) {
        if (mCurrentCornerRadius != cornerRadius) {
            mCurrentCornerRadius = cornerRadius;
            if (mTaskView != null) {
                mTaskView.setCornerRadius(cornerRadius);
            }
            invalidateOutline();
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ class BubbleBarExpandedViewDragController(
                wasFlungOut: Boolean
        ) {
            isStuckToDismiss = false
            animationHelper.animateUnstuckFromDismissView()
            animationHelper.animateUnstuckFromDismissView(target)
        }

        override fun onReleasedInTarget(