Add WorkspaceRevealAnim, successor to StaggeredWorkspaceAnim.

- Added support for widgets for the new close animation.
- This anim is used with the new app close.
- This anim is not compataible with the old app close, so we need to
  keep StaggeredWorkspaceAnim in case we turn the app close flag off.

- We will also use this anim in the lockscreen to launcher anim, will
  connect them in a follow up CL.

Bug: 188413065
Bug: 173107751
Test: manual
Change-Id: Icab3fd700f2c268bb1c4509a37466e4e6748f789
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 275dfda..1b8fcb3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -75,7 +75,6 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.RunnableList;
@@ -86,8 +85,8 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityCompat;
@@ -1213,10 +1212,7 @@
                             }
                         });
                     } else {
-                        float velocityPxPerS = DynamicResource.provider(mLauncher)
-                                .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
-                        anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
-                                .getAnimators());
+                        anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
                     }
                 }
             }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 5837a70..40c3e02 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -73,7 +73,7 @@
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.LauncherRecentsView;
 
 /**
@@ -384,8 +384,7 @@
             updateNonOverviewAnim(targetState, config);
             nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
 
-            new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */)
-                    .start();
+            new WorkspaceRevealAnim(mLauncher, false /* animateOverviewScrim */).start();
         } else {
             boolean canceled = targetState == NORMAL;
             if (canceled) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 1317b4c..e0f430d 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -102,10 +102,10 @@
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.SwipePipToHomeAnimator;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -201,7 +201,7 @@
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
 
     public static final long MAX_SWIPE_DURATION = 350;
-    public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
+    public static final long HOME_DURATION = WorkspaceRevealAnim.DURATION_MS;
 
     public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
     private static final float SWIPE_DURATION_MULTIPLIER =
@@ -1126,6 +1126,7 @@
                 windowAnim.start(mContext, velocityPxPerMs);
                 mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
             }
+            homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
             homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             mLauncherTransitionController = null;
 
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index f125063..811af7e 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -18,13 +18,14 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.Utilities.boundToRange;
 import static com.android.launcher3.Utilities.dpToPx;
-import static com.android.launcher3.Utilities.mapToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 
+import static java.lang.Math.round;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -52,10 +53,12 @@
 import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.views.FloatingIconView;
+import com.android.launcher3.views.FloatingView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.quickstep.util.AppCloseConfig;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -112,10 +115,6 @@
     private HomeAnimationFactory createIconHomeAnimationFactory(View workspaceView) {
         final ResourceProvider rp = DynamicResource.provider(mActivity);
         final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
-        float dpPerSecond = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp_per_s));
-        final float launcherAlphaMax =
-                rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress);
-
         RectF iconLocation = new RectF();
         FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView,
                 true /* hideOriginal */, iconLocation, false /* isOpening */);
@@ -123,73 +122,25 @@
         // 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.
         float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
