Simplifying options popup and overview handling

> Removing support for page drag-n-drop from pagedView
> Removing Overview UI from Launcher3 without quickstep and using options popup instead
> Removing touch handlers from CellLayouts and showing options popup based on workspace long press
> Excluding touch outside inset when showing the popup

Bug: 74136505
Change-Id: I34c2a7ff58452db26f5d1a85d554be40fc75f2b8
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 734aec3..7979082 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -292,7 +292,7 @@
             ViewCompat.setAccessibilityDelegate(this, null);
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
             getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-            setOnClickListener(mLauncher);
+            setOnClickListener(null);
         } else {
             if (dragType == WORKSPACE_ACCESSIBILITY_DRAG &&
                     !(mTouchHelper instanceof WorkspaceAccessibilityHelper)) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index ea52324..13971ad 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -515,12 +515,6 @@
         }
     }
 
-    public boolean shouldIgnoreLongPressToOverview(float touchX) {
-        boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
-        boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
-        return !isMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
-    }
-
     private static Context getContext(Context c, int orientation) {
         Configuration context = new Configuration(c.getResources().getConfiguration());
         context.orientation = orientation;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 03043f2..211a756 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -63,14 +63,6 @@
         return mContent;
     }
 
-    /**
-     * Registers the specified listener on the cell layout of the hotseat.
-     */
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        mContent.setOnLongClickListener(l);
-    }
-
     /* Get the orientation invariant order of the item in the hotseat for persistence. */
     int getOrderInHotseat(int x, int y) {
         return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 375deb7..b4f9409 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -31,7 +31,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.appwidget.AppWidgetHostView;
@@ -49,7 +48,6 @@
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -65,16 +63,12 @@
 import android.util.SparseArray;
 import android.view.ActionMode;
 import android.view.Display;
-import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.Menu;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -109,7 +103,6 @@
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.UiFactory;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -126,6 +119,7 @@
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.views.OptionsPopupView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -147,10 +141,8 @@
 /**
  * Default launcher application.
  */
-public class Launcher extends BaseActivity
-        implements LauncherExterns, OnClickListener, OnLongClickListener,
-                   LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
-                   WallpaperColorInfo.OnThemeChangeListener {
+public class Launcher extends BaseActivity implements LauncherExterns, LauncherModel.Callbacks,
+        LauncherProviderChangeListener, WallpaperColorInfo.OnThemeChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -228,7 +220,7 @@
     AllAppsTransitionController mAllAppsController;
 
     // UI and state for the overview panel
-    private ViewGroup mOverviewPanel;
+    private View mOverviewPanel;
 
     @Thunk boolean mWorkspaceLoading = true;
 
@@ -261,8 +253,6 @@
      */
     private PendingRequestArgs mPendingRequestArgs;
 
-    private final PointF mLastDispatchTouchEvent = new PointF();
-
     public ViewGroupFocusHelper mFocusHandler;
     private boolean mAppLaunchSuccess;
 
@@ -952,6 +942,7 @@
         mWorkspace = mDragLayer.findViewById(R.id.workspace);
         mWorkspace.initParentViews(mDragLayer);
         mOverviewPanel = findViewById(R.id.overview_panel);
+        mHotseat = findViewById(R.id.hotseat);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -960,15 +951,6 @@
         // Setup the drag layer
         mDragLayer.setup(this, mDragController);
 
-        // Setup the hotseat
-        mHotseat = (Hotseat) findViewById(R.id.hotseat);
-        if (mHotseat != null) {
-            mHotseat.setOnLongClickListener(this);
-        }
-
-        // Setup the workspace
-        mWorkspace.setHapticFeedbackEnabled(false);
-        mWorkspace.setOnLongClickListener(this);
         mWorkspace.setup(mDragController);
         // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
         // default state, otherwise we will update to the wrong offsets in RTL
@@ -1222,7 +1204,7 @@
         return mHotseat;
     }
 
-    public <T extends ViewGroup> T getOverviewPanel() {
+    public <T extends View> T getOverviewPanel() {
         return (T) mOverviewPanel;
     }
 
@@ -1659,51 +1641,6 @@
     }
 
     /**
-     * Launches the intent referred by the clicked shortcut.
-     *
-     * @param v The view representing the clicked shortcut.
-     */
-    @Override
-    public void onClick(View v) {
-        // Make sure that rogue clicks don't get through while allapps is launching, or after the
-        // view has detached (it's possible for this to happen if the view is removed mid touch).
-        if (v.getWindowToken() == null) {
-            return;
-        }
-
-        if (!mWorkspace.isFinishedSwitchingState()) {
-            return;
-        }
-
-        if (v instanceof Workspace) {
-            if (isInState(OVERVIEW)) {
-                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
-                        LauncherLogProto.Action.Direction.NONE,
-                        LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
-                mStateManager.goToState(NORMAL);
-            }
-            return;
-        }
-
-        if (v instanceof CellLayout) {
-            if (isInState(OVERVIEW)) {
-                int page = mWorkspace.indexOfChild(v);
-                getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
-                        LauncherLogProto.Action.Direction.NONE,
-                        LauncherLogProto.ContainerType.OVERVIEW, page);
-                mWorkspace.snapToPageFromOverView(page);
-                mStateManager.goToState(NORMAL);
-            }
-            return;
-        }
-    }
-
-    @SuppressLint("ClickableViewAccessibility")
-    public boolean onTouch(View v, MotionEvent event) {
-        return false;
-    }
-
-    /**
      * Event handler for the wallpaper picker button that appears after a long press
      * on the home screen.
      */
