Ensuring that we finish the last transition before starting a new one.

> Finishing the active animation instead of cancelling it. This ansures
  that the animation callbacks are called properly and RecentsAnimaiton is finished
> If a transition is already running, using main thread for next transtion so that
  this new transition is not started before the last transition is finished.
> If the transition is expected to finish at Launcher, directly use the Launcher
  consumer. RunningTaskInfo is not updated until the screen shot is complete.

Bug: 74481901
Change-Id: I2b1128f1f2eff0e6bd94b3adb9cef6ae0578bd0c
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 214b3f3..84dfa45 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -77,6 +77,12 @@
         }
     }
 
+    public void finishAnimation() {
+        if (mValueAnimator != null && mValueAnimator.isRunning()) {
+            mValueAnimator.end();
+        }
+    }
+
     public ObjectAnimator getCurrentAnimation() {
         return mValueAnimator;
     }
diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
index b3ebd77..5871a6d 100644
--- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
@@ -23,6 +23,7 @@
 public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
 
     protected Runnable mGestureEndCallback;
+    protected boolean mIsGoingToHome;
 
     public void setGestureEndCallback(Runnable gestureEndCallback) {
         mGestureEndCallback = gestureEndCallback;
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
new file mode 100644
index 0000000..b92678a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
@@ -0,0 +1,99 @@
+/*
+ * 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.view.Choreographer;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+/**
+ * A TouchConsumer which defers all events on the UIThread until the consumer is created.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class DeferredTouchConsumer implements TouchConsumer {
+
+    private final VelocityTracker mVelocityTracker;
+    private final DeferredTouchProvider mTouchProvider;
+
+    private MotionEventQueue mMyQueue;
+    private TouchConsumer mTarget;
+
+    public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
+        mVelocityTracker = VelocityTracker.obtain();
+        mTouchProvider = touchProvider;
+    }
+
+    @Override
+    public void accept(MotionEvent event) {
+        mTarget.accept(event);
+    }
+
+    @Override
+    public void reset() {
+        mTarget.reset();
+    }
+
+    @Override
+    public void updateTouchTracking(int interactionType) {
+        mTarget.updateTouchTracking(interactionType);
+    }
+
+    @Override
+    public void onQuickScrubEnd() {
+        mTarget.onQuickScrubEnd();
+    }
+
+    @Override
+    public void onQuickScrubProgress(float progress) {
+        mTarget.onQuickScrubProgress(progress);
+    }
+
+    @Override
+    public void preProcessMotionEvent(MotionEvent ev) {
+        mVelocityTracker.addMovement(ev);
+    }
+
+    @Override
+    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+        mMyQueue = queue;
+        return null;
+    }
+
+    @Override
+    public void deferInit() {
+        mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
+        mTarget.getIntrimChoreographer(mMyQueue);
+    }
+
+    @Override
+    public boolean forceToLauncherConsumer() {
+        return mTarget.forceToLauncherConsumer();
+    }
+
+    @Override
+    public boolean deferNextEventToMainThread() {
+        // If our target is still null, defer the next target as well
+        TouchConsumer target = mTarget;
+        return target == null ? true : target.deferNextEventToMainThread();
+    }
+
+    public interface DeferredTouchProvider {
+
+        TouchConsumer createTouchConsumer(VelocityTracker tracker);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
index 6e92d83..94b6faa 100644
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -53,6 +53,8 @@
             ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_RESET =
             ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
+    private static final int ACTION_DEFER_INIT =
+            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
 
     private final EventArray mEmptyArray = new EventArray();
     private final Object mExecutionLock = new Object();
@@ -76,10 +78,10 @@
     public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
         mMainChoreographer = choreographer;
         mConsumer = consumer;
-
         mCurrentChoreographer = mMainChoreographer;
         mCurrentRunnable = mMainFrameCallback;
-        setInterimChoreographerLocked(consumer.getIntrimChoreographer(this));
+
+        setInterimChoreographer(consumer.getIntrimChoreographer(this));
     }
 
     public void setInterimChoreographer(Choreographer choreographer) {
@@ -156,6 +158,9 @@
                         case ACTION_RESET:
                             mConsumer.reset();
                             break;
+                        case ACTION_DEFER_INIT:
+                            mConsumer.deferInit();
+                            break;
                         default:
                             Log.e(TAG, "Invalid virtual event: " + event.getAction());
                     }
@@ -204,6 +209,14 @@
         queueVirtualAction(ACTION_RESET, 0);
     }
 
+    public void deferInit() {
+        queueVirtualAction(ACTION_DEFER_INIT, 0);
+    }
+
+    public TouchConsumer getConsumer() {
+        return mConsumer;
+    }
+
     private static class EventArray extends ArrayList<MotionEvent> {
 
         public int lastEventAction = ACTION_CANCEL;
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index ff7d434..fca844c 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -319,13 +319,14 @@
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
     private void animateToProgress(float progress, long duration) {
+        mIsGoingToHome = Float.compare(progress, 1) == 0;
         ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
         anim.setInterpolator(Interpolators.SCROLL);
         anim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
-                mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0)
-                        ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS);
+                mStateCallback.setState(mIsGoingToHome
+                        ? STATE_SCALED_SNAPSHOT_RECENTS : STATE_SCALED_SNAPSHOT_APP);
             }
         });
         anim.start();
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 9b2e822..c96f6d7 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -25,9 +25,9 @@
 import static com.android.quickstep.RemoteRunnable.executeSafely;
 import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
 
+import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -39,6 +39,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Looper;
 import android.util.Log;
@@ -70,6 +71,7 @@
 /**
  * Touch consumer for handling events originating from an activity other than Launcher
  */
+@TargetApi(Build.VERSION_CODES.P)
 public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
     private static final String TAG = "ActivityTouchConsumer";
 
@@ -97,16 +99,17 @@
 
     private VelocityTracker mVelocityTracker;
     private MotionEventQueue mEventQueue;
+    private boolean mIsGoingToHome;
 
     public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
             RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
             MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
-            @HitTarget int downHitTarget) {
+            @HitTarget int downHitTarget, VelocityTracker velocityTracker) {
         super(base);
         mRunningTask = runningTaskInfo;
         mRecentsModel = recentsModel;
         mHomeIntent = homeIntent;
-        mVelocityTracker = VelocityTracker.obtain();
+        mVelocityTracker = velocityTracker;
         mISystemUiProxy = systemUiProxy;
         mMainThreadExecutor = mainThreadExecutor;
         mBackgroundThreadChoreographer = backgroundThreadChoreographer;
@@ -374,6 +377,7 @@
         if (mInteractionHandler != null) {
             final BaseSwipeInteractionHandler handler = mInteractionHandler;
             mInteractionHandler = null;
+            mIsGoingToHome = handler.mIsGoingToHome;
             mMainThreadExecutor.execute(handler::reset);
         }
     }
