Merge changes from topic "am-dbbb1f28-c3b5-48d1-928e-896c408bee5e" into ub-launcher3-master

* changes:
  [automerger] Allow clear all button in overview be clickable if visible am: 330a634648
  Allow clear all button in overview be clickable if visible
diff --git a/Android.mk b/Android.mk
index f3eb244..3945746 100644
--- a/Android.mk
+++ b/Android.mk
@@ -109,7 +109,7 @@
 LOCAL_MIN_SDK_VERSION := 21
 LOCAL_PACKAGE_NAME := Launcher3Go
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/AndroidManifest.xml \
@@ -199,7 +199,7 @@
 LOCAL_MIN_SDK_VERSION := 26
 LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/go/AndroidManifest.xml \
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index afaad38..0af2b28 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -32,7 +32,7 @@
 
     <!-- Launcher app transition -->
     <dimen name="content_trans_y">50dp</dimen>
-    <dimen name="workspace_trans_y">50dp</dimen>
+    <dimen name="springs_trans_y">-70dp</dimen>
     <dimen name="closing_window_trans_y">115dp</dimen>
 
     <dimen name="recents_empty_message_text_size">16sp</dimen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 14633af..252e3ea 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static android.view.View.TRANSLATION_Y;
 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -27,8 +28,10 @@
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OSCILLATE;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
 import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
@@ -54,6 +57,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Pair;
+import android.util.Property;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -115,12 +119,20 @@
 
     public static final int RECENTS_LAUNCH_DURATION = 336;
     public static final int RECENTS_QUICKSCRUB_LAUNCH_DURATION = 300;
-    private static final int LAUNCHER_RESUME_START_DELAY = 100;
+    private static final int LAUNCHER_RESUME_START_DELAY = 40;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
     // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
     public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
 
+    private static final int APP_CLOSE_ROW_START_DELAY_MS = 8;
+
+    // The sum of [slide, oscillate, and settle] should be <= LAUNCHER_RESUME_TOTAL_DURATION.
+    private static final int LAUNCHER_RESUME_TOTAL_DURATION = 346;
+    private static final int SPRING_SLIDE_DURATION = 166;
+    private static final int SPRING_OSCILLATE_DURATION = 130;
+    private static final int SPRING_SETTLE_DURATION = 50;
+
     private final Launcher mLauncher;
     private final DragLayer mDragLayer;
     private final AlphaProperty mDragLayerAlpha;
@@ -129,7 +141,8 @@
     private final boolean mIsRtl;
 
     private final float mContentTransY;
-    private final float mWorkspaceTransY;
+    private final float mStartSlideTransY;
+    private final float mEndSlideTransY;
     private final float mClosingWindowTransY;
 
     private DeviceProfile mDeviceProfile;
@@ -159,8 +172,9 @@
 
         Resources res = mLauncher.getResources();
         mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
-        mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
+        mStartSlideTransY = res.getDimensionPixelSize(R.dimen.springs_trans_y);
+        mEndSlideTransY = -mStartSlideTransY * 0.1f;
 
         mLauncher.addOnDeviceProfileChangeListener(this);
         registerRemoteAnimations();
@@ -772,25 +786,33 @@
             });
         } else {
             AnimatorSet workspaceAnimator = new AnimatorSet();
-
-            mDragLayer.setTranslationY(-mWorkspaceTransY);;
-            workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
-                    -mWorkspaceTransY, 0));
-
-            mDragLayerAlpha.setValue(0);
-            workspaceAnimator.play(ObjectAnimator.ofFloat(
-                    mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
-
             workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
-            workspaceAnimator.setDuration(333);
-            workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
+
+            ShortcutAndWidgetContainer currentPage = ((CellLayout) mLauncher.getWorkspace()
+                    .getChildAt(mLauncher.getWorkspace().getCurrentPage()))
+                    .getShortcutsAndWidgets();
+
+            // Set up springs on workspace items.
+            for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
+                View child = currentPage.getChildAt(i);
+                CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+                addStaggeredAnimationForView(child, workspaceAnimator, lp.cellY + lp.cellVSpan);
+            }
+
+            // Set up a spring for the shelf.
+            if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+                AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+                float shiftRange = allAppsController.getShiftRange();
+                float slideStart = shiftRange / (shiftRange - mStartSlideTransY);
+                float oscillateStart = shiftRange / (shiftRange - mEndSlideTransY);
+
+                buildSpringAnimation(workspaceAnimator, allAppsController, ALL_APPS_PROGRESS,
+                        0 /* startDelay */, slideStart, oscillateStart, 1f /* finalPosition */);
+            }
 
             mDragLayer.getScrim().hideSysUiScrim(true);
