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

Commit e88d8acd authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Adding spring animation when swiping up to home in 3P Launcher

Bug: 137197916
Change-Id: Ic9808a30e8cd4e0b2a221c4b03bc29489038e092
parent bf004210
Loading
Loading
Loading
Loading
+119 −0
Original line number Diff line number Diff line
@@ -19,12 +19,15 @@ import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.createPredefined;

import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;

import android.animation.Animator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -32,6 +35,7 @@ import android.content.Intent;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -48,12 +52,19 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
import com.android.quickstep.views.RecentsView;
@@ -369,9 +380,117 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
        return TaskView.getCurveScaleForInterpolation(interpolation);
    }

    /**
     * Creates an animation that transforms the current app window into the home app.
     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
     * @param homeAnimationFactory The home animation factory.
     */
    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
            HomeAnimationFactory homeAnimationFactory) {
        final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
        final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
                mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
        final RectF targetRect = homeAnimationFactory.getWindowTargetRect();

        final View floatingView = homeAnimationFactory.getFloatingView();
        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources());
        if (isFloatingIconView) {
            FloatingIconView fiv = (FloatingIconView) floatingView;
            anim.addAnimatorListener(fiv);
            fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
        }

        AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();

        // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
        // rounding at the end of the animation.
        float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
        float endRadius = startRect.width() / 6f;
        // We want the window alpha to be 0 once this threshold is met, so that the
        // FolderIconView can be seen morphing into the icon shape.
        final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
        anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
            @Override
            public void onUpdate(RectF currentRect, float progress) {
                homeAnim.setPlayFraction(progress);

                float alphaProgress = ACCEL_1_5.getInterpolation(progress);
                float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
                        windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
                mTransformParams.setProgress(progress)
                        .setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
                if (isFloatingIconView) {
                    mTransformParams.setCornerRadius(endRadius * progress + startRadius
                            * (1f - progress));
                }
                mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
                        false /* launcherOnTop */);

                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
                            windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
                }
            }

            @Override
            public void onCancel() {
                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).fastFinish();
                }
            }
        });
        anim.addAnimatorListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                homeAnim.dispatchOnStart();
            }

            @Override
            public void onAnimationSuccess(Animator animator) {
                homeAnim.getAnimationPlayer().end();
            }
        });
        return anim;
    }

    public interface Factory {

        BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask,
                long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
    }

    protected interface RunningWindowAnim {
        void end();

        void cancel();

        static RunningWindowAnim wrap(Animator animator) {
            return new RunningWindowAnim() {
                @Override
                public void end() {
                    animator.end();
                }

                @Override
                public void cancel() {
                    animator.cancel();
                }
            };
        }

        static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
            return new RunningWindowAnim() {
                @Override
                public void end() {
                    rectFSpringAnim.end();
                }

                @Override
                public void cancel() {
                    rectFSpringAnim.cancel();
                }
            };
        }
    }
}
+1 −7
Original line number Diff line number Diff line
@@ -151,16 +151,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
            @NonNull
            @Override
            public RectF getWindowTargetRect() {
                final int halfIconSize = dp.iconSizePx / 2;
                final float targetCenterX = dp.availableWidthPx / 2f;
                final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;

                if (canUseWorkspaceView) {
                    return iconLocation;
                } else {
                    // Fallback to animate to center of screen.
                    return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
                            targetCenterX + halfIconSize, targetCenterY + halfIconSize);
                    return HomeAnimationFactory.getDefaultWindowTargetRect(dp);
                }
            }

+5 −98
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -27,7 +26,6 @@ import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
@@ -52,7 +50,6 @@ import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -68,13 +65,11 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
@@ -83,7 +78,6 @@ import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
@@ -968,67 +962,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
     * @param homeAnimationFactory The home animation factory.
     */
    private RectFSpringAnim createWindowAnimationToHome(float startProgress,
            HomeAnimationFactory homeAnimationFactory) {
        final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
        final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
                mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
        final RectF targetRect = homeAnimationFactory.getWindowTargetRect();

        final View floatingView = homeAnimationFactory.getFloatingView();
        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources());
        if (isFloatingIconView) {
            FloatingIconView fiv = (FloatingIconView) floatingView;
            anim.addAnimatorListener(fiv);
            fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
        }

        AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();

        // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
        // rounding at the end of the animation.
        float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
        float endRadius = startRect.width() / 6f;
        // We want the window alpha to be 0 once this threshold is met, so that the
        // FolderIconView can be seen morphing into the icon shape.
        final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
        anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
            @Override
            public void onUpdate(RectF currentRect, float progress) {
                homeAnim.setPlayFraction(progress);

                float alphaProgress = ACCEL_1_5.getInterpolation(progress);
                float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
                        windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
                mTransformParams.setProgress(progress)
                        .setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
                if (isFloatingIconView) {
                    mTransformParams.setCornerRadius(endRadius * progress + startRadius
                            * (1f - progress));
                }
                mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
                        false /* launcherOnTop */);

                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
                            windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
                }

                updateSysUiFlags(Math.max(progress, mCurrentShift.value));
            }

    @Override
            public void onCancel() {
                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).fastFinish();
                }
            }
        });
    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
            HomeAnimationFactory homeAnimationFactory) {
        RectFSpringAnim anim =
                super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
        anim.addOnUpdateListener((r, p) -> updateSysUiFlags(Math.max(p, mCurrentShift.value)));
        anim.addAnimatorListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                homeAnim.dispatchOnStart();
                if (mActivity != null) {
                    mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
                }
@@ -1036,7 +978,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>

            @Override
            public void onAnimationSuccess(Animator animator) {
                homeAnim.getAnimationPlayer().end();
                if (mRecentsView != null) {
                    mRecentsView.post(mRecentsView::resetTaskVisuals);
                }
@@ -1270,38 +1211,4 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
        return app.isNotInRecents
                || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
    }

    private interface RunningWindowAnim {
        void end();

        void cancel();

        static RunningWindowAnim wrap(Animator animator) {
            return new RunningWindowAnim() {
                @Override
                public void end() {
                    animator.end();
                }

                @Override
                public void cancel() {
                    animator.cancel();
                }
            };
        }

        static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
            return new RunningWindowAnim() {
                @Override
                public void end() {
                    rectFSpringAnim.end();
                }

                @Override
                public void cancel() {
                    rectFSpringAnim.cancel();
                }
            };
        }
    }
}
+62 −22
Original line number Diff line number Diff line
@@ -18,11 +18,12 @@ package com.android.quickstep.inputconsumers;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;

