[Trackpad] Do not reverse two-finger gestures on the homescreen when reverse scrolling is on

- Swipe up to pull up all apps
- Swipe down to pull down notifications
- Same gesture to close in reverse scrolling mode

Bug: 301966246
Bug: 301966690
Test: manual
Change-Id: Ia3770ce8b279d24b2de4844f441f14e0feaf57c7
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index e30fe66..8cbf239 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -96,7 +96,7 @@
             return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
                     ? mLauncher.getStateManager().getLastState()
                     : NORMAL;
-        } else if (fromState == NORMAL && isDragTowardPositive) {
+        } else if (fromState == NORMAL && shouldOpenAllApps(isDragTowardPositive)) {
             return ALL_APPS;
         }
         return fromState;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 26ab3d6..cda7855 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -21,6 +21,7 @@
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
 
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
 
 import android.graphics.PointF;
@@ -57,6 +58,8 @@
     /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
     private boolean mCanIntercept;
 
+    private boolean mIsTrackpadReverseScroll;
+
     public StatusBarTouchController(Launcher l) {
         mLauncher = l;
         mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
@@ -92,6 +95,8 @@
             }
             mDownEvents.clear();
             mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
+            mIsTrackpadReverseScroll = !mLauncher.isNaturalScrollingEnabled()
+                    && isTrackpadScroll(ev);
         } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
             // Check!! should only set it only when threshold is not entered.
             mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
@@ -102,6 +107,9 @@
         if (action == ACTION_MOVE) {
             float dy = ev.getY(idx) - mDownEvents.get(pid).y;
             float dx = ev.getX(idx) - mDownEvents.get(pid).x;
+            if (mIsTrackpadReverseScroll) {
+                dy = -dy;
+            }
             // Currently input dispatcher will not do touch transfer if there are more than
             // one touch pointer. Hence, even if slope passed, only set the slippery flag
             // when there is single touch event. (context: InputDispatcher.cpp line 1445)
@@ -126,6 +134,7 @@
             mLauncher.getStatsLogManager().logger()
                     .log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
             setWindowSlippery(false);
+            mIsTrackpadReverseScroll = false;
             return true;
         }
         return true;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ec2816b..bd2e4a6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -78,6 +78,7 @@
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
+import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -210,6 +211,7 @@
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.ScreenOnTracker;
 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
+import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
@@ -430,6 +432,11 @@
     private final List<BackPressHandler> mBackPressedHandlers = new ArrayList<>();
     private boolean mIsColdStartupAfterReboot;
 
+    private boolean mIsNaturalScrollingEnabled;
+
+    private final SettingsCache.OnChangeListener mNaturalScrollingChangedListener =
+            enabled -> mIsNaturalScrollingEnabled = enabled;
+
     public static Launcher getLauncher(Context context) {
         return fromContext(context);
     }
@@ -578,6 +585,10 @@
         }
         getRootView().dispatchInsets();
 
+        final SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
+        settingsCache.register(TOUCHPAD_NATURAL_SCROLLING, mNaturalScrollingChangedListener);
+        mIsNaturalScrollingEnabled = settingsCache.getValue(TOUCHPAD_NATURAL_SCROLLING);
+
         // Listen for screen turning off
         ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener);
         getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
@@ -1692,6 +1703,8 @@
         super.onDestroy();
         ACTIVITY_TRACKER.onActivityDestroyed(this);
 
+        SettingsCache.INSTANCE.get(this).unregister(TOUCHPAD_NATURAL_SCROLLING,
+                mNaturalScrollingChangedListener);
         ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener);
         mWorkspace.removeFolderListeners();
         PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this);
@@ -3195,6 +3208,10 @@
         return !isWorkspaceLoading();
     }
 
+    public boolean isNaturalScrollingEnabled() {
+        return mIsNaturalScrollingEnabled;
+    }
+
     public void setWaitingForResult(PendingRequestArgs args) {
         mPendingRequestArgs = args;
     }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index cec4574..9aed4eb 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -68,6 +69,7 @@
     protected boolean mGoingBetweenStates = true;
     // Ratio of transition process [0, 1] to drag displacement (px)
     protected float mProgressMultiplier;
+    protected boolean mIsTrackpadReverseScroll;
 
     private boolean mNoIntercept;
     private boolean mIsLogContainerSet;
@@ -92,6 +94,9 @@
                 return false;
             }
 
+            mIsTrackpadReverseScroll = !mLauncher.isNaturalScrollingEnabled()
+                    && isTrackpadScroll(ev);
+
             // Now figure out which direction scroll events the controller will start
             // calling the callbacks.
             final int directionsToDetectScroll;
@@ -248,6 +253,11 @@
             }
             mIsLogContainerSet = true;
         }
+        // Only reverse the gesture to open all apps (not close) when trackpad reverse scrolling is
+        // on.
+        if (mIsTrackpadReverseScroll && mStartState == NORMAL) {
+            displacement = -displacement;
+        }
         return onDrag(displacement);
     }
 
@@ -274,6 +284,11 @@
             return;
         }
 
+        // Only reverse the gesture to open all apps (not close) when trackpad reverse scrolling is
+        // on.
+        if (mIsTrackpadReverseScroll && mStartState == NORMAL) {
+            velocity = -velocity;
+        }
         boolean fling = mDetector.isFling(velocity);
 
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -412,9 +427,15 @@
         mGoingBetweenStates = true;
         mDetector.finishedScrolling();
         mDetector.setDetectableScrollConditions(0, false);
+        mIsTrackpadReverseScroll = false;
     }
 
     private void cancelAnimationControllers() {
         mCurrentAnimation = null;
     }
+
+    protected boolean shouldOpenAllApps(boolean isDragTowardPositive) {
+        return (isDragTowardPositive && !mIsTrackpadReverseScroll)
+                || (!isDragTowardPositive && mIsTrackpadReverseScroll);
+    }
 }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 447d22b..8b9bc19 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -166,7 +166,7 @@
 
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        if (fromState == NORMAL && isDragTowardPositive) {
+        if (fromState == NORMAL && shouldOpenAllApps(isDragTowardPositive)) {
             return ALL_APPS;
         } else if (fromState == ALL_APPS && !isDragTowardPositive) {
             return NORMAL;
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
index 29ec5ab..e663ac2 100644
--- a/src/com/android/launcher3/util/SettingsCache.java
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -57,6 +57,9 @@
             "swipe_bottom_to_notification_enabled";
     public static final Uri ROTATION_SETTING_URI =
             Settings.System.getUriFor(ACCELEROMETER_ROTATION);
+    /** Hidden field {@link Settings.System#TOUCHPAD_NATURAL_SCROLLING}. */
+    public static final Uri TOUCHPAD_NATURAL_SCROLLING = Settings.System.getUriFor(
+            "TOUCHPAD_NATURAL_SCROLLING");
 
     private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString();
     private static final String GLOBAL_URI_PREFIX = Settings.Global.CONTENT_URI.toString();