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

Commit 95888c07 authored by Alan Viverette's avatar Alan Viverette
Browse files

Update the popup reveal animation to more closely match window reveal

Hand-waves the default interpolators for efficiency's sake until we can
implement interpolator caching or preloading.

Change-Id: Ibc618a0c092b08a33fb91265ec15665c94831b6b
parent a323e3f3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -34701,6 +34701,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
@@ -37250,6 +37250,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
@@ -15017,6 +15017,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) {
@@ -1278,11 +1247,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() {
@@ -1604,13 +1576,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);
@@ -1620,11 +1604,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.
@@ -1993,6 +1996,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