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

Commit 0a036e9c authored by Brian Isganitis's avatar Brian Isganitis
Browse files

Animate AbstractSlideInView translation shift with PendingAnimation.

The PendingAnimation gives us more flexibility than the AnimatorSet.

Test: Manual
Bug: 289290185
Flag: No
Change-Id: I3156a659098c44ce9e6b20e5e79e88742add629a
parent 99f30cb1
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -158,11 +158,11 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I
    }

    private void animateOpen() {
        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            return;
        }
        mIsOpen = true;
        setUpDefaultOpenAnimator().start();
        setUpDefaultOpenAnimation().start();
    }

    @Override
+7 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.launcher3.taskbar.allapps;

import static com.android.app.animation.Interpolators.EMPHASIZED;

import android.animation.Animator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -58,7 +59,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla

    /** Opens the all apps view. */
    void show(boolean animate) {
        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            return;
        }
        mIsOpen = true;
@@ -66,10 +67,12 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla
        mAllAppsCallbacks.onAllAppsTransitionStart(true);

        if (animate) {
            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, EMPHASIZED);
            mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(
            setUpOpenAnimation(mAllAppsCallbacks.getOpenDuration());
            Animator animator = mOpenCloseAnimation.getAnimationPlayer();
            animator.setInterpolator(EMPHASIZED);
            animator.addListener(AnimatorListeners.forEndCallback(
                    () -> mAllAppsCallbacks.onAllAppsTransitionEnd(true)));
            mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
            animator.start();
        } else {
            mTranslationShift = TRANSLATION_SHIFT_OPENED;
            mAllAppsCallbacks.onAllAppsTransitionEnd(true);
+118 −56
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.launcher3.views;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -24,16 +25,14 @@ import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCES
import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -51,6 +50,8 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;

@@ -66,8 +67,8 @@ import java.util.Optional;
public abstract class AbstractSlideInView<T extends Context & ActivityContext>
        extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener {

    protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
            new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
    protected static final FloatProperty<AbstractSlideInView<?>> TRANSLATION_SHIFT =
            new FloatProperty<>("translationShift") {

                @Override
                public Float get(AbstractSlideInView view) {
@@ -75,31 +76,54 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
                }

                @Override
                public void set(AbstractSlideInView view, Float value) {
                public void setValue(AbstractSlideInView view, float value) {
                    view.setTranslationShift(value);
                }
            };
    protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
    protected static final float TRANSLATION_SHIFT_OPENED = 0f;
    private static final float VIEW_NO_SCALE = 1f;
    private static final int NO_DURATION = -1;
    private static final int DEFAULT_DURATION = 300;

    protected final T mActivityContext;

    protected final SingleAxisSwipeDetector mSwipeDetector;
    protected @NonNull AnimatorSet mOpenCloseAnimator;
    private final ObjectAnimator mTranslationShiftAnimator;
    protected @NonNull AnimatorPlaybackController mOpenCloseAnimation;

    protected ViewGroup mContent;
    protected final View mColorScrim;

    /**
     * Interpolator for {@link #mOpenCloseAnimation} when we are closing due to dragging downwards.
     */
    private Interpolator mScrollInterpolator;
    private long mScrollDuration;
    /**
     * End progress for {@link #mOpenCloseAnimation} when we are closing due to dragging downloads.
     * <p>
     * There are two cases that determine this value:
     * <ol>
     *     <li>
     *         If the drag interrupts the opening transition (i.e. {@link #mToTranslationShift}
     *         is {@link #TRANSLATION_SHIFT_OPENED}), we need to animate back to {@code 0} to
     *         reverse the animation that was paused at {@link #onDragStart(boolean, float)}.
     *     </li>
     *     <li>
     *         If the drag started after the view is fully opened (i.e.
     *         {@link #mToTranslationShift} is {@link #TRANSLATION_SHIFT_CLOSED}), the animation
     *         that was set up at {@link #onDragStart(boolean, float)} for closing the view
     *         should go forward to {@code 1}.
     *     </li>
     * </ol>
     */
    private float mScrollEndProgress;

    // range [0, 1], 0=> completely open, 1=> completely closed
    protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
    /** {@link #mTranslationShift} at the invocation of {@link #onDragStart(boolean, float)}. */
    protected float mDragStartTranslationShift;
    protected float mFromTranslationShift;
    protected float mToTranslationShift;
    /** {@link #mOpenCloseAnimation} progress at {@link #onDragStart(boolean, float)}. */
    private float mDragStartProgress;

    protected boolean mNoIntercept;
    protected @Nullable OnCloseListener mOnCloseBeginListener;
@@ -128,52 +152,78 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
        mActivityContext = ActivityContext.lookupContext(context);

        mScrollInterpolator = Interpolators.SCROLL_CUBIC;
        mScrollDuration = NO_DURATION;
        mScrollDuration = DEFAULT_DURATION;
        mSwipeDetector = new SingleAxisSwipeDetector(context, this,
                SingleAxisSwipeDetector.VERTICAL);

        mOpenCloseAnimator = new AnimatorSet();
        mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
        mOpenCloseAnimation = new PendingAnimation(0).createPlaybackController();

        int scrimColor = getScrimColor(context);
        mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
    }

    /**
     * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters.
     * Sets up a {@link #mOpenCloseAnimation} for opening with default parameters.
     *
     * @see #setUpOpenCloseAnimator(float, Interpolator)
     * @see #setUpOpenCloseAnimation(float, float, long)
     */
    protected final AnimatorSet setUpDefaultOpenAnimator() {
        return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN);
    protected final AnimatorPlaybackController setUpDefaultOpenAnimation() {
        AnimatorPlaybackController animation = setUpOpenCloseAnimation(
                TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, DEFAULT_DURATION);
        animation.getAnimationPlayer().setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        return animation;
    }

    /**
     * Initializes a new {@link #mOpenCloseAnimator}.
     * <p>
     * Subclasses should override this method if they want to add more {@code Animator} instances
     * to the set.
     * Sets up a {@link #mOpenCloseAnimation} for opening with a given duration.
     *
     * @param translationShift             translation shift to animate to.
     * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}.
     * @return {@link #mOpenCloseAnimator}
     * @see #setUpOpenCloseAnimation(float, float, long)
     */
    protected AnimatorSet setUpOpenCloseAnimator(
            float translationShift, Interpolator translationShiftInterpolator) {
        mOpenCloseAnimator = new AnimatorSet();
        mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
    protected final AnimatorPlaybackController setUpOpenAnimation(long duration) {
        return setUpOpenCloseAnimation(
                TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, duration);
    }

    private AnimatorPlaybackController setUpCloseAnimation(long duration) {
        return setUpOpenCloseAnimation(
                TRANSLATION_SHIFT_OPENED, TRANSLATION_SHIFT_CLOSED, duration);
    }

    /**
     * Initializes a new {@link #mOpenCloseAnimation}.
     *
     * @param fromTranslationShift translation shift to animate from.
     * @param toTranslationShift   translation shift to animate to.
     * @param duration             animation duration.
     * @return {@link #mOpenCloseAnimation}
     */
    private AnimatorPlaybackController setUpOpenCloseAnimation(
            float fromTranslationShift, float toTranslationShift, long duration) {
        mFromTranslationShift = fromTranslationShift;
        mToTranslationShift = toTranslationShift;

        PendingAnimation animation = new PendingAnimation(duration);
        animation.addEndListener(b -> {
            mSwipeDetector.finishedScrolling();
            announceAccessibilityChanges();
        }));
        });

        mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat(
                TRANSLATION_SHIFT, translationShift));
        mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator);
        mOpenCloseAnimator.play(mTranslationShiftAnimator);
        animation.addFloat(
                this, TRANSLATION_SHIFT, fromTranslationShift, toTranslationShift, LINEAR);
        onOpenCloseAnimationPending(animation);

        return mOpenCloseAnimator;
        mOpenCloseAnimation = animation.createPlaybackController();
        return mOpenCloseAnimation;
    }

    /**
     * Invoked when a {@link #mOpenCloseAnimation} is being set up.
     * <p>
     * Subclasses can override this method to modify the animation before it's used to create a
     * {@link AnimatorPlaybackController}.
     */
    protected void onOpenCloseAnimationPending(PendingAnimation animation) {}

    protected void attachToContainer() {
        if (mColorScrim != null) {
            getPopupContainer().addView(mColorScrim);
@@ -316,29 +366,33 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
    }

    private boolean isOpeningAnimationRunning() {
        return mIsOpen && mOpenCloseAnimator.isRunning();
        return mIsOpen && mOpenCloseAnimation.getAnimationPlayer().isRunning();
    }

    /* SingleAxisSwipeDetector.Listener */

    @Override
    public void onDragStart(boolean start, float startDisplacement) {
        mOpenCloseAnimator.cancel();
        mDragStartTranslationShift = mTranslationShift;
        if (mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            mOpenCloseAnimation.pause();
            mDragStartProgress = mOpenCloseAnimation.getProgressFraction();
        } else {
            setUpCloseAnimation(DEFAULT_DURATION);
            mDragStartProgress = 0;
        }
    }

    @Override
    public boolean onDrag(float displacement) {
        setTranslationShift(Utilities.boundToRange(
                mDragStartTranslationShift + displacement / getShiftRange(),
                TRANSLATION_SHIFT_OPENED,
                TRANSLATION_SHIFT_CLOSED));
        float progress = mDragStartProgress
                + Math.signum(mToTranslationShift - mFromTranslationShift)
                * (displacement / getShiftRange());
        mOpenCloseAnimation.setPlayFraction(Utilities.boundToRange(progress, 0, 1));
        return true;
    }

    @Override
    public void onDragEnd(float velocity) {
        mDragStartTranslationShift = 0;
        float successfulShiftThreshold = mActivityContext.getDeviceProfile().isTablet
                ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS : SUCCESS_TRANSITION_PROGRESS;
        if ((mSwipeDetector.isFling(velocity) && velocity > 0)
@@ -346,10 +400,15 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
            mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
            mScrollDuration = BaseSwipeDetector.calculateDuration(
                    velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift);
            mScrollEndProgress = mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 0 : 1;
            close(true);
        } else {
            setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE)
                    .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
            ValueAnimator animator = mOpenCloseAnimation.getAnimationPlayer();
            animator.setInterpolator(Interpolators.DECELERATE);
            animator.setFloatValues(
                    mOpenCloseAnimation.getProgressFraction(),
                    mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 1 : 0);
            animator.setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
                    .start();
        }
    }
