Handling recents animation cancelation

When the animation is cancelled, if remove the pending handler
(incase it has not executed yet) and revert back to the old
state if it has executed.

Change-Id: Iddc305aaaeae78c06cbf6c1c271254409cc1487d
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 19e1cca..604b60b 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -21,6 +21,7 @@
 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.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
@@ -211,7 +212,7 @@
 
                         notifyGestureStarted();
                     }
-                } else {
+                } else if (mInteractionHandler != null) {
                     // Move
                     mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
                 }
@@ -322,7 +323,7 @@
                 switchToMainChoreographer();
             }
         });
-        handler.initWhenReady(mMainThreadExecutor);
+        handler.initWhenReady();
 
         Runnable startActivity = () -> ActivityManagerWrapper.getInstance()
                 .startRecentsActivity(mHomeIntent,
@@ -338,7 +339,7 @@
                             RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
                             Rect minimizedHomeBounds) {
                         if (mInteractionHandler == handler) {
-                            handler.setRecentsAnimation(controller, apps, homeContentInsets,
+                            handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
                                     minimizedHomeBounds);
                         } else {
                             controller.finish(false /* toHome */);
@@ -357,7 +358,7 @@
 
                     public void onAnimationCanceled() {
                         if (mInteractionHandler == handler) {
-                            handler.setRecentsAnimation(null, null, null, null);
+                            handler.onRecentsAnimationCanceled();
                         }
                     }
                 }, null, null);
