15/ Move some more logic into gesture/device state

- Bake overview/home component into the gesture state (it should never
  change mid-gesture), this allows us to remove OverviewComponentObserver
  refs from the handlers
- Move nav bar position into DeviceState
- Remove passing RecentsModel into the handlers, it already partially
  references it statically

Bug: 141886704
Change-Id: I62f9138651cbe1fb984b57b96e4212ebaa1ffb5d
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 43cdbdb..4e73a79 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -18,6 +18,8 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.util.NavBarPosition.ROTATION_LANDSCAPE;
+import static com.android.quickstep.util.NavBarPosition.ROTATION_SEASCAPE;
 
 import android.content.Context;
 import android.content.res.Configuration;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index c94c6d5..4f50e33 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -32,8 +31,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -51,7 +48,6 @@
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
-import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AppWindowAnimationHelper;
@@ -92,9 +88,8 @@
     protected final Context mContext;
     protected final RecentsAnimationDeviceState mDeviceState;
     protected final GestureState mGestureState;
-    protected final OverviewComponentObserver mOverviewComponentObserver;
     protected final BaseActivityInterface<T> mActivityInterface;
-    protected final RecentsModel mRecentsModel;
+    protected final InputConsumerController mInputConsumer;
 
     protected final AppWindowAnimationHelper mAppWindowAnimationHelper;
     protected final TransformParams mTransformParams = new TransformParams();
@@ -106,7 +101,6 @@
     protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
 
     protected final ActivityInitListener mActivityInitListener;
-    protected final InputConsumerController mInputConsumer;
 
     protected RecentsAnimationController mRecentsAnimationController;
     protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -127,20 +121,18 @@
     protected int mFinishingRecentsAnimationForNewTaskId = -1;
 
     protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, OverviewComponentObserver overviewComponentObserver,
-            RecentsModel recentsModel, InputConsumerController inputConsumer) {
+            GestureState gestureState, InputConsumerController inputConsumer) {
         mContext = context;
         mDeviceState = deviceState;
         mGestureState = gestureState;
-        mOverviewComponentObserver = overviewComponentObserver;
         mActivityInterface = gestureState.getActivityInterface();
-        mRecentsModel = recentsModel;
         mActivityInitListener =
                 mActivityInterface.createActivityInitListener(this::onActivityInit);
         mInputConsumer = inputConsumer;
 
         mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
         mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+
         initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext));
     }
@@ -371,7 +363,7 @@
 
     public void initWhenReady() {
         // Preload the plan
-        mRecentsModel.getTasks(null);
+        RecentsModel.INSTANCE.get(mContext).getTasks(null);
 
         mActivityInitListener.register();
     }
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 a97b0af..a8974e5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -248,7 +248,6 @@
             this::createFallbackNoButtonSwipeHandler;
 
     private ActivityManagerWrapper mAM;
-    private RecentsModel mRecentsModel;
     private OverviewCommandHelper mOverviewCommandHelper;
     private OverviewComponentObserver mOverviewComponentObserver;
     private InputConsumerController mInputConsumer;
@@ -327,7 +326,6 @@
     @UiThread
     public void onUserUnlocked() {
         mTaskAnimationManager = new TaskAnimationManager();
-        mRecentsModel = RecentsModel.INSTANCE.get(this);
         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
         mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
                 mOverviewComponentObserver);
@@ -431,8 +429,7 @@
                 TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
         MotionEvent event = (MotionEvent) ev;
         if (event.getAction() == ACTION_DOWN) {
-            GestureState newGestureState = new GestureState(
-                    mOverviewComponentObserver.getActivityInterface(),
+            GestureState newGestureState = new GestureState(mOverviewComponentObserver,
                     ActiveGestureLog.INSTANCE.generateAndSetLogId());
             newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
                     () -> mAM.getRunningTask(0)));
@@ -607,7 +604,7 @@
                     false /* startingInActivityBounds */);
         } else {
             final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
-            return new OverviewWithoutFocusInputConsumer(activity, gestureState,
+            return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState,
                     mInputMonitorCompat, disableHorizontalSwipe);
         }
     }
