Don't detach recents from app window after motion pause

- Update MotionPauseListener to have 2 methods: the existing
  onMotionPauseChanged(isPaused) and onMotionPauseDetected()
  - onMotionPauseDetected is the default as most listeners only
    care about the first detected pause
  - AbsSwipeUpHandler still listens to pause changes, to help
    determine the end target at the end of the gesture

Bug: 159089437
Change-Id: I6d14f106cdcbe4e07d8dddba1cee909a963615b1
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 591d3ca..a0f6dc5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -128,7 +128,7 @@
         mMotionPauseDetector.clear();
 
         if (handlingOverviewAnim()) {
-            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
         }
 
         if (mFromState == NORMAL && mToState == HINT_STATE) {
@@ -185,7 +185,7 @@
         }
     }
 
-    private void onMotionPauseChanged(boolean isPaused) {
+    private void onMotionPauseDetected() {
         if (mCurrentAnimation == null) {
             return;
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 4b0642f..87c246f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -80,7 +80,7 @@
  * the user as possible, also handles swipe up and hold to go to overview and swiping back home.
  */
 public class NoButtonQuickSwitchTouchController implements TouchController,
-        BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
+        BothAxesSwipeDetector.Listener {
 
     /** The minimum progress of the scale/translationY animation until drag end. */
     private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
@@ -167,7 +167,7 @@
         if (start) {
             mStartState = mLauncher.getStateManager().getState();
 
-            mMotionPauseDetector.setOnMotionPauseListener(this);
+            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
 
             // We have detected horizontal drag start, now allow swipe up as well.
             mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
@@ -177,8 +177,7 @@
         }
     }
 
-    @Override
-    public void onMotionPauseChanged(boolean isPaused) {
+    private void onMotionPauseDetected() {
         VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
     }
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index aaa2720..a932b30 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -89,6 +89,7 @@
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.InputConsumerProxy;
+import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TransformParams;
@@ -201,6 +202,7 @@
     // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
     private RunningWindowAnim mRunningWindowAnim;
     private boolean mIsMotionPaused;
+    private boolean mHasMotionEverBeenPaused;
 
     private boolean mContinuingLastGesture;
 
@@ -482,13 +484,20 @@
                 .getHighResLoadingState().setVisible(true);
     }
 
-    /**
-     * Called when motion pause is detected
-     */
-    public void onMotionPauseChanged(boolean isPaused) {
-        mIsMotionPaused = isPaused;
-        maybeUpdateRecentsAttachedState();
-        performHapticFeedback();
+    public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() {
+        return new MotionPauseDetector.OnMotionPauseListener() {
+            @Override
+            public void onMotionPauseDetected() {
+                mHasMotionEverBeenPaused = true;
+                maybeUpdateRecentsAttachedState();
+                performHapticFeedback();
+            }
+
+            @Override
+            public void onMotionPauseChanged(boolean isPaused) {
+                mIsMotionPaused = isPaused;
+            }
+        };
     }
 
     public void maybeUpdateRecentsAttachedState() {
@@ -519,7 +528,7 @@
             // The window is going away so make sure recents is always visible in this case.
             recentsAttachedToAppWindow = true;
         } else {
-            recentsAttachedToAppWindow = mIsMotionPaused || mIsLikelyToStartNewTask;
+            recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
         }
         mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index f02e5e6..0e1364a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -52,9 +52,9 @@
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.AbsSwipeUpHandler;
 import com.android.quickstep.AbsSwipeUpHandler.Factory;
+import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
@@ -365,7 +365,7 @@
         mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
                 mTaskAnimationManager.isRecentsAnimationRunning());
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
-        mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
+        mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
         Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
         mInteractionHandler.initWhenReady(intent);
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index b9827ff..a8bf333 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -42,18 +42,16 @@
         mMotionPauseMinDisplacement = context.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
         mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
-        mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
-            if (isPaused) {
-                SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
-                BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
-                        .getCreatedActivity();
-                if (launcherActivity != null) {
-                    launcherActivity.getRootView().performHapticFeedback(
-                            HapticFeedbackConstants.LONG_PRESS,
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                }
-                mMotionPauseDetector.clear();
+        mMotionPauseDetector.setOnMotionPauseListener(() -> {
+            SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
+            BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
+                    .getCreatedActivity();
+            if (launcherActivity != null) {
+                launcherActivity.getRootView().performHapticFeedback(
+                        HapticFeedbackConstants.LONG_PRESS,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
             }
+            mMotionPauseDetector.clear();
         });
     }
 
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 81c4d0c..8925235 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -182,7 +182,7 @@
                 mLaunchedAssistant = false;
                 mSwipeUpTouchTracker.init();
                 mMotionPauseDetector.clear();
-                mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+                mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
                 break;
             case MotionEvent.ACTION_MOVE:
                 mLastPos.set(event.getX(), event.getY());
@@ -220,7 +220,6 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mMotionPauseDetector.clear();
-                mMotionPauseDetector.setOnMotionPauseListener(null);
                 if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
                     mGestureCallback.onNavBarGestureAttempted(
                             HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -252,10 +251,8 @@
         return intercepted;
     }
 
-    protected void onMotionPauseChanged(boolean isPaused) {
-        if (isPaused) {
-            VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
-        }
+    protected void onMotionPauseDetected() {
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index b862936..d0f6879 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -184,11 +184,15 @@
         }
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
+            boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
             if (mIsPaused) {
                 AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
                 mHasEverBeenPaused = true;
             }
             if (mOnMotionPauseListener != null) {
+                if (isFirstDetectedPause) {
+                    mOnMotionPauseListener.onMotionPauseDetected();
+                }
                 mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
             }
         }
@@ -211,7 +215,10 @@
     }
 
     public interface OnMotionPauseListener {
-        void onMotionPauseChanged(boolean isPaused);
+        /** Called only the first time motion pause is detected. */
+        void onMotionPauseDetected();
+        /** Called every time motion changes from paused to not paused and vice versa. */
+        default void onMotionPauseChanged(boolean isPaused) { }
     }
 
     /**