Tune springs when app animates home into the hotseat on devices
with a taskbar.

Thought about using the Builder pattern here but the CL becomes
much larger due to SwipePipToHomeAnimator having its own
Builder.

Bug: 268026344
Test: swipe up to close app thats in hotseat
      swipe back to close app thats in hotseat

Change-Id: Idd0729224374579753fc91c7820f3b04a7d3e1a4
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index da28cfa..6cb060b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -136,6 +136,8 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig;
+import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
 import com.android.quickstep.util.SurfaceTransaction;
@@ -1342,6 +1344,7 @@
         }
 
         // Get floating view and target rect.
+        boolean isInHotseat = false;
         if (launcherView instanceof LauncherAppWidgetHostView) {
             Size windowSize = new Size(mDeviceProfile.availableWidthPx,
                     mDeviceProfile.availableHeightPx);
@@ -1357,12 +1360,17 @@
                             ? null
                             : mLauncher.getTaskbarUIController().findMatchingView(launcherView),
                     true /* hideOriginal */, targetRect, false /* isOpening */);
+            isInHotseat = launcherView.getTag() instanceof ItemInfo
+                    && ((ItemInfo) launcherView.getTag()).isInHotseat();
         } else {
             targetRect.set(getDefaultWindowTargetRect());
         }
 
-        RectFSpringAnim anim = new RectFSpringAnim(closingWindowStartRect, targetRect, mLauncher,
-                mDeviceProfile);
+        boolean useTaskbarHotseatParams = mDeviceProfile.isTaskbarPresent && isInHotseat;
+        RectFSpringAnim anim = new RectFSpringAnim(useTaskbarHotseatParams
+                ? new TaskbarHotseatSpringConfig(mLauncher, closingWindowStartRect, targetRect)
+                : new DefaultSpringConfig(mLauncher, mDeviceProfile, closingWindowStartRect,
+                        targetRect));
 
         // Hook up floating views to the closing window animators.
         final int rotationChange = getRotationChange(targets);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 3151374..5e38ff0 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -38,6 +38,7 @@
 
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ObjectWrapper;
@@ -122,6 +123,12 @@
                 return workspaceView;
             }
 
