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

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

Merge "Animate the expanded view into the dismiss target" into main

parents 8aac9ff0 4bbcb85d
Loading
Loading
Loading
Loading
+157 −12
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;
@@ -33,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget;

/**
 * Helper class to animate a {@link BubbleBarExpandedView} on a bubble.
@@ -44,6 +47,13 @@ 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;
    /**
     * Additional scale applied to expanded view when it is positioned inside a magnetic target.
     */
    private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f;
    private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300;
    private static final int EXPANDED_VIEW_DISMISS_DURATION = 250;

    /** Spring config for the expanded view scale-in animation. */
    private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -181,7 +191,8 @@ public class BubbleBarAnimationHelper {
            Log.w(TAG, "Trying to animate collapse without a bubble");
            return;
        }

        bbev.setScaleX(1f);
        bbev.setScaleY(1f);
        mExpandedViewContainerMatrix.setScaleX(1f);
        mExpandedViewContainerMatrix.setScaleY(1f);

@@ -208,12 +219,125 @@ public class BubbleBarAnimationHelper {
        mExpandedViewAlphaAnimator.reverse();
    }

    /**
     * Animates dismissal of currently expanded bubble
     *
     * @param endRunnable a runnable to run at the end of the animation
     */
    public void animateDismiss(Runnable endRunnable) {
        mIsExpanded = false;
        final BubbleBarExpandedView bbev = getExpandedView();
        if (bbev == null) {
            Log.w(TAG, "Trying to animate dismiss without a bubble");
            return;
        }

        int[] location = bbev.getLocationOnScreen();
        int diffFromBottom = mPositioner.getScreenRect().bottom - location[1];

        bbev.animate()
                // 2x distance from bottom so the view flies out
                .translationYBy(diffFromBottom * 2)
                .setDuration(EXPANDED_VIEW_DISMISS_DURATION)
                .withEndAction(endRunnable)
                .start();
    }

    /**
     * Animate current expanded bubble back to its rest position
     */
    public void animateToRestPosition() {
        BubbleBarExpandedView bbev = getExpandedView();
        if (bbev == null) {
            Log.w(TAG, "Trying to animate expanded view to rest position without a bubble");
            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))
                .start();
    }

    /**
     * Animates currently expanded bubble into the given {@link MagneticTarget}.
     *
     * @param target magnetic target to snap to
     * @param endRunnable a runnable to run at the end of the animation
     */
    public void animateIntoTarget(MagneticTarget target, @Nullable Runnable endRunnable) {
        BubbleBarExpandedView bbev = getExpandedView();
        if (bbev == null) {
            Log.w(TAG, "Trying to snap the expanded view to target without a bubble");
            return;
        }
        Point expandedViewCenter = getViewCenterOnScreen(bbev);

        // Calculate the difference between the target's center coordinates and the object's.
        // Animating the object's x/y properties by these values will center the object on top
        // of the magnetic target.
        float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x;
        float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y;

        // Calculate scale of expanded view so it fits inside the magnetic target
        float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight());
        float targetMaxSide = Math.max(target.getTargetView().getWidth(),
                target.getTargetView().getHeight());
        float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide;

        bbev.animate()
                .translationX(bbev.getTranslationX() + xDiff)
                .translationY(bbev.getTranslationY() + yDiff)
                .scaleX(scale)
                .scaleY(scale)
                .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
                .setInterpolator(Interpolators.EMPHASIZED)
                .withStartAction(() -> bbev.setAnimating(true))
                .withEndAction(() -> {
                    bbev.setAnimating(false);
                    if (endRunnable != null) {
                        endRunnable.run();
                    }
                })
                .start();
    }

    /**
     * Animate currently expanded view when it is released from dismiss view
     */
    public void animateUnstuckFromDismissView() {
        BubbleBarExpandedView expandedView = getExpandedView();
        if (expandedView == null) {
            Log.w(TAG, "Trying to unsnap the expanded view from dismiss without a bubble");
            return;
        }
        expandedView
                .animate()
                .scaleX(1f)
                .scaleY(1f)
                .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION)
                .setInterpolator(Interpolators.EMPHASIZED)
                .withStartAction(() -> expandedView.setAnimating(true))
                .withEndAction(() -> expandedView.setAnimating(false))
                .start();
    }

    /**
     * Cancel current animations
     */
    public void cancelAnimations() {
        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
        mExpandedViewAlphaAnimator.cancel();
        BubbleBarExpandedView bbev = getExpandedView();
        if (bbev != null) {
            bbev.animate().cancel();
        }
    }

    private @Nullable BubbleBarExpandedView getExpandedView() {
@@ -231,21 +355,42 @@ public class BubbleBarAnimationHelper {
            return;
        }

        boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
        final int padding = mPositioner.getBubbleBarExpandedViewPadding();
        final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
        final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
        final Size size = getExpandedViewSize();
        Point position = getExpandedViewRestPosition(size);
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams();
        lp.width = width;
        lp.height = height;
        lp.width = size.getWidth();
        lp.height = size.getHeight();
        bbev.setLayoutParams(lp);
        bbev.setX(position.x);
        bbev.setY(position.y);
        bbev.updateLocation();
        bbev.maybeShowOverflow();
    }

    private Point getExpandedViewRestPosition(Size size) {
        final int padding = mPositioner.getBubbleBarExpandedViewPadding();
        Point point = new Point();
        if (mLayerView.isOnLeft()) {
            bbev.setX(mPositioner.getInsets().left + padding);
            point.x = mPositioner.getInsets().left + padding;
        } else {
            bbev.setX(mPositioner.getAvailableRect().width() - width - padding);
            point.x = mPositioner.getAvailableRect().width() - size.getWidth() - padding;
        }
        bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
        bbev.updateLocation();
        bbev.maybeShowOverflow();
        point.y = mPositioner.getExpandedViewBottomForBubbleBar() - size.getHeight();
        return point;
    }

    private Size getExpandedViewSize() {
        boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
        final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
        final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
        return new Size(width, height);
    }

    private Point getViewCenterOnScreen(View view) {
        Point center = new Point();
        int[] onScreenLocation = view.getLocationOnScreen();
        center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f));
        center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f));
        return center;
    }
}
+70 −49
Original line number Diff line number Diff line
@@ -16,70 +16,67 @@

