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

Commit 33f0c93f authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Update the popup reveal animation to more closely match window reveal"

parents f6707568 95888c07
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -35834,6 +35834,7 @@ package android.view {
    method protected int getBottomPaddingOffset();
    method public float getCameraDistance();
    method public android.graphics.Rect getClipBounds();
    method public boolean getClipBounds(android.graphics.Rect);
    method public final boolean getClipToOutline();
    method public java.lang.CharSequence getContentDescription();
    method public final android.content.Context getContext();
+1 −0
Original line number Diff line number Diff line
@@ -38033,6 +38033,7 @@ package android.view {
    method protected int getBottomPaddingOffset();
    method public float getCameraDistance();
    method public android.graphics.Rect getClipBounds();
    method public boolean getClipBounds(android.graphics.Rect);
    method public final boolean getClipToOutline();
    method public java.lang.CharSequence getContentDescription();
    method public final android.content.Context getContext();
+18 −0
Original line number Diff line number Diff line
@@ -15101,6 +15101,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return (mClipBounds != null) ? new Rect(mClipBounds) : null;
    }
    /**
     * Populates an output rectangle with the clip bounds of the view,
     * returning {@code true} if successful or {@code false} if the view's
     * clip bounds are {@code null}.
     *
     * @param outRect rectangle in which to place the clip bounds of the view
     * @return {@code true} if successful or {@code false} if the view's
     *         clip bounds are {@code null}
     */
    public boolean getClipBounds(Rect outRect) {
        if (mClipBounds != null) {
            outRect.set(mClipBounds);
            return true;
        }
        return false;
    }
    /**
     * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common
     * case of an active Animation being run on the view.
+46 −36
Original line number Diff line number Diff line
@@ -94,7 +94,6 @@ public class PopupWindow {
    private final int[] mDrawingLocation = new int[2];
    private final int[] mScreenLocation = new int[2];
    private final Rect mTempRect = new Rect();
    private final Rect mAnchorBounds = new Rect();

    private Context mContext;
    private WindowManager mWindowManager;
@@ -159,28 +158,6 @@ public class PopupWindow {

    private WeakReference<View> mAnchor;

    private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
        @Override
        public Rect onGetEpicenter(Transition transition) {
            final View anchor = mAnchor != null ? mAnchor.get() : null;
            final View decor = mDecorView;
            if (anchor == null || decor == null) {
                return null;
            }

            final Rect anchorBounds = mAnchorBounds;
            final int[] anchorLocation = anchor.getLocationOnScreen();
            final int[] popupLocation = mDecorView.getLocationOnScreen();

            // Compute the position of the anchor relative to the popup.
            anchorBounds.set(0, 0, anchor.getWidth(), anchor.getHeight());
            anchorBounds.offset(anchorLocation[0] - popupLocation[0],
                    anchorLocation[1] - popupLocation[1]);

            return anchorBounds;
        }
    };

    private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
@@ -355,18 +332,10 @@ public class PopupWindow {

    public void setEnterTransition(Transition enterTransition) {
        mEnterTransition = enterTransition;

        if (mEnterTransition != null) {
            mEnterTransition.setEpicenterCallback(mEpicenterCallback);
        }
    }

    public void setExitTransition(Transition exitTransition) {
        mExitTransition = exitTransition;

        if (mExitTransition != null) {
            mExitTransition.setEpicenterCallback(mEpicenterCallback);
        }
    }

    private Transition getTransition(int resId) {
@@ -1281,11 +1250,14 @@ public class PopupWindow {

        final PopupDecorView decorView = mDecorView;
        decorView.setFitsSystemWindows(mLayoutInsetDecor);
        decorView.requestEnterTransition(mEnterTransition);

        setLayoutDirectionFromAnchor();

        mWindowManager.addView(decorView, p);

        if (mEnterTransition != null) {
            decorView.requestEnterTransition(mEnterTransition);
        }
    }

    private void setLayoutDirectionFromAnchor() {
@@ -1607,13 +1579,25 @@ public class PopupWindow {
        // Ensure any ongoing or pending transitions are canceled.
        decorView.cancelTransitions();

        unregisterForScrollChanged();

        mIsShowing = false;
        mIsTransitioningToDismiss = true;

        if (mExitTransition != null && decorView.isLaidOut()) {
            decorView.startExitTransition(mExitTransition, new TransitionListenerAdapter() {
        final Transition exitTransition = mExitTransition;
        if (exitTransition != null && decorView.isLaidOut()) {
            // The decor view is non-interactive during exit transitions.
            final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
            p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
            p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
            mWindowManager.updateViewLayout(decorView, p);

            final Rect epicenter = getRelativeAnchorBounds();
            exitTransition.setEpicenterCallback(new EpicenterCallback() {
                @Override
                public Rect onGetEpicenter(Transition transition) {
                    return epicenter;
                }
            });
            decorView.startExitTransition(exitTransition, new TransitionListenerAdapter() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    dismissImmediate(decorView, contentHolder, contentView);
@@ -1623,11 +1607,30 @@ public class PopupWindow {
            dismissImmediate(decorView, contentHolder, contentView);
        }

        // Clears the anchor view.
        unregisterForScrollChanged();

        if (mOnDismissListener != null) {
            mOnDismissListener.onDismiss();
        }
    }

    private Rect getRelativeAnchorBounds() {
        final View anchor = mAnchor != null ? mAnchor.get() : null;
        final View decor = mDecorView;
        if (anchor == null || decor == null) {
            return null;
        }

        final int[] anchorLocation = anchor.getLocationOnScreen();
        final int[] popupLocation = mDecorView.getLocationOnScreen();

        // Compute the position of the anchor relative to the popup.
        final Rect bounds = new Rect(0, 0, anchor.getWidth(), anchor.getHeight());
        bounds.offset(anchorLocation[0] - popupLocation[0], anchorLocation[1] - popupLocation[1]);
        return bounds;
    }

    /**
     * Removes the popup from the window manager and tears down the supporting
     * view hierarchy, if necessary.
@@ -1996,6 +1999,13 @@ public class PopupWindow {
                            observer.removeOnGlobalLayoutListener(this);
                        }

                        final Rect epicenter = getRelativeAnchorBounds();
                        enterTransition.setEpicenterCallback(new EpicenterCallback() {
                            @Override
                            public Rect onGetEpicenter(Transition transition) {
                                return epicenter;
                            }
                        });
                        startEnterTransition(enterTransition);
                    }
                });
+108 −11
Original line number Diff line number Diff line
@@ -17,15 +17,23 @@ package com.android.internal.transition;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.PathInterpolator;

import com.android.internal.R;

/**
 * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
@@ -36,10 +44,39 @@ public class EpicenterClipReveal extends Visibility {
    private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
    private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";

    public EpicenterClipReveal() {}
    private final TimeInterpolator mInterpolatorX;
    private final TimeInterpolator mInterpolatorY;
    private final boolean mCenterClipBounds;

    public EpicenterClipReveal() {
        mInterpolatorX = null;
        mInterpolatorY = null;
        mCenterClipBounds = false;
    }

    public EpicenterClipReveal(Context context, AttributeSet attrs) {
        super(context, attrs);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.EpicenterClipReveal, 0, 0);

        mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false);

        final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0);
        if (interpolatorX != 0) {
            mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
        } else {
            mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
        }

        final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0);
        if (interpolatorY != 0) {
            mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
        } else {
            mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
        }

        a.recycle();
    }

    @Override
@@ -82,7 +119,7 @@ public class EpicenterClipReveal extends Visibility {
        // Prepare the view.
        view.setClipBounds(start);

        return createRectAnimator(view, start, end, endValues);
        return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
    }

    @Override
@@ -98,17 +135,23 @@ public class EpicenterClipReveal extends Visibility {
        // Prepare the view.
        view.setClipBounds(start);

        return createRectAnimator(view, start, end, endValues);
        return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
    }

    private Rect getEpicenterOrCenter(Rect bestRect) {
        final Rect epicenter = getEpicenter();
        if (epicenter != null) {
            // Translate the clip bounds to be centered within the target bounds.
            if (mCenterClipBounds) {
                final int offsetX = bestRect.centerX() - epicenter.centerX();
                final int offsetY = bestRect.centerY() - epicenter.centerY();
                epicenter.offset(offsetX, offsetY);
            }
            return epicenter;
        }

        int centerX = bestRect.centerX();
        int centerY = bestRect.centerY();
        final int centerX = bestRect.centerX();
        final int centerY = bestRect.centerY();
        return new Rect(centerX, centerY, centerX, centerY);
    }

@@ -120,17 +163,71 @@ public class EpicenterClipReveal extends Visibility {
        return clipRect;
    }

    private Animator createRectAnimator(final View view, Rect start, Rect end,
            TransitionValues endValues) {
        final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
    private static Animator createRectAnimator(final View view, Rect start, Rect end,
            TransitionValues endValues, TimeInterpolator interpolatorX,
            TimeInterpolator interpolatorY) {
        final RectEvaluator evaluator = new RectEvaluator(new Rect());
        ObjectAnimator anim = ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end);
        anim.addListener(new AnimatorListenerAdapter() {
        final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);

        final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X);
        final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end);
        if (interpolatorX != null) {
            animX.setInterpolator(interpolatorX);
        }

        final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y);
        final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end);
        if (interpolatorY != null) {
            animY.setInterpolator(interpolatorY);
        }

        final AnimatorSet animSet = new AnimatorSet();
        animSet.playTogether(animX, animY);
        animSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                view.setClipBounds(terminalClip);
            }
        });
        return anim;
        return animSet;
    }

    private static class ClipDimenProperty extends Property<View, Rect> {
        public static final char TARGET_X = 'x';
        public static final char TARGET_Y = 'y';

        private final Rect mTempRect = new Rect();

        private final int mTargetDimension;

        public ClipDimenProperty(char targetDimension) {
            super(Rect.class, "clip_bounds_" + targetDimension);

            mTargetDimension = targetDimension;
        }

        @Override
        public Rect get(View object) {
            final Rect tempRect = mTempRect;
            if (!object.getClipBounds(tempRect)) {
                tempRect.setEmpty();
            }
            return tempRect;
        }

        @Override
        public void set(View object, Rect value) {
            final Rect tempRect = mTempRect;
            if (object.getClipBounds(tempRect)) {
                if (mTargetDimension == TARGET_X) {
                    tempRect.left = value.left;
                    tempRect.right = value.right;
                } else {
                    tempRect.top = value.top;
                    tempRect.bottom = value.bottom;
                }
                object.setClipBounds(tempRect);
            }
        }
    }
}
Loading