Support right click to bring up app menu and actions

Test: https://b.corp.google.com/action/issues/250092449/attachments/40159735?download=true
Change-Id: I159005cc982478896d442bb2675ccb1bb7f0322e
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index c707df0..ec29b29 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -16,12 +16,15 @@
 
 package com.android.launcher3;
 
+import android.os.Handler;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
 /**
- * Utility class to handle tripper long press on a view with custom timeout and stylus event
+ * Utility class to handle tripper long press or right click on a view with custom timeout and
+ * stylus event
  */
 public class CheckLongPressHelper {
 
@@ -34,6 +37,7 @@
     private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
 
     private boolean mHasPerformedLongPress;
+    private boolean mIsInMouseRightClick;
 
     private Runnable mPendingCheckForLongPress;
 
@@ -59,6 +63,26 @@
                 // start fresh on touch down.
                 cancelLongPress();
 
+                // Mouse right click should immediately trigger a long press
+                if (isMouseRightClickDownOrMove(ev)) {
+                    mIsInMouseRightClick = true;
+                    triggerLongPress();
+                    final Handler handler = mView.getHandler();
+                    if (handler != null) {
+                        // Send an ACTION_UP to end this click gesture to avoid user dragging with
+                        // mouse's right button. Note that we need to call
+                        // {@link Handler#postAtFrontOfQueue()} instead of {@link View#post()} to
+                        // make sure ACTION_UP is sent before any ACTION_MOVE if user is dragging.
+                        final MotionEvent actionUpEvent = MotionEvent.obtain(ev);
+                        actionUpEvent.setAction(MotionEvent.ACTION_UP);
+                        handler.postAtFrontOfQueue(() -> {
+                            mView.getRootView().dispatchTouchEvent(actionUpEvent);
+                            actionUpEvent.recycle();
+                        });
+                    }
+                    break;
+                }
+
                 postCheckForLongPress();
                 if (isStylusButtonPressed(ev)) {
                     triggerLongPress();
@@ -70,7 +94,8 @@
                 cancelLongPress();
                 break;
             case MotionEvent.ACTION_MOVE:
-                if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
+                if (mIsInMouseRightClick
+                        || !Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
                     cancelLongPress();
                 } else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
                     // Only trigger long press if it has not been cancelled before
@@ -98,9 +123,10 @@
     }
 
     /**
-     * Cancels any pending long press
+     * Cancels any pending long press and right click
      */
     public void cancelLongPress() {
+        mIsInMouseRightClick = false;
         mHasPerformedLongPress = false;
         clearCallbacks();
     }
@@ -150,4 +176,14 @@
         return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                 && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
     }
+
+    /**
+     * Detect ACTION_DOWN or ACTION_MOVE from mouse right button. Note that we cannot detect
+     * ACTION_UP from mouse's right button because, in that case,
+     * {@link MotionEvent#getButtonState()} returns 0 for any mouse button (right, middle, right).
+     */
+    private static boolean isMouseRightClickDownOrMove(MotionEvent event) {
+        return event.isFromSource(InputDevice.SOURCE_MOUSE)
+                && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0);
+    }
 }