Merge "Don't end atomic animation when passing through state" into ub-launcher3-edmonton
diff --git a/quickstep/res/layout/drag_handle_indicator.xml b/quickstep/res/layout/drag_handle_indicator.xml
deleted file mode 100644
index 9ee05d5..0000000
--- a/quickstep/res/layout/drag_handle_indicator.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-<com.android.quickstep.views.QuickstepDragIndicator
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drag_indicator"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:contentDescription="@string/accessibility_desc_recent_apps"
-    android:scaleType="centerInside"
-    android:tint="?attr/workspaceTextColor" />
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9e4d60c..ad5f767 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -30,8 +30,9 @@
     <dimen name="quickstep_fling_min_velocity">250dp</dimen>
 
     <!-- Launcher app transition -->
-    <dimen name="content_trans_y">25dp</dimen>
-    <dimen name="workspace_trans_y">80dp</dimen>
+    <dimen name="content_trans_y">50dp</dimen>
+    <dimen name="workspace_trans_y">50dp</dimen>
+    <dimen name="closing_window_trans_y">115dp</dimen>
 
     <dimen name="recents_empty_message_text_size">16sp</dimen>
     <dimen name="recents_empty_message_text_padding">16dp</dimen>
@@ -50,5 +51,4 @@
     <dimen name="clear_all_container_width">168dp</dimen>
 
     <dimen name="shelf_surface_radius">16dp</dimen>
-    <dimen name="shelf_surface_top_padding">4dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 4963f5d..fc02f72 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -19,12 +19,11 @@
 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 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.AGGRESSIVE_EASE_IN_OUT;
-import static com.android.launcher3.anim.Interpolators.APP_CLOSE_ALPHA;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
 import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
@@ -58,7 +57,6 @@
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -102,11 +100,10 @@
 
     public static final int RECENTS_LAUNCH_DURATION = 336;
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
-    private static final int CLOSING_TRANSITION_DURATION_MS = 350;
+    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;
-    public static final float ALL_APPS_PROGRESS_OVERSHOOT = 0.99581414f;
 
     private final DragLayer mDragLayer;
     private final Launcher mLauncher;
@@ -116,6 +113,7 @@
 
     private final float mContentTransY;
     private final float mWorkspaceTransY;
+    private final float mClosingWindowTransY;
 
     private DeviceProfile mDeviceProfile;
     private View mFloatingView;
@@ -129,6 +127,16 @@
         }
     };
 
+    private final Runnable mDragLayerResetRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
+            mDragLayer.setAlpha(1);
+            mDragLayer.setTranslationY(0);
+            mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+        }
+    };
+
     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationStart(Animator animation) {
@@ -151,6 +159,7 @@
         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);
 
         mLauncher.addOnDeviceProfileChangeListener(this);
         registerRemoteAnimations();