@@ -1860,62 +1797,6 @@
         return mAppLaunchSuccess;
     }
 
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        mLastDispatchTouchEvent.set(ev.getX(), ev.getY());
-        return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        if (!isDraggingEnabled()) return false;
-        if (isWorkspaceLocked()) return false;
-        if (!isInState(NORMAL) && !isInState(OVERVIEW)) return false;
-
-        boolean ignoreLongPressToOverview =
-                mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEvent.x);
-
-        if (v instanceof Workspace) {
-            if (!isInState(OVERVIEW)) {
-                if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
-                    getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
-                            Action.Direction.NONE, ContainerType.WORKSPACE,
-                            mWorkspace.getCurrentPage());
-                    UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
-                    mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                    return true;
-                } else {
-                    return false;
-                }
-            } else {
-                return false;
-            }
-        }
-
-        // The hotseat touch handling does not go through Workspace, and we always allow long press
-        // on hotseat items.
-        if (!mDragController.isDragging()) {
-            // User long pressed on empty space
-            if (mWorkspace.isPageRearrangeEnabled()) {
-                mWorkspace.startReordering(v);
-                getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
-                        Action.Direction.NONE, ContainerType.OVERVIEW);
-            } else {
-                if (ignoreLongPressToOverview) {
-                    return false;
-                }
-                getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
-                        Action.Direction.NONE, ContainerType.WORKSPACE,
-                        mWorkspace.getCurrentPage());
-                UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
-            }
-            mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-        }
-        return true;
-    }
-
     boolean isHotseatLayout(View layout) {
         // TODO: Remove this method
         return mHotseat != null && layout != null &&
@@ -2628,8 +2509,7 @@
 
                 // Setting the touch point to (-1, -1) will show the options popup in the center of
                 // the screen.
-                mLastDispatchTouchEvent.set(-1, -1);
-                UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
+                OptionsPopupView.show(this, -1, -1);
             }
             return true;
         }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index d39ec3e..9abb9c1 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -19,10 +19,7 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -30,8 +27,6 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -48,7 +43,6 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.util.Thunk;
@@ -105,31 +99,21 @@
     private VelocityTracker mVelocityTracker;
     protected int mPageSpacing = 0;
 
-    private float mParentDownMotionX;
-    private float mParentDownMotionY;
     private float mDownMotionX;
     private float mDownMotionY;
-    private float mDownScrollX;
-    private float mDragViewBaselineLeft;
     private float mLastMotionX;
     private float mLastMotionXRemainder;
-    private float mLastMotionY;
     private float mTotalMotionX;
 
-    private boolean mCancelTap;
-
     private int[] mPageScrolls;
 
     protected final static int TOUCH_STATE_REST = 0;
     protected final static int TOUCH_STATE_SCROLLING = 1;
     protected final static int TOUCH_STATE_PREV_PAGE = 2;
     protected final static int TOUCH_STATE_NEXT_PAGE = 3;
-    protected final static int TOUCH_STATE_REORDERING = 4;
 
     protected int mTouchState = TOUCH_STATE_REST;
 
-    protected OnLongClickListener mLongClickListener;
-
     protected int mTouchSlop;
     private int mMaximumVelocity;
     protected boolean mAllowOverScroll = true;
@@ -153,26 +137,6 @@
     @Thunk int mPageIndicatorViewId;
     protected T mPageIndicator;
 
-    // Reordering
-    // We use the min scale to determine how much to expand the actually PagedView measured
-    // dimensions such that when we are zoomed out, the view is not clipped
-    private static int REORDERING_DROP_REPOSITION_DURATION = 200;
-    @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300;
-    private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80;
-
-    @Thunk View mDragView;
-    private Runnable mSidePageHoverRunnable;
-    @Thunk int mSidePageHoverIndex = -1;
-    // This variable's scope is only for the duration of startReordering() and endReordering()
-    private boolean mReorderingStarted = false;
-    // This variable's scope is for the duration of startReordering() and after the zoomIn()
-    // animation after endReordering()
-    private boolean mIsReordering;
-    // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
-    private static final int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
-    private int mPostReorderingPreZoomInRemainingAnimationCount;
-    private Runnable mPostReorderingPreZoomInRunnable;
-
     // Convenience/caching
     private static final Matrix sTmpInvMatrix = new Matrix();
     private static final float[] sTmpPoint = new float[2];
@@ -237,47 +201,6 @@
         }
     }
 
-    // Convenience methods to map points from self to parent and vice versa
-    private float[] mapPointFromViewToParent(View v, float x, float y) {
-        sTmpPoint[0] = x;
-        sTmpPoint[1] = y;
-        v.getMatrix().mapPoints(sTmpPoint);
-        sTmpPoint[0] += v.getLeft();
-        sTmpPoint[1] += v.getTop();
-        return sTmpPoint;
-    }
-    private float[] mapPointFromParentToView(View v, float x, float y) {
-        sTmpPoint[0] = x - v.getLeft();
-        sTmpPoint[1] = y - v.getTop();
-        v.getMatrix().invert(sTmpInvMatrix);
-        sTmpInvMatrix.mapPoints(sTmpPoint);
-        return sTmpPoint;
-    }
-
-    private void updateDragViewTranslationDuringDrag() {
-        if (mDragView != null) {
-            float x = (mLastMotionX - mDownMotionX) + (getScrollX() - mDownScrollX) +
-                    (mDragViewBaselineLeft - mDragView.getLeft());
-            float y = mLastMotionY - mDownMotionY;
-            mDragView.setTranslationX(x);
-            mDragView.setTranslationY(y);
-
-            if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): "
-                    + x + ", " + y);
-        }
-    }
-
-    @Override
-    public void setScaleX(float scaleX) {
-        super.setScaleX(scaleX);
-        if (isReordering(true)) {
-            float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
-            mLastMotionX = p[0];
-            mLastMotionY = p[1];
-            updateDragViewTranslationDuringDrag();
-        }
-    }
-
     public T getPageIndicator() {
         return mPageIndicator;
     }