-
             // Pause page indicator animations as they lead to layer trashing.
             mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
-            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
             workspaceAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -801,6 +823,66 @@
         }
     }
 
+    /**
+     * Adds an alpha/trans animator for {@param v}, with a start delay based on the view's row.
+     *
+     * @param v View in a ShortcutAndWidgetContainer.
+     * @param row The bottom-most row that contains the view.
+     */
+    private void addStaggeredAnimationForView(View v, AnimatorSet outAnimator, int row) {
+        // Invert the rows, because we stagger starting from the bottom of the screen.
+        int invertedRow = LauncherAppState.getIDP(mLauncher).numRows - row + 1;
+        long startDelay = (long) (invertedRow * APP_CLOSE_ROW_START_DELAY_MS);
+
+        v.setAlpha(0);
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 1f);
+        alpha.setInterpolator(LINEAR);
+        alpha.setDuration(SPRING_SLIDE_DURATION + SPRING_OSCILLATE_DURATION);
+        alpha.setStartDelay(startDelay);
+        outAnimator.play(alpha);
+
+        buildSpringAnimation(outAnimator, v, TRANSLATION_Y, startDelay, mStartSlideTransY,
+                mEndSlideTransY, 0f /* finalPosition */);
+
+        outAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                v.setAlpha(1f);
+                v.setTranslationY(0);
+            }
+        });
+    }
+
+    /**
+     * Spring animations consists of three sequential animators: a slide, an oscillation, and
+     * a settle.
+     */
+    private <T> void buildSpringAnimation(AnimatorSet outAnimator, T objectToSpring,
+            Property<T, Float> property, long startDelay, float slideStart, float oscillateStart,
+            float finalPosition) {
+        // Ensures a clean hand-off between slide and oscillate.
+        float slideEnd = Utilities.mapToRange(0, 0, 1f, oscillateStart, finalPosition, OSCILLATE);
+
+        property.set(objectToSpring, slideStart);
+
+        ObjectAnimator slideIn = ObjectAnimator.ofFloat(objectToSpring, property, slideStart,
+                slideEnd);
+        slideIn.setInterpolator(DEACCEL);
+        slideIn.setStartDelay(startDelay);
+        slideIn.setDuration(SPRING_SLIDE_DURATION);
+
+        ObjectAnimator oscillate = ObjectAnimator.ofFloat(objectToSpring, property, oscillateStart,
+                finalPosition);
+        oscillate.setInterpolator(OSCILLATE);
+        oscillate.setDuration(SPRING_OSCILLATE_DURATION);
+
+        ObjectAnimator settle = ObjectAnimator.ofFloat(objectToSpring, property, finalPosition);
+        settle.setInterpolator(LINEAR);
+        settle.setDuration(SPRING_SETTLE_DURATION);
+
+        outAnimator.playSequentially(slideIn, oscillate, settle);
+    }
+
     private void resetContentView() {
         mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
         mDragLayerAlpha.setValue(1f);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index bcc4269..0eead88 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -18,7 +18,11 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
 import android.animation.TimeInterpolator;
@@ -51,6 +55,16 @@
 
     private static final String TAG = "PortraitStatesTouchCtrl";
 
+    /**
+     * The progress at which all apps content will be fully visible when swiping up from overview.
+     */
+    private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
+
+    /**
+     * The progress at which recents will begin fading out when swiping up from overview.
+     */
+    private static final float RECENTS_FADE_THRESHOLD = 0.88f;
+
     private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
 
     // If true, we will finish the current animation instantly on second touch.
@@ -125,7 +139,38 @@
 
         AnimatorSetBuilder builder = new AnimatorSetBuilder();
         builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
+        return builder;
+    }
 