@@ -187,7 +196,7 @@
                         anim.play(getIconAnimator(v));
                         if (launcherClosing) {
                             Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                                    getLauncherContentAnimator(false /* show */);
+                                    getLauncherContentAnimator(true /* isAppOpening */);
                             anim.play(launcherContentAnimator.first);
                             anim.addListener(new AnimatorListenerAdapter() {
                                 @Override
@@ -281,21 +290,21 @@
     /**
      * Content is everything on screen except the background and the floating view (if any).
      *
-     * @param show If true: Animate the content so that it moves upwards and fades in.
-     *             Else: Animate the content so that it moves downwards and fades out.
+     * @param isAppOpening True when this is called when an app is opening.
+     *                     False when this is called when an app is closing.
      */
-    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean show) {
+    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening) {
         AnimatorSet launcherAnimator = new AnimatorSet();
         Runnable endListener;
 
-        float[] alphas = show
-                ? new float[] {0, 1}
-                : new float[] {1, 0};
-        float[] trans = show
-                ? new float[] {mContentTransY, 0,}
-                : new float[] {0, mContentTransY};
+        if (mLauncher.isInState(ALL_APPS)) {
+            float[] alphas = isAppOpening
+                    ? new float[] {1, 0}
+                    : new float[] {0, 1};
+            float[] trans = isAppOpening
+                    ? new float[] {0, mContentTransY}
+                    : new float[] {-mContentTransY, 0};
 
-        if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) {
             // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
             final View appsView = mLauncher.getAppsView();
             final float startAlpha = appsView.getAlpha();
@@ -326,6 +335,9 @@
                 appsView.setLayerType(View.LAYER_TYPE_NONE, null);
             };
         } else {
+            float[] alphas = new float[] {1, 0};
+            float[] trans = new float[] {0, mContentTransY};
+
             mDragLayer.setAlpha(alphas[0]);
             mDragLayer.setTranslationY(trans[0]);
 
@@ -343,12 +355,7 @@
 
             // Pause page indicator animations as they lead to layer trashing.
             mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
-            endListener = () -> {
-                mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
-                mDragLayer.setAlpha(1);
-                mDragLayer.setTranslationY(0);
-                mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
-            };
+            endListener = mDragLayerResetRunnable;
         }
         return new Pair<>(launcherAnimator, endListener);
     }
@@ -646,16 +653,14 @@
      */
     private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
         Matrix matrix = new Matrix();
-        float height = mLauncher.getDeviceProfile().heightPx;
-        float width = mLauncher.getDeviceProfile().widthPx;
-        float endX = (mLauncher.<RecentsView>getOverviewPanel().isRtl() ? -width : width) * 1.16f;
-
         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
-        closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+        int duration = CLOSING_TRANSITION_DURATION_MS;
+        closingAnimator.setDuration(duration);
+        Rect crop = new Rect();
         closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
-            FloatProp mDx = new FloatProp(0, endX, 0, 350, AGGRESSIVE_EASE_IN_OUT);
-            FloatProp mScale = new FloatProp(1f, 0.8f, 0, 267, AGGRESSIVE_EASE);
-            FloatProp mAlpha = new FloatProp(1f, 0f, 0, 350, APP_CLOSE_ALPHA);
+            FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
+            FloatProp mScale = new FloatProp(1f, 1.075f, 0, duration, DEACCEL_1_7);
+            FloatProp mAlpha = new FloatProp(1f, 0f, 0, duration, DEACCEL_1_7);
 
             boolean isFirstFrame = true;
 
@@ -667,13 +672,16 @@
                     isFirstFrame = false;
                 }
                 for (RemoteAnimationTargetCompat app : targets) {
+                    crop.set(app.clipRect);
+                    crop.top = mDeviceProfile.getInsets().top;
                     if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
                         t.setAlpha(app.leash, mAlpha.value);
                         matrix.setScale(mScale.value, mScale.value,
                                 app.sourceContainerBounds.centerX(),
                                 app.sourceContainerBounds.centerY());
-                        matrix.postTranslate(mDx.value, 0);
+                        matrix.postTranslate(0, mDy.value);
                         matrix.postTranslate(app.position.x, app.position.y);
+                        t.setWindowCrop(app.leash, crop);
                         t.setMatrix(app.leash, matrix);
                     }
                 }
@@ -694,7 +702,7 @@
         if (mLauncher.isInState(LauncherState.ALL_APPS)
                 || mLauncher.getDeviceProfile().isVerticalBarLayout()) {
             Pair<AnimatorSet, Runnable> contentAnimator =
-                    getLauncherContentAnimator(true /* show */);
+                    getLauncherContentAnimator(false /* isAppOpening */);
             contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
             anim.play(contentAnimator.first);
             anim.addListener(new AnimatorListenerAdapter() {
@@ -706,53 +714,29 @@
         } else {
             AnimatorSet workspaceAnimator = new AnimatorSet();
 
-            mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY);
-            workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(),
-                    View.TRANSLATION_Y, mWorkspaceTransY, 0));
+            mDragLayer.setTranslationY(-mWorkspaceTransY);
+            workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
+                    -mWorkspaceTransY, 0));
 
-            View currentPage = ((CellLayout) mLauncher.getWorkspace()
-                    .getChildAt(mLauncher.getWorkspace().getCurrentPage()))
-                    .getShortcutsAndWidgets();
-            currentPage.setAlpha(0f);
-            workspaceAnimator.play(ObjectAnimator.ofFloat(currentPage, View.ALPHA, 0, 1f));
+            mDragLayer.setAlpha(0);
+            workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, 0, 1f));
 
             workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
             workspaceAnimator.setDuration(333);
-            workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            currentPage.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
+
+            // 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) {
-                    currentPage.setLayerType(View.LAYER_TYPE_NONE, null);
+                    mDragLayerResetRunnable.run();
                 }
             });