@@ -383,9 +306,6 @@
         // Update the page indicator (when we aren't reordering)
         if (mPageIndicator != null) {
             mPageIndicator.setPageDescription(getPageIndicatorDescription());
-            if (!isReordering(false)) {
-                mPageIndicator.setActiveMarker(getNextPage());
-            }
         }
     }
     protected void pageBeginTransition() {
@@ -421,21 +341,6 @@
         mWasInOverscroll = false;
     }
 
-    /**
-     * Registers the specified listener on each page contained in this workspace.
-     *
-     * @param l The listener used to respond to long clicks.
-     */
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        mLongClickListener = l;
-        final int count = getPageCount();
-        for (int i = 0; i < count; i++) {
-            getPageAt(i).setOnLongClickListener(l);
-        }
-        super.setOnLongClickListener(l);
-    }
-
     protected int getUnboundedScrollX() {
         return mUnboundedScrollX;
     }
@@ -490,14 +395,6 @@
             mOverScrollX = x;
             super.scrollTo(x, y);
         }
-
-        // Update the last motion events when scrolling
-        if (isReordering(true)) {
-            float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
-            mLastMotionX = p[0];
-            mLastMotionY = p[1];
-            updateDragViewTranslationDuringDrag();
-        }
     }
 
     private void sendScrollAccessibilityEvent() {
@@ -549,7 +446,6 @@
                 pageEndTransition();
             }
 
-            onPostReorderingAnimationCompleted();
             if (isAccessibilityEnabled(getContext())) {
                 // Notify the user when the page changes
                 announceForAccessibility(getCurrentPageDescription());
@@ -716,10 +612,6 @@
             setCurrentPage(getNextPage());
         }
         mChildCountOnLastLayout = childCount;
-
-        if (isReordering(true)) {
-            updateDragViewTranslationDuringDrag();
-        }
     }
 
     protected int getChildGap() {
@@ -937,12 +829,7 @@
                 // Remember location of down touch
                 mDownMotionX = x;
                 mDownMotionY = y;
-                mDownScrollX = getScrollX();
                 mLastMotionX = x;
-                mLastMotionY = y;
-                float[] p = mapPointFromViewToParent(this, x, y);
-                mParentDownMotionX = p[0];
-                mParentDownMotionY = p[1];
                 mLastMotionXRemainder = 0;
                 mTotalMotionX = 0;
                 mActivePointerId = ev.getPointerId(0);
@@ -990,6 +877,10 @@
         return mTouchState != TOUCH_STATE_REST;
     }
 
+    public boolean isHandlingTouch() {
+        return mTouchState != TOUCH_STATE_REST;
+    }
+
     protected void determineScrollingStart(MotionEvent ev) {
         determineScrollingStart(ev, 1.0f);
     }
@@ -1101,22 +992,12 @@
         dampedOverScroll(amount);
     }
 
-    /**
-     * return true if freescroll has been enabled, false otherwise
-     */
-    protected void enableFreeScroll() {
-        enableFreeScroll(false);
-    }
 
     protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
         setEnableFreeScroll(true);
         mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
     }
 
-    protected void disableFreeScroll() {
-        setEnableFreeScroll(false);
-    }
-
     private void setEnableFreeScroll(boolean freeScroll) {
         boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
@@ -1134,27 +1015,6 @@
         mAllowOverScroll = enable;
     }
 
-    private int getNearestHoverOverPageIndex() {
-        if (mDragView != null) {
-            int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
-                    + mDragView.getTranslationX());
-            int minDistance = Integer.MAX_VALUE;
-            int minIndex = indexOfChild(mDragView);
-            int maxPageNo = getChildCount() - 1;
-            for (int i = 0; i <= maxPageNo; i++) {
-                View page = getPageAt(i);
-                int pageX = (page.getLeft() + page.getMeasuredWidth() / 2);
-                int d = Math.abs(dragX - pageX);
-                if (d < minDistance) {
-                    minIndex = i;
-                    minDistance = d;
-                }
-            }
-            return minIndex;
-        }
-        return -1;
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         super.onTouchEvent(ev);
@@ -1178,11 +1038,7 @@
 
             // Remember where the motion event started
             mDownMotionX = mLastMotionX = ev.getX();
-            mDownMotionY = mLastMotionY = ev.getY();
-            mDownScrollX = getScrollX();
-            float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
-            mParentDownMotionX = p[0];
-            mParentDownMotionY = p[1];
+            mDownMotionY = ev.getY();
             mLastMotionXRemainder = 0;
             mTotalMotionX = 0;
             mActivePointerId = ev.getPointerId(0);
@@ -1215,82 +1071,6 @@
                 } else {
                     awakenScrollBars();
                 }