@@ -428,4 +432,15 @@
            }
         }
     }
+
+    @Override
+    public boolean forceToLauncherConsumer() {
+        return mIsGoingToHome;
+    }
+
+    @Override
+    public boolean deferNextEventToMainThread() {
+        // TODO: Consider also check if the eventQueue is using mainThread of not.
+        return mInteractionHandler != null;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index f35f6a6..768fbda 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -21,8 +21,6 @@
 import android.view.Choreographer;
 import android.view.MotionEvent;
 
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.function.Consumer;
@@ -64,4 +62,14 @@
     default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
         return null;
     }
+
+    default void deferInit() { }
+
+    default boolean deferNextEventToMainThread() {
+        return false;
+    }
+
+    default boolean forceToLauncherConsumer() {
+        return false;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 12c2170..5d0bec9 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -39,6 +39,7 @@
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
 
@@ -161,7 +162,6 @@
 
     private Choreographer mMainThreadChoreographer;
     private Choreographer mBackgroundThreadChoreographer;
-    private MotionEventQueue mNoOpEventQueue;
 
     @Override
     public void onCreate() {
@@ -171,8 +171,7 @@
         mMainThreadExecutor = new MainThreadExecutor();
         mOverviewCommandHelper = new OverviewCommandHelper(this);
         mMainThreadChoreographer = Choreographer.getInstance();
-        mNoOpEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
-        mEventQueue = mNoOpEventQueue;
+        mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
 
         sConnected = true;
 
@@ -194,31 +193,45 @@
     }
 
     private void onBinderPreMotionEvent(@HitTarget int downHitTarget) {
-        RunningTaskInfo runningTaskInfo = mAM.getRunningTask();
-
         mEventQueue.reset();
-
-        if (runningTaskInfo == null) {
-            mEventQueue = mNoOpEventQueue;
-        } else if (runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) {
-            mEventQueue = getLauncherEventQueue();
-        } else {
+        TouchConsumer oldConsumer = mEventQueue.getConsumer();
+        if (oldConsumer.deferNextEventToMainThread()) {
             mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
-                    new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
-                            mOverviewCommandHelper.homeIntent, mISystemUiProxy, mMainThreadExecutor,
-                            mBackgroundThreadChoreographer, downHitTarget));
+                    new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
+                            oldConsumer.forceToLauncherConsumer(), v)));
+            mEventQueue.deferInit();
+        } else {
+            mEventQueue = new MotionEventQueue(
+                    mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null));
         }
     }
 