-
-            // Animate the shelf in two parts: slide in, and overeshoot.
-            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            // The shelf will start offscreen
-            final float startY = ALL_APPS_PROGRESS_OFF_SCREEN;
-            // And will end slightly pulled up, so that there is something to overshoot back to 1f.
-            final float slideEnd = ALL_APPS_PROGRESS_OVERSHOOT;
-
-            allAppsController.setProgress(startY);
-
-            Animator allAppsSlideIn =
-                    ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, startY, slideEnd);
-            allAppsSlideIn.setStartDelay(LAUNCHER_RESUME_START_DELAY);
-            allAppsSlideIn.setDuration(317);
-            allAppsSlideIn.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-
-            Animator allAppsOvershoot =
-                    ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, slideEnd, 1f);
-            allAppsOvershoot.setDuration(153);
-            allAppsOvershoot.setInterpolator(Interpolators.OVERSHOOT_0);
-
             anim.play(workspaceAnimator);
-            anim.playSequentially(allAppsSlideIn, allAppsOvershoot);
-            anim.addListener(mReapplyStateListener);
         }
     }
-
     private boolean hasControlRemoteAppTransitionPermission() {
         return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
                 == PackageManager.PERMISSION_GRANTED;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index eaf8aa0..4029b82 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -97,9 +97,9 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return DRAG_HANDLE_INDICATOR;
+            return 0;
         } else {
-            return HOTSEAT_SEARCH_BOX | DRAG_HANDLE_INDICATOR |
+            return HOTSEAT_SEARCH_BOX |
                     (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
                             ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
         }
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 8923608..9416a29 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -21,9 +21,12 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
 
+import static com.android.launcher3.Utilities.getSystemProperty;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -33,6 +36,8 @@
 import android.util.Log;
 
 import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
@@ -52,6 +57,8 @@
 
     private static final String TAG = "OverviewFlags";
 
+    private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
+
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static OverviewInteractionState INSTANCE;
 
@@ -77,6 +84,7 @@
 
     private final SwipeUpGestureEnabledSettingObserver mSwipeUpSettingObserver;
 
+    private final Context mContext;
     private final Handler mUiHandler;
     private final Handler mBgHandler;
 
@@ -88,12 +96,18 @@
     private Runnable mOnSwipeUpSettingChangedListener;
 
     private OverviewInteractionState(Context context) {
+        mContext = context;
         mUiHandler = new Handler(this::handleUiMessage);
         mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
 
-        mSwipeUpSettingObserver = new SwipeUpGestureEnabledSettingObserver(mUiHandler,
-                context.getContentResolver());
-        mSwipeUpSettingObserver.register();
+        if (shouldIgnoreSwipeUpEnabledSettings()) {
+            mSwipeUpSettingObserver = null;
+            mSwipeUpEnabled = true;
+        } else {
+            mSwipeUpSettingObserver = new SwipeUpGestureEnabledSettingObserver(mUiHandler,
+                    context.getContentResolver());
+            mSwipeUpSettingObserver.register();
+        }
     }
 
     public boolean isSwipeUpGestureEnabled() {
@@ -125,6 +139,8 @@
                 break;
             case MSG_SET_SWIPE_UP_ENABLED:
                 mSwipeUpEnabled = msg.arg1 != 0;
+                resetHomeBounceSeenOnQuickstepEnabledFirstTime();
+
                 if (mOnSwipeUpSettingChangedListener != null) {
                     mOnSwipeUpSettingChangedListener.run();
                 }
@@ -171,6 +187,7 @@
             mResolver.registerContentObserver(Settings.Secure.getUriFor(SWIPE_UP_SETTING_NAME),
                     false, this);
             mSwipeUpEnabled = getValue();
+            resetHomeBounceSeenOnQuickstepEnabledFirstTime();
         }
 
         @Override
@@ -184,4 +201,23 @@
             return Settings.Secure.getInt(mResolver, SWIPE_UP_SETTING_NAME, 0) == 1;
         }
     }