-        return new LauncherHomeAnimationFactory() {
+
+        return new FloatingViewHomeAnimationFactory(floatingIconView) {
 
             // There is a delay in loading the icon, so we need to keep the window
             // opaque until it is ready.
             private boolean mIsFloatingIconReady = false;
 
-            private @Nullable ValueAnimator mBounceBackAnimator;
-
             @Override
             public RectF getWindowTargetRect() {
-                if (PROTOTYPE_APP_CLOSE.get()) {
-                    // We want the target rect to be at this offset position, so that all
-                    // launcher content can spring back upwards.
-                    floatingIconView.setPositionOffsetY(transY);
-                }
+                super.getWindowTargetRect();
                 return iconLocation;
             }
 
             @Override
             public void setAnimation(RectFSpringAnim anim) {
+                super.setAnimation(anim);
                 anim.addAnimatorListener(floatingIconView);
                 floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
                 floatingIconView.setFastFinishRunnable(anim::end);
-                if (PROTOTYPE_APP_CLOSE.get()) {
-                    mBounceBackAnimator = bounceBackToRestingPosition();
-                    // Use a spring to put drag layer translation back to 0.
-                    anim.addAnimatorListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            floatingIconView.setPositionOffsetY(0);
-                            mBounceBackAnimator.start();
-                        }
-                    });
-
-                    Workspace workspace = mActivity.getWorkspace();
-                    workspace.setPivotToScaleWithSelf(mActivity.getHotseat());
-                }
-            }
-
-            private ValueAnimator bounceBackToRestingPosition() {
-                DragLayer dl = mActivity.getDragLayer();
-                Workspace workspace = mActivity.getWorkspace();
-                Hotseat hotseat = mActivity.getHotseat();
-
-                final float startValue = transY;
-                final float endValue = 0;
-                // Ensures the velocity is always aligned with the direction.
-                float pixelPerSecond = Math.abs(dpPerSecond) * Math.signum(endValue - transY);
-
-                ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
-                        .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
-                        .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
-                        .setMinimumVisibleChange(1f)
-                        .setStartValue(startValue)
-                        .setEndValue(endValue)
-                        .setStartVelocity(pixelPerSecond)
-                        .build(dl, VIEW_TRANSLATE_Y);
-                springTransY.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        dl.setTranslationY(0f);
-                        dl.setAlpha(1f);
-                        SCALE_PROPERTY.set(workspace, 1f);
-                        SCALE_PROPERTY.set(hotseat, 1f);
-                    }
-                });
-                return springTransY;
             }
 
             @Override
@@ -204,34 +155,15 @@
             @Override
             public void update(@Nullable AppCloseConfig config, RectF currentRect,
                     float progress, float radius) {
+                super.update(config, currentRect, progress, radius);
                 int fgAlpha = 255;
                 if (config != null && PROTOTYPE_APP_CLOSE.get()) {
-                    DragLayer dl = mActivity.getDragLayer();
-                    float translationY = config.getWorkspaceTransY();
-                    dl.setTranslationY(translationY);
-
-                    float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR);
-                    dl.setAlpha(Math.min(alpha, 1f));
-
-                    float scale = Math.min(1f, config.getWorkspaceScale());
-                    SCALE_PROPERTY.set(mActivity.getWorkspace(), scale);
-                    SCALE_PROPERTY.set(mActivity.getHotseat(), scale);
-                    SCALE_PROPERTY.set(mActivity.getAppsView(), scale);
-
                     progress = config.getInterpolatedProgress();
                     fgAlpha = config.getFgAlpha();
                 }
                 floatingIconView.update(1f, fgAlpha, currentRect, progress,
                         windowAlphaThreshold, radius, false);
             }
-
-            @Override
-            public void onCancel() {
-                floatingIconView.fastFinish();
-                if (mBounceBackAnimator != null) {
-                    mBounceBackAnimator.cancel();
-                }
-            }
         };
     }
 
@@ -246,10 +178,11 @@
                 hostView, backgroundLocation, windowSize,
                 mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent);
 
-        return new LauncherHomeAnimationFactory() {
+        return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
 
             @Override
             public RectF getWindowTargetRect() {
+                super.getWindowTargetRect();
                 return backgroundLocation;
             }
 
@@ -260,6 +193,8 @@
 
             @Override
             public void setAnimation(RectFSpringAnim anim) {
+                super.setAnimation(anim);
+
                 anim.addAnimatorListener(floatingWidgetView);
                 floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
                 floatingWidgetView.setFastFinishRunnable(anim::end);
@@ -273,15 +208,11 @@
             @Override
             public void update(@Nullable AppCloseConfig config, RectF currentRect,
                     float progress, float radius) {
+                super.update(config, currentRect, progress, radius);
                 floatingWidgetView.update(currentRect, 1 /* floatingWidgetAlpha */,
                         config != null ? config.getFgAlpha() : 1f /* foregroundAlpha */,
                         0 /* fallbackBackgroundAlpha */, 1 - progress);
             }
-
-            @Override
-            public void onCancel() {
-                floatingWidgetView.fastFinish();
-            }
         };
     }
 