-    private MotionEventQueue getLauncherEventQueue() {
+    private TouchConsumer getCurrentTouchConsumer(
+            @HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) {
+        RunningTaskInfo runningTaskInfo = mAM.getRunningTask();
+
+        if (runningTaskInfo == null && !forceToLauncher) {
+            return mNoOpTouchConsumer;
+        } else if (forceToLauncher ||
+                runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) {
+            return getLauncherConsumer();
+        } else {
+            if (tracker == null) {
+                tracker = VelocityTracker.obtain();
+            }
+            return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
+                            mOverviewCommandHelper.homeIntent, mISystemUiProxy, mMainThreadExecutor,
+                            mBackgroundThreadChoreographer, downHitTarget, tracker);
+        }
+    }
+
+    private TouchConsumer getLauncherConsumer() {
         Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
         if (launcher == null) {
-            return mNoOpEventQueue;
+            return mNoOpTouchConsumer;
         }
-
         View target = launcher.getDragLayer();
-        return new MotionEventQueue(mMainThreadChoreographer,
-                new LauncherTouchConsumer(launcher, target));
+        return new LauncherTouchConsumer(launcher, target);
     }
 
     private static class LauncherTouchConsumer implements TouchConsumer {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index bab8ef1..7c242fa 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -720,13 +720,14 @@
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
     private void animateToProgress(float progress, long duration) {
+        mIsGoingToHome = Float.compare(progress, 1) == 0;
         ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
         anim.setInterpolator(Interpolators.SCROLL);
         anim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
-                setStateOnUiThread((Float.compare(mCurrentShift.value, 0) == 0)
-                        ? STATE_SCALED_CONTROLLER_APP : STATE_SCALED_CONTROLLER_RECENTS);
+                setStateOnUiThread(mIsGoingToHome ?
+                        STATE_SCALED_CONTROLLER_RECENTS : STATE_SCALED_CONTROLLER_APP);
             }
         });
         anim.start();
@@ -747,7 +748,7 @@
     }
 
     private void invalidateHandler() {
-        mCurrentShift.cancelAnimation();
+        mCurrentShift.finishAnimation();
 
         if (mGestureEndCallback != null) {
             mGestureEndCallback.run();
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index d5813f3..41b66a4 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -333,6 +333,10 @@
     }
 
     public void moveToRestState() {
+        if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
+            // The user is doing something. Lets not mess it up
+            return;
+        }
         if (mState.disableRestore) {
             goToState(getRestState());
             // Reset history