+
+    private boolean shouldIgnoreSwipeUpEnabledSettings() {
+        String sdkInt = getSystemProperty("ro.product.first_api_level", "0");
+        try {
+            return Integer.parseInt(sdkInt) >= Build.VERSION_CODES.P;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
+        if (mSwipeUpEnabled && !Utilities.getPrefs(mContext).getBoolean(
+                HAS_ENABLED_QUICKSTEP_ONCE, true)) {
+            Utilities.getPrefs(mContext).edit()
+                    .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
+                    .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
+                    .apply();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index b1fa5e2..2ff70dd 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -43,8 +43,7 @@
             extraSpace = 0;
         } else {
             Resources res = context.getResources();
-            extraSpace = dp.hotseatBarSizePx + res.getDimension(R.dimen.shelf_surface_top_padding)
-                    + res.getDimension(R.dimen.shelf_surface_radius);
+            extraSpace = dp.hotseatBarSizePx + res.getDimension(R.dimen.vertical_drag_handle_size);
         }
         calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
     }
diff --git a/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java b/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
deleted file mode 100644
index 5e9cd6e..0000000
--- a/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.views;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.views.LauncherDragIndicator;
-
-public class QuickstepDragIndicator extends LauncherDragIndicator {
-
-    public QuickstepDragIndicator(Context context) {
-        super(context);
-    }
-
-    public QuickstepDragIndicator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public QuickstepDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    private boolean isInOverview() {
-        return mLauncher.isInState(OVERVIEW);
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setContentDescription(getContext().getString(R.string.all_apps_button_label));
-    }
-
-    @Override
-    protected void initCustomActions(AccessibilityNodeInfo info) {
-        if (!isInOverview()) {
-            super.initCustomActions(info);
-        }
-    }
-
-    @Override
-    public void onClick(View view) {
-        mLauncher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                ControlType.ALL_APPS_BUTTON,
-                isInOverview() ? ContainerType.TASKSWITCHER : ContainerType.WORKSPACE);
-        mLauncher.getStateManager().goToState(ALL_APPS);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index aa04672..b47af2d 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -31,7 +31,6 @@
 import android.util.AttributeSet;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.uioverrides.OverviewState;
 import com.android.launcher3.util.Themes;
@@ -49,8 +48,6 @@
     private static final int THRESHOLD_ALPHA_DARK = 102;
     private static final int THRESHOLD_ALPHA_LIGHT = 46;
 
-    private final Launcher mLauncher;
-
     // In transposed layout, we simply draw a flat color.
     private boolean mDrawingFlatColor;
 
@@ -58,7 +55,6 @@
     private final int mEndAlpha;
     private final int mThresholdAlpha;
     private final float mRadius;
-    private final float mTopPadding;
     private final float mMaxScrimAlpha;
     private final Paint mPaint;
 
@@ -77,15 +73,12 @@
 
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        mLauncher = Launcher.getLauncher(context);
         mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
 
         mEndAlpha = Color.alpha(mEndScrim);
         mThresholdAlpha = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)
                 ? THRESHOLD_ALPHA_DARK : THRESHOLD_ALPHA_LIGHT;
         mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
-        mTopPadding = mLauncher.getResources().getDimension(R.dimen.shelf_surface_top_padding);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
         // Just assume the easiest UI for now, until we have the proper layout information.
@@ -110,10 +103,10 @@
             mRemainingScreenPathValid = false;
             updateColors();
         }
+        updateDragHandleAlpha();
         invalidate();
     }
 
-
     @Override
     public void updateColors() {
         super.updateColors();
@@ -131,6 +124,7 @@
                         (1 - mProgress) / (1 - mMoveThreshold)));
                 mShelfColor = setAlphaComponent(mEndScrim, alpha);
             }
+
             mRemainingScreenColor = 0;
         } else if (mProgress <= 0) {
             mScrimMoveFactor = 0;
@@ -150,23 +144,42 @@
     }
 
     @Override