+    public static AnimatorSetBuilder getOverviewToAllAppsAnimation() {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
+                0, ALL_APPS_CONTENT_FADE_THRESHOLD));
+        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
+                RECENTS_FADE_THRESHOLD, 1));
+        return builder;
+    }
+
+    private AnimatorSetBuilder getAllAppsToOverviewAnimation() {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
+                1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
+        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
+                0f, 1 - RECENTS_FADE_THRESHOLD));
+        return builder;
+    }
+
+    @Override
+    protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
+            LauncherState toState) {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        if (fromState == NORMAL && toState == OVERVIEW) {
+            builder = getNormalToOverviewAnimation();
+        } else if (fromState == OVERVIEW && toState == ALL_APPS) {
+            builder = getOverviewToAllAppsAnimation();
+        } else if (fromState == ALL_APPS && toState == OVERVIEW) {
+            builder = getAllAppsToOverviewAnimation();
+        }
         return builder;
     }
 
@@ -139,13 +184,8 @@
 
         float totalShift = endVerticalShift - startVerticalShift;
 
-        final AnimatorSetBuilder builder;
-
-        if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) {
-            builder = getNormalToOverviewAnimation();
-        } else {
-            builder = new AnimatorSetBuilder();
-        }
+        final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
+                : getAnimatorSetBuilderForStates(mFromState, mToState);
 
         cancelPendingAnim();
 
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
index 336be2b..0785093 100644
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -25,10 +25,13 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.uioverrides.PortraitStatesTouchController;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -67,8 +70,10 @@
         AllAppsTransitionController controller = mLauncher.getAllAppsController();
         // TODO: Scale it down so that we can reach all-apps in screen space
         mMaxSwipeDistance = Math.max(1, controller.getProgress() * controller.getShiftRange());
-        mAnimator = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(ALL_APPS, Math.round(2 * mMaxSwipeDistance));
+
+        AnimatorSetBuilder builder = PortraitStatesTouchController.getOverviewToAllAppsAnimation();
+        mAnimator = mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, builder,
+                Math.round(2 * mMaxSwipeDistance), null, LauncherStateManager.ANIM_ALL);
         mAnimator.dispatchOnStart();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index d7e527c..8b5e832 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -16,7 +16,6 @@
 package com.android.quickstep.views;
 
 import static android.support.v4.graphics.ColorUtils.setAlphaComponent;
-
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -33,6 +32,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 
@@ -55,7 +55,7 @@
     // For shelf mode
     private final int mEndAlpha;
     private final float mRadius;
-    private final float mMaxScrimAlpha;
+    private final int mMaxScrimAlpha;
     private final Paint mPaint;
 
     // Mid point where the alpha changes
@@ -78,7 +78,7 @@
 
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
+        mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
 
         mEndAlpha = Color.alpha(mEndScrim);
         mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