@@ -724,15 +721,13 @@
     private BaseSwipeUpHandler createWindowTransformSwipeHandler(GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
         return  new WindowTransformSwipeHandler(this, mDeviceState, mTaskAnimationManager,
-                gestureState, touchTimeMs, mOverviewComponentObserver, continuingLastGesture,
-                mInputConsumer, mRecentsModel);
+                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
     }
 
     private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,
             long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
         return new FallbackNoButtonInputConsumer(this, mDeviceState, gestureState,
-                mOverviewComponentObserver, mRecentsModel, mInputConsumer, isLikelyToStartNewTask,
-                continuingLastGesture);
+                mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
     }
 
     protected boolean shouldNotifyBackGesture() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 1e636e1..b55ec20 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -191,11 +191,9 @@
 
     public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
-            long touchTimeMs, OverviewComponentObserver overviewComponentObserver,
-            boolean continuingLastGesture, InputConsumerController inputConsumer,
-            RecentsModel recentsModel) {
-        super(context, deviceState, gestureState, overviewComponentObserver, recentsModel,
-                inputConsumer);
+            long touchTimeMs, boolean continuingLastGesture,
+            InputConsumerController inputConsumer) {
+        super(context, deviceState, gestureState, inputConsumer);
         mTaskAnimationManager = taskAnimationManager;
         mTouchTimeMs = touchTimeMs;
         mContinuingLastGesture = continuingLastGesture;
@@ -379,7 +377,7 @@
 
     private void onDeferredActivityLaunch() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
+            mActivityInterface.switchRunningTaskViewToScreenshot(
                     null, () -> {
                         mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
                     });
@@ -521,7 +519,7 @@
 
     @Override
     public Intent getLaunchIntent() {
-        return mOverviewComponentObserver.getOverviewIntent();
+        return mGestureState.getOverviewIntent();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index 5ffb975..b6cd456 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -46,11 +46,9 @@
 import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.MultiStateCallback;
-import com.android.quickstep.OverviewComponentObserver;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.RecentsModel;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.RecentsAnimationTargets;
@@ -111,11 +109,9 @@
     private RunningWindowAnim mFinishAnimation;
 
     public FallbackNoButtonInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, OverviewComponentObserver overviewComponentObserver,
-            RecentsModel recentsModel, InputConsumerController inputConsumer,
+            GestureState gestureState, InputConsumerController inputConsumer,
             boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
-        super(context, deviceState, gestureState, overviewComponentObserver, recentsModel,
-                inputConsumer);
+        super(context, deviceState, gestureState, inputConsumer);
         mLauncherAlpha.value = 1;
 
         mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
@@ -225,9 +221,9 @@
     @Override
     public Intent getLaunchIntent() {
         if (mInQuickSwitchMode || mSwipeUpOverHome) {
-            return mOverviewComponentObserver.getOverviewIntent();
+            return mGestureState.getOverviewIntent();
         } else {
-            return mOverviewComponentObserver.getHomeIntent();
+            return mGestureState.getHomeIntent();
         }
     }
 
@@ -324,7 +320,7 @@
                 if (mSwipeUpOverHome) {
                     mRecentsAnimationController.finish(false, null, false);
                     // Send a home intent to clear the task stack
-                    mContext.startActivity(mOverviewComponentObserver.getHomeIntent());
+                    mContext.startActivity(mGestureState.getHomeIntent());
                 } else {
                     mRecentsAnimationController.finish(true, null, true);
                 }
@@ -351,7 +347,7 @@
                 extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
                 extras.putInt(EXTRA_TASK_ID, runningTaskId);
 
-                Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
+                Intent intent = new Intent(mGestureState.getOverviewIntent())
                         .putExtras(extras);
                 mContext.startActivity(intent, options.toBundle());
                 mRecentsAnimationController.cleanupScreenshot();
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 f8676ec..bf2128d 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
@@ -57,7 +57,6 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -85,8 +84,6 @@
 
     private final BaseSwipeUpHandler.Factory mHandlerFactory;
 
-    private final NavBarPosition mNavBarPosition;
-
     private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
     private final MotionPauseDetector mMotionPauseDetector;
     private final float mMotionPauseMinDisplacement;
@@ -142,8 +139,6 @@
 
         boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
         mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
-
-        mNavBarPosition = new NavBarPosition(base);
         mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
 
         float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
@@ -175,7 +170,7 @@
         if (mPassedWindowMoveSlop && mInteractionHandler != null
                 && !mRecentsViewDispatcher.hasConsumer()) {
             mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
-                    mNavBarPosition.getRotationMode()));
+                    mDeviceState.getNavBarPosition().getRotationMode()));
         }
         int edgeFlags = ev.getEdgeFlags();
         ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -356,8 +351,10 @@
                         ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
                 float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
                 float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