import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -31,10 +32,13 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;

import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseSwipeUpHandler;
import com.android.quickstep.MultiStateCallback;
@@ -44,6 +48,7 @@ import com.android.quickstep.RecentsModel;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.ObjectWrapper;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -102,10 +107,10 @@ public class FallbackNoButtonInputConsumer extends
    private final boolean mRunningOverHome;
    private final boolean mSwipeUpOverHome;


    private final RunningTaskInfo mRunningTaskInfo;

    private Animator mFinishAnimation;
    private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
    private RunningWindowAnim mFinishAnimation;

    public FallbackNoButtonInputConsumer(Context context,
            OverviewComponentObserver overviewComponentObserver,
@@ -226,6 +231,8 @@ public class FallbackNoButtonInputConsumer extends
    @Override
    public void updateFinalShift() {
        mTransformParams.setProgress(mCurrentShift.value);
        mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
                && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
        if (mRecentsAnimationWrapper.targetSet != null) {
            applyTransformUnchecked();
        }
@@ -240,6 +247,7 @@ public class FallbackNoButtonInputConsumer extends

    @Override
    public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
        mEndVelocityPxPerMs.set(0, velocity.y / 1000);
        if (mInQuickSwitchMode) {
            // For now set it to non-null, it will be reset before starting the animation
            mEndTarget = LAST_TASK;
@@ -288,12 +296,14 @@ public class FallbackNoButtonInputConsumer extends
        }
    }


    private void onHandlerInvalidated() {
        mActivityInitListener.unregister();
        if (mGestureEndCallback != null) {
            mGestureEndCallback.run();
        }
        if (mFinishAnimation != null) {
            mFinishAnimation.end();
        }
    }

    private void onHandlerInvalidatedWithRecents() {
@@ -364,31 +374,39 @@ public class FallbackNoButtonInputConsumer extends
        }

        float endProgress = mEndTarget.mEndProgress;

        if (mCurrentShift.value != endProgress || mInQuickSwitchMode) {
            AnimatorSet anim = new AnimatorSet();
            anim.play(mLauncherAlpha.animateToValue(
                    mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
            anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));


        long duration = (long) (mEndTarget.mDurationMultiplier *
                Math.abs(endProgress - mCurrentShift.value));
        if (mRecentsView != null) {
            duration = Math.max(duration, mRecentsView.getScroller().getDuration());
        }

            anim.setDuration(duration);
            anim.addListener(new AnimationSuccessListener() {
        if (mCurrentShift.value != endProgress || mInQuickSwitchMode) {
            AnimationSuccessListener endListener = new AnimationSuccessListener() {

                @Override
                public void onAnimationSuccess(Animator animator) {
                    finishAnimationTargetSetAnimationComplete();
                    mFinishAnimation = null;
                }
            });
            };

            if (mEndTarget == HOME && !mRunningOverHome) {
                RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
                anim.addAnimatorListener(endListener);
                anim.start(mEndVelocityPxPerMs);
                mFinishAnimation = RunningWindowAnim.wrap(anim);
            } else {

                AnimatorSet anim = new AnimatorSet();
                anim.play(mLauncherAlpha.animateToValue(
                        mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
                anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));

                anim.setDuration(duration);
                anim.addListener(endListener);
                anim.start();
            mFinishAnimation = anim;
                mFinishAnimation = RunningWindowAnim.wrap(anim);
            }

        } else {
            finishAnimationTargetSetAnimationComplete();
        }
@@ -412,4 +430,26 @@ public class FallbackNoButtonInputConsumer extends
        mRecentsAnimationWrapper.setController(null);
        setStateOnUiThread(STATE_HANDLER_INVALIDATED);
    }

    /**
     * Creates an animation that transforms the current app window into the home app.
     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
     */
    private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
        HomeAnimationFactory factory = new HomeAnimationFactory() {
            @Override
            public RectF getWindowTargetRect() {
                return HomeAnimationFactory.getDefaultWindowTargetRect(mDp);
            }

            @Override
            public AnimatorPlaybackController createActivityAnimationToHome() {
                AnimatorSet anim = new AnimatorSet();
                anim.play(mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1));
                anim.setDuration(duration);
                return AnimatorPlaybackController.wrap(anim, duration);
            }
        };
        return createWindowAnimationToHome(startProgress, factory);
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -237,6 +237,6 @@ public class RectFSpringAnim {
    public interface OnUpdateListener {
        void onUpdate(RectF currentRect, float progress);

        void onCancel();
        default void onCancel() { }
    }
}
Loading