@@ -381,7 +382,7 @@
      * the animation can still be running.
      */
     private void finishTouchTracking() {
-        if (mTouchThresholdCrossed) {
+        if (mTouchThresholdCrossed && mInteractionHandler != null) {
             mVelocityTracker.computeCurrentVelocity(1000,
                     ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
 
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index cb510c8..5d01009 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,6 +16,10 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.quickstep.TaskView.CURVE_FACTOR;
+import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
+
 import android.animation.LayoutTransition;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -54,10 +58,6 @@
 
 import java.util.ArrayList;
 
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.quickstep.TaskView.CURVE_FACTOR;
-import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
-
 /**
  * A list of recent tasks.
  */
@@ -108,6 +108,8 @@
     private Matrix mFadeMatrix;
     private boolean mScrimOnLeft;
 
+    private boolean mFirstTaskIconScaledDown = false;
+
     public RecentsView(Context context) {
         this(context, null);
     }
@@ -298,6 +300,7 @@
             taskView.setAlpha(1f);
             loader.loadTaskData(task);
         }
+        applyIconScale(false /* animate */);
 
         if (oldChildCount != getChildCount()) {
             mQuickScrubController.snapToPageForCurrentQuickScrubSection();
@@ -486,6 +489,26 @@
         return mQuickScrubController;
     }
 
+    public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) {
+        if (mFirstTaskIconScaledDown == isScaledDown) {
+            return;
+        }
+        mFirstTaskIconScaledDown = isScaledDown;
+        applyIconScale(animate);
+    }
+
+    private void applyIconScale(boolean animate) {
+        float scale = mFirstTaskIconScaledDown ? 0 : 1;
+        TaskView firstTask = (TaskView) getChildAt(mFirstTaskIndex);
+        if (firstTask != null) {
+            if (animate) {
+                firstTask.animateIconToScale(scale);
+            } else {
+                firstTask.setIconScale(scale);
+            }
+        }
+    }
+
     @Override
     public void draw(Canvas canvas) {
         if (mScrim == null) {
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 8865a42..f21742a 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -19,7 +19,6 @@
 import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK;
 import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
 
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -28,7 +27,6 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
@@ -68,23 +66,9 @@
 
     private static final long SCALE_ICON_DURATION = 120;
 
-    private static final Property<TaskView, Float> SCALE_ICON_PROPERTY =
-            new Property<TaskView, Float>(Float.TYPE, "scale_icon") {
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mIconScale;
-                }
-
-                @Override
-                public void set(TaskView taskView, Float iconScale) {
-                    taskView.setIconScale(iconScale);
-                }
-            };
-
     private Task mTask;
     private TaskThumbnailView mSnapshotView;
     private ImageView mIconView;
-    private float mIconScale = 1f;
 
     public TaskView(Context context) {
         this(context, null);
@@ -185,16 +169,13 @@
     }
 
     public void animateIconToScale(float scale) {
-        ObjectAnimator.ofFloat(this, SCALE_ICON_PROPERTY, scale)
-                .setDuration(SCALE_ICON_DURATION).start();
+        mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
     }
 
     protected void setIconScale(float iconScale) {
-        mIconScale = iconScale;
-        if (mIconView != null) {
-            mIconView.setScaleX(mIconScale);
-            mIconView.setScaleY(mIconScale);
-        }
+        mIconView.animate().cancel();
+        mIconView.setScaleX(iconScale);
+        mIconView.setScaleY(iconScale);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index ddddbf6..d8b8709 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -77,32 +77,36 @@
 
     // Launcher UI related states
     private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
-    private static final int STATE_LAUNCHER_DRAWN = 1 << 1;
-    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 2;
+    private static final int STATE_LAUNCHER_STARTED = 1 << 1;
+    private static final int STATE_LAUNCHER_DRAWN = 1 << 2;
+    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3;
 
     // Internal initialization states
-    private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 3;
+    private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
 
     // Interaction finish states
-    private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 4;
-    private static final int STATE_SCALED_CONTROLLER_APP = 1 << 5;
+    private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
+    private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
 
-    private static final int STATE_HANDLER_INVALIDATED = 1 << 6;
-    private static final int STATE_GESTURE_STARTED = 1 << 7;
+    private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
+    private static final int STATE_GESTURE_STARTED = 1 << 8;
+    private static final int STATE_GESTURE_CANCELLED = 1 << 9;
 
     // States for quick switch/scrub
-    private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 8;
-    private static final int STATE_QUICK_SWITCH = 1 << 9;
-    private static final int STATE_QUICK_SCRUB_START = 1 << 10;
-    private static final int STATE_QUICK_SCRUB_END = 1 << 11;
+    private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10;
+    private static final int STATE_QUICK_SWITCH = 1 << 11;
+    private static final int STATE_QUICK_SCRUB_START = 1 << 12;
+    private static final int STATE_QUICK_SCRUB_END = 1 << 13;
 
 
     private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE;
+            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+            | STATE_LAUNCHER_STARTED;
 
     // For debugging, keep in sync with above states
     private static final String[] STATES = new String[] {
             "STATE_LAUNCHER_PRESENT",
+            "STATE_LAUNCHER_STARTED",
             "STATE_LAUNCHER_DRAWN",
             "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
             "STATE_APP_CONTROLLER_RECEIVED",
@@ -110,6 +114,7 @@
             "STATE_SCALED_CONTROLLER_APP",
             "STATE_HANDLER_INVALIDATED",
             "STATE_GESTURE_STARTED",
+            "STATE_GESTURE_CANCELLED",
             "STATE_SWITCH_TO_SCREENSHOT_COMPLETE",
             "STATE_QUICK_SWITCH",
             "STATE_QUICK_SCRUB_START",
@@ -201,6 +206,8 @@
                 this::launcherFrameDrawn);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
                 this::notifyGestureStarted);
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+                | STATE_GESTURE_CANCELLED, this::resetStateForAnimationCancel);
 
         mStateCallback.addCallback(STATE_SCALED_CONTROLLER_APP | STATE_APP_CONTROLLER_RECEIVED,
                 this::resumeLastTask);
@@ -303,37 +310,53 @@
         // For the duration of the gesture, lock the screen orientation to ensure that we do not
         // rotate mid-quickscrub
         mLauncher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+        mRecentsView = mLauncher.getOverviewPanel();
+        mQuickScrubController = mRecentsView.getQuickScrubController();
+        mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
 
+        mStateCallback.setState(STATE_LAUNCHER_PRESENT);
+        if (alreadyOnHome) {
+            onLauncherStart(launcher);
+        } else {
+            launcher.setOnStartCallback(this::onLauncherStart);
+        }
+        return true;
+    }
+
+    private void onLauncherStart(final Launcher launcher) {
+        if (mLauncher != launcher) {
+            return;
+        }
+        if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) {
+            return;
+        }
+
+        mStateCallback.setState(STATE_LAUNCHER_STARTED);
         LauncherState startState = mLauncher.getStateManager().getState();
         if (startState.disableRestore) {
             startState = mLauncher.getStateManager().getRestState();
         }
         mLauncher.getStateManager().setRestState(startState);
 
-        AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
+        AbstractFloatingView.closeAllOpenViews(mLauncher, mWasLauncherAlreadyVisible);
 
-        mRecentsView = mLauncher.getOverviewPanel();
-        mQuickScrubController = mRecentsView.getQuickScrubController();
-        mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
 
-        final int state;
         if (mWasLauncherAlreadyVisible) {
             DeviceProfile dp = mLauncher.getDeviceProfile();
             long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-            mLauncherTransitionController = launcher.getStateManager()
+            mLauncherTransitionController = mLauncher.getStateManager()
                     .createAnimationToNewWorkspace(OVERVIEW, accuracy);
             mLauncherTransitionController.dispatchOnStart();
             mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
 
-            state = STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN
-                    | STATE_LAUNCHER_PRESENT;
+            mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
         } else {
             TraceHelper.beginSection("WTS-init");
-            launcher.getStateManager().goToState(OVERVIEW, false);
+            mLauncher.getStateManager().goToState(OVERVIEW, false);
             TraceHelper.partitionSection("WTS-init", "State changed");
 
             // TODO: Implement a better animation for fading in
-            View rootView = launcher.getRootView();
+            View rootView = mLauncher.getRootView();
             rootView.setAlpha(0);
             rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
 
@@ -349,17 +372,14 @@
                     mStateCallback.setState(STATE_LAUNCHER_DRAWN);
                 }
             });