-            } else if (mTouchState == TOUCH_STATE_REORDERING) {
-                // Update the last motion position
-                mLastMotionX = ev.getX();
-                mLastMotionY = ev.getY();
-
-                // Update the parent down so that our zoom animations take this new movement into
-                // account
-                float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
-                mParentDownMotionX = pt[0];
-                mParentDownMotionY = pt[1];
-                updateDragViewTranslationDuringDrag();
-
-                // Find the closest page to the touch point
-                final int dragViewIndex = indexOfChild(mDragView);
-
-                if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
-                if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
-                if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
-                if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
-
-                final int pageUnderPointIndex = getNearestHoverOverPageIndex();
-                // Do not allow any page to be moved to 0th position.
-                if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
-                    if (0 <= pageUnderPointIndex && pageUnderPointIndex <= getPageCount() - 1 &&
-                            pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
-                        mSidePageHoverIndex = pageUnderPointIndex;
-                        mSidePageHoverRunnable = new Runnable() {
-                            @Override
-                            public void run() {
-                                // Setup the scroll to the correct page before we swap the views
-                                snapToPage(pageUnderPointIndex);
-
-                                // For each of the pages between the paged view and the drag view,
-                                // animate them from the previous position to the new position in
-                                // the layout (as a result of the drag view moving in the layout)
-                                int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
-                                int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
-                                        dragViewIndex + 1 : pageUnderPointIndex;
-                                int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
-                                        dragViewIndex - 1 : pageUnderPointIndex;
-                                for (int i = lowerIndex; i <= upperIndex; ++i) {
-                                    View v = getChildAt(i);
-                                    // dragViewIndex < pageUnderPointIndex, so after we remove the
-                                    // drag view all subsequent views to pageUnderPointIndex will
-                                    // shift down.
-                                    int oldX = getChildOffset(i);
-                                    int newX = getChildOffset(i + shiftDelta);
-
-                                    // Animate the view translation from its old position to its new
-                                    // position
-                                    ObjectAnimator anim = (ObjectAnimator) v.getTag();
-                                    if (anim != null) {
-                                        anim.cancel();
-                                    }
-
-                                    v.setTranslationX(oldX - newX);
-                                    anim = LauncherAnimUtils.ofFloat(v, View.TRANSLATION_X, 0);
-                                    anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
-                                    anim.start();
-                                    v.setTag(anim);
-                                }
-
-                                removeView(mDragView);
-                                addView(mDragView, pageUnderPointIndex);
-                                mSidePageHoverIndex = -1;
-                                if (mPageIndicator != null) {
-                                    mPageIndicator.setActiveMarker(getNextPage());
-                                }
-                            }
-                        };
-                        postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
-                    }
-                } else {
-                    removeCallbacks(mSidePageHoverRunnable);
-                    mSidePageHoverIndex = -1;
-                }
             } else {
                 determineScrollingStart(ev);
             }
@@ -1391,25 +1171,8 @@
                 } else {
                     snapToDestination();
                 }
-            } else if (mTouchState == TOUCH_STATE_REORDERING) {
-                // Update the last motion position
-                mLastMotionX = ev.getX();
-                mLastMotionY = ev.getY();
-
-                // Update the parent down so that our zoom animations take this new movement into
-                // account
-                float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
-                mParentDownMotionX = pt[0];
-                mParentDownMotionY = pt[1];
-                updateDragViewTranslationDuringDrag();
-            } else {
-                if (!mCancelTap) {
-                    onUnhandledTap(ev);
-                }
             }
 
-            // Remove the callback to wait for the side page hover timeout
-            removeCallbacks(mSidePageHoverRunnable);
             // End any intermediate reordering states
             resetTouchState();
             break;
@@ -1437,8 +1200,6 @@
 
     private void resetTouchState() {
         releaseVelocityTracker();
-        endReordering();
-        mCancelTap = false;
         mTouchState = TOUCH_STATE_REST;
         mActivePointerId = INVALID_POINTER;
     }
@@ -1452,10 +1213,6 @@
     protected void onScrollInteractionEnd() {
     }
 
-    protected void onUnhandledTap(MotionEvent ev) {
-        Launcher.getLauncher(getContext()).onClick(this);
-    }
-
     @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1512,7 +1269,6 @@
             // TODO: Make this decision more intelligent.
             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
             mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
-            mLastMotionY = ev.getY(newPointerIndex);
             mLastMotionXRemainder = 0;
             mActivePointerId = ev.getPointerId(newPointerIndex);
             if (mVelocityTracker != null) {
@@ -1689,139 +1445,6 @@
         if (getNextPage() < getChildCount() -1) snapToPage(getNextPage() + 1);
     }
 
