Merge "Fixing widget not laid-out properly in preview" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index fc1556e..872633c 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -415,8 +415,8 @@
 
     @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        return Stream.concat(super.getSupportedShortcuts(),
-                Stream.of(WellbeingModel.SHORTCUT_FACTORY));
+        return Stream.concat(Stream.of(WellbeingModel.SHORTCUT_FACTORY),
+                super.getSupportedShortcuts());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index dccc9e1..66ccee6 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -34,6 +34,7 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
@@ -508,20 +509,23 @@
                 launcherAnimator.play(scaleAnim);
             });
 
-            int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
-            int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
-            int[] colors = isAppOpening
-                    ? new int[] {scrimColorTrans, scrimColor}
-                    : new int[] {scrimColor, scrimColorTrans};
-            ScrimView scrimView = mLauncher.getScrimView();
-            if (scrimView.getBackground() instanceof ColorDrawable) {
-                scrimView.setBackgroundColor(colors[0]);
+            final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
+            if (scrimEnabled) {
+                int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
+                int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
+                int[] colors = isAppOpening
+                        ? new int[]{scrimColorTrans, scrimColor}
+                        : new int[]{scrimColor, scrimColorTrans};
+                ScrimView scrimView = mLauncher.getScrimView();
+                if (scrimView.getBackground() instanceof ColorDrawable) {
+                    scrimView.setBackgroundColor(colors[0]);
 
-                ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
-                        colors);
-                scrim.setDuration(CONTENT_SCRIM_DURATION);
-                scrim.setInterpolator(DEACCEL_1_5);
-                launcherAnimator.play(scrim);
+                    ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
+                            colors);
+                    scrim.setDuration(CONTENT_SCRIM_DURATION);
+                    scrim.setInterpolator(DEACCEL_1_5);
+                    launcherAnimator.play(scrim);
+                }
             }
 
             // Pause page indicator animations as they lead to layer trashing.
@@ -532,7 +536,9 @@
                     SCALE_PROPERTY.set(view, 1f);
                     view.setLayerType(View.LAYER_TYPE_NONE, null);
                 });
-                scrimView.setBackgroundColor(Color.TRANSPARENT);
+                if (scrimEnabled) {
+                    mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
+                }
                 mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
             };
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index f4168d9..255ba1e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,27 +15,29 @@
  */
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
-import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_LAUNCHER_STATE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.graphics.Rect;
 import android.view.MotionEvent;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 
 
 /**
@@ -51,12 +53,19 @@
     final TaskbarDragLayer mTaskbarDragLayer;
     final TaskbarView mTaskbarView;
 
+    private final AnimatedFloat mIconAlignmentForResumedState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForGestureState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+
     private AnimatedFloat mTaskbarBackgroundAlpha;
     private AlphaProperty mIconAlphaForHome;
-    private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
     private TaskbarKeyguardController mKeyguardController;
 
+    private LauncherState mTargetStateOverride = null;
+    private TaskbarControllers mControllers;
+
     public LauncherTaskbarUIController(
             BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
         mContext = context;
@@ -67,7 +76,6 @@
         mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
         mHotseatController = new TaskbarHotseatController(
                 mLauncher, mTaskbarView::updateHotseatItems);
-
     }
 
     @Override
@@ -77,21 +85,17 @@
         MultiValueAlpha taskbarIconAlpha = taskbarControllers.taskbarViewController
                 .getTaskbarIconAlpha();
         mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
-        mTaskbarStateHandler.setAnimationController(taskbarIconAlpha.getProperty(
-                ALPHA_INDEX_LAUNCHER_STATE));
+        mControllers = taskbarControllers;
+
         mHotseatController.init();
-        setTaskbarViewVisible(!mLauncher.hasBeenResumed());
         mLauncher.setTaskbarUIController(this);
         mKeyguardController = taskbarControllers.taskbarKeyguardController;
+        onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
+        mIconAlignmentForResumedState.finishAnimation();
     }
 
     @Override
     protected void onDestroy() {
-        if (mAnimator != null) {
-            // End this first, in case it relies on properties that are about to be cleaned up.
-            mAnimator.end();
-        }
-        mTaskbarStateHandler.setAnimationController(null);
         mHotseatController.cleanup();
         setTaskbarViewVisible(true);
         mLauncher.getHotseat().setIconsAlpha(1f);
@@ -100,7 +104,7 @@
 
     @Override
     protected boolean isTaskbarTouchable() {
-        return !mIsAnimatingToLauncher;
+        return !mIsAnimatingToLauncher && mTargetStateOverride == null;
     }
 
     @Override
@@ -128,63 +132,82 @@
             }
         }
 
-        long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
-        if (mAnimator != null) {
-            mAnimator.cancel();
-        }
-        if (isResumed) {
-            mAnimator = createAnimToLauncher(mLauncher.getStateManager().getState(), duration);
-        } else {
-            mAnimator = createAnimToApp(duration);
-        }
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimator = null;
-            }
-        });
-        mAnimator.start();
+        ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
+                getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
+                .setDuration(QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
+
+        anim.addListener(AnimatorListeners.forEndCallback(() -> mIsAnimatingToLauncher = false));
+        anim.start();
+        mIsAnimatingToLauncher = isResumed;
     }
 
     /**
-     * Create Taskbar animation when going from an app to Launcher.
+     * Create Taskbar animation when going from an app to Launcher as part of recents transition.
      * @param toState If known, the state we will end up in when reaching Launcher.
-     * TODO: Move this and createAnimToApp to TaskbarStateHandler using the BACKGROUND state
+     * @param callbacks callbacks to track the recents animation lifecycle. The state change is
+     *                 automatically reset once the recents animation finishes
      */
-    public Animator createAnimToLauncher(@NonNull LauncherState toState, long duration) {
-        PendingAnimation anim = new PendingAnimation(duration);
-        mTaskbarStateHandler.setState(toState, anim);
-
-        anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 0, LINEAR);
-        mTaskbarView.alignIconsWithLauncher(mLauncher.getDeviceProfile(), anim);
-
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mIsAnimatingToLauncher = true;
-            }
-
+    public Animator createAnimToLauncher(@NonNull LauncherState toState,
+            @NonNull RecentsAnimationCallbacks callbacks,
+            long duration) {
+        ObjectAnimator animator = mIconAlignmentForGestureState
+                .animateToValue(mIconAlignmentForGestureState.value, 1)
+                .setDuration(duration);
+        animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                mIsAnimatingToLauncher = false;
-                setTaskbarViewVisible(false);
+                mTargetStateOverride = null;
             }
