Refactoring the touch handling into a different class.

This makes it easier to maintain states for a single touch session (from down to up/cancel)

Change-Id: I27da239376337cd02c104fea54e39b2e30b53fab
diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
index aa210b8..21b032b 100644
--- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
@@ -18,7 +18,7 @@
 import android.support.annotation.WorkerThread;
 
 import com.android.launcher3.states.InternalStateHandler;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
 
 public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
 
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index e2abd59..40246e2 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -15,10 +15,11 @@
  */
 package com.android.quickstep;
 
-import static com.android.quickstep.TouchInteractionService.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchInteractionService.isInteractionQuick;
+
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
@@ -29,10 +30,8 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.UserHandle;
 import android.support.annotation.UiThread;
 import android.view.View;
 import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -51,10 +50,9 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
new file mode 100644
index 0000000..df012df
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.quickstep.RemoteRunnable.executeSafely;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TraceHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.AssistDataReceiver;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+/**
+ * Touch consumer for handling events originating from an activity other than Launcher
+ */
+public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
+    private static final String TAG = "ActivityTouchConsumer";
+
+    private final RunningTaskInfo mRunningTask;
+    private final RecentsModel mRecentsModel;
+    private final Intent mHomeIntent;
+    private final ISystemUiProxy mISystemUiProxy;
+    private final MainThreadExecutor mMainThreadExecutor;
+
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+    private int mActivePointerId = INVALID_POINTER_ID;
+    private boolean mTouchThresholdCrossed;
+    private int mTouchSlop;
+    private float mStartDisplacement;
+    private BaseSwipeInteractionHandler mInteractionHandler;
+    private int mDisplayRotation;
+    private Rect mStableInsets = new Rect();
+
+    private VelocityTracker mVelocityTracker;
+
+    public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
+            RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
+            MainThreadExecutor mainThreadExecutor) {
+        super(base);
+        mRunningTask = runningTaskInfo;
+        mRecentsModel = recentsModel;
+        mHomeIntent = homeIntent;
+        mVelocityTracker = VelocityTracker.obtain();
+        mISystemUiProxy = systemUiProxy;
+        mMainThreadExecutor = mainThreadExecutor;
+    }
+
+    @Override
+    public void accept(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                TraceHelper.beginSection("TouchInt");
+                mActivePointerId = ev.getPointerId(0);
+                mDownPos.set(ev.getX(), ev.getY());
+                mLastPos.set(mDownPos);
+                mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+                mTouchThresholdCrossed = false;
+
+                // Start the window animation on down to give more time for launcher to draw
+                if (!isUsingScreenShot()) {
+                    startTouchTrackingForWindowAnimation();
+                }
+
+                mVelocityTracker.addMovement(ev);
+                Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+                mDisplayRotation = display.getRotation();
+                WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+                break;
+            }
+            case ACTION_POINTER_UP: {
+                int ptrIdx = ev.getActionIndex();
+                int ptrId = ev.getPointerId(ptrIdx);
+                if (ptrId == mActivePointerId) {
+                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+                    mDownPos.set(
+                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+                    mActivePointerId = ev.getPointerId(newPointerIdx);
+                    mVelocityTracker.clear();
+                }
+                break;
+            }
+            case ACTION_MOVE: {
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == INVALID_POINTER_ID) {
+                    break;
+                }
+                mVelocityTracker.addMovement(ev);
+                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+                float displacement = ev.getY(pointerIndex) - mDownPos.y;
+                if (isNavBarOnRight()) {
+                    displacement = ev.getX(pointerIndex) - mDownPos.x;
+                } else if (isNavBarOnLeft()) {
+                    displacement = mDownPos.x - ev.getX(pointerIndex);
+                }
+                if (!mTouchThresholdCrossed) {
+                    mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
+                    if (mTouchThresholdCrossed) {
+                        mStartDisplacement = Math.signum(displacement) * mTouchSlop;
+
+                        if (isUsingScreenShot()) {
+                            startTouchTrackingForScreenshotAnimation();
+                        }
+
+                        // Notify the handler that the gesture has actually started
+                        mInteractionHandler.onGestureStarted();
+
+                        // Notify the system that we have started tracking the event
+                        if (mISystemUiProxy != null) {
+                            executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
+                        }
+                    }
+                } else {
+                    // Move
+                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+                }
+                break;
+            }
+            case ACTION_CANCEL:
+                // TODO: Should be different than ACTION_UP
+            case ACTION_UP: {
+                TraceHelper.endSection("TouchInt");
+
+                finishTouchTracking();
+                break;
+            }
+        }
+    }
+
+    private boolean isNavBarOnRight() {
+        return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
+    }
+
+    private boolean isNavBarOnLeft() {
+        return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
+    }
+
+    private boolean isUsingScreenShot() {
+        return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
+    }
+
+    /**
+     * Called when the gesture has started.
+     */
+    private void startTouchTrackingForScreenshotAnimation() {
+        // Create the shared handler
+        final NavBarSwipeInteractionHandler handler =
+                new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
+
+        TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
+
+        // Start the recents activity on a background thread
+        BackgroundExecutor.get().submit(() -> {
+            // Get the snap shot before
+            handler.setTaskSnapshot(getCurrentTaskSnapshot());
+
+            // Start the launcher activity with our custom handler
+            Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
+            startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
+            TraceHelper.partitionSection("TouchInt", "Home started");
+        });
+
+        // Preload the plan
+        mRecentsModel.loadTasks(mRunningTask.id, null);
+        mInteractionHandler = handler;
+        mInteractionHandler.setGestureEndCallback(this::onFinish);
+    }
+
+    private Bitmap getCurrentTaskSnapshot() {
+        TraceHelper.beginSection("TaskSnapshot");
+        // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
+        Point displaySize = new Point();
+        Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+        display.getRealSize(displaySize);
+        int rotation = display.getRotation();
+        // The rotation is backwards in landscape, so flip it.
+        if (rotation == Surface.ROTATION_270) {
+            rotation = Surface.ROTATION_90;
+        } else if (rotation == Surface.ROTATION_90) {
+            rotation = Surface.ROTATION_270;
+        }
+        try {
+            return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
+                    false, rotation).toBitmap();
+        } catch (Exception e) {
+            Log.e(TAG, "Error capturing snapshot", e);
+
+            // Return a dummy bitmap
+            Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
+            bitmap.eraseColor(Color.WHITE);
+            return bitmap;
+        } finally {
+            TraceHelper.endSection("TaskSnapshot");
+        }
+    }
+
+    private void startTouchTrackingForWindowAnimation() {
+        // Create the shared handler
+        final WindowTransformSwipeHandler handler =
+                new WindowTransformSwipeHandler(mRunningTask, this);
+        BackgroundExecutor.get().submit(() -> {
+            ActivityManagerWrapper.getInstance().startRecentsActivity(mHomeIntent,
+                    new AssistDataReceiver() {
+                        @Override
+                        public void onHandleAssistData(Bundle bundle) {
+                            // Pass to AIAI
+                        }
+                    },
+                    new RecentsAnimationListener() {
+                        public void onAnimationStart(
+                                RecentsAnimationControllerCompat controller,
+                                RemoteAnimationTargetCompat[] apps) {
+                            if (mInteractionHandler == handler) {
+                                handler.setRecentsAnimation(controller, apps);
+
+                            } else {
+                                controller.finish(false /* toHome */);
+                            }
+                        }
+
+                        public void onAnimationCanceled() {
+                            if (mInteractionHandler == handler) {
+                                handler.setRecentsAnimation(null, null);
+                            }
+                        }
+                    }, null, null);
+        });
+
+        // Preload the plan
+        mRecentsModel.loadTasks(mRunningTask.id, null);
+        mInteractionHandler = handler;
+        handler.setGestureEndCallback(this::onFinish);
+        handler.setLauncherOnDrawCallback(() -> {
+            if (handler == mInteractionHandler) {
+                switchToMainConsumer();
+            }
+        });
+        mMainThreadExecutor.execute(handler::initWhenReady);
+    }
+
+    /**
+     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+     * the animation can still be running.
+     */
+    private void finishTouchTracking() {
+        if (mTouchThresholdCrossed) {
+            mVelocityTracker.computeCurrentVelocity(1000,
+                    ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+
+            float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
+                    : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
+                            : mVelocityTracker.getYVelocity(mActivePointerId);
+            mInteractionHandler.onGestureEnded(velocity);
+        } else if (!isUsingScreenShot()) {
+            // Since we start touch tracking on DOWN, we may reach this state without actually
+            // starting the gesture. In that case, just cleanup immediately.
+            final BaseSwipeInteractionHandler handler = mInteractionHandler;
+            mMainThreadExecutor.execute(handler::reset);
+        }
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+
+        onTouchTrackingComplete();
+    }
+
+    @Override
+    public void reset() {
+        // Clean up the old interaction handler
+        if (mInteractionHandler != null) {
+            final BaseSwipeInteractionHandler handler = mInteractionHandler;
+            mMainThreadExecutor.execute(handler::reset);
+            mInteractionHandler = null;
+        }
+    }
+
+    @Override
+    public void updateTouchTracking(int interactionType) {
+        mMainThreadExecutor.execute(() -> {
+            if (mInteractionHandler != null) {
+                mInteractionHandler.updateInteractionType(interactionType);
+            }
+        });
+    }
+
+    @Override
+    public boolean shouldUseBackgroundConsumer() {
+        return !isUsingScreenShot();
+    }
+
+    @Override
+    public void onQuickScrubEnd() {
+        if (mInteractionHandler != null) {
+            mInteractionHandler.onQuickScrubEnd();
+        }
+    }
+
+    @Override
+    public void onQuickScrubProgress(float progress) {
+        if (mInteractionHandler != null) {
+            mInteractionHandler.onQuickScrubProgress(progress);
+        }
+    }
+
+    private void onFinish() {
+        mInteractionHandler = null;
+    }
+
+    public void onTouchTrackingComplete() { }
+
+    public void switchToMainConsumer() { }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
new file mode 100644
index 0000000..d4d3a3f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.annotation.IntDef;
+import android.view.MotionEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.function.Consumer;
+
+@TargetApi(Build.VERSION_CODES.O)
+@FunctionalInterface
+public interface TouchConsumer extends Consumer<MotionEvent> {
+
+    static boolean isInteractionQuick(@InteractionType int interactionType) {
+        return interactionType == INTERACTION_QUICK_SCRUB ||
+                interactionType == INTERACTION_QUICK_SWITCH;
+    }
+
+    @IntDef(flag = true, value = {
+            INTERACTION_NORMAL,
+            INTERACTION_QUICK_SWITCH,
+            INTERACTION_QUICK_SCRUB
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface InteractionType {}
+    int INTERACTION_NORMAL = 0;
+    int INTERACTION_QUICK_SWITCH = 1;
+    int INTERACTION_QUICK_SCRUB = 2;
+
+    default void reset() { }
+
+    default boolean shouldUseBackgroundConsumer() {
+        return false;
+    }
+
+    default void updateTouchTracking(@InteractionType int interactionType) { }
+
+    default void onQuickScrubEnd() { }
+
+    default void onQuickScrubProgress(float progress) { }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 67146ff..ab496ad 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,58 +21,34 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.INVALID_POINTER_ID;
 
-import static com.android.quickstep.RemoteRunnable.executeSafely;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityOptions;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.support.annotation.IntDef;
 import android.util.Log;
 import android.view.Choreographer;
-import android.view.Display;
 import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.WindowManager;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.model.ModelPreload;
-import com.android.launcher3.util.TraceHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.AssistDataReceiver;
-import com.android.systemui.shared.system.BackgroundExecutor;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.function.Consumer;
 
 /**
  * Service connected by system-UI for handling touch interaction.
@@ -84,17 +60,6 @@
 
     private static final String TAG = "TouchInteractionService";
 
-    @IntDef(flag = true, value = {
-            INTERACTION_NORMAL,
-            INTERACTION_QUICK_SWITCH,
-            INTERACTION_QUICK_SCRUB
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface InteractionType {}
-    public static final int INTERACTION_NORMAL = 0;
-    public static final int INTERACTION_QUICK_SWITCH = 1;
-    public static final int INTERACTION_QUICK_SCRUB = 2;
-
     /**
      * A background thread used for handling UI for another window.
      */
@@ -115,34 +80,29 @@
 
         @Override
         public void onQuickSwitch() {
-            updateTouchTracking(INTERACTION_QUICK_SWITCH);
+            mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH);
         }
 
         @Override
         public void onQuickScrubStart() {
-            updateTouchTracking(INTERACTION_QUICK_SCRUB);
+            mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
             sQuickScrubEnabled = true;
         }
 
         @Override
         public void onQuickScrubEnd() {
-            if (mInteractionHandler != null) {
-                mInteractionHandler.onQuickScrubEnd();
-            }
+            mCurrentConsumer.onQuickScrubEnd();
             sQuickScrubEnabled = false;
         }
 
         @Override
         public void onQuickScrubProgress(float progress) {
-            if (mInteractionHandler != null) {
-                mInteractionHandler.onQuickScrubProgress(progress);
-            }
+            mCurrentConsumer.onQuickScrubProgress(progress);
         }
     };
 
-    private final Consumer<MotionEvent> mOtherActivityTouchConsumer
-            = this::handleTouchDownOnOtherActivity;
-    private final Consumer<MotionEvent> mNoOpTouchConsumer = (ev) -> {};
+    private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
+    private TouchConsumer mCurrentConsumer = mNoOpTouchConsumer;
 
     private static boolean sConnected = false;
     private static boolean sQuickScrubEnabled = false;
@@ -162,20 +122,7 @@
     private ComponentName mLauncher;
     private MotionEventQueue mEventQueue;
     private MainThreadExecutor mMainThreadExecutor;
-
-    private final PointF mDownPos = new PointF();
-    private final PointF mLastPos = new PointF();
-    private int mActivePointerId = INVALID_POINTER_ID;
-    private VelocityTracker mVelocityTracker;
-    private boolean mTouchThresholdCrossed;
-    private int mTouchSlop;
-    private float mStartDisplacement;
-    private BaseSwipeInteractionHandler mInteractionHandler;
-    private int mDisplayRotation;
-    private Rect mStableInsets = new Rect();
-
     private ISystemUiProxy mISystemUiProxy;
-
     private Choreographer mBackgroundThreadChoreographer;
 
     @Override
@@ -218,265 +165,45 @@
         if (ev.getActionMasked() == ACTION_DOWN) {
             mRunningTask = mAM.getRunningTask();
 
+            mCurrentConsumer.reset();
             if (mRunningTask == null) {
-                mEventQueue.setConsumer(mNoOpTouchConsumer);
-                mEventQueue.setInterimChoreographer(null);
+                mCurrentConsumer = mNoOpTouchConsumer;
             } else if (mRunningTask.topActivity.equals(mLauncher)) {
-                mEventQueue.setConsumer(getLauncherConsumer());
-                mEventQueue.setInterimChoreographer(null);
+                mCurrentConsumer = getLauncherConsumer();
             } else {
-                mEventQueue.setConsumer(mOtherActivityTouchConsumer);
-                mEventQueue.setInterimChoreographer(
-                        isUsingScreenShot() ? null : mBackgroundThreadChoreographer);
+                mCurrentConsumer = getOtherActivityConsumer();
             }
+
+            mEventQueue.setConsumer(mCurrentConsumer);
+            mEventQueue.setInterimChoreographer(mCurrentConsumer.shouldUseBackgroundConsumer()
+                    ? mBackgroundThreadChoreographer : null);
         }
         mEventQueue.queue(ev);
     }
 
-    private void handleTouchDownOnOtherActivity(MotionEvent ev) {
-        if (ev.getActionMasked() != ACTION_DOWN && mVelocityTracker == null) {
-            return;
-        }
-        switch (ev.getActionMasked()) {
-            case ACTION_DOWN: {
-                TraceHelper.beginSection("TouchInt");
-                mActivePointerId = ev.getPointerId(0);
-                mDownPos.set(ev.getX(), ev.getY());
-                mLastPos.set(mDownPos);
-                mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
-                mTouchThresholdCrossed = false;
+    private TouchConsumer getOtherActivityConsumer() {
+        TouchConsumer consumer = new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel,
+                mHomeIntent, mISystemUiProxy, mMainThreadExecutor) {
 
-                // Clean up the old interaction handler
-                if (mInteractionHandler != null) {
-                    final BaseSwipeInteractionHandler handler = mInteractionHandler;
-                    mMainThreadExecutor.execute(handler::reset);
-                    mInteractionHandler = null;
+            @Override
+            public void switchToMainConsumer() {
+                if (mCurrentConsumer == this) {
+                    mEventQueue.setInterimChoreographer(null);
                 }
-
-                // Start the window animation on down to give more time for launcher to draw
-                if (!isUsingScreenShot()) {
-                    startTouchTrackingForWindowAnimation();
-                }
-
-                if (mVelocityTracker == null) {
-                    mVelocityTracker = VelocityTracker.obtain();
-                } else {
-                    mVelocityTracker.clear();
-                }
-                mVelocityTracker.addMovement(ev);
-
-                Display display = getSystemService(WindowManager.class).getDefaultDisplay();
-                mDisplayRotation = display.getRotation();
-                WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-                break;
             }
-            case ACTION_POINTER_UP: {
-                int ptrIdx = ev.getActionIndex();
-                int ptrId = ev.getPointerId(ptrIdx);
-                if (ptrId == mActivePointerId) {
-                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
-                    mDownPos.set(
-                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
-                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
-                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
-                    mActivePointerId = ev.getPointerId(newPointerIdx);
-                    mVelocityTracker.clear();
+
+            @Override
+            public void onTouchTrackingComplete() {
+                if (mCurrentConsumer == this) {
+                    mCurrentConsumer = mNoOpTouchConsumer;
+                    mEventQueue.setConsumer(mCurrentConsumer);
                 }
-                break;
             }
-            case ACTION_MOVE: {
-                int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex == INVALID_POINTER_ID) {
-                    break;
-                }
-                mVelocityTracker.addMovement(ev);
-                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
-                float displacement = ev.getY(pointerIndex) - mDownPos.y;
-                if (isNavBarOnRight()) {
-                    displacement = ev.getX(pointerIndex) - mDownPos.x;
-                } else if (isNavBarOnLeft()) {
-                    displacement = mDownPos.x - ev.getX(pointerIndex);
-                }
-                if (!mTouchThresholdCrossed) {
-                    mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
-                    if (mTouchThresholdCrossed) {
-                        mStartDisplacement = Math.signum(displacement) * mTouchSlop;
-
-                        if (isUsingScreenShot()) {
-                            startTouchTrackingForScreenshotAnimation();
-                        }
-
-                        // Notify the handler that the gesture has actually started
-                        mInteractionHandler.onGestureStarted();
-
-                        // Notify the system that we have started tracking the event
-                        if (mISystemUiProxy != null) {
-                            executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
-                        }
-                    }
-                } else {
-                    // Move
-                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
-                }
-                break;
-            }
-            case ACTION_CANCEL:
-                // TODO: Should be different than ACTION_UP
-            case ACTION_UP: {
-                TraceHelper.endSection("TouchInt");
-
-                finishTouchTracking();
-                mEventQueue.setConsumer(mNoOpTouchConsumer);
-                break;
-            }
-        }
+        };
+        return consumer;
     }
 
-    private boolean isNavBarOnRight() {
-        return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
-    }
-
-    private boolean isNavBarOnLeft() {
-        return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
-    }
-
-    private boolean isUsingScreenShot() {
-        return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
-    }
-
-    /**
-     * Called when the gesture has started.
-     */
-    private void startTouchTrackingForScreenshotAnimation() {
-        // Create the shared handler
-        final NavBarSwipeInteractionHandler handler =
-                new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
-
-        TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
-
-        // Start the recents activity on a background thread
-        BackgroundExecutor.get().submit(() -> {
-            // Get the snap shot before
-            handler.setTaskSnapshot(getCurrentTaskSnapshot());
-
-            // Start the launcher activity with our custom handler
-            Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
-            startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
-            TraceHelper.partitionSection("TouchInt", "Home started");
-        });
-
-        // Preload the plan
-        mRecentsModel.loadTasks(mRunningTask.id, null);
-        mInteractionHandler = handler;
-        mInteractionHandler.setGestureEndCallback(() ->  mInteractionHandler = null);
-    }
-
-    private void startTouchTrackingForWindowAnimation() {
-        // Create the shared handler
-        final WindowTransformSwipeHandler handler =
-                new WindowTransformSwipeHandler(mRunningTask, this);
-        BackgroundExecutor.get().submit(() -> {
-            ActivityManagerWrapper.getInstance().startRecentsActivity(mHomeIntent,
-                    new AssistDataReceiver() {
-                        @Override
-                        public void onHandleAssistData(Bundle bundle) {
-                            // Pass to AIAI
-                        }
-                    },
-                    new RecentsAnimationListener() {
-                        public void onAnimationStart(
-                                RecentsAnimationControllerCompat controller,
-                                RemoteAnimationTargetCompat[] apps) {
-                            if (mInteractionHandler == handler) {
-                                handler.setRecentsAnimation(controller, apps);
-
-                            } else {
-                                controller.finish(false /* toHome */);
-                            }
-                        }
-
-                        public void onAnimationCanceled() {
-                            if (mInteractionHandler == handler) {
-                                handler.setRecentsAnimation(null, null);
-                            }
-                        }
-                    }, null, null);
-        });
-
-        // Preload the plan
-        mRecentsModel.loadTasks(mRunningTask.id, null);
-        mInteractionHandler = handler;
-        handler.setGestureEndCallback(() -> {
-            if (handler == mInteractionHandler) {
-                mInteractionHandler = null;
-            }
-        });
-        handler.setLauncherOnDrawCallback(() -> {
-            if (handler == mInteractionHandler) {
-                mEventQueue.setInterimChoreographer(null);
-            }
-        });
-        mMainThreadExecutor.execute(handler::initWhenReady);
-    }
-
-    private void updateTouchTracking(@InteractionType int interactionType) {
-        final BaseSwipeInteractionHandler handler = mInteractionHandler;
-        mMainThreadExecutor.execute(() -> handler.updateInteractionType(interactionType));
-    }
-
-    /**
-     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
-     * the animation can still be running.
-     */
-    private void finishTouchTracking() {
-        if (mTouchThresholdCrossed) {
-            mVelocityTracker.computeCurrentVelocity(1000,
-                    ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
-            float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
-                    : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
-                    : mVelocityTracker.getYVelocity(mActivePointerId);
-            mInteractionHandler.onGestureEnded(velocity);
-        } else if (!isUsingScreenShot()) {
-            // Since we start touch tracking on DOWN, we may reach this state without actually
-            // starting the gesture. In that case, just cleanup immediately.
-            final BaseSwipeInteractionHandler handler = mInteractionHandler;
-            mMainThreadExecutor.execute(handler::reset);
-        }
-        mVelocityTracker.recycle();
-        mVelocityTracker = null;
-    }
-
-    private Bitmap getCurrentTaskSnapshot() {
-        TraceHelper.beginSection("TaskSnapshot");
-        // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
-        Point displaySize = new Point();
-        Display display = getSystemService(WindowManager.class).getDefaultDisplay();
-        display.getRealSize(displaySize);
-        int rotation = display.getRotation();
-        // The rotation is backwards in landscape, so flip it.
-        if (rotation == Surface.ROTATION_270) {
-            rotation = Surface.ROTATION_90;
-        } else if (rotation == Surface.ROTATION_90) {
-            rotation = Surface.ROTATION_270;
-        }
-        try {
-            return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
-                    false, rotation).toBitmap();
-        } catch (Exception e) {
-            Log.e(TAG, "Error capturing snapshot", e);
-
-            // Return a dummy bitmap
-            Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
-            bitmap.eraseColor(Color.WHITE);
-            return bitmap;
-        } finally {
-            TraceHelper.endSection("TaskSnapshot");
-        }
-    }
-
-    private Consumer<MotionEvent> getLauncherConsumer() {
+    private TouchConsumer getLauncherConsumer() {
 
         Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
         if (launcher == null) {
@@ -490,15 +217,18 @@
         return new LauncherTouchConsumer(target);
     }
 
-    private class LauncherTouchConsumer implements Consumer<MotionEvent> {
+    private class LauncherTouchConsumer implements TouchConsumer {
 
         private final View mTarget;
         private final int[] mLocationOnScreen = new int[2];
+        private final PointF mDownPos = new PointF();
+        private final int mTouchSlop;
 
         private boolean mTrackingStarted = false;
 
         LauncherTouchConsumer(View target) {
             mTarget = target;
+            mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
         }
 
         @Override
@@ -507,7 +237,6 @@
             if (action == ACTION_DOWN) {
                 mTrackingStarted = false;
                 mDownPos.set(ev.getX(), ev.getY());
-                mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
             } else if (!mTrackingStarted) {
                 switch (action) {
                     case ACTION_POINTER_UP:
@@ -559,9 +288,4 @@
         new Handler(sRemoteUiThread.getLooper()).post(() ->
                 mBackgroundThreadChoreographer = Choreographer.getInstance());
     }
-
-    public static boolean isInteractionQuick(@InteractionType int interactionType) {
-        return interactionType == INTERACTION_QUICK_SCRUB ||
-                interactionType == INTERACTION_QUICK_SWITCH;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 6416dc1..3364c36 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,10 +17,10 @@
 
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchInteractionService.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchInteractionService.isInteractionQuick;
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.Animator;
@@ -54,7 +54,7 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.TouchInteractionService.InteractionType;
+import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;