@@ -323,6 +254,120 @@
                 true /* toRecents */, callback, true /* sendUserLeaveHint */);
     }
 
+    private class FloatingViewHomeAnimationFactory extends LauncherHomeAnimationFactory {
+
+        private final float mTransY;
+        private final FloatingView mFloatingView;
+        private ValueAnimator mBounceBackAnimator;
+        private final AnimatorSet mWorkspaceReveal;
+
+        FloatingViewHomeAnimationFactory(FloatingView floatingView) {
+            mFloatingView = floatingView;
+
+            ResourceProvider rp = DynamicResource.provider(mActivity);
+            mTransY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
+
+            mWorkspaceReveal = PROTOTYPE_APP_CLOSE.get()
+                    ? new WorkspaceRevealAnim(mActivity, true /* animateScrim */).getAnimators()
+                    : null;
+        }
+
+        @Override
+        public @NonNull RectF getWindowTargetRect() {
+            if (PROTOTYPE_APP_CLOSE.get()) {
+                // We want the target rect to be at this offset position, so that all
+                // launcher content can spring back upwards.
+                mFloatingView.setPositionOffsetY(mTransY);
+            }
+            return super.getWindowTargetRect();
+        }
+
+        @Override
+        public boolean shouldPlayAtomicWorkspaceReveal() {
+            return false;
+        }
+
+        @Override
+        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
+                float radius) {
+            if (config != null && PROTOTYPE_APP_CLOSE.get()) {
+                DragLayer dl = mActivity.getDragLayer();
+                float translationY = config.getWorkspaceTransY();
+                dl.setTranslationY(translationY);
+
+                long duration = mWorkspaceReveal.getDuration();
+                long playTime = boundToRange(round(duration * progress), 0, duration);
+                mWorkspaceReveal.setCurrentPlayTime(playTime);
+            }
+        }
+
+        protected void bounceBackToRestingPosition() {
+            final float startValue = mTransY;
+            final float endValue = 0;
+            // Ensures the velocity is always aligned with the direction.
+            float pixelPerSecond = Math.abs(mSwipeVelocity) * Math.signum(endValue - mTransY);
+
+            DragLayer dl = mActivity.getDragLayer();
+            Workspace workspace = mActivity.getWorkspace();
+            Hotseat hotseat = mActivity.getHotseat();
+
+            ResourceProvider rp = DynamicResource.provider(mActivity);
+            ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
+                    .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
+                    .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
+                    .setMinimumVisibleChange(1f)
+                    .setStartValue(startValue)
+                    .setEndValue(endValue)
+                    .setStartVelocity(pixelPerSecond)
+                    .build(dl, VIEW_TRANSLATE_Y);
+            springTransY.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dl.setTranslationY(0f);
+                    dl.setAlpha(1f);
+                    SCALE_PROPERTY.set(workspace, 1f);
+                    SCALE_PROPERTY.set(hotseat, 1f);
+                }
+            });
+
+            mBounceBackAnimator = springTransY;
+            mBounceBackAnimator.start();
+        }
+
+        @Override
+        public void setAnimation(RectFSpringAnim anim) {
+            if (PROTOTYPE_APP_CLOSE.get()) {
+                // Use a spring to put drag layer translation back to 0.
+                anim.addAnimatorListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mFloatingView.setPositionOffsetY(0);
+                        bounceBackToRestingPosition();
+                    }
+                });
+
+                // Will be updated manually below so that the two animations are in sync.
+                mWorkspaceReveal.start();
+                mWorkspaceReveal.pause();
+
+                anim.addAnimatorListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mWorkspaceReveal.end();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onCancel() {
+            mFloatingView.fastFinish();
+            if (mBounceBackAnimator != null) {
+                mBounceBackAnimator.cancel();
+            }
+        }
+    }
+
     private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
         @NonNull
         @Override
