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

Commit 86a2f911 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Using TaskViewSimulator for both Launcher and fallback recents

Fixing fling to home animation in landscape and multi-window orientation

Bug: 155896573
Bug: 155816922
Change-Id: I5fdff8694eced415978345b026858f5167c2a198
parent 9d85f523
Loading
Loading
Loading
Loading
+206 −154
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
package com.android.quickstep;

import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
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;
@@ -23,9 +25,13 @@ import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -35,32 +41,34 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import com.android.launcher3.BaseDraggingActivity;
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.touch.PagedOrientationHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.quickstep.util.WindowSizeStrategy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;

import java.util.ArrayList;
import java.util.function.Consumer;
@@ -93,7 +101,9 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    protected final BaseActivityInterface<T> mActivityInterface;
    protected final InputConsumerController mInputConsumer;

    protected AppWindowAnimationHelper mAppWindowAnimationHelper;
    protected final TaskViewSimulator mTaskViewSimulator;
    private AnimatorPlaybackController mWindowTransitionController;

    protected final TransformParams mTransformParams = new TransformParams();

    // Shift in the range of [0, 1].
@@ -113,7 +123,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    protected T mActivity;
    protected Q mRecentsView;
    protected DeviceProfile mDp;
    private final int mPageSpacing;

    protected Runnable mGestureEndCallback;