-        });
 
-        return anim.buildAnim();
-    }
-
-    private Animator createAnimToApp(long duration) {
-        PendingAnimation anim = new PendingAnimation(duration);
-        anim.setFloat(mTaskbarBackgroundAlpha, AnimatedFloat.VALUE, 1, LINEAR);
-        anim.addListener(AnimatorListeners.forEndCallback(mTaskbarView.resetIconPosition(anim)));
-        anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                setTaskbarViewVisible(true);
+                mTargetStateOverride = toState;
             }
         });
-        return anim.buildAnim();
+        callbacks.addListener(new RecentsAnimationListener() {
+            @Override
+            public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+                endGestureStateOverride();
+            }
+
+            @Override
+            public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+                endGestureStateOverride();
+            }
+
+            private void endGestureStateOverride() {
+                callbacks.removeListener(this);
+                mIconAlignmentForGestureState
+                        .animateToValue(mIconAlignmentForGestureState.value, 0)
+                        .start();
+            }
+        });
+        return animator;
+    }
+
+    private float getCurrentIconAlignmentRatio() {
+        return  Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
+    }
+
+    private void onIconAlignmentRatioChanged() {
+        if (mControllers == null) {
+            return;
+        }
+        float alignment = getCurrentIconAlignmentRatio();
+        mControllers.taskbarViewController.setLauncherIconAlignment(
+                alignment, mLauncher.getDeviceProfile());
+
+        mTaskbarBackgroundAlpha.updateValue(1 - alignment);
+
+        LauncherState state = mTargetStateOverride != null ? mTargetStateOverride
+                : mLauncher.getStateManager().getState();
+        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+            // If the hotseat icons are visible, then switch taskbar in last frame
+            setTaskbarViewVisible(alignment < 1);
+        } else {
+            mLauncher.getHotseat().setIconsAlpha(1);
+            mIconAlphaForHome.setValue(1 - alignment);
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 000799b..106ebe5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -17,7 +17,6 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
@@ -93,6 +92,7 @@
     private final ViewCache mViewCache = new ViewCache();
 
     private final boolean mIsSafeModeEnabled;
+    private boolean mIsDestroyed = false;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
             TaskbarNavButtonController buttonController) {
@@ -208,6 +208,7 @@
      * Called when this instance of taskbar is no longer needed
      */
     public void onDestroy() {
+        mIsDestroyed = true;
         setUIController(TaskbarUIController.DEFAULT);
         mControllers.onDestroy();
         mWindowManager.removeViewImmediate(mDragLayer);
@@ -252,7 +253,7 @@
      * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
      */
     public void setTaskbarWindowHeight(int height) {
-        if (mWindowLayoutParams.height == height) {
+        if (mWindowLayoutParams.height == height || mIsDestroyed) {
             return;
         }
         if (height != MATCH_PARENT) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index 20d4133..edd2a22 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -18,45 +18,33 @@
 import static com.android.launcher3.LauncherState.TASKBAR;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.SystemUiProxy;
 
 /**
- * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
- * isn't present (i.e. {@link #setAnimationController} is never called).
+ * StateHandler to animate Taskbar according to Launcher's state machine.
  */
 public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
 
     private final BaseQuickstepLauncher mLauncher;
 
-    // Contains Taskbar-related properties we should aniamte. If null, don't do anything.
-    private @Nullable MultiValueAlpha.AlphaProperty mTaskbarAlpha = null;
-
     private AnimatedFloat mNavbarButtonAlpha = new AnimatedFloat(this::updateNavbarButtonAlpha);
 
     public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
     }
 
-    public void setAnimationController(MultiValueAlpha.AlphaProperty taskbarAlpha) {
-        mTaskbarAlpha = taskbarAlpha;
-        // Reapply state.
-        setState(mLauncher.getStateManager().getState());
-        updateNavbarButtonAlpha();
-    }
-
     @Override
     public void setState(LauncherState state) {
         setState(state, PropertySetter.NO_ANIM_PROPERTY_SETTER);
+        // Force update the alpha in case it was not initialized properly
+        updateNavbarButtonAlpha();
     }
 
     @Override
@@ -69,12 +57,7 @@
      * Sets the provided state
      */
     public void setState(LauncherState toState, PropertySetter setter) {
-        if (mTaskbarAlpha == null) {
-            return;
-        }
-
         boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
-        setter.setFloat(mTaskbarAlpha, MultiValueAlpha.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
         // Make the nav bar visible in states that taskbar isn't visible.
         // TODO: We should draw our own handle instead of showing the nav bar.
         float navbarButtonAlpha = isTaskbarVisible ? 0f : 1f;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index a952182..7753f96 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,11 +15,6 @@
  */
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -34,10 +29,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -105,51 +98,6 @@
         mIconLongClickListener = mControllerCallbacks.getOnLongClickListener();
     }
 
-    /**
-     * Aligns the icons in the taskbar to that of Launcher.
-     */
-    public void alignIconsWithLauncher(DeviceProfile launcherDp, PropertySetter setter) {
-        Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(getContext());
-        float scaleUp = ((float) launcherDp.iconSizePx)
-                / mActivityContext.getDeviceProfile().iconSizePx;
-        int hotseatCellSize =
-                (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right)
-                        / launcherDp.numShownHotseatIcons;
-
-        int offsetY = launcherDp.getTaskbarOffsetY();
-        setter.setFloat(this, VIEW_TRANSLATE_Y, -offsetY, LINEAR);
-        mActivityContext.setTaskbarWindowHeight(
-                mActivityContext.getDeviceProfile().taskbarSize + offsetY);
-
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            ItemInfo info = (ItemInfo) child.getTag();
-            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
-
-            float childCenter = (child.getLeft() + child.getRight()) / 2;
-            float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId
-                    + hotseatCellSize / 2;
-            setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
-        }
-    }
-
-    /**
-     * Aligns the icons in the taskbar to that of Launcher.
-     * @return a callback to be executed at the end of the setter
-     */
-    public Runnable resetIconPosition(PropertySetter setter) {
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            setter.setFloat(child, SCALE_PROPERTY, 1, LINEAR);
-            setter.setFloat(child, VIEW_TRANSLATE_X, 0, LINEAR);
-        }
-        setter.setFloat(this, VIEW_TRANSLATE_Y, 0, LINEAR);
-        return () -> mActivityContext.setTaskbarWindowHeight(
-                mActivityContext.getDeviceProfile().taskbarSize);
-    }
-
     private void removeAndRecycle(View view) {
         removeView(view);
         view.setOnClickListener(null);
@@ -195,6 +143,7 @@
                     // so if the info changes we need to reinflate. This should only happen if a new
                     // folder is dragged to the position that another folder previously existed.
                     removeAndRecycle(hotseatView);
+                    hotseatView = null;
                 } else {
                     // View found
                     break;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 10cc926..c7ac4a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -15,19 +15,29 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.graphics.Rect;
 import android.view.View;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.MultiValueAlpha;
 
 /**
  * Handles properties/data collection, then passes the results to TaskbarView to render.
  */
 public class TaskbarViewController {
+    private static final Runnable NO_OP = () -> { };
 
     public static final int ALPHA_INDEX_HOME = 0;
-    public static final int ALPHA_INDEX_LAUNCHER_STATE = 1;
-    public static final int ALPHA_INDEX_IME = 2;
-    public static final int ALPHA_INDEX_KEYGUARD = 3;
+    public static final int ALPHA_INDEX_IME = 1;
+    public static final int ALPHA_INDEX_KEYGUARD = 2;
 
     private final TaskbarActivityContext mActivity;
     private final TaskbarView mTaskbarView;
@@ -36,10 +46,15 @@
     // Initialized in init.
     private TaskbarControllers mControllers;
 
+    // Animation to align icons with Launcher, created lazily. This allows the controller to be
+    // active only during the animation and does not need to worry about layout changes.
+    private AnimatorPlaybackController mIconAlignControllerLazy = null;
+    private Runnable mOnControllerPreCreateCallback = NO_OP;
+
     public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
         mActivity = activity;
         mTaskbarView = taskbarView;
-        mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4);
+        mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 3);
         mTaskbarIconAlpha.setUpdateVisibility(true);
     }
 
@@ -72,6 +87,60 @@
     }
 
     /**
+     * Sets the taskbar icon alignment relative to Launcher hotseat icons
+     * @param alignmentRatio [0, 1]
+     *                       0 => not aligned
+     *                       1 => fully aligned
+     */
+    public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+        if (mIconAlignControllerLazy == null) {
+            mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
+        }
+        mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
+        if (alignmentRatio <= 0 || alignmentRatio >= 1) {
+            // Cleanup lazy controller so that it is created again in next animation
+            mIconAlignControllerLazy = null;
+        }
+    }
+
+    /**
+     * Creates an animation for aligning the taskbar icons with the provided Launcher device profile
+     */
+    private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
+        mOnControllerPreCreateCallback.run();
+        PendingAnimation setter = new PendingAnimation(100);
+        Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
+        float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx;
+        int hotseatCellSize =
+                (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right)
+                        / launcherDp.numShownHotseatIcons;
+
+        int offsetY = launcherDp.getTaskbarOffsetY();
+        setter.setFloat(mTaskbarView, VIEW_TRANSLATE_Y, -offsetY, LINEAR);
+
+        int collapsedHeight = mActivity.getDeviceProfile().taskbarSize;
+        int expandedHeight = collapsedHeight + offsetY;
+        setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
+                anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
+
+        int count = mTaskbarView.getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = mTaskbarView.getChildAt(i);
+            ItemInfo info = (ItemInfo) child.getTag();
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+
+            float childCenter = (child.getLeft() + child.getRight()) / 2;
+            float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId
+                    + hotseatCellSize / 2;
+            setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+        }
+
+        AnimatorPlaybackController controller = setter.createPlaybackController();
+        mOnControllerPreCreateCallback = () -> controller.setPlayFraction(0);
+        return controller;
+    }
+
+    /**
      * Callbacks for {@link TaskbarView} to interact with its controller.
      */
     public class TaskbarViewCallbacks {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f0b02b3..ec9893c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -192,7 +192,7 @@
     @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         return Stream.concat(
-                super.getSupportedShortcuts(), Stream.of(mHotseatPredictionController));
+                Stream.of(mHotseatPredictionController), super.getSupportedShortcuts());
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0ebaea2..a8658a7 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1107,7 +1107,8 @@
                     mActivityRestartListener);
 
             mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher(
-                    mGestureState.getEndTarget(), duration);
+                    mGestureState.getEndTarget(), duration,
+                    mTaskAnimationManager.getCurrentCallbacks());
             if (mParallelRunningAnim != null) {
                 mParallelRunningAnim.start();
             }
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index a45ced4..2699b07 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -346,7 +346,8 @@
      * an optional additional animation with the same duration.
      */
     public @Nullable Animator getParallelAnimationToLauncher(
-            GestureState.GestureEndTarget endTarget, long duration) {
+            GestureState.GestureEndTarget endTarget, long duration,
+            RecentsAnimationCallbacks callbacks) {
         if (endTarget == RECENTS) {
             ACTIVITY_TYPE activity = getCreatedActivity();
             if (activity == null) {
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 30abfbb..799a4c2 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -284,14 +284,15 @@
 
     @Override
     public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
-            long duration) {
+            long duration, RecentsAnimationCallbacks callbacks) {
         LauncherTaskbarUIController uiController = getTaskbarController();
-        Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
-        if (uiController == null) {
+        Animator superAnimator = super.getParallelAnimationToLauncher(
+                endTarget, duration, callbacks);
+        if (uiController == null || callbacks == null) {
             return superAnimator;
         }
         LauncherState toState = stateFromGestureEndTarget(endTarget);
-        Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
+        Animator taskbarAnimator = uiController.createAnimToLauncher(toState, callbacks, duration);
         if (superAnimator == null) {
             return taskbarAnimator;
         } else {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 9731bf1..1c178ad 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -28,6 +28,7 @@
 import android.os.SystemProperties;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
@@ -261,6 +262,11 @@
         mLastAppearedTaskTarget = null;
     }
 
+    @Nullable
+    public RecentsAnimationCallbacks getCurrentCallbacks() {
+        return mCallbacks;
+    }
+
     public void dump() {
         // TODO
     }
diff --git a/res/drawable/ic_block_no_shadow.xml b/res/drawable/ic_block_no_shadow.xml
index be9aa07..6ac61f4 100644
--- a/res/drawable/ic_block_no_shadow.xml
+++ b/res/drawable/ic_block_no_shadow.xml
@@ -16,8 +16,8 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="20dp"
     android:height="20dp"
-    android:viewportHeight="20.0"
-    android:viewportWidth="20.0"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
     android:tint="?android:attr/textColorPrimary">
     <path
         android:fillColor="@android:color/white"
diff --git a/res/drawable/ic_drag_handle.xml b/res/drawable/ic_drag_handle.xml
index 0181ff1..9db75f4 100644
--- a/res/drawable/ic_drag_handle.xml
+++ b/res/drawable/ic_drag_handle.xml
@@ -19,7 +19,7 @@
         android:height="@dimen/deep_shortcut_drag_handle_size"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
-        android:tint="?android:attr/textColorHint" >
+        android:tint="?android:attr/textColorPrimary" >
 
     <path
         android:pathData="M20,9H4v2h16V9z M4,15h16v-2H4V15z"
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index 14cecac..fbabdd2 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -16,8 +16,8 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="20dp"
         android:height="20dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
         android:fillColor="@android:color/white"
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 6337fae..2cdf1f4 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -45,6 +45,6 @@
         android:layout_height="@dimen/system_shortcut_icon_size"
         android:layout_marginStart="@dimen/system_shortcut_margin_start"
         android:layout_gravity="start|center_vertical"
-        android:backgroundTint="?android:attr/textColorTertiary"/>
+        android:backgroundTint="?android:attr/textColorPrimary"/>
 
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/widget_shortcut_container.xml b/res/layout/widget_shortcut_container.xml
new file mode 100644
index 0000000..a4d8eb4
--- /dev/null
+++ b/res/layout/widget_shortcut_container.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/system_shortcut_full"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/system_shortcut_header_height"
+    android:orientation="horizontal"
+    android:gravity="end|center_vertical"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:tag="@string/popup_container_iterate_children"
+    android:clipToPadding="true">
+</LinearLayout>
diff --git a/res/values-v28/styles.xml b/res/values-v28/styles.xml
deleted file mode 100644
index 7df9ce5..0000000
--- a/res/values-v28/styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2019 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.
-*/
--->
-<resources>
-    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" >
-        <item name="android:textFontWeight">400</item>
-    </style>
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a94ff06..c828146 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -98,7 +98,7 @@
     <dimen name="all_apps_header_tab_height">48dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
-    <dimen name="all_apps_header_bottom_padding">16dp</dimen>
+    <dimen name="all_apps_header_bottom_padding">6dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
     <dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 38bc272..7274aa9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -214,7 +214,9 @@
         <item name="disabledIconAlpha">.54</item>
     </style>
 
-    <style name="BaseIconUnBounded" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+    <style name="BaseIconRoot" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
+
+    <style name="BaseIconUnBounded" parent="BaseIconRoot">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
@@ -276,7 +278,7 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
-    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault" />
+    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
 
     <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index c7286e5..2d87957 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -36,6 +36,10 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 @RunWith(RobolectricTestRunner.class)
 public final class LauncherAppWidgetProviderInfoTest {
 
@@ -234,10 +238,11 @@
         Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
 
         InvariantDeviceProfile idp = new InvariantDeviceProfile();
-        idp.supportedProfiles.add(profile);
+        List<DeviceProfile> supportedProfiles = new ArrayList<>(idp.supportedProfiles);
+        supportedProfiles.add(profile);
+        idp.supportedProfiles = Collections.unmodifiableList(supportedProfiles);
         idp.numColumns = NUM_OF_COLS;
         idp.numRows = NUM_OF_ROWS;
         return idp;
     }
-
 }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 7db34a5..85d17cf 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -36,8 +36,6 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
-import androidx.appcompat.content.res.AppCompatResources;
-
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -82,6 +80,8 @@
     private boolean mAccessibleDrag;
     /** An item must be dragged at least this many pixels before this drop target is enabled. */
     private final int mDragDistanceThreshold;
+    /** The size of the drawable shown in the drop target. */
+    private final int mDrawableSize;
 
     protected CharSequence mText;
     protected ColorStateList mOriginalTextColor;
@@ -103,6 +103,7 @@
 
         Resources resources = getResources();
         mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
+        mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
     }
 
     @Override
@@ -120,14 +121,18 @@
     }
 
     protected void setDrawable(int resId) {
+        mDrawable = getContext().getDrawable(resId).mutate();
+        mDrawable.setBounds(0, 0, mDrawableSize, mDrawableSize);
+        setDrawable(mDrawable);
+    }
+
+    private void setDrawable(Drawable drawable) {
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
         if (mTextVisible) {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
-            mDrawable = getCompoundDrawablesRelative()[0];
+            setCompoundDrawablesRelative(drawable, null, null, null);
         } else {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(0, resId, 0, 0);
-            mDrawable = getCompoundDrawablesRelative()[1];
+            setCompoundDrawablesRelative(null, drawable, null, null);
         }
     }
 
@@ -238,11 +243,8 @@
             return;
         }
         final DragLayer dragLayer = mLauncher.getDragLayer();