-            state = STATE_LAUNCHER_PRESENT;
 
             // Optimization, hide the all apps view to prevent layout while initializing
             mLauncher.getAppsView().setVisibility(View.GONE);
         }
 
         mRecentsView.showTask(mRunningTaskId);
+        mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
         mLauncherLayoutListener.open();
-
-        mStateCallback.setState(state);
-        return true;
     }
 
     public void setLauncherOnDrawCallback(Runnable callback) {
@@ -512,7 +532,7 @@
         }
     }
 
-    public void setRecentsAnimation(RecentsAnimationControllerCompat controller,
+    public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
             RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) {
         if (apps != null) {
             // Use the top closing app to determine the insets for the animation
@@ -553,11 +573,14 @@
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
     }
 
-    public void onGestureStarted() {
-        if (mLauncher != null) {
-            notifyGestureStarted();
-        }
+    public void onRecentsAnimationCanceled() {
+        mRecentsAnimationWrapper.setController(null, null);
+        clearReference();
+        setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+    }
 
+    public void onGestureStarted() {
+        notifyGestureStarted();
         setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
         mRecentsAnimationWrapper.enableInputConsumer();
@@ -568,15 +591,10 @@
      * on both background and UI threads
      */
     private void notifyGestureStarted() {
-        mLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
-
-        mMainExecutor.execute(() -> {
-            // Prepare to animate the first icon.
-            View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
-            if (currentRecentsPage instanceof TaskView) {
-                ((TaskView) currentRecentsPage).setIconScale(0f);
-            }
-        });
+        final Launcher curLauncher = mLauncher;
+        if (curLauncher != null) {
+            curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
+        }
     }
 
     @WorkerThread
@@ -650,6 +668,13 @@
 
         // Restore the requested orientation to the user preference after the gesture has ended
         mLauncher.updateRequestedOrientation();
+        mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
+    }
+
+    private void resetStateForAnimationCancel() {
+        LauncherState startState = mLauncher.getStateManager().getRestState();
+        boolean animate = mWasLauncherAlreadyVisible || mGestureStarted;
+        mLauncher.getStateManager().goToState(startState, animate);
     }
 
     public void layoutListenerClosed() {
@@ -682,10 +707,7 @@
         mLauncher.getStateManager().reapplyState();
 
         // Animate the first icon.
-        View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
-        if (currentRecentsPage instanceof TaskView) {
-            ((TaskView) currentRecentsPage).animateIconToScale(1f);
-        }
+        mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
     }
 
     public void onQuickScrubEnd() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b4093b7..2275110 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -239,6 +239,7 @@
 
     @Thunk boolean mWorkspaceLoading = true;
 
+    private OnStartCallback mOnStartCallback;
     private OnResumeCallback mOnResumeCallback;
 
     private ViewOnDrawExecutor mPendingExecutor;
@@ -794,6 +795,10 @@
         super.onStart();
         FirstFrameAnimatorHelper.setIsVisible(true);
 
+        if (mOnStartCallback != null) {
+            mOnStartCallback.onLauncherStart(this);
+            mOnStartCallback = null;
+        }
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStart();
         }
@@ -2175,6 +2180,10 @@
         mOnResumeCallback = callback;
     }
 