@@ -122,18 +131,16 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    protected boolean mCanceled;
    protected int mLastStartedTaskId = -1;

    private RecentsOrientedState mOrientedState;

    protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
            GestureState gestureState, InputConsumerController inputConsumer) {
            GestureState gestureState, InputConsumerController inputConsumer,
            WindowSizeStrategy windowSizeStrategy) {
        mContext = context;
        mDeviceState = deviceState;
        mGestureState = gestureState;
        mActivityInterface = gestureState.getActivityInterface();
        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
        mInputConsumer = inputConsumer;
        mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
        mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
        mTaskViewSimulator = new TaskViewSimulator(context, windowSizeStrategy);
    }

    /**
@@ -193,7 +200,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
                updateFinalShift();
            }
        });
        mRecentsView.setAppWindowAnimationHelper(mAppWindowAnimationHelper);
        runOnRecentsAnimationStart(() ->
                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                        mRecentsAnimationTargets));
@@ -247,36 +253,29 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
    }

    protected void updateSource(Rect stackBounds, RemoteAnimationTargetCompat runningTarget) {
        mAppWindowAnimationHelper.updateSource(stackBounds, runningTarget);
    }

    @Override
    public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
            RecentsAnimationTargets targets) {
        mRecentsAnimationController = recentsAnimationController;
        mRecentsAnimationTargets = targets;
        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
        final Rect overviewStackBounds;
        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
                mGestureState.getRunningTaskId());

        if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
            overviewStackBounds = mActivityInterface
            Rect overviewStackBounds = mActivityInterface
                    .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
            dp = dp.getMultiWindowProfile(mContext, overviewStackBounds);
        } else {
            // If we are not in multi-window mode, home insets should be same as system insets.
            dp = dp.copy(mContext);
            overviewStackBounds = getStackBounds(dp);
        }
        dp.updateInsets(targets.homeContentInsets);
        dp.updateIsSeascape(mContext);
        if (runningTaskTarget != null) {
            updateSource(overviewStackBounds, runningTaskTarget);
            mTaskViewSimulator.setPreview(runningTaskTarget);
        }

        mAppWindowAnimationHelper.prepareAnimation(dp);
        initTransitionEndpoints(dp);

        // Notify when the animation starts
@@ -338,7 +337,8 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
            return new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
                    loc[1] + rootView.getHeight());
        } else {
            return new Rect(0, 0, dp.widthPx, dp.heightPx);
            return new Rect(dp.windowX, dp.windowY,
                    dp.windowX + dp.widthPx, dp.windowY + dp.heightPx);
        }
    }

@@ -348,25 +348,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
        mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength(
                dp, mContext, TEMP_RECT);

        if (!dp.isMultiWindowMode) {
            // When updating the target rect, also update the home bounds since the location on
            // screen of the launcher window may be stale (position is not updated until first
            // traversal after the window is resized).  We only do this for non-multiwindow because
            // we otherwise use the minimized home bounds provided by the system.
            mAppWindowAnimationHelper.updateHomeBounds(getStackBounds(dp));
        }
        int displayRotation = 0;
        if (mOrientedState != null && mOrientedState.isMultipleOrientationSupportedByDevice()) {
            // TODO(b/150300347): The first recents animation after launcher is started with the
            //  foreground app not in landscape will look funky until that bug is fixed
            displayRotation = mOrientedState.getDisplayRotation();

            RectF tempRectF = new RectF(TEMP_RECT);
            mOrientedState.mapRectFromRotation(displayRotation,
                    tempRectF, dp.widthPx, dp.heightPx);
            tempRectF.roundOut(TEMP_RECT);
        }
        mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
        if (mDeviceState.isFullyGesturalNavMode()) {
            // We can drag all the way to the top of the screen.
            mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
@@ -375,6 +356,24 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
            mDragLengthFactorStartPullback = dragFactorStartAndMaxProgress.first;
            mDragLengthFactorMaxPullback = dragFactorStartAndMaxProgress.second;
        }

        mTaskViewSimulator.setDp(dp);
        mTaskViewSimulator.setLayoutRotation(
                mDeviceState.getCurrentActiveRotation(),
                mDeviceState.getDisplayRotation());

        AnimatorSet anim = new AnimatorSet();
        anim.setDuration(mTransitionDragLength * 2);
        anim.setInterpolator(t -> t * mDragLengthFactor);
        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
                AnimatedFloat.VALUE,
                mTaskViewSimulator.getFullScreenScale(), 1));
        anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
                AnimatedFloat.VALUE,
                BACKGROUND_APP.getOverviewFullscreenProgress(),
                OVERVIEW.getOverviewFullscreenProgress()));
        mWindowTransitionController =
                AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
    }

    /**
@@ -385,9 +384,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    protected boolean onActivityInit(Boolean alreadyOnHome) {
        T createdActivity = mActivityInterface.getCreatedActivity();
        if (createdActivity != null) {
            mOrientedState = ((RecentsView) createdActivity.getOverviewPanel())
                .getPagedViewOrientedState();
            mAppWindowAnimationHelper = new AppWindowAnimationHelper(mOrientedState, mContext);
            initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
                .getDeviceProfile(mContext));
        }
@@ -436,34 +432,21 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    }

    /**
     * Applies the transform on the recents animation without any additional null checks
     * Applies the transform on the recents animation
     */
    protected void applyTransformUnchecked() {
        float shift = mCurrentShift.value;
        float offset = mRecentsView == null ? 0 : mRecentsView.getScrollOffsetScaled();
        float taskSize = getOrientationHandler()
            .getPrimarySize(mAppWindowAnimationHelper.getTargetRect());
        float offsetScale = getTaskCurveScaleForOffset(offset, taskSize);
        mTransformParams
                .setProgress(shift)
                .setOffset(offset)
                .setOffsetScale(offsetScale)
                .setTargetSet(mRecentsAnimationTargets);
        mAppWindowAnimationHelper.applyTransform(mTransformParams);
    }
    protected void applyWindowTransform() {
        if (mWindowTransitionController != null) {
            float progress = mCurrentShift.value / mDragLengthFactor;
            mWindowTransitionController.setPlayFraction(progress);
            mTransformParams.setTargetSet(mRecentsAnimationTargets);

    private float getTaskCurveScaleForOffset(float offset, float taskSize) {
        int dpPixel = getOrientationHandler().getShortEdgeLength(mDp);
        float distanceToReachEdge = dpPixel / 2 + taskSize / 2 + mPageSpacing;
        float interpolation = Math.min(1, offset / distanceToReachEdge);
        return TaskView.getCurveScaleForInterpolation(interpolation);
            mTaskViewSimulator.setScroll(mRecentsView == null ? 0 : mRecentsView.getScrollOffset());
            mTaskViewSimulator.apply(mTransformParams);
        }
    }

    protected PagedOrientationHandler getOrientationHandler() {
        if (mOrientedState == null) {
            return PagedOrientationHandler.PORTRAIT;
        }
        return mOrientedState.getOrientationHandler();
        return mTaskViewSimulator.getOrientationState().getOrientationHandler();
    }

    /**
@@ -474,82 +457,78 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
            HomeAnimationFactory homeAnimationFactory) {
        final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
        final View floatingView = homeAnimationFactory.getFloatingView();
        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
        final RectF startRect = new RectF(mAppWindowAnimationHelper.applyTransform(
                mTransformParams.setProgress(startProgress)
                    .setTargetSet(mRecentsAnimationTargets)));
        if (isFloatingIconView) {
            mOrientedState.mapInverseRectFromNormalOrientation(
                    startRect, mDp.widthPx, mDp.heightPx);
        }
        final FloatingIconView fiv = homeAnimationFactory.mIconView;
        final boolean isFloatingIconView = fiv != null;

        mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
        mTaskViewSimulator.apply(mTransformParams
                .setProgress(startProgress)
                .setTargetSet(mRecentsAnimationTargets));
        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());

        // Matrix to map a rect in Launcher space to window space
        Matrix homeToWindowPositionMap = new Matrix();
        mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);

        final RectF startRect = new RectF(cropRectF);
        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
        // Move the startRect to Launcher space as floatingIconView runs in Launcher
        Matrix windowToHomePositionMap = new Matrix();
        homeToWindowPositionMap.invert(windowToHomePositionMap);
        windowToHomePositionMap.mapRect(startRect);

        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
        if (isFloatingIconView) {
            FloatingIconView fiv = (FloatingIconView) floatingView;
            anim.addAnimatorListener(fiv);
            fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
            fiv.setFastFinishRunnable(anim::end);
        }

        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 = mAppWindowAnimationHelper.getCurrentCornerRadius();
        float endRadius = startRect.width() / 6f;
        SpringAnimationRunner runner = new SpringAnimationRunner(
                homeAnimationFactory, cropRectF, homeToWindowPositionMap);
        anim.addOnUpdateListener(runner);
        anim.addAnimatorListener(runner);
        return anim;
    }

        float startTransformProgress = mTransformParams.getProgress();
        float endTransformProgress = 1;
    public interface Factory {

        // 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;
        final RectF rotatedRect = new RectF();
        anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
        BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
                boolean continuingLastGesture, boolean isLikelyToStartNewTask);
    }

            @Override
            public void onUpdate(RectF currentRect, float progress) {
                homeAnim.setPlayFraction(progress);
    protected interface RunningWindowAnim {
        void end();

                rotatedRect.set(currentRect);
                if (isFloatingIconView) {
                    mOrientedState.mapRectFromNormalOrientation(
                            rotatedRect, mDp.widthPx, mDp.heightPx);
                    mTransformParams.setCornerRadius(endRadius * progress + startRadius
                        * (1f - progress));
                }
                mTransformParams.setProgress(
                    Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
                    .setCurrentRect(rotatedRect)
                    .setTargetAlpha(getWindowAlpha(progress));
                mAppWindowAnimationHelper.applyTransform(mTransformParams);
        void cancel();

                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
                            windowAlphaThreshold, mAppWindowAnimationHelper.getCurrentCornerRadius(),
                            false);
                }
        static RunningWindowAnim wrap(Animator animator) {
            return new RunningWindowAnim() {
                @Override
                public void end() {
                    animator.end();
                }

                @Override
            public void onCancel() {
                if (isFloatingIconView) {
                    ((FloatingIconView) floatingView).fastFinish();
                public void cancel() {
                    animator.cancel();
                }
            };
        }
        });
        anim.addAnimatorListener(new AnimationSuccessListener() {

        static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
            return new RunningWindowAnim() {
                @Override
            public void onAnimationStart(Animator animation) {
                homeAnim.dispatchOnStart();
                public void end() {
                    rectFSpringAnim.end();
                }

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

    /**
@@ -570,43 +549,116 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
        return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
    }

    public interface Factory {
    protected abstract class HomeAnimationFactory {

        BaseSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs,
                boolean continuingLastGesture, boolean isLikelyToStartNewTask);
        private FloatingIconView mIconView;

        public HomeAnimationFactory(@Nullable FloatingIconView iconView) {
            mIconView = iconView;
        }

    protected interface RunningWindowAnim {
        void end();
        public @NonNull RectF getWindowTargetRect() {
            PagedOrientationHandler orientationHandler = getOrientationHandler();
            DeviceProfile dp = mDp;
            final int halfIconSize = dp.iconSizePx / 2;
            float primaryDimension = orientationHandler
                    .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
            float secondaryDimension = orientationHandler
                    .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
            final float targetX =  primaryDimension / 2f;
            final float targetY = secondaryDimension - dp.hotseatBarSizePx;
            // Fallback to animate to center of screen.
            return new RectF(targetX - halfIconSize, targetY - halfIconSize,
                    targetX + halfIconSize, targetY + halfIconSize);
        }

        void cancel();
        public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();

        public void playAtomicAnimation(float velocity) {
            // No-op
        }
    }

    private class SpringAnimationRunner extends AnimationSuccessListener
            implements RectFSpringAnim.OnUpdateListener, BuilderProxy {

        final Rect mCropRect = new Rect();
        final Matrix mMatrix = new Matrix();

        final RectF mWindowCurrentRect = new RectF();
        final Matrix mHomeToWindowPositionMap;

        final FloatingIconView mFIV;
        final AnimatorPlaybackController mHomeAnim;
        final RectF mCropRectF;

        final float mStartRadius;
        final float mEndRadius;
        final float mWindowAlphaThreshold;

        SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF,
                Matrix homeToWindowPositionMap) {
            mHomeAnim = factory.createActivityAnimationToHome();
            mCropRectF = cropRectF;
            mHomeToWindowPositionMap = homeToWindowPositionMap;

            cropRectF.roundOut(mCropRect);
            mFIV = factory.mIconView;

            // 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.
            mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
            mEndRadius = cropRectF.width() / 2f;

            // 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.
            mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f;
        }

        static RunningWindowAnim wrap(Animator animator) {
            return new RunningWindowAnim() {
        @Override
                public void end() {
                    animator.end();
        public void onUpdate(RectF currentRect, float progress) {
            mHomeAnim.setPlayFraction(progress);
            mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);

            mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
            float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
            mTransformParams
                    .setTargetAlpha(getWindowAlpha(progress))
                    .setCornerRadius(cornerRadius);

            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
            if (mFIV != null) {
                mFIV.update(currentRect, 1f, progress,
                        mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false);
            }
        }

        @Override
                public void cancel() {
                    animator.cancel();
        public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
                TransformParams params) {
            if (app.mode == targetMode
                    && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                builder.withMatrix(mMatrix)
                        .withWindowCrop(mCropRect)
                        .withCornerRadius(params.getCornerRadius());
            }
            };
        }

        static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
            return new RunningWindowAnim() {
        @Override
                public void end() {
                    rectFSpringAnim.end();
        public void onCancel() {
            if (mFIV != null) {
                mFIV.fastFinish();
            }
        }

        @Override
                public void cancel() {
                    rectFSpringAnim.cancel();
        public void onAnimationStart(Animator animation) {
            mHomeAnim.dispatchOnStart();
        }
            };

        @Override
        public void onAnimationSuccess(Animator animator) {
            mHomeAnim.getAnimationPlayer().end();
        }
    }
}
+0 −37

File changed.

Preview size limit exceeded, changes collapsed.

+6 −25

File changed.

Preview size limit exceeded, changes collapsed.

+0 −65

File changed.

Preview size limit exceeded, changes collapsed.

+60 −70

File changed.

Preview size limit exceeded, changes collapsed.

Loading