+    protected void updateDragHandleAlpha() {
+        if (mDrawingFlatColor) {
+            super.updateDragHandleAlpha();
+        } else if (mDragHandle != null) {
+            mDragHandle.setAlpha(255);
+        }
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
+        float translate = drawBackground(canvas);
+
+        if (mDragHandle != null) {
+            canvas.translate(0, -translate);
+            mDragHandle.draw(canvas);
+            canvas.translate(0, translate);
+        }
+    }
+
+    private float drawBackground(Canvas canvas) {
         if (mDrawingFlatColor) {
             if (mCurrentFlatColor != 0) {
                 canvas.drawColor(mCurrentFlatColor);
             }
-            return;
+            return 0;
         }
 
         if (mShelfColor == 0) {
-            return;
+            return 0;
         } else if (mScrimMoveFactor <= 0) {
             canvas.drawColor(mShelfColor);
-            return;
+            return getHeight();
         }
 
         float minTop = getHeight() - mMinSize;
-        float top = minTop * mScrimMoveFactor - mTopPadding - mRadius;
+        float top = minTop * mScrimMoveFactor - mDragHandleSize;
 
         // Draw the scrim over the remaining screen if needed.
         if (mRemainingScreenColor != 0) {
@@ -192,5 +205,6 @@
         mPaint.setColor(mShelfColor);
         canvas.drawRoundRect(0, top, getWidth(), getHeight() + mRadius,
                 mRadius, mRadius, mPaint);
+        return minTop - mDragHandleSize - top;
     }
 }
diff --git a/res/drawable/all_apps_handle_landscape.xml b/res/drawable/drag_handle_indicator.xml
similarity index 77%
rename from res/drawable/all_apps_handle_landscape.xml
rename to res/drawable/drag_handle_indicator.xml
index 15518ff..b01b84a 100644
--- a/res/drawable/all_apps_handle_landscape.xml
+++ b/res/drawable/drag_handle_indicator.xml
@@ -15,25 +15,25 @@
 -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="@dimen/dynamic_grid_min_page_indicator_size"
-    android:height="@dimen/dynamic_grid_min_page_indicator_size"
-    android:viewportWidth="48.0"
-    android:viewportHeight="48.0" >
+    android:width="@dimen/vertical_drag_handle_size"
+    android:height="@dimen/vertical_drag_handle_size"
+    android:viewportWidth="36.0"
+    android:viewportHeight="36.0" >
 
     <group
-        android:translateX="17.5"
-        android:translateY="17.5">
+        android:translateX="11.5"
+        android:translateY="11.5">
         <path
             android:pathData="M2 8.5L6.5 4L11 8.5"
             android:strokeColor="?attr/workspaceAmbientShadowColor"
-            android:strokeWidth="4"
+            android:strokeWidth="3.6"
             android:strokeLineCap="round"
             android:strokeLineJoin="round" />
 
         <path
             android:pathData="M2 8.5L6.5 4L11 8.5"
             android:strokeColor="?attr/workspaceTextColor"
-            android:strokeWidth="2"
+            android:strokeWidth="1.8"
             android:strokeLineCap="round"
             android:strokeLineJoin="round" />
         </group>
diff --git a/res/drawable/ic_drag_indicator.xml b/res/drawable/ic_drag_indicator.xml
deleted file mode 100644
index d50bdd3..0000000
--- a/res/drawable/ic_drag_indicator.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:height="2dp"
-        android:width="16dp"
-        android:viewportHeight="2.0"
-        android:viewportWidth="16.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M1,0h14c0.55,0,1,0.45,1,1s-0.45,1-1,1H1C0.45,2,0,1.55,0,1S0.45,0,1,0z"/>
-</vector>
\ No newline at end of file
diff --git a/res/layout/drag_handle_indicator.xml b/res/layout/drag_handle_indicator.xml
deleted file mode 100644
index d5a7b8a..0000000
--- a/res/layout/drag_handle_indicator.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-<com.android.launcher3.views.LauncherDragIndicator
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drag_indicator"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:contentDescription="@string/all_apps_button_label"
-    android:scaleType="centerInside"
-    android:tint="?attr/workspaceTextColor" />
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index cd8a425..ec8bd5c 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -67,9 +67,6 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
-        <include android:id="@+id/drag_indicator"
-            layout="@layout/drag_handle_indicator" />
-
         <!-- DO NOT CHANGE THE ID -->
         <include
             android:id="@+id/hotseat"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b1ad11e..cd050e8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -42,6 +42,7 @@
     <dimen name="all_apps_scrim_radius">8dp</dimen>
     <dimen name="all_apps_scrim_margin">8dp</dimen>
     <dimen name="all_apps_scrim_blur">4dp</dimen>
+    <dimen name="vertical_drag_handle_size">24dp</dimen>
 
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8088f64..2f83f45 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -196,7 +196,6 @@
     private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
-    private View mDragHandleIndicator;
     @Nullable private View mHotseatSearchBox;
 
     private DropTargetBar mDropTargetBar;