+            @Override
+            public boolean isInHotseat() {
+                return workspaceView.getTag() instanceof ItemInfo
+                        && ((ItemInfo) workspaceView.getTag()).isInHotseat();
+            }
+
             @NonNull
             @Override
             public RectF getWindowTargetRect() {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 291f835..f913aff 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -39,6 +39,8 @@
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig;
+import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig;
 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
@@ -157,6 +159,13 @@
     protected abstract class HomeAnimationFactory {
         protected float mSwipeVelocity;
 
+        /**
+         * Returns true if we know the home animation involves an item in the hotseat.
+         */
+        public boolean isInHotseat() {
+            return false;
+        }
+
         public @NonNull RectF getWindowTargetRect() {
             PagedOrientationHandler orientationHandler = getOrientationHandler();
             DeviceProfile dp = mDp;
@@ -288,7 +297,11 @@
         homeToWindowPositionMap.invert(windowToHomePositionMap);
         windowToHomePositionMap.mapRect(startRect);
 
-        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext, mDp);
+        boolean useTaskbarHotseatParams = mDp.isTaskbarPresent
+                && homeAnimationFactory.isInHotseat();
+        RectFSpringAnim anim = new RectFSpringAnim(useTaskbarHotseatParams
+                ? new TaskbarHotseatSpringConfig(mContext, startRect, targetRect)
+                : new DefaultSpringConfig(mContext, mDp, startRect, targetRect));
         homeAnimationFactory.setAnimation(anim);
 
         SpringAnimationRunner runner = new SpringAnimationRunner(
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index 68739ba..251b756 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -128,37 +128,27 @@
 
     @Tracking
     public final int mTracking;
+    protected final float mStiffnessX;
+    protected final float mStiffnessY;
+    protected final float mDampingX;
+    protected final float mDampingY;
+    protected final float mRectStiffness;
 
-    public RectFSpringAnim(RectF startRect, RectF targetRect, Context context,
-            @Nullable DeviceProfile deviceProfile) {
-        mStartRect = startRect;
-        mTargetRect = targetRect;
+    public RectFSpringAnim(SpringConfig config) {
+        mStartRect = config.startRect;
+        mTargetRect = config.targetRect;
         mCurrentCenterX = mStartRect.centerX();
 
-        ResourceProvider rp = DynamicResource.provider(context);
-        mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change);
-        mMaxVelocityPxPerS = (int) rp.getDimension(R.dimen.swipe_up_max_velocity);
+        mMinVisChange = config.minVisChange;
+        mMaxVelocityPxPerS = config.maxVelocityPxPerS;
         setCanRelease(true);
 
-        if (deviceProfile == null) {
-            mTracking = startRect.bottom < targetRect.bottom
-                    ? TRACKING_BOTTOM
-                    : TRACKING_TOP;
-        } else {
-            int heightPx = deviceProfile.heightPx;
-            Rect padding = deviceProfile.workspacePadding;
-
-            final float topThreshold = heightPx / 3f;
-            final float bottomThreshold = deviceProfile.heightPx - padding.bottom;
-
-            if (targetRect.bottom > bottomThreshold) {
-                mTracking = TRACKING_BOTTOM;
-            } else if (targetRect.top < topThreshold) {
-                mTracking = TRACKING_TOP;
-            } else {
-                mTracking = TRACKING_CENTER;
-            }
-        }
+        mTracking = config.tracking;
+        mStiffnessX = config.stiffnessX;
+        mStiffnessY = config.stiffnessY;
+        mDampingX = config.dampingX;
+        mDampingY = config.dampingY;
+        mRectStiffness = config.rectStiffness;
 
         mCurrentY = getTrackedYFromRect(mStartRect);
     }
@@ -240,14 +230,15 @@
         float maxXValue = Math.max(startX, endX);
 
         mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
-                dampedXVelocityPxPerS, mMinVisChange, minXValue, maxXValue, onXEndListener);
+                dampedXVelocityPxPerS, mMinVisChange, minXValue, maxXValue, mDampingX, mStiffnessX,
+                onXEndListener);
 
         float startY = mCurrentY;
         float endY = getTrackedYFromRect(mTargetRect);
         float minYValue = Math.min(startY, endY);
         float maxYValue = Math.max(startY, endY);
         mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, dampedYVelocityPxPerS,
-                mMinVisChange, minYValue, maxYValue, onYEndListener);
+                mMinVisChange, minYValue, maxYValue, mDampingY, mStiffnessY, onYEndListener);
 
         float minVisibleChange = Math.abs(1f / mStartRect.height());
         ResourceProvider rp = DynamicResource.provider(context);
@@ -368,4 +359,98 @@
 
         default void onCancel() { }
     }
