Merge "Tune quick scrub" into ub-launcher3-edmonton
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 29399142..e346310 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -118,6 +118,7 @@
                 finish();
             } else if (mFinished) {
                 // Animation callback was already finished, skip the animation.
+                mAnimator.start();
                 mAnimator.end();
             } else {
                 // Start the animation
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 2028501..c146246 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -275,8 +275,10 @@
         }
 
         private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
-            LatencyTrackerCompat.logToggleRecents(
-                    (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+            if (LatencyTrackerCompat.isEnabled(mContext)) {
+                LatencyTrackerCompat.logToggleRecents(
+                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+            }
 
             if (mListener != null) {
                 mListener.unregister();
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 20ecde9..b472d61 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -59,6 +59,9 @@
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * A simple activity to show the recently launched tasks
  */
@@ -271,4 +274,11 @@
                 .addCategory(Intent.CATEGORY_HOME)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
     }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.println(prefix + "Misc:");
+        dumpMisc(writer);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 28d6366..807dae8 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -433,9 +433,8 @@
         mLayoutListener.setHandler(this);
         buildAnimationController();
 
-        final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs;
         if (LatencyTrackerCompat.isEnabled(mContext)) {
-            LatencyTrackerCompat.logToggleRecents((int) transitionDelay);
+            LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a8e38a1..05793f1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -21,8 +21,8 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -121,6 +121,8 @@
     // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
     private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
 
+    private boolean mIsClearAllButtonFullyRevealed;
+
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
@@ -337,11 +339,15 @@
         }
     }
 
+    private int getScrollEnd() {
+        return mIsRtl ? 0 : mMaxScrollX;
+    }
+
     private float calculateClearAllButtonAlpha() {
         final int childCount = getChildCount();
         if (mShowEmptyMessage || childCount == 0) return 0;
 
-        final int scrollEnd = mIsRtl ? 0 : mMaxScrollX;
+        final int scrollEnd = getScrollEnd();
         final int oldestChildScroll = getScrollForPage(childCount - 1);
 
         return Utilities.boundToRange(
@@ -352,6 +358,7 @@
     private void updateClearAllButtonAlpha() {
         if (mClearAllButton != null) {
             final float alpha = calculateClearAllButtonAlpha();
+            mIsClearAllButtonFullyRevealed = alpha == 1;
             mClearAllButton.setAlpha(alpha * mContentAlpha);
         }
     }
@@ -363,9 +370,18 @@
     }
 
     @Override
+    protected void restoreScrollOnLayout() {
+        if (mIsClearAllButtonFullyRevealed) {
+            scrollAndForceFinish(getScrollEnd());
+        } else {
+            super.restoreScrollOnLayout();
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN && mTouchState == TOUCH_STATE_REST
-                && mScroller.isFinished() && mClearAllButton.getAlpha() > 0) {
+                && mScroller.isFinished() && mIsClearAllButtonFullyRevealed) {
             mClearAllButton.getHitRect(mTempRect);
             mTempRect.offset(-getLeft(), -getTop());
             if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
@@ -829,7 +845,7 @@
                removeView(taskView);
                if (getChildCount() == 0) {
                    onAllTasksRemoved();
-               } else {
+               } else if (!mIsClearAllButtonFullyRevealed) {
                    snapToPageImmediately(pageToSnapTo);
                }
            }
@@ -1038,14 +1054,7 @@
         }
         updateClearAllButtonAlpha();
 
-        if (!mShowEmptyMessage) return;
-
-        // The icon needs to be centered. Need to scoll to horizontal 0 because with Clear-All
-        // space on the right, it's not guaranteed that after deleting all tasks, the horizontal
-        // scroll position will be zero.
-        scrollTo(0, 0);
-
-        if (hasValidSize && mEmptyTextLayout == null) {
+        if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
             int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
             mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
                     mEmptyMessagePaint, availableWidth)
@@ -1072,7 +1081,7 @@
             mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
                     mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
             canvas.save();
-            canvas.translate((mTempRect.left - mTempRect.right) / 2,
+            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
                     (mTempRect.top - mTempRect.bottom) / 2);
             mEmptyIcon.draw(canvas);
             canvas.translate(mEmptyMessagePadding,
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index b47af2d..69b77b4 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -47,6 +47,7 @@
 
     private static final int THRESHOLD_ALPHA_DARK = 102;
     private static final int THRESHOLD_ALPHA_LIGHT = 46;
+    private static final int THRESHOLD_ALPHA_SUPER_LIGHT = 128;
 
     // In transposed layout, we simply draw a flat color.
     private boolean mDrawingFlatColor;
@@ -76,8 +77,13 @@
         mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
 
         mEndAlpha = Color.alpha(mEndScrim);
-        mThresholdAlpha = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)
-                ? THRESHOLD_ALPHA_DARK : THRESHOLD_ALPHA_LIGHT;
+        if (Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)) {
+            mThresholdAlpha = THRESHOLD_ALPHA_DARK;
+        } else if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
+            mThresholdAlpha = THRESHOLD_ALPHA_SUPER_LIGHT;
+        } else {
+            mThresholdAlpha = THRESHOLD_ALPHA_LIGHT;
+        }
         mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8526d7c..631626f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -60,7 +60,7 @@
         <item name="android:textColorTertiary">#CCFFFFFF</item>
         <item name="android:textColorHint">#A0FFFFFF</item>
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
-        <item name="android:colorPrimary">#FF333333</item>
+        <item name="android:colorPrimary">#FF212121</item>
         <item name="allAppsScrimColor">#EA212121</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="popupColorPrimary">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1f70cfa..3e4589d 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.app.Activity;
@@ -24,15 +25,14 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.support.annotation.IntDef;
-import android.view.Display;
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.SystemUiController;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 
@@ -223,4 +223,12 @@
     public interface MultiWindowModeChangedListener {
         void onMultiWindowModeChanged(boolean isInMultiWindowMode);
     }
+
+    protected void dumpMisc(PrintWriter writer) {
+        writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
+        writer.println(" orientation=" + getResources().getConfiguration().orientation);
+        writer.println(" mSystemUiController: " + mSystemUiController);
+        writer.println(" mActivityFlags: " + mActivityFlags);
+        writer.println(" mForceInvisible: " + mForceInvisible);
+    }
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 168bd08..8d79737 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -107,6 +107,8 @@
     private final BitmapFactory.Options mLowResOptions;
     private final BitmapFactory.Options mHighResOptions;
 
+    private int mPendingIconRequestCount = 0;
+
     public IconCache(Context context, InvariantDeviceProfile inv) {
         mContext = context;
         mPackageManager = context.getPackageManager();
@@ -411,8 +413,13 @@
      */
     public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
             final ItemInfoWithIcon info) {
-        Runnable request = new Runnable() {
+        Preconditions.assertUIThread();
+        if (mPendingIconRequestCount <= 0) {
+            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND);
+        }
+        mPendingIconRequestCount ++;
 
+        IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
             @Override
             public void run() {
                 if (info instanceof AppInfo || info instanceof ShortcutInfo) {
@@ -420,17 +427,21 @@
                 } else if (info instanceof PackageItemInfo) {
                     getTitleAndIconForApp((PackageItemInfo) info, false);
                 }
-                mMainThreadExecutor.execute(new Runnable() {
-
-                    @Override
-                    public void run() {
-                        caller.reapplyItemInfo(info);
-                    }
+                mMainThreadExecutor.execute(() -> {
+                    caller.reapplyItemInfo(info);
+                    onEnd();
                 });
             }
         };
-        mWorkerHandler.post(request);
-        return new IconLoadRequest(request, mWorkerHandler);
+        Utilities.postAsyncCallback(mWorkerHandler, request);
+        return request;
+    }
+
+    private void onIconRequestEnd() {
+        mPendingIconRequestCount --;
+        if (mPendingIconRequestCount <= 0) {
+            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        }
     }
 
     /**
@@ -707,17 +718,27 @@
         return false;
     }
 
-    public static class IconLoadRequest {
-        private final Runnable mRunnable;
+    public static abstract class IconLoadRequest implements Runnable {
         private final Handler mHandler;
+        private final Runnable mEndRunnable;
 
-        IconLoadRequest(Runnable runnable, Handler handler) {
-            mRunnable = runnable;
+        private boolean mEnded = false;
+
+        IconLoadRequest(Handler handler, Runnable endRunnable) {
             mHandler = handler;
+            mEndRunnable = endRunnable;
         }
 
         public void cancel() {
-            mHandler.removeCallbacks(mRunnable);
+            mHandler.removeCallbacks(this);
+            onEnd();
+        }
+
+        public void onEnd() {
+            if (!mEnded) {
+                mEnded = true;
+                mEndRunnable.run();
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 12d29a8..bf73149 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2279,8 +2279,8 @@
         writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
         writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
         writer.println(" mPendingActivityResult=" + mPendingActivityResult);
-        writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
-        writer.println(" orientation=" + getResources().getConfiguration().orientation);
+        writer.println(" mRotationHelper: " + mRotationHelper);
+        dumpMisc(writer);
 
         try {
             FileLog.flushAll(writer);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e10f974..eb816c5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -94,7 +94,6 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mCurrentPage;
-    private int mChildCountOnLastLayout;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
@@ -241,6 +240,12 @@
         return index;
     }
 
+    protected void scrollAndForceFinish(int scrollX) {
+        scrollTo(scrollX, 0);
+        mScroller.setFinalX(scrollX);
+        forceFinishScroller(true);
+    }
+
     /**
      * Updates the scroll of the current page immediately to its final scroll position.  We use this
      * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
@@ -252,9 +257,7 @@
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
             newX = getScrollForPage(mCurrentPage);
         }
-        scrollTo(newX, 0);
-        mScroller.setFinalX(newX);
-        forceFinishScroller(true);
+        scrollAndForceFinish(newX);
     }
 
     private void abortScrollerAnimation(boolean resetNextPage) {
@@ -541,22 +544,27 @@
         setMeasuredDimension(widthSize, heightSize);
     }
 
+    protected void restoreScrollOnLayout() {
+        setCurrentPage(getNextPage());
+    }
+
     @SuppressLint("DrawAllocation")
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         mIsLayoutValid = true;
-        if (getChildCount() == 0) {
+        final int childCount = getChildCount();
+        boolean pageScrollChanged = false;
+        if (mPageScrolls == null || childCount != mPageScrolls.length) {
+            mPageScrolls = new int[childCount];
+            pageScrollChanged = true;
+        }
+
+        if (childCount == 0) {
             return;
         }
 
         if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
-        final int childCount = getChildCount();
 
-        boolean pageScrollChanged = false;
-        if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
-            mPageScrolls = new int[childCount];
-            pageScrollChanged = true;
-        }
         if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) {
             pageScrollChanged = true;
         }
@@ -591,9 +599,8 @@
         }
 
         if (mScroller.isFinished() && pageScrollChanged) {
-            setCurrentPage(getNextPage());
+            restoreScrollOnLayout();
         }
-        mChildCountOnLastLayout = childCount;
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 3a1837d..7fef904 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2008 The Android Open Source Project
  *
@@ -479,6 +480,7 @@
                 case ANIMATION_END_REMAIN_VISIBLE:
                     break;
                 }
+                mDropAnim = null;
             }
         });
         mDropAnim.start();
@@ -488,6 +490,7 @@
         if (mDropAnim != null) {
             mDropAnim.cancel();
         }
+        mDropAnim = null;
         if (mDropView != null) {
             mDragController.onDeferredEndDrag(mDropView);
         }
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index a8a41f6..7d8a4db 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -73,6 +73,18 @@
         return getDefaultView(this);
     }
 
+    @Override
+    protected View getDefaultView() {
+        View v = super.getDefaultView();
+        v.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Launcher.getLauncher(getContext()).startSearch("", false, null, true);
+            }
+        });
+        return v;
+    }
+
     public static View getDefaultView(ViewGroup parent) {
         View v = LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.qsb_default_view, parent, false);
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 0036bb9..e866445 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -150,4 +150,12 @@
             mActivity.setRequestedOrientation(activityFlags);
         }
     }
+
+    @Override
+    public String toString() {
+        return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
+                + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mAutoRotateEnabled=%b]",
+                mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
+                mIgnoreAutoRotateSettings, mAutoRotateEnabled);
+    }
 }
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 7ef53a9..86995b7 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -16,11 +16,14 @@
 
 package com.android.launcher3.util;
 
+import android.text.TextUtils;
 import android.view.View;
 import android.view.Window;
 
 import com.android.launcher3.Utilities;
 
+import java.util.Arrays;
+
 /**
  * Utility class to manage various window flags to control system UI.
  */
@@ -78,4 +81,9 @@
             mWindow.getDecorView().setSystemUiVisibility(newFlags);
         }
     }