@@ -933,7 +932,6 @@
         mOverviewPanel = findViewById(R.id.overview_panel);
         mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
         mHotseat = findViewById(R.id.hotseat);
-        mDragHandleIndicator = findViewById(R.id.drag_indicator);
         mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1194,10 +1192,6 @@
         return mHotseat;
     }
 
-    public View getDragHandleIndicator() {
-        return mDragHandleIndicator;
-    }
-
     public View getHotseatSearchBox() {
         return mHotseatSearchBox;
     }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6790b7f..4e10ab6 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -51,7 +51,6 @@
     public static final int ALL_APPS_HEADER = 1 << 2;
     public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
     public static final int ALL_APPS_CONTENT = 1 << 4;
-    public static final int DRAG_HANDLE_INDICATOR = 1 << 5;
 
     protected static final int FLAG_MULTI_PAGE = 1 << 0;
     protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
@@ -197,9 +196,9 @@
 
     public int getVisibleElements(Launcher launcher) {
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR;
+            return HOTSEAT_ICONS;
         }
-        return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR | HOTSEAT_SEARCH_BOX;
+        return HOTSEAT_ICONS | HOTSEAT_SEARCH_BOX;
     }
 
     /**
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 720c574..d870109 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -194,7 +194,7 @@
 
     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
     private static final int FOLDER_CREATION_TIMEOUT = 0;
-    public static final int REORDER_TIMEOUT = 350;
+    public static final int REORDER_TIMEOUT = 650;
     private final Alarm mFolderCreationAlarm = new Alarm();
     private final Alarm mReorderAlarm = new Alarm();
     private PreviewBackground mFolderCreateBg;
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 55968db..d6b2349 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.DRAG_HANDLE_INDICATOR;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
@@ -103,10 +102,6 @@
                 (elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0,
                 pageAlphaProvider.interpolator);
 
-        propertySetter.setViewAlpha(mLauncher.getDragHandleIndicator(),
-                (elements & DRAG_HANDLE_INDICATOR) != 0 ? 1 : 0,
-                pageAlphaProvider.interpolator);
-
         // Set scrim
         propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS,
                 state.getWorkspaceScrimAlpha(mLauncher), Interpolators.LINEAR);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4a0b622..113571a 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -101,7 +101,6 @@
             mAppsView.setAlpha(1);
             mLauncher.getHotseat().setTranslationY(0);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
-            mLauncher.getDragHandleIndicator().setTranslationY(0);
         }
     }
 
@@ -125,7 +124,6 @@
         if (!mIsVerticalLayout) {
             mLauncher.getHotseat().setTranslationY(hotseatTranslation);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
-            mLauncher.getDragHandleIndicator().setTranslationY(hotseatTranslation);
         }
 
         // Use a light system UI (dark icons) if all apps is behind at least half of the
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 06ddf22..0d388fe 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -38,6 +38,7 @@
 
     public static final Interpolator DEACCEL = new DecelerateInterpolator();
     public static final Interpolator DEACCEL_1_5 = new DecelerateInterpolator(1.5f);
+    public static final Interpolator DEACCEL_1_7 = new DecelerateInterpolator(1.7f);
     public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
     public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
     public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
@@ -57,8 +58,6 @@
         EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
     }
 
-    public static final Interpolator APP_CLOSE_ALPHA = new PathInterpolator(0.4f, 0, 1f, 1f);
-
     public static final Interpolator OVERSHOOT_0 = new OvershootInterpolator(0);
 
     public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
diff --git a/src/com/android/launcher3/views/LauncherDragIndicator.java b/src/com/android/launcher3/views/LauncherDragIndicator.java
deleted file mode 100644
index 986e4be..0000000
--- a/src/com/android/launcher3/views/LauncherDragIndicator.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.views;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-
-public class LauncherDragIndicator extends ImageView implements Insettable, OnClickListener {
-
-    private static final int WALLPAPERS = R.string.wallpaper_button_text;
-    private static final int WIDGETS = R.string.widget_button_text;
-    private static final int SETTINGS = R.string.settings_button_text;
-
-    protected final Launcher mLauncher;
-
-    public LauncherDragIndicator(Context context) {
-        this(context, null);
-    }
-
-    public LauncherDragIndicator(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LauncherDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mLauncher = Launcher.getLauncher(context);
-        setOnClickListener(this);
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-
-        if (grid.isVerticalBarLayout()) {
-            if (grid.isSeascape()) {
-                lp.leftMargin = grid.hotseatBarSidePaddingPx;
-                lp.rightMargin = insets.right;
-                lp.gravity =  Gravity.RIGHT | Gravity.BOTTOM;
-            } else {
-                lp.leftMargin = insets.left;
-                lp.rightMargin = grid.hotseatBarSidePaddingPx;
-                lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
-            }
-            lp.bottomMargin = grid.workspacePadding.bottom;
-            setImageResource(R.drawable.all_apps_handle_landscape);
-        } else {
-            lp.leftMargin = lp.rightMargin = 0;
-            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.bottomMargin = getPortraitBottomMargin(grid, insets);
-            setImageResource(R.drawable.ic_drag_indicator);
-        }
-
-        lp.width = lp.height = grid.pageIndicatorSizePx;
-        setLayoutParams(lp);
-    }
-
-    protected int getPortraitBottomMargin(DeviceProfile grid, Rect insets) {
-        return grid.hotseatBarSizePx + insets.bottom - grid.pageIndicatorSizePx;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        initCustomActions(info);
-    }
-
-    protected void initCustomActions(AccessibilityNodeInfo info) {
-        Context context = getContext();
-        if (Utilities.isWallpaperAllowed(context)) {
-            info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
-        }
-        info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
-        info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (action == WALLPAPERS) {
-            return OptionsPopupView.startWallpaperPicker(this);
-        } else if (action == WIDGETS) {
-            return OptionsPopupView.onWidgetsClicked(this);
-        } else if (action == SETTINGS) {
-            return OptionsPopupView.startSettings(this);
-        }
-        return super.performAccessibilityAction(action, arguments);
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (!mLauncher.isInState(ALL_APPS)) {
-            mLauncher.getUserEventDispatcher().logActionOnControl(
-                    Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
-            mLauncher.getStateManager().goToState(ALL_APPS);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index b49b565..28602f5 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -15,28 +15,63 @@
  */
 package com.android.launcher3.views;
 