-        final Rect from = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-
         final Rect to = getIconRect(d);
-        final float scale = (float) to.width() / from.width();
+        final float scale = (float) to.width() / d.dragView.getMeasuredWidth();
         d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
         mDropTargetBar.deferOnDragEnd();
 
@@ -252,9 +254,9 @@
             mLauncher.getStateManager().goToState(NORMAL);
         };
 
-        dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
+        dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f,
                 DRAG_VIEW_DROP_DURATION,
-                Interpolators.DEACCEL_2, Interpolators.LINEAR, onAnimationEndRunnable,
+                Interpolators.DEACCEL_2, onAnimationEndRunnable,
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
@@ -329,11 +331,7 @@
         if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
             mTextVisible = isVisible;
             setText(newText);
-            if (mTextVisible) {
-                setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
-            } else {
-                setCompoundDrawablesRelativeWithIntrinsicBounds(null, mDrawable, null, null);
-            }
+            setDrawable(mDrawable);
         }
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 115d3ae..a2bd201 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -138,7 +138,10 @@
     public int defaultLayoutId;
     int demoModeLayoutId;
 
-    public final List<DeviceProfile> supportedProfiles = new ArrayList<>();
+    /**
+     * An immutable list of supported profiles.
+     */
+    public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
 
     @Nullable public DevicePaddings devicePaddings;
 