-                float velocity = mNavBarPosition.isRightEdge() ? velocityX
-                        : mNavBarPosition.isLeftEdge() ? -velocityX
+                float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+                        ? velocityX
+                        : mDeviceState.getNavBarPosition().isLeftEdge()
+                                ? -velocityX
                                 : velocityY;
 
                 mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
@@ -410,9 +407,9 @@
     }
 
     private float getDisplacement(MotionEvent ev) {
-        if (mNavBarPosition.isRightEdge()) {
+        if (mDeviceState.getNavBarPosition().isRightEdge()) {
             return ev.getX() - mDownPos.x;
-        } else if (mNavBarPosition.isLeftEdge()) {
+        } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
             return mDownPos.x - ev.getX();
         } else {
             return ev.getY() - mDownPos.y;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 50069ea..d700a37 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -35,36 +35,35 @@
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 public class OverviewWithoutFocusInputConsumer implements InputConsumer {
 
+    private final Context mContext;
+    private final RecentsAnimationDeviceState mDeviceState;
+    private final GestureState mGestureState;
     private final InputMonitorCompat mInputMonitor;
     private final boolean mDisableHorizontalSwipe;
     private final PointF mDownPos = new PointF();
     private final float mSquaredTouchSlop;
-    private final Context mContext;
-    private final NavBarPosition mNavBarPosition;
-    private final BaseActivityInterface mActivityInterface;
 
     private boolean mInterceptedTouch;
     private VelocityTracker mVelocityTracker;
 
-    public OverviewWithoutFocusInputConsumer(Context context, GestureState gestureState,
+    public OverviewWithoutFocusInputConsumer(Context context,
+            RecentsAnimationDeviceState deviceState, GestureState gestureState,
             InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
+        mContext = context;
+        mDeviceState = deviceState;
+        mGestureState = gestureState;
         mInputMonitor = inputMonitor;
         mDisableHorizontalSwipe = disableHorizontalSwipe;
-        mContext = context;
-        mActivityInterface = gestureState.getActivityInterface();
         mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
-        mNavBarPosition = new NavBarPosition(context);
-
         mVelocityTracker = VelocityTracker.obtain();
     }
 
@@ -135,8 +134,11 @@
         mVelocityTracker.computeCurrentVelocity(100);
         float velocityX = mVelocityTracker.getXVelocity();
         float velocityY = mVelocityTracker.getYVelocity();
-        float velocity = mNavBarPosition.isRightEdge()
-                ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
+        float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+                ? -velocityX
+                : mDeviceState.getNavBarPosition().isLeftEdge()
+                        ? velocityX
+                        : -velocityY;
 
         final boolean triggerQuickstep;
         int touch = Touch.FLING;
@@ -150,7 +152,7 @@
         }
 
         if (triggerQuickstep) {
-            mActivityInterface.closeOverlay();
+            mGestureState.getActivityInterface().closeOverlay();
             ActivityManagerWrapper.getInstance()
                     .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
             ActiveGestureLog.INSTANCE.addLog("startQuickstep");
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
deleted file mode 100644
index e2524b1..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_LANDSCAPE;
-import static com.android.launcher3.uioverrides.QuickstepLauncher.ROTATION_SEASCAPE;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-
-import android.content.Context;
-import android.view.Surface;
-
-import com.android.launcher3.graphics.RotationMode;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.SysUINavigationMode;
-
-/**
- * Utility class to check nav bar position
- */
-public class NavBarPosition {
-
-    private final SysUINavigationMode.Mode mMode;
-    private final int mDisplayRotation;
-
-    public NavBarPosition(Context context) {
-        mMode = SysUINavigationMode.getMode(context);
-        mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
-    }
-
-    public boolean isRightEdge() {
-        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
-    }
-
-    public boolean isLeftEdge() {
-        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
-    }
-
-    public RotationMode getRotationMode() {
-        return isLeftEdge() ? ROTATION_SEASCAPE
-                : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 28ed32c..ae0886b 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -18,7 +18,9 @@
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 
 import android.app.ActivityManager;
+import android.content.Intent;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import java.util.ArrayList;
@@ -57,6 +59,8 @@
         public final boolean recentsAttachedToAppWindow;
     }
 
+    private static final String TAG = "GestureState";
+
     private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
     private static int FLAG_COUNT = 0;
     private static int getFlagForIndex(String name) {
@@ -99,6 +103,8 @@
 
 
     // Needed to interact with the current activity
+    private final Intent mHomeIntent;
+    private final Intent mOverviewIntent;
     private final BaseActivityInterface mActivityInterface;
     private final MultiStateCallback mStateCallback;
     private final int mGestureId;
@@ -108,20 +114,31 @@
     // TODO: This can be removed once we stop finishing the animation when starting a new task
     private int mFinishingRecentsAnimationTaskId = -1;
 
-    public GestureState(BaseActivityInterface activityInterface, int gestureId) {
-        mActivityInterface = activityInterface;
+    public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
+        mHomeIntent = componentObserver.getHomeIntent();
+        mOverviewIntent = componentObserver.getOverviewIntent();
+        mActivityInterface = componentObserver.getActivityInterface();
         mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
         mGestureId = gestureId;
     }
 
     public GestureState() {
         // Do nothing, only used for initializing the gesture state prior to user unlock
+        mHomeIntent = new Intent();
+        mOverviewIntent = new Intent();
         mActivityInterface = null;
         mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
         mGestureId = -1;
     }
 
     /**
+     * @return whether the gesture state has the provided {@param stateMask} flags set.
+     */
+    public boolean hasState(int stateMask) {
+        return mStateCallback.hasStates(stateMask);
+    }
+
+    /**
      * Sets the given {@param stateFlag}s.
      */
     public void setState(int stateFlag) {
@@ -136,6 +153,20 @@
     }
 
     /**
+     * @return the intent for the Home component.
+     */
+    public Intent getHomeIntent() {
+        return mHomeIntent;
+    }
+
+    /**
+     * @return the intent for the Overview component.
+     */
+    public Intent getOverviewIntent() {
+        return mOverviewIntent;
+    }
+
+    /**
      * @return the interface to the activity handing the UI updates for this gesture.
      */
     public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 9d5120d..333e179 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -48,11 +48,10 @@
     private final Consumer<RecentsAnimationController> mOnFinishedListener;
     private final boolean mShouldMinimizeSplitScreen;
 
-    private boolean mWindowThresholdCrossed = false;
-
     private InputConsumerController mInputConsumerController;
     private Supplier<InputConsumer> mInputProxySupplier;
     private InputConsumer mInputConsumer;
+    private boolean mWindowThresholdCrossed = false;
     private boolean mTouchInProgress;
     private boolean mFinishPending;
 
@@ -62,8 +61,6 @@
         mController = controller;
         mOnFinishedListener = onFinishedListener;
         mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
-
-        setWindowThresholdCrossed(mWindowThresholdCrossed);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 1855e64..81f411e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,8 +17,10 @@
 
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 
+import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
 import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
 import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -35,14 +37,17 @@
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Process;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -57,6 +62,7 @@
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -72,16 +78,17 @@
         NavigationModeChangeListener,
         DefaultDisplay.DisplayInfoChangeListener {
 
-    private Context mContext;
-    private UserManagerCompat mUserManager;
-    private SysUINavigationMode mSysUiNavMode;
-    private DefaultDisplay mDefaultDisplay;
-    private int mDisplayId;
+    private final Context mContext;
+    private final UserManagerCompat mUserManager;
+    private final SysUINavigationMode mSysUiNavMode;
+    private final DefaultDisplay mDefaultDisplay;
+    private final int mDisplayId;
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
     private @SystemUiStateFlags int mSystemUiStateFlags;
     private SysUINavigationMode.Mode mMode = THREE_BUTTONS;
+    private NavBarPosition mNavBarPosition;
 
     private final RectF mSwipeUpTouchRegion = new RectF();
     private final Region mDeferredGestureRegion = new Region();
@@ -108,11 +115,13 @@
     private ComponentName mGestureBlockedActivity;
 
     public RecentsAnimationDeviceState(Context context) {
+        final ContentResolver resolver = context.getContentResolver();
         mContext = context;
         mUserManager = UserManagerCompat.getInstance(context);
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
         mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
         mDisplayId = mDefaultDisplay.getInfo().id;
+        runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
 
         // Register for user unlocked if necessary
         mIsUserUnlocked = mUserManager.isUserUnlocked(Process.myUserHandle());
@@ -155,7 +164,6 @@
         for (Runnable r : mOnDestroyActions) {
             r.run();
         }
-        mDefaultDisplay.removeChangeListener(this);
     }
 
     /**
@@ -183,6 +191,7 @@
             mExclusionListener.unregister();
         }
         mMode = newMode;
+        mNavBarPosition = new NavBarPosition(mMode, mDefaultDisplay.getInfo());
     }
 
     @Override
@@ -191,6 +200,7 @@
             return;
         }
 
+        mNavBarPosition = new NavBarPosition(mMode, info);
         updateGestureTouchRegions();
     }
 
@@ -202,6 +212,13 @@
     }
 
     /**
+     * @return the nav bar position for the current nav bar mode and display rotation.
+     */
+    public NavBarPosition getNavBarPosition() {
+        return mNavBarPosition;
+    }
+
+    /**
      * @return whether the current nav mode is fully gestural.
      */
     public boolean isFullyGesturalNavMode() {
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
new file mode 100644
index 0000000..a4614de
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.Surface;
+
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Utility class to check nav bar position.
+ */
+public class NavBarPosition {
+
+    public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
+        @Override
+        public void mapRect(int left, int top, int right, int bottom, Rect out) {
+            out.left = top;
+            out.top = right;
+            out.right = bottom;
+            out.bottom = left;
+        }
+
+        @Override
+        public void mapInsets(Context context, Rect insets, Rect out) {
+            // If there is a display cutout, the top insets in portrait would also include the
+            // cutout, which we will get as the left inset in landscape. Using the max of left and
+            // top allows us to cover both cases (with or without cutout).
+            if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+                out.top = Math.max(insets.top, insets.left);
+                out.bottom = Math.max(insets.right, insets.bottom);
+                out.left = out.right = 0;
+            } else {
+                out.top = Math.max(insets.top, insets.left);
+                out.bottom = insets.right;
+                out.left = insets.bottom;
+                out.right = 0;
+            }
+        }
+    };
+
+    public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
+        @Override
+        public void mapRect(int left, int top, int right, int bottom, Rect out) {
+            out.left = bottom;
+            out.top = left;
+            out.right = top;
+            out.bottom = right;
+        }
+
+        @Override
+        public void mapInsets(Context context, Rect insets, Rect out) {
+            if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+                out.top = Math.max(insets.top, insets.right);
+                out.bottom = Math.max(insets.left, insets.bottom);
+                out.left = out.right = 0;
+            } else {
+                out.top = Math.max(insets.top, insets.right);
+                out.bottom = insets.left;
+                out.right = insets.bottom;
+                out.left = 0;
+            }
+        }
+
+        @Override
+        public int toNaturalGravity(int absoluteGravity) {
+            int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+            int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+            if (horizontalGravity == Gravity.RIGHT) {
+                horizontalGravity = Gravity.LEFT;
+            } else if (horizontalGravity == Gravity.LEFT) {
+                horizontalGravity = Gravity.RIGHT;
+            }
+
+            if (verticalGravity == Gravity.TOP) {
+                verticalGravity = Gravity.BOTTOM;
+            } else if (verticalGravity == Gravity.BOTTOM) {
+                verticalGravity = Gravity.TOP;
+            }
+
+            return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
+                    & ~Gravity.VERTICAL_GRAVITY_MASK)
+                    | horizontalGravity | verticalGravity;
+        }
+    };
+
+    private final SysUINavigationMode.Mode mMode;
+    private final int mDisplayRotation;
+
+    public NavBarPosition(SysUINavigationMode.Mode mode, DefaultDisplay.Info info) {
+        mMode = mode;
+        mDisplayRotation = info.rotation;
+    }
+
+    public boolean isRightEdge() {
+        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
+    }
+
+    public boolean isLeftEdge() {
+        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
+    }
+
+    public RotationMode getRotationMode() {
+        return isLeftEdge() ? ROTATION_SEASCAPE
+                : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
+    }
+}