@@ -144,9 +144,10 @@
         } else {
             mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
 
+            // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
             int alpha = Math.round(
                     Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
-                            (float) mMidAlpha, LINEAR));
+                            (float) mMidAlpha, Interpolators.clampToProgress(ACCEL, 0.5f, 1f)));
             mShelfColor = setAlphaComponent(mEndScrim, alpha);
 
             int remainingScrimAlpha = Math.round(
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 17361ac..304d012 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -48,7 +48,7 @@
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
             android:id="@+id/page_indicator"
             android:layout_width="match_parent"
-            android:layout_height="4dp"
+            android:layout_height="@dimen/vertical_drag_handle_size"
             android:layout_gravity="bottom|center_horizontal"
             android:theme="@style/HomeScreenElementTheme" />
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 07e0b04..3bb7a79 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -34,6 +34,8 @@
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
+    <!-- Extra bottom padding for non-tall devices. -->
+    <dimen name="dynamic_grid_hotseat_bottom_non_tall_padding">0dp</dimen>
     <dimen name="dynamic_grid_hotseat_size">80dp</dimen>
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
@@ -42,6 +44,7 @@
     <dimen name="all_apps_scrim_margin">8dp</dimen>
     <dimen name="all_apps_scrim_blur">4dp</dimen>
     <dimen name="vertical_drag_handle_size">24dp</dimen>
+    <dimen name="vertical_drag_handle_overlap_workspace">0dp</dimen>
 
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 20c4a5f..820c125 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -72,6 +72,7 @@
 
     // Drag handle
     public final int verticalDragHandleSizePx;
+    private final int verticalDragHandleOverlapWorkspace;
 
     // Workspace icons
     public int iconSizePx;
@@ -101,7 +102,7 @@
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
     public final int hotseatBarTopPaddingPx;
-    public final int hotseatBarBottomPaddingPx;
+    public int hotseatBarBottomPaddingPx;
     // Start is the side next to the nav bar, end is the side next to the workspace
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
@@ -135,6 +136,17 @@
         this.isLandscape = isLandscape;
         this.isMultiWindowMode = isMultiWindowMode;
 
+        // Determine sizes.
+        widthPx = width;
+        heightPx = height;
+        if (isLandscape) {
+            availableWidthPx = maxSize.x;
+            availableHeightPx = minSize.y;
+        } else {
+            availableWidthPx = minSize.x;
+            availableHeightPx = maxSize.y;
+        }
+
         Resources res = context.getResources();
         DisplayMetrics dm = res.getDisplayMetrics();
 
@@ -142,6 +154,8 @@
         isTablet = res.getBoolean(R.bool.is_tablet);
         isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
         isPhone = !isTablet && !isLargeTablet;
+        float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
+        boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
 
         // Some more constants
         transposeLayoutWithOrientation =
@@ -164,6 +178,8 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
         verticalDragHandleSizePx = res.getDimensionPixelSize(
                 R.dimen.vertical_drag_handle_size);
+        verticalDragHandleOverlapWorkspace =
+                res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
         topWorkspacePadding =
@@ -178,8 +194,9 @@
 
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
-        hotseatBarBottomPaddingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+        hotseatBarBottomPaddingPx = (isTallDevice ? 0
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
+                + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
         hotseatBarSidePaddingEndPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         // Add a bit of space between nav bar and hotseat in multi-window vertical bar layout.
@@ -191,30 +208,19 @@
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
                         + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
 
-        // Determine sizes.
-        widthPx = width;
-        heightPx = height;
-        if (isLandscape) {
-            availableWidthPx = maxSize.x;
-            availableHeightPx = minSize.y;
-        } else {
-            availableWidthPx = minSize.x;
-            availableHeightPx = maxSize.y;
-        }
-
         // Calculate all of the remaining variables.
         updateAvailableDimensions(dm, res);
 
         // Now that we have all of the variables calculated, we can tune certain sizes.
-        float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
-        boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
         if (!isVerticalBarLayout() && isPhone && isTallDevice) {
             // We increase the hotseat size when there is extra space.
             // ie. For a display with a large aspect ratio, we can keep the icons on the workspace
             // in portrait mode closer together by adding more height to the hotseat.
             // Note: This calculation was created after noticing a pattern in the design spec.
-            int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx;
-            hotseatBarSizePx += extraSpace - verticalDragHandleSizePx;
+            int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
+                    - verticalDragHandleSizePx;
+            hotseatBarSizePx += extraSpace;
+            hotseatBarBottomPaddingPx += extraSpace;
 
             // Recalculate the available dimensions using the new hotseat size.
             updateAvailableDimensions(dm, res);
@@ -440,7 +446,8 @@
                 padding.right = hotseatBarSizePx;
             }
         } else {
-            int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx;
+            int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx
+                    - verticalDragHandleOverlapWorkspace;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 04a32f7..37538ae 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -640,6 +640,7 @@
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
                 ShortcutInfo info = shortcutProvider.get();
+                getModelWriter().updateItemInDatabase(info);
                 ArrayList<ShortcutInfo> update = new ArrayList<>();
                 update.add(info);
                 bindUpdatedShortcuts(update, info.user);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index ccd5586..24a8d51 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -5,6 +5,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -151,7 +152,7 @@
     @Override
     public void setState(LauncherState state) {
         setProgress(state.getVerticalProgress(mLauncher));
-        setAlphas(state, NO_ANIM_PROPERTY_SETTER);
+        setAlphas(state, null, new AnimatorSetBuilder());
         onProgressAnimationEnd();
     }
 
@@ -164,7 +165,7 @@
             AnimatorSetBuilder builder, AnimationConfig config) {
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
-            setAlphas(toState, config.getPropertySetter(builder));
+            setAlphas(toState, config, builder);
             // Fail fast
             onProgressAnimationEnd();
             return;
@@ -186,19 +187,24 @@
 
         builder.play(anim);
 
-        setAlphas(toState, config.getPropertySetter(builder));
+        setAlphas(toState, config, builder);
     }
 
-    private void setAlphas(LauncherState toState, PropertySetter setter) {
+    private void setAlphas(LauncherState toState, AnimationConfig config,
+            AnimatorSetBuilder builder) {
+        PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
+                : config.getPropertySetter(builder);
         int visibleElements = toState.getVisibleElements(mLauncher);
         boolean hasHeader = (visibleElements & ALL_APPS_HEADER) != 0;
         boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
         boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
-        setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, LINEAR);
-        setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, LINEAR);
-        setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, LINEAR);
-        mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter);
+        Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
+        setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, allAppsFade);
+        setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, allAppsFade);
+        setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, allAppsFade);
+        mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter,
+                allAppsFade);
 
         setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
                 (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, LINEAR);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 462e7f3..eaa7774 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Point;