+
+    @Override
+    public String toString() {
+        return "mStates=" + Arrays.toString(mStates);
+    }
 }
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 28602f5..5dcfe4a 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,14 +18,24 @@
 import static android.content.Context.ACCESSIBILITY_SERVICE;
 import static android.support.v4.graphics.ColorUtils.compositeColors;
 import static android.support.v4.graphics.ColorUtils.setAlphaComponent;
+import static android.view.MotionEvent.ACTION_DOWN;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
@@ -85,6 +95,8 @@
 
     protected final int mDragHandleSize;
     private final Rect mDragHandleBounds;
+    private final RectF mHitRect = new RectF();
+
     private final AccessibilityHelper mAccessibilityHelper;
     @Nullable
     protected Drawable mDragHandle;
@@ -110,6 +122,7 @@
     @Override
     public void setInsets(Rect insets) {
         updateDragHandleBounds();
+        updateDragHandleVisibility(null);
     }
 
     @Override
@@ -179,6 +192,46 @@
         }
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean value = super.onTouchEvent(event);
+        if (!value && mDragHandle != null && event.getAction() == ACTION_DOWN
+                && mDragHandle.getAlpha() == 255
+                && mHitRect.contains(event.getX(), event.getY())) {
+
+            final Drawable drawable = mDragHandle;
+            mDragHandle = null;
+            drawable.setBounds(mDragHandleBounds);
+
+            Rect topBounds = new Rect(mDragHandleBounds);
+            topBounds.offset(0, -mDragHandleBounds.height() / 2);
+
+            Rect invalidateRegion = new Rect(mDragHandleBounds);
+            invalidateRegion.top = topBounds.top;
+
+            Keyframe frameTop = Keyframe.ofObject(0.6f, topBounds);
+            frameTop.setInterpolator(DEACCEL);
+            Keyframe frameBot = Keyframe.ofObject(1, mDragHandleBounds);
+            frameBot.setInterpolator(ACCEL);
+            PropertyValuesHolder holder = PropertyValuesHolder .ofKeyframe("bounds",
+                    Keyframe.ofObject(0, mDragHandleBounds), frameTop, frameBot);
+            holder.setEvaluator(new RectEvaluator());
+
+            ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    getOverlay().remove(drawable);
+                    updateDragHandleVisibility(drawable);
+                }
+            });
+            anim.addUpdateListener((v) -> invalidate(invalidateRegion));
+            getOverlay().add(drawable);
+            anim.start();
+        }
+        return value;
+    }
+
     protected void updateDragHandleBounds() {
         DeviceProfile grid = mLauncher.getDeviceProfile();
         final int left;
@@ -198,6 +251,9 @@
             topMargin = grid.hotseatBarSizePx;
         }
         mDragHandleBounds.offsetTo(left, top - topMargin);
