Merge "Disabling horizontal swipes if the app has draggable content at the bottom" into ub-launcher3-qt-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 7563c3f..294a997 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -58,6 +58,8 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
+import androidx.annotation.BinderThread;
+
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
@@ -84,6 +86,7 @@
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -268,6 +271,9 @@
     private final RectF mSwipeTouchRegion = new RectF();
     private ComponentName mGestureBlockingActivity;
 
+    private Region mExclusionRegion;
+    private SystemGestureExclusionListenerCompat mExclusionListener;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -284,14 +290,23 @@
             mIsUserUnlocked = false;
             registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
         }
-        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
 
         mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
                 .getDisplayId();
-
         String blockingActivity = getString(R.string.gesture_blocking_activity);
         mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
                 ComponentName.unflattenFromString(blockingActivity);
+
+        mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) {
+            @Override
+            @BinderThread
+            public void onExclusionChanged(Region region) {
+                // Assignments are atomic, it should be safe on binder thread
+                mExclusionRegion = region;
+            }
+        };
+
+        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
         sConnected = true;
     }
 
@@ -370,6 +385,12 @@
 
         disposeEventHandlers();
         initInputMonitor();
+
+        if (mMode == Mode.NO_BUTTON) {
+            mExclusionListener.register();
+        } else {
+            mExclusionListener.unregister();
+        }
     }
 
     @Override
@@ -437,6 +458,7 @@
         sConnected = false;
         Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+        mExclusionListener.unregister();
 
         super.onDestroy();
     }
@@ -557,10 +579,15 @@
         final ActivityControlHelper activityControl =
                 mOverviewComponentObserver.getActivityControlHelper();
         boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+
+        // mExclusionRegion can change on binder thread, use a local instance here.
+        Region exclusionRegion = mExclusionRegion;
+        boolean disableHorizontalSwipe = mMode == Mode.NO_BUTTON && exclusionRegion != null
+                && exclusionRegion.contains((int) event.getX(), (int) event.getY());
         return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
                 mOverviewComponentObserver.getOverviewIntent(), activityControl,
                 shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
-                mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion);
+                mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe);
     }
 
     private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index b0acffa..0ed4c99 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -111,6 +111,7 @@
 
     private final float mDragSlop;
     private final float mSquaredTouchSlop;
+    private final boolean mDisableHorizontalSwipe;
 
     // Slop used to check when we start moving window.
     private boolean mPassedDragSlop;
@@ -132,7 +133,7 @@
             InputConsumerController inputConsumer,
             Consumer<OtherActivityInputConsumer> onCompleteCallback,
             SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
-            RectF swipeTouchRegion) {
+            RectF swipeTouchRegion, boolean disableHorizontalSwipe) {
         super(base);
 
         mMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -162,6 +163,7 @@
         mSquaredTouchSlop = slop * slop;
 
         mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture;
+        mDisableHorizontalSwipe = !mPassedTouchSlop && disableHorizontalSwipe;
     }
 
     @Override
@@ -169,6 +171,13 @@
         return TYPE_OTHER_ACTIVITY;
     }
 
+    private void forceCancelGesture(MotionEvent ev) {
+        int action = ev.getAction();
+        ev.setAction(ACTION_CANCEL);
+        finishTouchTracking(ev);
+        ev.setAction(action);
+    }
+
     @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mVelocityTracker == null) {
@@ -216,10 +225,7 @@
                     // Cancel interaction in case of multi-touch interaction
                     int ptrIdx = ev.getActionIndex();
                     if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
-                        int action = ev.getAction();
-                        ev.setAction(ACTION_CANCEL);
-                        finishTouchTracking(ev);
-                        ev.setAction(action);
+                        forceCancelGesture(ev);
                     }
                 }
                 break;
@@ -258,7 +264,15 @@
                 }
 
                 if (!mPassedTouchSlop) {
-                    if (squaredHypot(displacementX, mLastPos.y - mDownPos.y) >= mSquaredTouchSlop) {
+                    float displacementY = mLastPos.y - mDownPos.y;
+                    if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
+                        if (mDisableHorizontalSwipe
+                                && Math.abs(displacementX) > Math.abs(displacementY)) {
+                            // Horizontal gesture is not allowed in this region
+                            forceCancelGesture(ev);
+                            break;
+                        }
+
                         mPassedTouchSlop = true;
 
                         if (mIsDeferredDownTarget) {