@@ -371,24 +430,27 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
        Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);

        if (!animate) {
            mOpenCloseAnimator.cancel();
            mOpenCloseAnimation.pause();
            setTranslationShift(TRANSLATION_SHIFT_CLOSED);
            onCloseComplete();
            return;
        }

        final Interpolator interpolator;
        final long duration;
        final ValueAnimator animator;
        if (mSwipeDetector.isIdleState()) {
            interpolator = getIdleInterpolator();
            duration = defaultDuration;
            setUpCloseAnimation(defaultDuration);
            animator = mOpenCloseAnimation.getAnimationPlayer();
            animator.setInterpolator(getIdleInterpolator());
        } else {
            interpolator = mScrollInterpolator;
            duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration;
            animator = mOpenCloseAnimation.getAnimationPlayer();
            animator.setInterpolator(mScrollInterpolator);
            animator.setDuration(mScrollDuration);
            mOpenCloseAnimation.getAnimationPlayer().setFloatValues(
                    mOpenCloseAnimation.getProgressFraction(), mScrollEndProgress);
        }
        setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator)
                .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
        mOpenCloseAnimator.setDuration(duration).start();

        animator.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
        animator.start();
    }

    protected Interpolator getIdleInterpolator() {
+2 −2
Original line number Diff line number Diff line
@@ -116,11 +116,11 @@ public class WidgetsEduView extends AbstractSlideInView<Launcher> implements Ins
    }

    private void animateOpen() {
        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            return;
        }
        mIsOpen = true;
        setUpDefaultOpenAnimator().start();
        setUpDefaultOpenAnimation().start();
    }

    /** Shows widget education dialog. */
+2 −2
Original line number Diff line number Diff line
@@ -128,11 +128,11 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivi
    }

    private void animateOpen() {
        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
        if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
            return;
        }
        mIsOpen = true;
        setUpDefaultOpenAnimator().start();
        setUpDefaultOpenAnimation().start();
    }

    @Override
Loading