@@ -28,6 +26,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.R;
@@ -227,8 +226,9 @@
         p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
     }
 
-    public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter) {
-        setter.setViewAlpha(this, hasContent ? 1 : 0, LINEAR);
+    public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
+            Interpolator fadeInterpolator) {
+        setter.setViewAlpha(this, hasContent ? 1 : 0, fadeInterpolator);
         allowTouchForwarding(hasContent);
     }
 
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index f10bce8..307f258 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -35,6 +35,7 @@
     public static final int ANIM_WORKSPACE_FADE = 2;
     public static final int ANIM_OVERVIEW_SCALE = 3;
     public static final int ANIM_OVERVIEW_FADE = 4;
+    public static final int ANIM_ALL_APPS_FADE = 5;
 
     protected final ArrayList<Animator> mAnims = new ArrayList<>();
 
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index a4cba4f..efb08a1 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -112,6 +112,29 @@
         }
     };
 
+    /**
+     * Interpolates using a particular section of the damped oscillation function.
+     * The section is selected by scaling and shifting the function.
+     */
+    public static final Interpolator OSCILLATE = new Interpolator() {
+
+        // Used to scale the oscillations horizontally
+        private final float horizontalScale = 1f;
+        // Used to shift the oscillations horizontally
+        private final float horizontalShift = 05f;
+        // Used to scale the oscillations vertically
+        private final float verticalScale = 1f;
+        // Used to shift the oscillations vertically
+        private final float verticalShift = 1f;
+
+        @Override
+        public float getInterpolation(float t) {
+            t = horizontalScale * (t + horizontalShift);
+            return (float) ((verticalScale * (Math.exp(-t) * Math.cos(2 * Math.PI * t)))
+                    + verticalShift);
+        }
+    };
+
     private static final float FAST_FLING_PX_MS = 10;
 
     public static Interpolator scrollInterpolatorForVelocity(float velocity) {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 0d2bcb2..d478d48 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -341,7 +341,7 @@
 
     private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
             long duration) {
-        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState);
         mLauncher.getStateManager().prepareForAtomicAnimation(fromState, targetState, builder);
         AnimationConfig config = new AnimationConfig();
         config.animComponents = ATOMIC_COMPONENT;
@@ -352,6 +352,11 @@
         return builder.build();
     }
 
+    protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
+            LauncherState toState) {
+        return new AnimatorSetBuilder();
+    }
+
     @Override
     public void onDragEnd(float velocity, boolean fling) {
         final int logAction = fling ? Touch.FLING : Touch.SWIPE;