@@ -313,10 +316,10 @@
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, metrics);
 
-        supportedProfiles.clear();
+        final List<DeviceProfile> localSupportedProfiles = new ArrayList<>();
         defaultWallpaperSize = new Point(displayInfo.currentSize);
         for (WindowBounds bounds : displayInfo.supportedBounds) {
-            supportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
+            localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
                     .setUseTwoPanels(isSplitDisplay)
                     .setWindowBounds(bounds).build());
 
@@ -334,6 +337,7 @@
             defaultWallpaperSize.x =
                     Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth));
         }
+        supportedProfiles = Collections.unmodifiableList(localSupportedProfiles);
 
         ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index aba8a40..a08fa1f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2732,9 +2732,6 @@
     public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
             final Runnable onCompleteRunnable, int animationType, final View finalView,
             boolean external) {
-        Rect from = new Rect();
-        mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
-
         int[] finalPos = new int[2];
         float scaleXY[] = new float[2];
         boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
@@ -2778,8 +2775,8 @@
                     }
                 }
             };
-            dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
-                    finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
+            dragLayer.animateViewIntoPosition(dragView, finalPos[0],
+                    finalPos[1], 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
                     duration, this);
         }
     }
@@ -2838,6 +2835,10 @@
                 removeWorkspaceItem(mDragInfo.cell);
             }
         } else if (mDragInfo != null) {
+            // When drag is cancelled, reattach content view back to its original parent.
+            if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+                d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+            }
             final CellLayout cellLayout = mLauncher.getCellLayout(
                     mDragInfo.container, mDragInfo.screenId);
             if (cellLayout != null) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 7003df1..39d1a00 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -21,6 +21,8 @@
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -81,10 +83,7 @@
         ScrimView.ScrimDrawingController {
 
     public static final float PULL_MULTIPLIER = .02f;
-    public static final float FLING_VELOCITY_MULTIPLIER = 2000f;
-
-    // Starts the springs after at least 25% of the animation has passed.
-    public static final float FLING_ANIMATION_THRESHOLD = 0.25f;
+    public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
 
     private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
@@ -645,20 +644,18 @@
     /**
      * Adds an update listener to {@param animator} that adds springs to the animation.
      */
-    public void addSpringFromFlingUpdateListener(ValueAnimator animator, float velocity) {
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            boolean shouldSpring = true;
-
+    public void addSpringFromFlingUpdateListener(ValueAnimator animator,
+            float velocity /* release velocity */,
+            float progress /* portion of the distance to travel*/) {
+        animator.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                if (shouldSpring
-                        && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
-                    absorbSwipeUpVelocity(Math.max(100, Math.abs(
-                            Math.round(velocity * FLING_VELOCITY_MULTIPLIER))));
-                    // calculate the velocity of using the not user controlled interpolator
-                    // of when the container reach the end.
-                    shouldSpring = false;
-                }
+            public void onAnimationStart(Animator animator) {
+                float distance = (float) ((1 - progress) * getHeight()); // px
+                float settleVelocity = Math.min(0, distance
+                        / (AllAppsTransitionController.INTERP_COEFF * animator.getDuration())
+                        + velocity);
+                absorbSwipeUpVelocity(Math.max(1000, Math.abs(
+                        Math.round(settleVelocity * FLING_VELOCITY_MULTIPLIER))));
             }
         });
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 8ec8269..a0551f0 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -57,6 +57,8 @@
  */
 public class AllAppsTransitionController
         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
+    // This constant should match the second derivative of the animator interpolator.
+    public static final float INTERP_COEFF = 1.7f;
     private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 5e3177a..fe2c50c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -220,6 +220,10 @@
             "ENABLE_TWO_PANEL_HOME", true,
             "Uses two panel on home screen. Only applicable on large screen devices.");
 
