Merge "Quick step/scrub/switch logging - state transition happening due to Home and back is handled by   specifying src target as 'from' container and dst target as the 'to'   container - Source and Destination container shows FROM and TO state for SWIPE/FLING - event.isStateChange = true indicates that an action resulted in   state transition - Elapsed container millis is the screen time on the source container" into ub-launcher3-master
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 8ff0a86..c400ffd 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 0e8dea6..28bf19c 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -81,32 +81,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",
@@ -114,6 +118,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",
@@ -205,6 +210,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);
@@ -307,37 +314,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() {
 
@@ -353,17 +376,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) {
@@ -516,7 +536,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
@@ -557,11 +577,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();
@@ -572,15 +595,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
@@ -678,6 +696,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() {
@@ -710,10 +735,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 f76d4eb..b87511a 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();
         }
@@ -2177,6 +2182,10 @@
         mOnResumeCallback = callback;
     }
 
+    public void setOnStartCallback(OnStartCallback callback) {
+        mOnStartCallback = callback;
+    }
+
     /**
      * Implementation of the method from LauncherModel.Callbacks.
      */
@@ -2882,4 +2891,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