-    @Override
-    public boolean performLongClick() {
-        mCancelTap = true;
-        return super.performLongClick();
-    }
-
-    public static class SavedState extends BaseSavedState {
-        int currentPage = -1;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Thunk SavedState(Parcel in) {
-            super(in);
-            currentPage = in.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(currentPage);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR =
-                new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
-    // Animate the drag view back to the original position
-    private void animateDragViewToOriginalPosition() {
-        if (mDragView != null) {
-            Animator anim = LauncherAnimUtils.ofPropertyValuesHolder(mDragView,
-                    new PropertyListBuilder()
-                            .scale(1)
-                            .translationX(0)
-                            .translationY(0)
-                            .build())
-                    .setDuration(REORDERING_DROP_REPOSITION_DURATION);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    onPostReorderingAnimationCompleted();
-                }
-            });
-            anim.start();
-        }
-    }
-
-    public void onStartReordering() {
-        // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
-        mTouchState = TOUCH_STATE_REORDERING;
-        mIsReordering = true;
-
-        // We must invalidate to trigger a redraw to update the layers such that the drag view
-        // is always drawn on top
-        invalidate();
-    }
-
-    @Thunk void onPostReorderingAnimationCompleted() {
-        // Trigger the callback when reordering has settled
-        --mPostReorderingPreZoomInRemainingAnimationCount;
-        if (mPostReorderingPreZoomInRunnable != null &&
-                mPostReorderingPreZoomInRemainingAnimationCount == 0) {
-            mPostReorderingPreZoomInRunnable.run();
-            mPostReorderingPreZoomInRunnable = null;
-        }
-    }
-
-    public void onEndReordering() {
-        mIsReordering = false;
-    }
-
-    public boolean startReordering(View v) {
-        int dragViewIndex = indexOfChild(v);
-
-        // Do not allow the first page to be moved around
-        if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
-
-        // Check if we are within the reordering range
-        if (0 <= dragViewIndex && dragViewIndex <= getPageCount() - 1) {
-            // Find the drag view under the pointer
-            mDragView = getChildAt(dragViewIndex);
-            mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start();
-            mDragViewBaselineLeft = mDragView.getLeft();
-            mReorderingStarted = true;
-
-            snapToPage(getPageNearestToCenterOfScreen());
-            disableFreeScroll();
-            onStartReordering();
-            return true;
-        }
-        return false;
-    }
-
-    boolean isReordering(boolean testTouchState) {
-        boolean state = mIsReordering;
-        if (testTouchState) {
-            state &= (mTouchState == TOUCH_STATE_REORDERING);
-        }
-        return state;
-    }
-    void endReordering() {
-        // For simplicity, we call endReordering sometimes even if reordering was never started.
-        // In that case, we don't want to do anything.
-        if (!mReorderingStarted) return;
-        mReorderingStarted = false;
-
-        mPostReorderingPreZoomInRunnable = new Runnable() {
-            public void run() {
-                // If we haven't flung-to-delete the current child,
-                // then we just animate the drag view back into position
-                onEndReordering();
-
-                enableFreeScroll();
-            }
-        };
-
-        mPostReorderingPreZoomInRemainingAnimationCount =
-                NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
-        // Snap to the current page
-        snapToPage(indexOfChild(mDragView), 0);
-        // Animate the drag view back to the front position
-        animateDragViewToOriginalPosition();
-    }
-
     /* Accessibility */
     @SuppressWarnings("deprecation")
     @Override
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f329f5e..9960953 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -59,7 +59,6 @@
 import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
-import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
@@ -82,6 +81,7 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.touch.WorkspaceTouchListener;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -242,7 +242,6 @@
     private Runnable mOnOverlayHiddenCallback;
 
     private boolean mForceDrawAdjacentPages = false;
-    private boolean mPageRearrangeEnabled = false;
 
     // Total over scrollX in the overlay direction.
     private float mOverlayTranslation;
@@ -250,8 +249,6 @@
     // Handles workspace state transitions
     private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
-    private AccessibilityDelegate mPagesAccessibilityDelegate;
-
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -286,6 +283,7 @@
 
         // Attach a scrim
         new WorkspaceAndHotseatScrim(this).attach();
+        setOnTouchListener(new WorkspaceTouchListener(mLauncher, this));
     }
 
     @Override
@@ -475,7 +473,6 @@
         }
         CellLayout cl = ((CellLayout) child);
         cl.setOnInterceptTouchListener(this);
-        cl.setClickable(true);
         cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         super.onViewAdded(child);
     }
@@ -555,10 +552,6 @@
         // created CellLayout.
         CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
-        newScreen.setOnLongClickListener(mLongClickListener);
-        newScreen.setOnClickListener(mLauncher);
-        newScreen.setSoundEffectsEnabled(false);
-
         int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
         int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
         newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
@@ -938,7 +931,6 @@
 
         child.setHapticFeedbackEnabled(false);
         child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
-
         if (child instanceof DropTarget) {
             mDragController.addDropTarget((DropTarget) child);
         }
@@ -1378,8 +1370,7 @@
     }
 
     private void updateChildrenLayersEnabled() {
-        boolean enableChildrenLayers =
-                isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition();
+        boolean enableChildrenLayers = mIsSwitchingState || isPageInTransition();
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
             mChildrenLayersEnabled = enableChildrenLayers;
@@ -1463,40 +1454,6 @@
         mOutlineProvider = outlineProvider;
     }
 
-    public void onStartReordering() {
-        super.onStartReordering();
-        // Reordering handles its own animations, disable the automatic ones.
-        disableLayoutTransitions();
-    }
-
-    public void onEndReordering() {
-        super.onEndReordering();
-
-        if (mLauncher.isWorkspaceLoading()) {
-            // Invalid and dangerous operation if workspace is loading
-            return;
-        }
-
-        ArrayList<Long> prevScreenOrder = (ArrayList<Long>) mScreenOrder.clone();
-        mScreenOrder.clear();
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            CellLayout cl = ((CellLayout) getChildAt(i));
-            mScreenOrder.add(getIdForScreen(cl));
-        }
-
-        for (int i = 0; i < prevScreenOrder.size(); i++) {
-            if (mScreenOrder.get(i) != prevScreenOrder.get(i)) {
-                mLauncher.getUserEventDispatcher().logOverviewReorder();
-                break;
-            }
-        }
-        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
-
-        // Re-enable auto layout transitions for page deletion.
-        enableLayoutTransitions();
-    }
-
     public void snapToPageFromOverView(int whichPage) {
         snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
     }