+    public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
+            "ENABLE_SCRIM_FOR_APP_LAUNCH", false,
+            "Enables scrim during app launch animation.");
+
     public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
             "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
 
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 011325d..5ee4203 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -17,14 +17,19 @@
 
 package com.android.launcher3.dragndrop;
 
+import static android.animation.ObjectAnimator.ofFloat;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.TypeEvaluator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -44,10 +49,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringProperty;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.graphics.Scrim;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -69,11 +75,9 @@
     private DragController mDragController;
 
     // Variables relating to animation of views after drop
-    private ValueAnimator mDropAnim = null;
+    private Animator mDropAnim = null;
 
-    @Thunk DragView mDropView = null;
-    @Thunk int mAnchorViewInitialScrollX = 0;
-    @Thunk View mAnchorView = null;
+    private DragView mDropView = null;
 
     private boolean mHoverPointClosesFolder = false;
 
@@ -220,12 +224,7 @@
     public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
             float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
             int duration) {
-        Rect r = new Rect();
-        getViewRectRelativeToSelf(dragView, r);
-        final int fromX = r.left;
-        final int fromY = r.top;
-
-        animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
+        animateViewIntoPosition(dragView, pos[0], pos[1], alpha, scaleX, scaleY,
                 onFinishRunnable, animationEndStyle, duration, null);
     }
 
@@ -241,11 +240,6 @@
         parentChildren.measureChild(child);
         parentChildren.layoutChild(child);
 
-        Rect dragViewBounds = new Rect();
-        getViewRectRelativeToSelf(dragView, dragViewBounds);
-        final int fromX = dragViewBounds.left;
-        final int fromY = dragViewBounds.top;
-
         float coord[] = new float[2];
         float childScale = child.getScaleX();
 
@@ -288,51 +282,50 @@
 
         child.setVisibility(INVISIBLE);
         Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
-        animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
+        animateViewIntoPosition(dragView, toX, toY, 1, toScale, toScale,
                 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
     }
 
-    public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
-            final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
-            float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
-            int animationEndStyle, int duration, View anchorView) {
-        Rect from = new Rect(fromX, fromY, fromX +
-                view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
-        Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
-        animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
-                null, null, onCompleteRunnable, animationEndStyle, anchorView);
-    }
-
     /**
      * This method animates a view at the end of a drag and drop animation.
-     *
+     */
+    public void animateViewIntoPosition(final DragView view,
+            final int toX, final int toY, float finalAlpha,
+            float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
+            int animationEndStyle, int duration, View anchorView) {
+        Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
+        animateView(view, to, finalAlpha, finalScaleX, finalScaleY, duration,
+                null, onCompleteRunnable, animationEndStyle, anchorView);
+    }
+
+    /**
+     * This method animates a view at the end of a drag and drop animation.
      * @param view The view to be animated. This view is drawn directly into DragLayer, and so
      *        doesn't need to be a child of DragLayer.
-     * @param from The initial location of the view. Only the left and top parameters are used.
      * @param to The final location of the view. Only the left and top parameters are used. This
-     *        location doesn't account for scaling, and so should be centered about the desired
-     *        final location (including scaling).
+*        location doesn't account for scaling, and so should be centered about the desired
+*        final location (including scaling).
      * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
      * @param finalScaleX The final scale of the view. The view is scaled about its center.
      * @param finalScaleY The final scale of the view. The view is scaled about its center.
      * @param duration The duration of the animation.
      * @param motionInterpolator The interpolator to use for the location of the view.
-     * @param alphaInterpolator The interpolator to use for the alpha of the view.
      * @param onCompleteRunnable Optional runnable to run on animation completion.
      * @param animationEndStyle Whether or not to fade out the view once the animation completes.
-     *        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
+*        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
      * @param anchorView If not null, this represents the view which the animated view stays
-     *        anchored to in case scrolling is currently taking place. Note: currently this is
-     *        only used for the X dimension for the case of the workspace.
      */