+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 com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+import android.support.v4.widget.ExploreByTouchHelper;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.Themes;
 
+import java.util.List;
+
 /**
  * Simple scrim which draws a flat color
  */
-public class ScrimView extends View implements Insettable, OnChangeListener {
+public class ScrimView extends View implements Insettable, OnChangeListener,
+        AccessibilityStateChangeListener, StateListener {
 
+    private static final int WALLPAPERS = R.string.wallpaper_button_text;
+    private static final int WIDGETS = R.string.widget_button_text;
+    private static final int SETTINGS = R.string.settings_button_text;
+
+    private final Rect mTempRect = new Rect();
+    private final int[] mTempPos = new int[2];
+
+    protected final Launcher mLauncher;
     private final WallpaperColorInfo mWallpaperColorInfo;
+    private final AccessibilityManager mAM;
     protected final int mEndScrim;
 
     protected float mMaxScrimAlpha;
@@ -48,28 +83,56 @@
     protected int mEndFlatColor;
     protected int mEndFlatColorAlpha;
 
+    protected final int mDragHandleSize;
+    private final Rect mDragHandleBounds;
+    private final AccessibilityHelper mAccessibilityHelper;
+    @Nullable
+    protected Drawable mDragHandle;
+
     public ScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mLauncher = Launcher.getLauncher(context);
         mWallpaperColorInfo = WallpaperColorInfo.getInstance(context);
         mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
 
         mMaxScrimAlpha = 0.7f;
+
+        mDragHandleSize = context.getResources()
+                .getDimensionPixelSize(R.dimen.vertical_drag_handle_size);
+        mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize);
+
+        mAccessibilityHelper = new AccessibilityHelper();
+        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
+
+        mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
     }
 
     @Override
-    public void setInsets(Rect insets) { }
+    public void setInsets(Rect insets) {
+        updateDragHandleBounds();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        updateDragHandleBounds();
+    }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mWallpaperColorInfo.addOnChangeListener(this);
         onExtractedColorsChanged(mWallpaperColorInfo);
+
+        mAM.addAccessibilityStateChangeListener(this);
+        onAccessibilityStateChanged(mAM.isEnabled());
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mWallpaperColorInfo.removeOnChangeListener(this);
+        mAM.removeAccessibilityStateChangeListener(this);
     }
 
     @Override