+        mHitRect.set(mDragHandleBounds);
+        float inset = -mDragHandleSize / 2;
+        mHitRect.inset(inset, inset);
 
         if (mDragHandle != null) {
             mDragHandle.setBounds(mDragHandleBounds);
@@ -210,17 +266,29 @@
         stateManager.removeStateListener(this);
 
         if (enabled) {
-            mDragHandle = mLauncher.getDrawable(R.drawable.drag_handle_indicator);
-            mDragHandle.setBounds(mDragHandleBounds);
-
             stateManager.addStateListener(this);
             onStateSetImmediately(mLauncher.getStateManager().getState());
-
-            updateDragHandleAlpha();
         } else {
-            mDragHandle = null;
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
         }
-        invalidate();
+        updateDragHandleVisibility(null);
+    }
+
+    private void updateDragHandleVisibility(Drawable recycle) {
+        boolean visible = mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
+        boolean wasVisible = mDragHandle != null;
+        if (visible != wasVisible) {
+            if (visible) {
+                mDragHandle = recycle != null ? recycle :
+                        mLauncher.getDrawable(R.drawable.drag_handle_indicator);
+                mDragHandle.setBounds(mDragHandleBounds);
+
+                updateDragHandleAlpha();
+            } else {
+                mDragHandle = null;
+            }
+            invalidate();
+        }
     }
 
     @Override