-    public void animateView(final DragView view, final Rect from, final Rect to,
-            final float finalAlpha, final float initScaleX, final float initScaleY,
-            final float finalScaleX, final float finalScaleY, int duration,
-            final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
-            final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
+    public void animateView(final DragView view, final Rect to,
+            final float finalAlpha, final float finalScaleX, final float finalScaleY, int duration,
+            final Interpolator motionInterpolator, final Runnable onCompleteRunnable,
+            final int animationEndStyle, View anchorView) {
+        view.cancelAnimation();
+        view.requestLayout();
+
+        final int[] from = getViewLocationRelativeToSelf(view);
 
         // Calculate the duration of the animation based on the object's distance
-        final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top);
+        final float dist = (float) Math.hypot(to.left - from[0], to.top - from[1]);
         final Resources res = getResources();
         final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
 
@@ -346,93 +339,45 @@
         }
 
         // Fall back to cubic ease out interpolator for the animation if none is specified
-        TimeInterpolator interpolator = null;
-        if (alphaInterpolator == null || motionInterpolator == null) {
-            interpolator = DEACCEL_1_5;
-        }
+        TimeInterpolator interpolator =
+                motionInterpolator == null ? DEACCEL_1_5 : motionInterpolator;
 
         // Animate the view
-        final float initAlpha = view.getAlpha();
-        final float dropViewScale = view.getScaleX();
-        AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float percent = (Float) animation.getAnimatedValue();
-                final int width = view.getMeasuredWidth();
-                final int height = view.getMeasuredHeight();
+        PendingAnimation anim = new PendingAnimation(duration);
+        anim.add(ofFloat(view, View.SCALE_X, finalScaleX), interpolator, SpringProperty.DEFAULT);
+        anim.add(ofFloat(view, View.SCALE_Y, finalScaleY), interpolator, SpringProperty.DEFAULT);
+        anim.setViewAlpha(view, finalAlpha, interpolator);
+        anim.setFloat(view, VIEW_TRANSLATE_Y, to.top, interpolator);
 
-                float alphaPercent = alphaInterpolator == null ? percent :
-                        alphaInterpolator.getInterpolation(percent);
-                float motionPercent = motionInterpolator == null ? percent :
-                        motionInterpolator.getInterpolation(percent);
-
-                float initialScaleX = initScaleX * dropViewScale;
-                float initialScaleY = initScaleY * dropViewScale;
-                float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
-                float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
-                float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
-
-                float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
-                float fromTop = from.top + (initialScaleY - 1f) * height / 2;
-
-                int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
-                int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
-
-                int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
-                    (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
-
-                int xPos = x - mDropView.getScrollX() + anchorAdjust;
-                int yPos = y - mDropView.getScrollY();
-
-                mDropView.setTranslationX(xPos);
-                mDropView.setTranslationY(yPos);
-                mDropView.setScaleX(scaleX);
-                mDropView.setScaleY(scaleY);
-                mDropView.setAlpha(alpha);
-            }
-        };
-        animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
-                anchorView);
+        ObjectAnimator xMotion = ofFloat(view, VIEW_TRANSLATE_X, to.left);
+        if (anchorView != null) {
+            final int startScroll = anchorView.getScrollX();
+            TypeEvaluator<Float> evaluator = (f, s, e) -> mapRange(f, s, e)
+                    + (anchorView.getScaleX() * (startScroll - anchorView.getScrollX()));
+            xMotion.setEvaluator(evaluator);
+        }
+        anim.add(xMotion, interpolator, SpringProperty.DEFAULT);
+        if (onCompleteRunnable != null) {
+            anim.addListener(forEndCallback(onCompleteRunnable));
+        }
+        playDropAnimation(view, anim.buildAnim(), animationEndStyle);
     }
 
