Merge "Use onTouchListener to listen for long press on maximize window button" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index ca91d58..716f8b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -57,6 +57,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
@@ -362,7 +363,7 @@
 
     private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
             implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
-            DragDetector.MotionEventHandler{
+            DragDetector.MotionEventHandler {
 
         private final int mTaskId;
         private final WindowContainerToken mTaskToken;
@@ -371,6 +372,7 @@
         private final GestureDetector mGestureDetector;
 
         private boolean mIsDragging;
+        private boolean mHasLongClicked;
         private boolean mShouldClick;
         private int mDragPointerId = -1;
 
@@ -394,11 +396,9 @@
                     RunningTaskInfo remainingTask = getOtherSplitTask(mTaskId);
                     mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
                 }
-                decoration.closeMaximizeMenu();
             } else if (id == R.id.back_button) {
                 mTaskOperations.injectBackKey();
             } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
-                decoration.closeMaximizeMenu();
                 if (!decoration.isHandleMenuActive()) {
                     moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
                     decoration.createHandleMenu();
@@ -433,7 +433,6 @@
                     mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
                 }
             } else if (id == R.id.maximize_window) {
-                moveTaskToFront(decoration.mTaskInfo);
                 if (decoration.isMaximizeMenuActive()) {
                     decoration.closeMaximizeMenu();
                     return;
@@ -467,10 +466,26 @@
         public boolean onTouch(View v, MotionEvent e) {
             final int id = v.getId();
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption
-                    && id != R.id.open_menu_button && id != R.id.close_window) {
+                    && id != R.id.open_menu_button && id != R.id.close_window
+                    && id != R.id.maximize_window) {
                 return false;
             }
             moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
+
+            if (!mHasLongClicked) {
+                final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+                decoration.closeMaximizeMenu();
+            }
+
+            final long eventDuration = e.getEventTime() - e.getDownTime();
+            final boolean shouldLongClick = id == R.id.maximize_window && !mIsDragging
+                    && !mHasLongClicked && eventDuration >= ViewConfiguration.getLongPressTimeout();
+            if (shouldLongClick) {
+                v.performLongClick();
+                mHasLongClicked = true;
+                return true;
+            }
+
             return mDragDetector.onMotionEvent(v, e);
         }
 
@@ -483,7 +498,6 @@
                 if (decoration.isMaximizeMenuActive()) {
                     decoration.closeMaximizeMenu();
                 } else {
-                    decoration.closeHandleMenu();
                     decoration.createMaximizeMenu();
                 }
                 return true;
@@ -519,11 +533,13 @@
                             e.getRawY(0));
                     mIsDragging = false;
                     mShouldClick = true;
+                    mHasLongClicked = false;
                     return true;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     final DesktopModeWindowDecoration decoration =
                             mWindowDecorByTaskId.get(mTaskId);
+                    decoration.closeMaximizeMenu();
                     if (e.findPointerIndex(mDragPointerId) == -1) {
                         mDragPointerId = e.getPointerId(0);
                     }
@@ -542,7 +558,7 @@
                 case MotionEvent.ACTION_CANCEL: {
                     final boolean wasDragging = mIsDragging;
                     if (!wasDragging) {
-                        if (mShouldClick && v != null) {
+                        if (mShouldClick && v != null && !mHasLongClicked) {
                             v.performClick();
                             mShouldClick = false;
                             return true;
@@ -685,14 +701,16 @@
     // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
     private void handleEventOutsideFocusedCaption(MotionEvent ev,
             DesktopModeWindowDecoration relevantDecor) {
+        // Returns if event occurs within caption
+        if (relevantDecor == null || relevantDecor.checkTouchEventInCaption(ev)) {
+            return;
+        }
+
         final int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            if (relevantDecor == null) {
-                return;
-            }
-
             if (!mTransitionDragActive) {
                 relevantDecor.closeHandleMenuIfNeeded(ev);
+                relevantDecor.closeMaximizeMenuIfNeeded(ev);
             }
         }
     }
@@ -1024,7 +1042,6 @@
         public void onDragStart(int taskId) {
             final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
             decoration.closeHandleMenu();
-            decoration.closeMaximizeMenu();
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index a976584..eba1a36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -521,6 +521,20 @@
         }
     }
 
+    /**
+     * Close an open maximize menu if input is outside of menu coordinates
+     *
+     * @param ev the tapped point to compare against
+     */
+    void closeMaximizeMenuIfNeeded(MotionEvent ev) {
+        if (!isMaximizeMenuActive()) return;
+
+        final PointF inputPoint = offsetCaptionLocation(ev);
+        if (!mMaximizeMenu.isValidMenuInput(inputPoint)) {
+            closeMaximizeMenu();
+        }
+    }
+
     boolean isFocused() {
         return mTaskInfo.isFocused;
     }
@@ -560,6 +574,13 @@
     }
 
     /**
+     * Returns true if motion event is within the caption's root view's bounds.
+     */
+    boolean checkTouchEventInCaption(MotionEvent ev) {
+        return checkEventInCaptionView(ev, getCaptionViewId());
+    }
+
+    /**
      * Check a passed MotionEvent if a click has occurred on any button on this caption
      * Note this should only be called when a regular onClick is not possible
      * (i.e. the button was clicked through status bar layer)
@@ -574,6 +595,7 @@
             clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
         } else {
             mHandleMenu.checkClickEvent(ev);
+            closeHandleMenuIfNeeded(ev);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 050d1e9..921708f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -25,6 +25,7 @@
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.SurfaceControlViewHost
+import android.view.View
 import android.view.View.OnClickListener
 import android.view.WindowManager
 import android.view.WindowlessWindowManager
@@ -151,4 +152,28 @@
                 R.id.maximize_menu_snap_left_button
         ).setOnClickListener(onClickListener)
     }
+
+    /**
+     * A valid menu input is one of the following:
+     * An input that happens in the menu views.
+     * Any input before the views have been laid out.
+     *
+     * @param inputPoint the input to compare against.
+     */
+    fun isValidMenuInput(inputPoint: PointF): Boolean {
+        val menuView = maximizeMenu?.mWindowViewHost?.view ?: return true
+        return !viewsLaidOut() || pointInView(menuView, inputPoint.x - menuPosition.x,
+                inputPoint.y - menuPosition.y)
+    }
+
+    private fun pointInView(v: View, x: Float, y: Float): Boolean {
+        return v.left <= x && v.right >= x && v.top <= y && v.bottom >= y
+    }
+
+    /**
+     * Check if the views for maximize menu can be seen.
+     */
+    private fun viewsLaidOut(): Boolean {
+        return maximizeMenu?.mWindowViewHost?.view?.isLaidOut ?: false
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index d64312a..b739ad3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -41,6 +41,7 @@
         openMenuButton.setOnTouchListener(onCaptionTouchListener)
         closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
         maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+        maximizeWindowButton.setOnTouchListener(onCaptionTouchListener)
         maximizeWindowButton.onLongClickListener = onLongClickListener
         closeWindowButton.setOnTouchListener(onCaptionTouchListener)
         appNameTextView.text = appName