+
+    private abstract static class SpringConfig {
+        protected RectF startRect;
+        protected RectF targetRect;
+        protected @Tracking int tracking;
+        protected float stiffnessX;
+        protected float stiffnessY;
+        protected float dampingX;
+        protected float dampingY;
+        protected float rectStiffness;
+        protected float minVisChange;
+        protected int maxVelocityPxPerS;
+
+        private SpringConfig(Context context, RectF start, RectF target) {
+            startRect = start;
+            targetRect = target;
+
+            ResourceProvider rp = DynamicResource.provider(context);
+            minVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change);
+            maxVelocityPxPerS = (int) rp.getDimension(R.dimen.swipe_up_max_velocity);
+        }
+    }
+
+    /**
+     * Standard spring configuration parameters.
+     */
+    public static class DefaultSpringConfig extends SpringConfig {
+
+        public DefaultSpringConfig(Context context, DeviceProfile deviceProfile,
+                RectF startRect, RectF targetRect) {
+            super(context, startRect, targetRect);
+
+            ResourceProvider rp = DynamicResource.provider(context);
+            tracking = getDefaultTracking(deviceProfile);
+            stiffnessX = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness);
+            stiffnessY = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness);
+            dampingX = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
+            dampingY = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
+
+            this.startRect = startRect;
+            this.targetRect = targetRect;
+
+            // Increase the stiffness for devices where we want the window size to transform
+            // quicker.
+            boolean shouldUseHigherStiffness = deviceProfile != null
+                    && (deviceProfile.isLandscape || deviceProfile.isTablet);
+            rectStiffness = shouldUseHigherStiffness
+                    ? rp.getFloat(R.dimen.swipe_up_rect_scale_higher_stiffness)
+                    : rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
+        }
+
+        private @Tracking int getDefaultTracking(@Nullable DeviceProfile deviceProfile) {
+            @Tracking int tracking;
+            if (deviceProfile == null) {
+                tracking = startRect.bottom < targetRect.bottom
+                        ? TRACKING_BOTTOM
+                        : TRACKING_TOP;
+            } else {
+                int heightPx = deviceProfile.heightPx;
+                Rect padding = deviceProfile.workspacePadding;
+
+                final float topThreshold = heightPx / 3f;
+                final float bottomThreshold = deviceProfile.heightPx - padding.bottom;
+
+                if (targetRect.bottom > bottomThreshold) {
+                    tracking = TRACKING_BOTTOM;
+                } else if (targetRect.top < topThreshold) {
+                    tracking = TRACKING_TOP;
+                } else {
+                    tracking = TRACKING_CENTER;
+                }
+            }
+            return tracking;
+        }
+    }
+
+    /**
+     * Spring configuration parameters for Taskbar/Hotseat items on devices that have a taskbar.
+     */
+    public static class TaskbarHotseatSpringConfig extends SpringConfig {
+
+        public TaskbarHotseatSpringConfig(Context context, RectF start, RectF target) {
+            super(context, start, target);
+
+            ResourceProvider rp = DynamicResource.provider(context);
+            tracking = TRACKING_CENTER;
+            stiffnessX = rp.getFloat(R.dimen.taskbar_swipe_up_rect_x_stiffness);
+            stiffnessY = rp.getFloat(R.dimen.taskbar_swipe_up_rect_y_stiffness);
+            dampingX = rp.getFloat(R.dimen.taskbar_swipe_up_rect_x_damping);
+            dampingY = rp.getFloat(R.dimen.taskbar_swipe_up_rect_y_damping);
+            rectStiffness = rp.getFloat(R.dimen.taskbar_swipe_up_rect_scale_stiffness);
+        }
+    }
+
 }
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index e6214f6..1112f4d 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -124,7 +124,8 @@
             int cornerRadius,
             int shadowRadius,
             @NonNull View view) {
-        super(startBounds, new RectF(destinationBoundsTransformed), context, null);
+        super(new DefaultSpringConfig(context, null, startBounds,
+                new RectF(destinationBoundsTransformed)));
         mTaskId = taskId;
         mActivityInfo = activityInfo;
         mLeash = leash;
diff --git a/res/values/config.xml b/res/values/config.xml
index 1206dd8..e13b51c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -164,6 +164,13 @@
     <item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
     <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
 
+    <!-- These params are only used for hotseat items on devices that have a taskbar. -->
+    <item name="taskbar_swipe_up_rect_x_stiffness" type="dimen" format="float">350</item>
+    <item name="taskbar_swipe_up_rect_x_damping" type="dimen" format="float">0.9</item>
+    <item name="taskbar_swipe_up_rect_y_stiffness" type="dimen" format="float">200</item>
+    <item name="taskbar_swipe_up_rect_y_damping" type="dimen" format="float">0.78</item>
+    <item name="taskbar_swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
+
     <item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
     <item name="staggered_stiffness" type="dimen" format="float">150</item>
     <dimen name="unlock_staggered_velocity_dp_per_s">2dp</dimen>
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index 51eab4c..daf0156 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -41,11 +41,9 @@
 
     public <K> FlingSpringAnim(K object, Context context, FloatPropertyCompat<K> property,
             float startPosition, float targetPosition, float startVelocityPxPerS,
-            float minVisChange, float minValue, float maxValue,
+            float minVisChange, float minValue, float maxValue, float damping, float stiffness,
             OnAnimationEndListener onEndListener) {
         ResourceProvider rp = DynamicResource.provider(context);
-        float damping = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio);
-        float stiffness = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness);
         float friction = rp.getFloat(R.dimen.swipe_up_rect_xy_fling_friction);
 
         mFlingAnim = new FlingAnimation(object, property)
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 4eb2e9e..41a603d 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -307,6 +307,13 @@
     }
 
     /**
+     * Returns if an Item is in the hotseat.
+     */
+    public boolean isInHotseat() {
+        return container == CONTAINER_HOTSEAT || container == CONTAINER_HOTSEAT_PREDICTION;
+    }
+
+    /**
      * Returns whether this item should use the background animation.
      */
     public boolean shouldUseBackgroundAnimation() {