@@ -1556,47 +1513,17 @@
         if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
             int total = getPageCount();
             for (int i = 0; i < total; i++) {
-                updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i), i);
+                updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i));
             }
             setImportantForAccessibility(accessibilityFlag);
         }
     }
 
-    private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page, int pageNo) {
-        if (isPageRearrangeEnabled()) {
-            page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-            page.getShortcutsAndWidgets().setImportantForAccessibility(
-                    IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-            page.setContentDescription(getPageDescription(pageNo));
-
-            // No custom action for the first page.
-            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || pageNo > 0) {
-                if (mPagesAccessibilityDelegate == null) {
-                    mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
-                }
-                page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
-            }
-        } else {
-            page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-            page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
-            page.setContentDescription(null);
-            page.setAccessibilityDelegate(null);
-        }
-    }
-
-    public void setPageRearrangeEnabled(boolean isEnabled) {
-        if (mPageRearrangeEnabled != isEnabled) {
-            mPageRearrangeEnabled = isEnabled;
-            if (isEnabled) {
-                enableFreeScroll();
-            } else {
-                disableFreeScroll();
-            }
-        }
-    }
-
-    public boolean isPageRearrangeEnabled() {
-        return mPageRearrangeEnabled;
+    private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page) {
+        page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+        page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
+        page.setContentDescription(null);
+        page.setAccessibilityDelegate(null);
     }
 
     public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
@@ -1612,10 +1539,6 @@
                 protected void enableAccessibleDrag(boolean enable) {
                     super.enableAccessibleDrag(enable);
                     setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
-
-                    // We need to allow our individual children to become click handlers in this
-                    // case, so temporarily unset the click handlers.
-                    setOnClickListener(enable ? null : mLauncher);
                 }
             });
         }
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
deleted file mode 100644
index f9eb2ed..0000000
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2015 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.accessibility;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-
-public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate {
-
-    private static final int MOVE_BACKWARD = R.id.action_move_screen_backwards;
-    private static final int MOVE_FORWARD = R.id.action_move_screen_forwards;
-
-    private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
-    private final Workspace mWorkspace;
-
-    public OverviewScreenAccessibilityDelegate(Workspace workspace) {
-        mWorkspace = workspace;
-
-        Context context = mWorkspace.getContext();
-        boolean isRtl = Utilities.isRtl(context.getResources());
-        mActions.put(MOVE_BACKWARD, new AccessibilityAction(MOVE_BACKWARD,
-                context.getText(isRtl ? R.string.action_move_screen_right :
-                    R.string.action_move_screen_left)));
-        mActions.put(MOVE_FORWARD, new AccessibilityAction(MOVE_FORWARD,
-                context.getText(isRtl ? R.string.action_move_screen_left :
-                    R.string.action_move_screen_right)));
-    }
-
-    @Override
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        if (host != null) {
-            if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS ) {
-                int index = mWorkspace.indexOfChild(host);
-                mWorkspace.setCurrentPage(index);
-            } else if (action == MOVE_FORWARD) {
-                movePage(mWorkspace.indexOfChild(host) + 1, host);
-                return true;
-            } else if (action == MOVE_BACKWARD) {
-                movePage(mWorkspace.indexOfChild(host) - 1, host);
-                return true;
-            }
-        }
-
-        return super.performAccessibilityAction(host, action, args);
-    }
-
-    private void movePage(int finalIndex, View view) {
-        mWorkspace.onStartReordering();
-        mWorkspace.removeView(view);
-        mWorkspace.addView(view, finalIndex);
-        mWorkspace.onEndReordering();
-        mWorkspace.announceForAccessibility(mWorkspace.getContext().getText(R.string.screen_moved));
-
-        mWorkspace.updateAccessibilityFlags();
-        view.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(host, info);
-
-        int index = mWorkspace.indexOfChild(host);
-        if (index < mWorkspace.getChildCount() - 1) {
-            info.addAction(mActions.get(MOVE_FORWARD));
-        }
-
-        int startIndex = FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0;
-        if (index > startIndex) {
-            info.addAction(mActions.get(MOVE_BACKWARD));
-        }
-    }
-}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2b42429..1bdd554 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -207,11 +207,11 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mContent = (FolderPagedView) findViewById(R.id.folder_content);
+        mContent = findViewById(R.id.folder_content);
         mContent.setFolder(this);
 