@@ -91,6 +154,7 @@
         if (mProgress != progress) {
             mProgress = progress;
             updateColors();
+            updateDragHandleAlpha();
             invalidate();
         }
     }
@@ -102,10 +166,158 @@
                 mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
     }
 
+    protected void updateDragHandleAlpha() {
+        if (mDragHandle != null) {
+            mDragHandle.setAlpha(Math.round(255 * Utilities.boundToRange(mProgress, 0, 1)));
+        }
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (mCurrentFlatColor != 0) {
             canvas.drawColor(mCurrentFlatColor);
         }
     }
+
+    protected void updateDragHandleBounds() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        final int left;
+        final int width = getMeasuredWidth();
+        final int top = getMeasuredHeight() - mDragHandleSize - grid.getInsets().bottom;
+        final int topMargin;
+
+        if (grid.isVerticalBarLayout()) {
+            topMargin = grid.workspacePadding.bottom;
+            if (grid.isSeascape()) {
+                left = width - grid.getInsets().right - mDragHandleSize;
+            } else {
+                left = mDragHandleSize + grid.getInsets().left;
+            }
+        } else {
+            left = (width - mDragHandleSize) / 2;
+            topMargin = grid.hotseatBarSizePx;
+        }
+        mDragHandleBounds.offsetTo(left, top - topMargin);
+
+        if (mDragHandle != null) {
+            mDragHandle.setBounds(mDragHandleBounds);
+        }
+    }
+
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        LauncherStateManager stateManager = mLauncher.getStateManager();
+        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;
+        }
+        invalidate();
+    }
+
+    @Override
+    public boolean dispatchHoverEvent(MotionEvent event) {
+        return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public void onFocusChanged(boolean gainFocus, int direction,
+            Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) {}
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        onStateSetImmediately(finalState);
+    }
+
+    @Override
+    public void onStateSetImmediately(LauncherState state) {
+        setImportantForAccessibility(state == ALL_APPS
+                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+    }
+
+    private class AccessibilityHelper extends ExploreByTouchHelper {
+
+        private static final int DRAG_HANDLE_ID = 1;
+
+        public AccessibilityHelper() {
+            super(ScrimView.this);
+        }
+
+        @Override
+        protected int getVirtualViewAt(float x, float y) {
+            return  mDragHandleBounds.contains((int) x, (int) y)
+                    ? DRAG_HANDLE_ID : INVALID_ID;
+        }
+
+        @Override
+        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+            virtualViewIds.add(DRAG_HANDLE_ID);
+        }
+
+        @Override
+        protected void onPopulateNodeForVirtualView(int virtualViewId,
+                AccessibilityNodeInfoCompat node) {
+            node.setContentDescription(getContext().getString(R.string.all_apps_button_label));
+            node.setBoundsInParent(mDragHandleBounds);
+
+            getLocationOnScreen(mTempPos);
+            mTempRect.set(mDragHandleBounds);
+            mTempRect.offset(mTempPos[0], mTempPos[1]);
+            node.setBoundsInScreen(mTempRect);
+
+            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
+            node.setClickable(true);
+            node.setFocusable(true);
+
+            if (mLauncher.isInState(NORMAL)) {
+                Context context = getContext();
+                if (Utilities.isWallpaperAllowed(context)) {
+                    node.addAction(
+                            new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS)));
+                }
+                node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS)));
+                node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS)));
+            }
+        }
+
+        @Override
+        protected boolean onPerformActionForVirtualView(
+                int virtualViewId, int action, Bundle arguments) {
+            if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
+                mLauncher.getUserEventDispatcher().logActionOnControl(
+                        Action.Touch.TAP, ControlType.ALL_APPS_BUTTON,
+                        mLauncher.getStateManager().getState().containerType);
+                mLauncher.getStateManager().goToState(ALL_APPS);
+                return true;
+            } else if (action == WALLPAPERS) {
+                return OptionsPopupView.startWallpaperPicker(ScrimView.this);
+            } else if (action == WIDGETS) {
+                return OptionsPopupView.onWidgetsClicked(ScrimView.this);
+            } else if (action == SETTINGS) {
+                return OptionsPopupView.startSettings(ScrimView.this);
+            }
+
+            return false;
+        }
+    }
 }