+    public void setOnStartCallback(OnStartCallback callback) {
+        mOnStartCallback = callback;
+    }
+
     /**
      * Implementation of the method from LauncherModel.Callbacks.
      */
@@ -2880,4 +2889,12 @@
 
         void onLauncherResume();
     }
+
+    /**
+     * Callback for listening for onStart
+     */
+    public interface OnStartCallback {
+
+        void onLauncherStart(Launcher launcher);
+    }
 }
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index 7298383..d3c0fef 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -24,7 +24,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.Preconditions;
 
 import java.lang.ref.WeakReference;
 
@@ -38,7 +37,7 @@
 
     public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
 
-    private static WeakReference<InternalStateHandler> sPendingHandler = new WeakReference<>(null);
+    private static final Scheduler sScheduler = new Scheduler();
 
     /**
      * Initializes the handler when the launcher is ready.
@@ -53,30 +52,12 @@
         return intent;
     }
 
-    public final void initWhenReady(MainThreadExecutor executor) {
-        sPendingHandler = new WeakReference<>(this);
-        executor.execute(this::initIfReadOnUIThread);
-    }
-
-    private void initIfReadOnUIThread() {
-        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-        if (app == null) {
-            return;
-        }
-        Callbacks cb = app.getModel().getCallback();
-        if (!(cb instanceof Launcher)) {
-            return;
-        }
-        Launcher launcher = (Launcher) cb;
-        if (!init(launcher, launcher.isStarted())) {
-            sPendingHandler.clear();
-        }
+    public final void initWhenReady() {
+        sScheduler.schedule(this);
     }
 
     public void clearReference() {
-        if (sPendingHandler.get() == this) {
-            sPendingHandler.clear();
-        }
+        sScheduler.clearReference(this);
     }
 
     public static boolean handleCreate(Launcher launcher, Intent intent) {
@@ -101,14 +82,53 @@
             }
         }
         if (!result && !explicitIntent) {
-            InternalStateHandler pendingHandler = sPendingHandler.get();
-            if (pendingHandler != null) {
-                if (!pendingHandler.init(launcher, alreadyOnHome)) {
-                    sPendingHandler.clear();
-                }
-                result = true;
-            }
+            result = sScheduler.initIfPending(launcher, alreadyOnHome);
         }
         return result;
     }
+
+    private static class Scheduler implements Runnable {
+
+        private WeakReference<InternalStateHandler> mPendingHandler = new WeakReference<>(null);
+        private MainThreadExecutor mMainThreadExecutor;
+
+        public synchronized void schedule(InternalStateHandler handler) {
+            mPendingHandler = new WeakReference<>(handler);
+            if (mMainThreadExecutor == null) {
+                mMainThreadExecutor = new MainThreadExecutor();
+            }
+            mMainThreadExecutor.execute(this);
+        }
+
+        @Override
+        public void run() {
+            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+            if (app == null) {
+                return;
+            }
+            Callbacks cb = app.getModel().getCallback();
+            if (!(cb instanceof Launcher)) {
+                return;
+            }
+            Launcher launcher = (Launcher) cb;
+            initIfPending(launcher, launcher.isStarted());
+        }
+
+        public synchronized boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
+            InternalStateHandler pendingHandler = mPendingHandler.get();
+            if (pendingHandler != null) {
+                if (!pendingHandler.init(launcher, alreadyOnHome)) {
+                    mPendingHandler.clear();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public synchronized void clearReference(InternalStateHandler handler) {
+            if (mPendingHandler.get() == handler) {
+                mPendingHandler.clear();
+            }
+        }
+    }
 }
\ No newline at end of file