-        mPageIndicator = (PageIndicatorDots) findViewById(R.id.folder_page_indicator);
-        mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
+        mPageIndicator = findViewById(R.id.folder_page_indicator);
+        mFolderName = findViewById(R.id.folder_name);
         mFolderName.setOnBackKeyListener(this);
         mFolderName.setOnFocusChangeListener(this);
 
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
new file mode 100644
index 0000000..df11686
--- /dev/null
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -0,0 +1,143 @@
+/*
+ * 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.touch;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.ViewConfiguration.getLongPressTimeout;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Helper class to handle touch on empty space in workspace and show options popup on long press
+ */
+public class WorkspaceTouchListener implements OnTouchListener, Runnable {
+
+    /**
+     * STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
+     * This next event is used to send an ACTION_CANCEL to Workspace, to that it clears any
+     * temporary scroll state. After that, the state is set to COMPLETED, and we just eat up all
+     * subsequent motion events.
+     */
+    private static final int STATE_CANCELLED = 0;
+    private static final int STATE_REQUESTED = 1;
+    private static final int STATE_PENDING_PARENT_INFORM = 2;
+    private static final int STATE_COMPLETED = 3;
+
+    private final Rect mTempRect = new Rect();
+    private final Launcher mLauncher;
+    private final Workspace mWorkspace;
+    private final PointF mTouchDownPoint = new PointF();
+
+    private int mLongPressState = STATE_CANCELLED;
+
+    public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
+        mLauncher = launcher;
+        mWorkspace = workspace;
+    }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent ev) {
+        int action = ev.getActionMasked();
+        if (action == ACTION_DOWN) {
+            // Check if we can handle long press.
+            boolean handleLongPress = AbstractFloatingView.getTopOpenView(mLauncher) == null
+                    && mLauncher.isInState(NORMAL);
+
+            if (handleLongPress) {
+                // Check if the event is not near the edges
+                DeviceProfile dp = mLauncher.getDeviceProfile();
+                DragLayer dl = mLauncher.getDragLayer();
+                Rect insets = dp.getInsets();
+
+                mTempRect.set(insets.left, insets.top, dl.getWidth() - insets.right,
+                        dl.getHeight() - insets.bottom);
+                mTempRect.inset(dp.edgeMarginPx, dp.edgeMarginPx);
+                handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
+            }
+
+            cancelLongPress();
+            if (handleLongPress) {
+                mLongPressState = STATE_REQUESTED;
+                mTouchDownPoint.set(ev.getX(), ev.getY());
+                mWorkspace.postDelayed(this, getLongPressTimeout());
+            }
+
+            mWorkspace.onTouchEvent(ev);
+            // Return true to keep receiving touch events
+            return true;
+        }
+
+        if (mLongPressState == STATE_PENDING_PARENT_INFORM) {
+            // Inform the workspace to cancel touch handling
+            ev.setAction(ACTION_CANCEL);
+            mWorkspace.onTouchEvent(ev);
+            ev.setAction(action);
+            mLongPressState = STATE_COMPLETED;
+        }
+
+        if (mLongPressState == STATE_COMPLETED) {
+            // We have handled the touch, so workspace does not need to know anything anymore.
+            return true;
+        } else if (mLongPressState == STATE_REQUESTED) {
+            mWorkspace.onTouchEvent(ev);
+            if (action == ACTION_UP || action == ACTION_CANCEL || mWorkspace.isHandlingTouch()) {
+                cancelLongPress();
+            }
+            return true;
+        } else {
+            // We don't want to handle touch, let workspace handle it as usual.
+            return false;
+        }
+    }
+
+    private void cancelLongPress() {
+        mWorkspace.removeCallbacks(this);
+        mLongPressState = STATE_CANCELLED;
+    }
+
+    @Override
+    public void run() {
+        if (mLongPressState == STATE_REQUESTED) {
+            mLongPressState = STATE_PENDING_PARENT_INFORM;
+            mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
+
+            mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+                    Action.Direction.NONE, ContainerType.WORKSPACE,
+                    mWorkspace.getCurrentPage());
+            OptionsPopupView.show(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
new file mode 100644
index 0000000..21b6773
--- /dev/null
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -0,0 +1,310 @@
+/*
+ * 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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.Toast;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.RevealOutlineAnimation;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.ColorScrim;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.widget.WidgetsFullSheet;
+
+/**
+ * Popup shown on long pressing an empty space in launcher
+ */
+public class OptionsPopupView extends AbstractFloatingView
+        implements OnClickListener, OnLongClickListener {
+
+    private final float mOutlineRadius;
+    private final Launcher mLauncher;
+    private final PointF mTouchPoint = new PointF();
+
+    private final ColorScrim mScrim;
+
+    protected Animator mOpenCloseAnimator;
+
+    public OptionsPopupView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
+        setClipToOutline(true);
+        setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
+            }
+        });
+
+        mLauncher = Launcher.getLauncher(context);
+        mScrim = ColorScrim.createExtractedColorScrim(this);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        attachListeners(findViewById(R.id.wallpaper_button));
+        attachListeners(findViewById(R.id.widget_button));
+        attachListeners(findViewById(R.id.settings_button));
+    }
+
+    private void attachListeners(View view) {
+        view.setOnClickListener(this);
+        view.setOnLongClickListener(this);
+    }
+
+    @Override
+    public void onClick(View view) {
+        handleViewClick(view, Action.Touch.TAP);
+    }
+
+    @Override
+    public boolean onLongClick(View view) {
+        return handleViewClick(view, Action.Touch.LONGPRESS);
+    }
+
+    private boolean handleViewClick(View view, int action) {
+        if (view.getId() == R.id.wallpaper_button) {
+            mLauncher.onClickWallpaperPicker(null);
+            logTap(action, ControlType.WALLPAPER_BUTTON);
+            close(true);
+            return true;
+        } else if (view.getId() == R.id.widget_button) {
+            logTap(action, ControlType.WIDGETS_BUTTON);
+            if (onWidgetsClicked(mLauncher)) {
+                close(true);
+                return true;
+            }
+        } else if (view.getId() == R.id.settings_button) {
+            startSettings(mLauncher);
+            logTap(action, ControlType.SETTINGS_BUTTON);
+            close(true);
+            return true;
+        }
+        return false;
+    }
+
+    private void logTap(int action, int controlType) {
+        mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() != MotionEvent.ACTION_DOWN) {
+            return false;
+        }
+        if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
+            return false;
+        }
+        close(true);
+        return true;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (animate) {
+            animateClose();
+        } else {
+            closeComplete();
+        }
+    }
+
+    protected void animateClose() {
+        if (!mIsOpen) {
+            return;
+        }
+        mIsOpen = false;
+
+        final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+        closeAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
+
+        // Rectangular reveal (reversed).
+        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+                .createRevealAnimator(this, true);
+        closeAnim.play(revealAnim);
+
+        Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+        fadeOut.setInterpolator(Interpolators.DEACCEL);
+        closeAnim.play(fadeOut);
+
+        Animator gradientAlpha = ObjectAnimator.ofFloat(mScrim, ColorScrim.PROGRESS, 0);
+        gradientAlpha.setInterpolator(Interpolators.DEACCEL);
+        closeAnim.play(gradientAlpha);
+
+        closeAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mOpenCloseAnimator = null;
+                closeComplete();
+            }
+        });
+        if (mOpenCloseAnimator != null) {
+            mOpenCloseAnimator.cancel();
+        }
+        mOpenCloseAnimator = closeAnim;
+        closeAnim.start();
+    }
+
+    /**
+     * Closes the popup without animation.
+     */
+    private void closeComplete() {
+        if (mOpenCloseAnimator != null) {
+            mOpenCloseAnimator.cancel();
+            mOpenCloseAnimator = null;
+        }
+        mIsOpen = false;
+        mLauncher.getDragLayer().removeView(this);
+    }
+
+    @Override
+    public void logActionCommand(int command) {
+        // TODO:
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_OPTIONS_POPUP) != 0;
+    }
+
+    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        Rect startRect = new Rect();
+        startRect.offset((int) (mTouchPoint.x - lp.x), (int) (mTouchPoint.y - lp.y));
+
+        Rect endRect = new Rect(0, 0, lp.width, lp.height);
+        if (getOutlineProvider() instanceof RevealOutlineAnimation) {
+            ((RevealOutlineAnimation) getOutlineProvider()).getOutline(endRect);
+        }
+
+        return new RoundedRectRevealOutlineProvider
+                (mOutlineRadius, mOutlineRadius, startRect, endRect);
+    }
+
+    private void animateOpen() {
+        mIsOpen = true;
+        final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+        openAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
+
+        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+                .createRevealAnimator(this, false);
+        openAnim.play(revealAnim);
+
+        Animator gradientAlpha = ObjectAnimator.ofFloat(mScrim, ColorScrim.PROGRESS, 1);
+        gradientAlpha.setInterpolator(Interpolators.ACCEL);
+        openAnim.play(gradientAlpha);
+
+        mOpenCloseAnimator = openAnim;
+
+        openAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mOpenCloseAnimator = null;
+            }
+        });
+        openAnim.start();
+    }
+
+    public static void show(Launcher launcher, float x, float y) {
+        DragLayer dl = launcher.getDragLayer();
+        OptionsPopupView view = (OptionsPopupView) launcher.getLayoutInflater()
+                .inflate(R.layout.longpress_options_menu, dl, false);
+        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) view.getLayoutParams();
+
+        int maxWidth = dl.getWidth();
+        int maxHeight = dl.getHeight();
+        if (x <= 0 || y <= 0 || x >= maxWidth || y >= maxHeight) {
+            x = maxWidth / 2;
+            y = maxHeight / 2;
+        }
+        view.mTouchPoint.set(x, y);
+
+        int height = lp.height;
+
+        // Find a good width;
+        int childCount = view.getChildCount();
+        int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+        int widthSpec = MeasureSpec.makeMeasureSpec(maxWidth / childCount, MeasureSpec.AT_MOST);
+        int maxChildWidth = 0;
+
+        for (int i = 0; i < childCount; i ++) {
+            View child = ((ViewGroup) view.getChildAt(i)).getChildAt(0);
+            child.measure(widthSpec, heightSpec);
+            maxChildWidth = Math.max(maxChildWidth, child.getMeasuredWidth());
+        }
+        Rect insets = dl.getInsets();
+        int margin = (int) (2 * view.getElevation());
+
+        int width = Math.min(maxWidth - insets.left - insets.right - 2 * margin,
+                maxChildWidth * childCount);
+        lp.width = width;
+
+        // Position is towards the finger
+        lp.customPosition = true;
+        lp.x = Utilities.boundToRange((int) (x - width / 2), insets.left + margin,
+                maxWidth - insets.right - width - margin);
+        lp.y = Utilities.boundToRange((int) (y - height / 2), insets.top + margin,
+                maxHeight - insets.bottom - height - margin);
+
+        launcher.getDragLayer().addView(view);
+        view.animateOpen();
+    }
+
+    public static boolean onWidgetsClicked(Launcher launcher) {
+        if (launcher.getPackageManager().isSafeMode()) {
+            Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+            return false;
+        } else {
+            WidgetsFullSheet.show(launcher, true /* animated */);
+            return true;
+        }
+    }
+
+    public static void startSettings(Launcher launcher) {
+        launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+                .setPackage(launcher.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+}