-    public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
-            TimeInterpolator interpolator, final Runnable onCompleteRunnable,
-            final int animationEndStyle, View anchorView) {
+    /**
+     * Runs a previously constructed drop animation
+     */
+    public void playDropAnimation(final DragView view, Animator animator, int animationEndStyle) {
         // Clean up the previous animations
         if (mDropAnim != null) mDropAnim.cancel();
 
         // Show the drop view if it was previously hidden
         mDropView = view;
-        mDropView.cancelAnimation();
-        mDropView.requestLayout();
-
-        // Set the anchor view if the page is scrolling
-        if (anchorView != null) {
-            mAnchorViewInitialScrollX = anchorView.getScrollX();
-        }
-        mAnchorView = anchorView;
-
         // Create and start the animation
-        mDropAnim = new ValueAnimator();
-        mDropAnim.setInterpolator(interpolator);
-        mDropAnim.setDuration(duration);
-        mDropAnim.setFloatValues(0f, 1f);
-        mDropAnim.addUpdateListener(updateCb);
-        mDropAnim.addListener(new AnimatorListenerAdapter() {
-            public void onAnimationEnd(Animator animation) {
-                if (onCompleteRunnable != null) {
-                    onCompleteRunnable.run();
-                }
-                switch (animationEndStyle) {
-                case ANIMATION_END_DISAPPEAR:
-                    clearAnimatedView();
-                    break;
-                case ANIMATION_END_REMAIN_VISIBLE:
-                    break;
-                }
-                mDropAnim = null;
-            }
-        });
+        mDropAnim = animator;
+        mDropAnim.addListener(forEndCallback(() -> mDropAnim = null));
+        if (animationEndStyle == ANIMATION_END_DISAPPEAR) {
+            mDropAnim.addListener(forEndCallback(this::clearAnimatedView));
+        }
         mDropAnim.start();
     }
 
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 3457804..54967a9 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -34,7 +34,6 @@
         float totalScale = scaleForItem(curNumItems);
         float transX;
         float transY;
-        float overlayAlpha = 0;
 
         if (index == EXIT_INDEX) {
             // 0 1 * <-- Exit position (row 0, col 2)
@@ -55,10 +54,9 @@
         transY = mTmpPoint[1];
 
         if (params == null) {
-            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+            params = new PreviewItemDrawingParams(transX, transY, totalScale);
         } else {
             params.update(transX, transY, totalScale);
-            params.overlayAlpha = overlayAlpha;
         }
         return params;
     }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 57b289e..4fe85be 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -79,7 +79,7 @@
     private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
     private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
 
-    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
     private final FolderGridOrganizer mPreviewVerifier;
 
     private ObjectAnimator mBgColorAnimator;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index ed2d0a9..aedb224 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -113,7 +113,7 @@
     FolderGridOrganizer mPreviewVerifier;
     ClippedFolderIconLayoutRule mPreviewLayoutRule;
     private PreviewItemManager mPreviewItemManager;
-    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
     private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
 
     boolean mAnimating = false;
@@ -336,8 +336,6 @@
         if (animateView != null && mActivity instanceof Launcher) {
             final Launcher launcher = (Launcher) mActivity;
             DragLayer dragLayer = launcher.getDragLayer();
-            Rect from = new Rect();
-            dragLayer.getViewRectRelativeToSelf(animateView, from);
             Rect to = finalRect;
             if (to == null) {
                 to = new Rect();
@@ -391,7 +389,7 @@
             to.offset(center[0] - animateView.getMeasuredWidth() / 2,
                     center[1] - animateView.getMeasuredHeight() / 2);
 
-            float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
+            float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 1f : 0f;
 
             float finalScale = scale * scaleRelativeToDragLayer;
 
@@ -402,15 +400,19 @@
                 finalScale *= containerScale;
             }
 
-            dragLayer.animateView(animateView, from, to, finalAlpha,
-                    1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
-                    Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
-                    null, DragLayer.ANIMATION_END_DISAPPEAR, null);
+            final int finalIndex = index;
+            dragLayer.animateView(animateView, to, finalAlpha,
+                    finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
+                    Interpolators.DEACCEL_2,
+                    () -> {
+                        mPreviewItemManager.hidePreviewItem(finalIndex, false);
+                        mFolder.showItem(item);
+                    }, 
+                    DragLayer.ANIMATION_END_DISAPPEAR, null);
 
             mFolder.hideItem(item);
 
             if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
-            final int finalIndex = index;
 
             FolderNameInfos nameInfos = new FolderNameInfos();
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
@@ -430,8 +432,6 @@
     private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
             FolderNameInfos nameInfos, InstanceId instanceId) {
         postDelayed(() -> {
-            mPreviewItemManager.hidePreviewItem(finalIndex, false);
-            mFolder.showItem(item);
             setLabelSuggestion(nameInfos, instanceId);
             invalidate();
         }, DROP_IN_ANIMATION_DURATION);
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
index edfd2ba..e20bafb 100644
--- a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -45,7 +45,7 @@
             };
 
     private static final PreviewItemDrawingParams sTmpParams =
-            new PreviewItemDrawingParams(0, 0, 0, 0);
+            new PreviewItemDrawingParams(0, 0, 0);
     private static final float[] sTempParamsArray = new float[3];
 
     private final ObjectAnimator mAnimator;
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index 5746be8..58efdc1 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -27,17 +27,15 @@
     float transX;
     float transY;
     float scale;
-    float overlayAlpha;
     public FolderPreviewItemAnim anim;
     public boolean hidden;
     public Drawable drawable;
     public WorkspaceItemInfo item;
 
-    PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+    PreviewItemDrawingParams(float transX, float transY, float scale) {
         this.transX = transX;
         this.transY = transY;
         this.scale = scale;
-        this.overlayAlpha = overlayAlpha;
     }
 
     public void update(float transX, float transY, float scale) {
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index baafbc9..a6674fc 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -260,7 +260,7 @@
             params.remove(params.size() - 1);
         }
         while (items.size() > params.size()) {
-            params.add(new PreviewItemDrawingParams(0, 0, 0, 0));
+            params.add(new PreviewItemDrawingParams(0, 0, 0));
         }
 
         int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 7bfa3ef..3c0a54a 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -212,7 +212,8 @@
                         }
 
                         if (si.isPromise() && isNewApkAvailable) {
-                            boolean isTargetValid = true;
+                            boolean isTargetValid = !cn.getClassName().equals(
+                                    IconCache.EMPTY_CLASS_NAME);
                             if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                                 List<ShortcutInfo> shortcut =
                                         new ShortcutRequest(context, mUser)
@@ -225,7 +226,7 @@
                                     si.updateFromDeepShortcutInfo(shortcut.get(0), context);
                                     infoUpdated = true;
                                 }
-                            } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
+                            } else if (isTargetValid) {
                                 isTargetValid = context.getSystemService(LauncherApps.class)
                                         .isActivityEnabled(cn, mUser);
                             }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 332390d..2ae12ac 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -96,6 +96,8 @@
     private int mNumNotifications;
     private ViewGroup mNotificationContainer;
 
+    private ViewGroup mWidgetContainer;
+
     private ViewGroup mDeepShortcutContainer;
 
     private ViewGroup mSystemShortcutContainer;
@@ -234,7 +236,7 @@
 
     @Override
     protected List<View> getChildrenForColorExtraction() {
-        return Arrays.asList(mSystemShortcutContainer, mDeepShortcutContainer,
+        return Arrays.asList(mSystemShortcutContainer, mWidgetContainer, mDeepShortcutContainer,
                 mNotificationContainer);
     }
 
@@ -298,10 +300,24 @@
             updateHiddenShortcuts();
 
             if (!systemShortcuts.isEmpty()) {
-                mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
                 for (SystemShortcut shortcut : systemShortcuts) {
-                    initializeSystemShortcut(
-                            R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
+                    if (shortcut instanceof SystemShortcut.Widgets) {
+                        if (mWidgetContainer == null) {
+                            mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
+                                    this);
+                        }
+                        initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
+                                shortcut);
+                    }
+                }
+                mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
+
+                for (SystemShortcut shortcut : systemShortcuts) {
+                    if (!(shortcut instanceof SystemShortcut.Widgets)) {
+                        initializeSystemShortcut(
+                                R.layout.system_shortcut_icon_only, mSystemShortcutContainer,
+                                shortcut);
+                    }
                 }
             }
         } else {
@@ -524,25 +540,34 @@
             mLauncher.getPopupDataProvider().setChangeListener(null);
         }
 