@@ -336,8 +381,12 @@
 
         @Override
         public void playAtomicAnimation(float velocity) {
-            new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */,
-                    !PROTOTYPE_APP_CLOSE.get()).start();
+            if (!PROTOTYPE_APP_CLOSE.get()) {
+                new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */)
+                        .start();
+            } else if (shouldPlayAtomicWorkspaceReveal()) {
+                new WorkspaceRevealAnim(mActivity, true).start();
+            }
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 29a00d1..b79e934 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -129,6 +129,7 @@
     }
 
     protected abstract class HomeAnimationFactory {
+        protected float mSwipeVelocity;
 
         public @NonNull RectF getWindowTargetRect() {
             PagedOrientationHandler orientationHandler = getOrientationHandler();
@@ -152,10 +153,18 @@
 
         public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
 
+        public void setSwipeVelocity(float velocity) {
+            mSwipeVelocity = velocity;
+        }
+
         public void playAtomicAnimation(float velocity) {
             // No-op
         }
 
+        public boolean shouldPlayAtomicWorkspaceReveal() {
+            return true;
+        }
+
         public void setAnimation(RectFSpringAnim anim) { }
 
         public boolean keepWindowOpaque() { return false; }
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 10b7662..badb41a 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -30,7 +30,7 @@
 
 /**
  * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
- * normal state transition and may play a {@link StaggeredWorkspaceAnim} if we're starting from an
+ * normal state transition and may play a {@link WorkspaceRevealAnim} if we're starting from an
  * upward fling.
  */
 public class OverviewToHomeAnim {
@@ -51,7 +51,7 @@
 
     /**
      * Starts the animation. If velocity < 0 (i.e. upwards), also plays a
-     * {@link StaggeredWorkspaceAnim}.
+     * {@link WorkspaceRevealAnim}.
      */
     public void animateWithVelocity(float velocity) {
         StateManager<LauncherState> stateManager = mLauncher.getStateManager();
@@ -61,18 +61,18 @@
         }
         AnimatorSet anim = new AnimatorSet();
 
-        boolean playStaggeredWorkspaceAnim = velocity < 0;
-        if (playStaggeredWorkspaceAnim) {
-            StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
-                    mLauncher, velocity, false /* animateOverviewScrim */);
-            staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
+        boolean playWorkspaceRevealAnim = velocity < 0;
+        if (playWorkspaceRevealAnim) {
+            WorkspaceRevealAnim workspaceRevealAnim = new WorkspaceRevealAnim(mLauncher,
+                    false /* animateOverviewScrim */);
+            workspaceRevealAnim.addAnimatorListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
                     mIsHomeStaggeredAnimFinished = true;
                     maybeOverviewToHomeAnimComplete();
                 }
             });
-            anim.play(staggeredWorkspaceAnim.getAnimators());
+            anim.play(workspaceRevealAnim.getAnimators());
         } else {
             mIsHomeStaggeredAnimFinished = true;
         }
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
new file mode 100644
index 0000000..50da93b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DynamicResource;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.plugins.ResourceProvider;
+
+/**
+ * Creates an animation that reveals the workspace.
+ * This is used in conjunction with the swipe up to home animation.
+ */
+public class WorkspaceRevealAnim {
+
+    // Should be used for animations running alongside this WorkspaceRevealAnim.
+    public static final int DURATION_MS = 350;
+
+    private final float mScaleStart;
+    private final AnimatorSet mAnimators = new AnimatorSet();
+
+    public WorkspaceRevealAnim(Launcher launcher, boolean animateOverviewScrim) {
+        prepareToAnimate(launcher, animateOverviewScrim);
+
+        ResourceProvider rp = DynamicResource.provider(launcher);
+        mScaleStart = rp.getFloat(R.dimen.swipe_up_scale_start);
+
+        Workspace workspace = launcher.getWorkspace();
+        workspace.setPivotToScaleWithSelf(launcher.getHotseat());
+
+        // Add reveal animations.
+        addRevealAnimatorsForView(workspace);
+        addRevealAnimatorsForView(launcher.getHotseat());
+
+        // Add overview scrim animation.
+        if (animateOverviewScrim) {
+            PendingAnimation overviewScrimBuilder = new PendingAnimation(DURATION_MS);
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(overviewScrimBuilder, NORMAL, new StateAnimationConfig());
+            mAnimators.play(overviewScrimBuilder.buildAnim());
+        }
+
+        // Add depth controller animation.
+        if (launcher instanceof BaseQuickstepLauncher) {
+            PendingAnimation depthBuilder = new PendingAnimation(DURATION_MS);
+            DepthController depth = ((BaseQuickstepLauncher) launcher).getDepthController();
+            depth.setStateWithAnimation(NORMAL, new StateAnimationConfig(), depthBuilder);
+            mAnimators.play(depthBuilder.buildAnim());
+        }
+
+        // Add sysui scrim animation.
+        mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f));
+
+        mAnimators.setDuration(DURATION_MS);
+        mAnimators.setInterpolator(Interpolators.DECELERATED_EASE);
+    }
+
+    private void addRevealAnimatorsForView(View v) {
+        ObjectAnimator scale = ObjectAnimator.ofFloat(v, SCALE_PROPERTY, mScaleStart, 1f);
+        scale.setDuration(DURATION_MS);
+        scale.setInterpolator(Interpolators.DECELERATED_EASE);
+        mAnimators.play(scale);
+
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0, 1f);
+        alpha.setDuration(DURATION_MS);
+        alpha.setInterpolator(Interpolators.DECELERATED_EASE);
+        mAnimators.play(alpha);
+    }
+
+    /**
+     * Setup workspace with 0 duration.
+     */
+    private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
+        StateAnimationConfig config = new StateAnimationConfig();
+        config.animFlags = SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER | SKIP_SCRIM;
+        config.duration = 0;
+        // setRecentsAttachedToAppWindow() will animate recents out.
+        launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
+
+        // Stop scrolling so that it doesn't interfere with the translation offscreen.
+        launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
+
+        if (animateOverviewScrim) {
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(NO_ANIM_PROPERTY_SETTER, BACKGROUND_APP, config);
+        }
+    }
+
+    public AnimatorSet getAnimators() {
+        return mAnimators;
+    }
+
+    public WorkspaceRevealAnim addAnimatorListener(Animator.AnimatorListener listener) {
+        mAnimators.addListener(listener);
+        return this;
+    }
+
+    /**
+     * Starts the animation.
+     */
+    public void start() {
+        mAnimators.start();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 121e094..0012dd8 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.views.FloatingView;
 import com.android.launcher3.views.ListenerView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.RoundedCornerEnforcement;
@@ -41,7 +42,7 @@
 /** A view that mimics an App Widget through a launch animation. */
 @TargetApi(Build.VERSION_CODES.S)
 public class FloatingWidgetView extends FrameLayout implements AnimatorListener,
-        OnGlobalLayoutListener {
+        OnGlobalLayoutListener, FloatingView {
     private static final Matrix sTmpMatrix = new Matrix();
 
     private final Launcher mLauncher;
@@ -59,6 +60,8 @@
     private Runnable mOnTargetChangeRunnable;
     private boolean mAppTargetIsTranslucent;
 
+    private float mIconOffsetY;
+
     public FloatingWidgetView(Context context) {
         this(context, null);
     }
@@ -129,6 +132,7 @@
     }
 
     /** Callback at the end or early exit of the animation. */
+    @Override
     public void fastFinish() {
         if (isUninitialized()) return;
         Runnable fastFinishRunnable = mFastFinishRunnable;
@@ -192,6 +196,12 @@
         positionViews();
     }
 
+    @Override
+    public void setPositionOffsetY(float y) {
+        mIconOffsetY = y;
+        onGlobalLayout();
+    }
+
     /** Sets the layout parameters of the floating view and its background view child. */
     private void positionViews() {
         LayoutParams layoutParams = (LayoutParams) getLayoutParams();
@@ -200,7 +210,7 @@
 
         // FloatingWidgetView layout is forced LTR
         mBackgroundView.setTranslationX(mBackgroundPosition.left);
-        mBackgroundView.setTranslationY(mBackgroundPosition.top);
+        mBackgroundView.setTranslationY(mBackgroundPosition.top + mIconOffsetY);
         LayoutParams backgroundParams = (LayoutParams) mBackgroundView.getLayoutParams();
         backgroundParams.leftMargin = 0;
         backgroundParams.topMargin = 0;
@@ -215,7 +225,8 @@
             sTmpMatrix.setTranslate(-mBackgroundOffset.left - mAppWidgetView.getLeft(),
                     -mBackgroundOffset.top - mAppWidgetView.getTop());
             sTmpMatrix.postScale(foregroundScale, foregroundScale);
-            sTmpMatrix.postTranslate(mBackgroundPosition.left, mBackgroundPosition.top);
+            sTmpMatrix.postTranslate(mBackgroundPosition.left, mBackgroundPosition.top
+                    + mIconOffsetY);
             mForegroundOverlayView.setMatrix(sTmpMatrix);
         }
     }
@@ -240,6 +251,7 @@
     }
 
     private void recycle() {
+        mIconOffsetY = 0;
         mEndRunnable = null;
         mFastFinishRunnable = null;
         mOnTargetChangeRunnable = null;
diff --git a/res/values/config.xml b/res/values/config.xml
index 102d4e0..77c7e98 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -126,7 +126,7 @@
 
     <item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
 
-    <item name="swipe_up_scale_start"  type="dimen" format="float">0.98</item>
+    <item name="swipe_up_scale_start"  type="dimen" format="float">0.88</item>
     <item name="swipe_up_duration"  type="dimen" format="float">400</item>
 
     <item name="swipe_up_trans_y_dp"  type="dimen" format="float">4.5</item>
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 81581fa..f973c2b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -63,7 +63,7 @@
  */
 @TargetApi(Build.VERSION_CODES.Q)
 public class FloatingIconView extends FrameLayout implements
-        Animator.AnimatorListener, OnGlobalLayoutListener {
+        Animator.AnimatorListener, OnGlobalLayoutListener, FloatingView {
 
     private static final String TAG = FloatingIconView.class.getSimpleName();
 
@@ -443,6 +443,7 @@
         mFastFinishRunnable = runnable;
     }
 
+    @Override
     public void fastFinish() {
         if (mFastFinishRunnable != null) {
             mFastFinishRunnable.run();
@@ -475,9 +476,7 @@
     @Override
     public void onAnimationRepeat(Animator animator) {}
 
-    /**
-     * Offsets and updates the position of this view by {@param y}.
-     */
+    @Override
     public void setPositionOffsetY(float y) {
         mIconOffsetY = y;
         onGlobalLayout();
diff --git a/src/com/android/launcher3/views/FloatingView.java b/src/com/android/launcher3/views/FloatingView.java
new file mode 100644
index 0000000..ea4fd15
--- /dev/null
+++ b/src/com/android/launcher3/views/FloatingView.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+/**
+ * Shared interface for floating views.
+ */
+public interface FloatingView {
+
+    /**
+     * Offsets and updates the position of this view by {@param y}.
+     */
+    void setPositionOffsetY(float y);
+
+    /**
+     * Fast finish the animation.
+     */
+    void fastFinish();
+}