package com.android.wm.shell.bubbles.bar

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.graphics.PointF
import android.graphics.Rect
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
import com.android.wm.shell.animation.Interpolators
import com.android.wm.shell.common.bubbles.DismissView
import com.android.wm.shell.common.bubbles.RelativeTouchListener
import com.android.wm.shell.common.magnetictarget.MagnetizedObject

/** Controller for handling drag interactions with [BubbleBarExpandedView] */
@SuppressLint("ClickableViewAccessibility")
class BubbleBarExpandedViewDragController(
    private val expandedView: BubbleBarExpandedView,
    private val dismissView: DismissView,
    private val animationHelper: BubbleBarAnimationHelper,
    private val onDismissed: () -> Unit
) {

    var isStuckToDismiss: Boolean = false
        private set

    private var expandedViewInitialTranslationX = 0f
    private var expandedViewInitialTranslationY = 0f
    private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
        MagnetizedObject.magnetizeView(expandedView)
    private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget

    init {
        expandedView.handleView.setOnTouchListener(HandleDragListener())
        magnetizedExpandedView.magnetListener = MagnetListener()
        magnetizedExpandedView.animateStuckToTarget =
            {
                    target: MagnetizedObject.MagneticTarget,
                    _: Float,
                    _: Float,
                    _: Boolean,
                    after: (() -> Unit)? ->
                animationHelper.animateIntoTarget(target, after)
            }

    private fun finishDrag(x: Float, y: Float, viewInitialX: Float, viewInitialY: Float) {
        val dismissCircleBounds = Rect().apply { dismissView.circle.getBoundsOnScreen(this) }
        if (dismissCircleBounds.contains(x.toInt(), y.toInt())) {
            onDismissed()
        } else {
            resetExpandedViewPosition(viewInitialX, viewInitialY)
        }
        dismissView.hide()
    }
        magnetizedDismissTarget =
            MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
        magnetizedExpandedView.addTarget(magnetizedDismissTarget)

    private fun resetExpandedViewPosition(initialX: Float, initialY: Float) {
        val listener =
            object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animation: Animator) {
                    expandedView.isAnimating = true
                }
        val dragMotionEventHandler = HandleDragListener()

                override fun onAnimationEnd(animation: Animator) {
                    expandedView.isAnimating = false
        expandedView.handleView.setOnTouchListener { view, event ->
            if (event.actionMasked == MotionEvent.ACTION_DOWN) {
                expandedViewInitialTranslationX = expandedView.translationX
                expandedViewInitialTranslationY = expandedView.translationY
            }
            val magnetConsumed = magnetizedExpandedView.maybeConsumeMotionEvent(event)
            // Move events can be consumed by the magnetized object
            if (event.actionMasked == MotionEvent.ACTION_MOVE && magnetConsumed) {
                return@setOnTouchListener true
            }
            return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
        }
        expandedView
            .animate()
            .translationX(initialX)
            .translationY(initialY)
            .setDuration(RESET_POSITION_ANIM_DURATION)
            .setInterpolator(Interpolators.EMPHASIZED_DECELERATE)
            .setListener(listener)
            .start()
    }

    private inner class HandleDragListener : RelativeTouchListener() {

        private val expandedViewRestPosition = PointF()

        override fun onDown(v: View, ev: MotionEvent): Boolean {
            // While animating, don't allow new touch events
            if (expandedView.isAnimating) {
                return false
            }
            expandedViewRestPosition.x = expandedView.translationX
            expandedViewRestPosition.y = expandedView.translationY
            return true
            return !expandedView.isAnimating
        }

        override fun onMove(
@@ -90,8 +87,8 @@ class BubbleBarExpandedViewDragController(
            dx: Float,
            dy: Float
        ) {
            expandedView.translationX = expandedViewRestPosition.x + dx
            expandedView.translationY = expandedViewRestPosition.y + dy
            expandedView.translationX = expandedViewInitialTranslationX + dx
            expandedView.translationY = expandedViewInitialTranslationY + dy
            dismissView.show()
        }

@@ -105,16 +102,40 @@ class BubbleBarExpandedViewDragController(
            velX: Float,
            velY: Float
        ) {
            finishDrag(ev.rawX, ev.rawY, expandedViewRestPosition.x, expandedViewRestPosition.y)
            finishDrag()
        }

        override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
            resetExpandedViewPosition(expandedViewRestPosition.x, expandedViewRestPosition.y)
            finishDrag()
        }

        private fun finishDrag() {
            if (!isStuckToDismiss) {
                animationHelper.animateToRestPosition()
                dismissView.hide()
            }
        }
    }

    private inner class MagnetListener : MagnetizedObject.MagnetListener {
        override fun onStuckToTarget(target: MagnetizedObject.MagneticTarget) {
            isStuckToDismiss = true
        }

        override fun onUnstuckFromTarget(
            target: MagnetizedObject.MagneticTarget,
            velX: Float,
            velY: Float,
            wasFlungOut: Boolean
        ) {
            isStuckToDismiss = false
            animationHelper.animateUnstuckFromDismissView()
        }

    companion object {
        const val RESET_POSITION_ANIM_DURATION = 300L
        override fun onReleasedInTarget(target: MagnetizedObject.MagneticTarget) {
            onDismissed()
            dismissView.hide()
        }
    }
}
+11 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar;

import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE;

import android.annotation.Nullable;
import android.content.Context;
@@ -36,7 +37,6 @@ import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.bubbles.DismissView;
@@ -206,10 +206,13 @@ public class BubbleBarLayerView extends FrameLayout
                }
            });

            mDragController = new BubbleBarExpandedViewDragController(mExpandedView, mDismissView,
            mDragController = new BubbleBarExpandedViewDragController(
                    mExpandedView,
                    mDismissView,
                    mAnimationHelper,
                    () -> {
                        mBubbleController.dismissBubble(mExpandedBubble.getKey(),
                                Bubbles.DISMISS_USER_GESTURE);
                                DISMISS_USER_GESTURE);
                        return Unit.INSTANCE;
                    });

@@ -241,7 +244,11 @@ public class BubbleBarLayerView extends FrameLayout
        mIsExpanded = false;
        final BubbleBarExpandedView viewToRemove = mExpandedView;
        mEducationViewController.hideEducation(/* animated = */ true);
        if (mDragController != null && mDragController.isStuckToDismiss()) {
            mAnimationHelper.animateDismiss(() -> removeView(viewToRemove));
        } else {
            mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
        }
        mBubbleController.getSysuiProxy().onStackExpandChanged(false);
        mExpandedView = null;
        mDragController = null;