+        private View getWidgetsView(ViewGroup container) {
+            for (int i = container.getChildCount() - 1; i >= 0; --i) {
+                View systemShortcutView = container.getChildAt(i);
+                if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
+                    return systemShortcutView;
+                }
+            }
+            return null;
+        }
+
         @Override
         public void onWidgetsBound() {
             ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
             SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
-            View widgetsView = null;
-            int count = mSystemShortcutContainer.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
-                if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
-                    widgetsView = systemShortcutView;
-                    break;
-                }
+            View widgetsView = getWidgetsView(PopupContainerWithArrow.this);
+            if (widgetsView == null && mWidgetContainer != null) {
+                widgetsView = getWidgetsView(mWidgetContainer);
             }
 
             if (widgetInfo != null && widgetsView == null) {
                 // We didn't have any widgets cached but now there are some, so enable the shortcut.
                 if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
-                    initializeSystemShortcut(R.layout.system_shortcut_icon_only,
-                            mSystemShortcutContainer, widgetInfo);
+                    if (mWidgetContainer == null) {
+                        mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
+                                PopupContainerWithArrow.this);
+                    }
+                    initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
+                            widgetInfo);
                 } else {
                     // If using the expanded system shortcut (as opposed to just the icon), we need
                     // to reopen the container to ensure measurements etc. all work out. While this
@@ -554,8 +579,10 @@
                 }
             } else if (widgetInfo == null && widgetsView != null) {
                 // No widgets exist, but we previously added the shortcut so remove it.
-                if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
-                    mSystemShortcutContainer.removeView(widgetsView);
+                if (mSystemShortcutContainer
+                        != PopupContainerWithArrow.this
+                        && mWidgetContainer != null) {
+                    mWidgetContainer.removeView(widgetsView);
                 } else {
                     close(false);
                     PopupContainerWithArrow.showForIcon(mOriginalIcon);
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index a086635..90f37f3 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -335,12 +335,10 @@
         mCurrentAnimation.dispatchOnStart();
         if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
             if (mAllAppsOvershootStarted) {
-
                 mLauncher.getAppsView().onRelease();
                 mAllAppsOvershootStarted = false;
-
             } else {
-                mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+                mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity, progress);
             }
         }
         anim.start();
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index c9aa51c..ac864e9 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -1,12 +1,14 @@
 package com.android.launcher3.util;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 
@@ -35,7 +37,7 @@
     protected final float mUX, mUY;
 
     protected Rect mIconRect;
-    protected Rect mFrom;
+    protected RectF mFrom;
     protected int mDuration;
     protected float mAnimationTimeFraction;
 
@@ -55,17 +57,17 @@
     @Override
     public void run() {
         mIconRect = mDropTarget.getIconRect(mDragObject);
+        mDragObject.dragView.cancelAnimation();
+        mDragObject.dragView.requestLayout();
 
         // Initiate from
-        mFrom = new Rect();
-        mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, mFrom);
-        float scale = mDragObject.dragView.getScaleX();
-        float xOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredWidth()) / 2f;
-        float yOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredHeight()) / 2f;
-        mFrom.left += xOffset;
-        mFrom.right -= xOffset;
-        mFrom.top += yOffset;
-        mFrom.bottom -= yOffset;
+        Rect from = new Rect();
+        mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, from);
+
+        mFrom = new RectF(from);
+        mFrom.inset(
+                ((1 - mDragObject.dragView.getScaleX()) * from.width()) / 2f,
+                ((1 - mDragObject.dragView.getScaleY()) * from.height()) / 2f);
         mDuration = Math.abs(mUY) > Math.abs(mUX) ? initFlingUpDuration() : initFlingLeftDuration();
 
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
@@ -95,17 +97,15 @@
             }
         };
 
-        Runnable onAnimationEndRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.getStateManager().goToState(NORMAL);
-                mDropTarget.completeDrop(mDragObject);
-            }
-        };
-
         mDropTarget.onDrop(mDragObject, mDragOptions);
-        mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator,
-                onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        anim.setDuration(duration).setInterpolator(tInterpolator);
+        anim.addUpdateListener(this);
+        anim.addListener(forEndCallback(() -> {
+            mLauncher.getStateManager().goToState(NORMAL);
+            mDropTarget.completeDrop(mDragObject);
+        }));
+        mDragLayer.playDropAnimation(mDragObject.dragView, anim, DragLayer.ANIMATION_END_DISAPPEAR);
     }
 
     /**
@@ -129,7 +129,7 @@
         }
         double t = (-mUY - Math.sqrt(d)) / mAY;
 
-        float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX();
+        float sX = -mFrom.centerX() + mIconRect.exactCenterX();
 
         // Find horizontal acceleration such that: u*t + a*t*t/2 = s
         mAX = (float) ((sX - t * mUX) * 2 / (t * t));
@@ -157,7 +157,7 @@
         }
         double t = (-mUX - Math.sqrt(d)) / mAX;
 
-        float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY();
+        float sY = -mFrom.centerY() + mIconRect.exactCenterY();
 
         // Find vertical acceleration such that: u*t + a*t*t/2 = s
         mAY = (float) ((sY - t * mUY) * 2 / (t * t));
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 01c0b56..76dfb3c 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -430,18 +430,20 @@
     }
 
     public void getViewRectRelativeToSelf(View v, Rect r) {
+        int[] loc = getViewLocationRelativeToSelf(v);
+        r.set(loc[0], loc[1], loc[0] + v.getMeasuredWidth(), loc[1] + v.getMeasuredHeight());
+    }
+
+    protected int[] getViewLocationRelativeToSelf(View v) {
         int[] loc = new int[2];
         getLocationInWindow(loc);
         int x = loc[0];
         int y = loc[1];
 
         v.getLocationInWindow(loc);
-        int vX = loc[0];
-        int vY = loc[1];
-
-        int left = vX - x;
-        int top = vY - y;
-        r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
+        loc[0] -= x;
+        loc[1] -= y;
+        return loc;
     }
 
     @Override
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 6f47df0..06bc26a 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -293,7 +293,7 @@
         try {
             final AppIconMenu menu = allApps.
                     getAppIcon(APP_NAME).
-                    openMenu();
+                    openDeepShortcutMenu();
 
             executeOnLauncher(
                     launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
@@ -341,7 +341,7 @@
         try {
             final AppIconMenu menu = allApps
                     .getAppIcon(APP_NAME)
-                    .openMenu();
+                    .openDeepShortcutMenu();
             final AppIconMenuItem menuItem0 = menu.getMenuItem(0);
             final AppIconMenuItem menuItem2 = menu.getMenuItem(2);
 
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 56723b1..57fd08a 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -51,6 +51,16 @@
         }
     }
 
+    /**
+     * Long-clicks the icon to open its menu, and looks at the deep shortcuts container only.
+     */
+    public AppIconMenu openDeepShortcutMenu() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
+                    mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
+        }
+    }
+
     @Override
     protected void addExpectedEventsForLongClick() {
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);