Merge "Revert "Add ScreenRecord to methods affected by a flaky problem"" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index aa31261..1c0c773 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,18 +15,10 @@
  */
 package com.android.launcher3.taskbar;
 
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
-import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
-import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
-import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.ColorInt;
 import android.graphics.Rect;
 import android.os.RemoteException;
@@ -44,28 +36,17 @@
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.RecentsAnimationCallbacks;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Set;
-import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 /**
@@ -77,82 +58,15 @@
 
     private final BaseQuickstepLauncher mLauncher;
 
-    private final AnimatedFloat mIconAlignmentForResumedState =
-            new AnimatedFloat(this::onIconAlignmentRatioChanged);
-    private final AnimatedFloat mIconAlignmentForGestureState =
-            new AnimatedFloat(this::onIconAlignmentRatioChanged);
-    private final AnimatedFloat mIconAlignmentForLauncherState =
-            new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
-
     private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
             this::onStashedInAppChanged;
 
-    private final StateManager.StateListener<LauncherState> mStateListener =
-            new StateManager.StateListener<LauncherState>() {
-                private Animator mAnimator;
-
-                @Override
-                public void onStateTransitionStart(LauncherState toState) {
-                    // Stash animation from going to launcher should be already handled in
-                    // createAnimToLauncher.
-                    TaskbarStashController controller = mControllers.taskbarStashController;
-                    long duration = TASKBAR_STASH_DURATION;
-                    controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
-                            toState.isTaskbarStashed());
-                    Animator stashAnimator = controller.applyStateWithoutStart(duration);
-                    if (stashAnimator != null) {
-                        if (mAnimator != null) {
-                            mAnimator.cancel();
-                        }
-                        PendingAnimation pendingAnimation = new PendingAnimation(duration);
-                        pendingAnimation.add(stashAnimator);
-                        pendingAnimation.setFloat(mIconAlignmentForLauncherState,
-                                AnimatedFloat.VALUE, toState.isTaskbarStashed() ? 0 : 1,
-                                FAST_OUT_SLOW_IN);
-                        pendingAnimation.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationStart(Animator animator) {
-                                mTargetStateOverrideForStateTransition = toState;
-                                // Copy hotseat alpha over to taskbar icons
-                                mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
-                                mLauncher.getHotseat().setIconsAlpha(0);
-                            }
-
-                            @Override
-                            public void onAnimationEnd(Animator animator) {
-                                if (toState.isTaskbarStashed()) {
-                                    // Reset hotseat alpha to default
-                                    mLauncher.getHotseat().setIconsAlpha(1);
-                                }
-                                mTargetStateOverrideForStateTransition = null;
-                                mAnimator = null;
-                            }
-                        });
-                        mAnimator = pendingAnimation.buildAnim();
-                        mAnimator.start();
-                    }
-                }
-
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    TaskbarStashController controller = mControllers.taskbarStashController;
-                    controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
-                            finalState.isTaskbarStashed());
-                    controller.applyState();
-                }
-            };
-
     // Initialized in init.
     private TaskbarControllers mControllers;
-    private AnimatedFloat mTaskbarBackgroundAlpha;
     private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
-    private AlphaProperty mIconAlphaForHome;
-    private boolean mIsAnimatingToLauncherViaResume;
-    private boolean mIsAnimatingToLauncherViaGesture;
     private TaskbarKeyguardController mKeyguardController;
-
-    private LauncherState mTargetStateOverride = null;
-    private LauncherState mTargetStateOverrideForStateTransition = null;
+    private final TaskbarLauncherStateController
+            mTaskbarLauncherStateController = new TaskbarLauncherStateController();
 
     private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener =
             new DeviceProfile.OnDeviceProfileChangeListener() {
@@ -171,37 +85,26 @@
     protected void init(TaskbarControllers taskbarControllers) {
         mControllers = taskbarControllers;
 
-        mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
-                .getTaskbarBackgroundAlpha();
+        mTaskbarLauncherStateController.init(mControllers, mLauncher);
         mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
                 .getOverrideBackgroundAlpha();
 
-        MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
-        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
-
         mLauncher.setTaskbarUIController(this);
         mKeyguardController = taskbarControllers.taskbarKeyguardController;
 
         onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
-        mIconAlignmentForResumedState.finishAnimation();
-        onIconAlignmentRatioChanged();
 
         onStashedInAppChanged(mLauncher.getDeviceProfile());
         mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
-        mLauncher.getStateManager().addStateListener(mStateListener);
         mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener);
     }
 
     @Override
     protected void onDestroy() {
         onLauncherResumedOrPaused(false);
-        mIconAlignmentForResumedState.finishAnimation();
-        mIconAlignmentForGestureState.finishAnimation();
-        mIconAlignmentForLauncherState.finishAnimation();
+        mTaskbarLauncherStateController.onDestroy();
 
         mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
-        mLauncher.getStateManager().removeStateListener(mStateListener);
-        mLauncher.getHotseat().setIconsAlpha(1f);
         mLauncher.setTaskbarUIController(null);
         mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener);
         updateTaskTransitionSpec(true);
@@ -209,11 +112,7 @@
 
     @Override
     protected boolean isTaskbarTouchable() {
-        return !isAnimatingToLauncher();
-    }
-
-    private boolean isAnimatingToLauncher() {
-        return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+        return !mTaskbarLauncherStateController.isAnimatingToLauncher();
     }
 
     @Override
@@ -240,24 +139,9 @@
             }
         }
 
-        long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
-        if (fromInit) {
-            // Since we are creating the starting state, we don't have a state to animate from, so
-            // set our state immediately.
-            duration = 0;
-        }
-        ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
-                getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
-                .setDuration(duration);
-
-        anim.addListener(AnimatorListeners.forEndCallback(
-                () -> mIsAnimatingToLauncherViaResume = false));
-        anim.start();
-        mIsAnimatingToLauncherViaResume = isResumed;
-
-        TaskbarStashController stashController = mControllers.taskbarStashController;
-        stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
-        stashController.applyState(duration);
+        mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
+        mTaskbarLauncherStateController.applyState(
+                fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
     }
 
     /**
@@ -268,77 +152,7 @@
      */
     public Animator createAnimToLauncher(@NonNull LauncherState toState,
             @NonNull RecentsAnimationCallbacks callbacks, long duration) {
-        AnimatorSet animatorSet = new AnimatorSet();
-        TaskbarStashController stashController = mControllers.taskbarStashController;
-        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
-                toState.isTaskbarStashed());
-        if (toState.isTaskbarStashed()) {
-            animatorSet.play(stashController.applyStateWithoutStart(duration));
-        } else {
-            animatorSet.play(mIconAlignmentForGestureState
-                    .animateToValue(1)
-                    .setDuration(duration));
-        }
-        animatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                mTargetStateOverride = null;
-                animator.removeListener(this);
-            }
-
-            @Override
-            public void onAnimationStart(Animator animator) {
-                mTargetStateOverride = toState;
-                mIsAnimatingToLauncherViaGesture = true;
-                stashController.updateStateForFlag(FLAG_IN_APP, false);
-                stashController.applyState(duration);
-            }
-        });
-
-        TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
-        callbacks.addListener(listener);
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        recentsView.setTaskLaunchListener(() -> {
-            listener.endGestureStateOverride(true);
-            callbacks.removeListener(listener);
-        });
-
-        return animatorSet;
-    }
-
-    private float getCurrentIconAlignmentRatio() {
-        return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
-    }
-
-    private float getCurrentIconAlignmentRatioForLauncherState() {
-        return mIconAlignmentForLauncherState.value;
-    }
-
-    private void onIconAlignmentRatioChangedForStateTransition() {
-        onIconAlignmentRatioChanged(
-                mTargetStateOverrideForStateTransition != null
-                        ? mTargetStateOverrideForStateTransition
-                        : mLauncher.getStateManager().getState(),
-                this::getCurrentIconAlignmentRatioForLauncherState);
-    }
-
-    private void onIconAlignmentRatioChanged() {
-        onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
-                : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
-    }
-
-    private void onIconAlignmentRatioChanged(LauncherState state,
-            Supplier<Float> alignmentSupplier) {
-        if (mControllers == null) {
-            return;
-        }
-        float alignment = alignmentSupplier.get();
-        mControllers.taskbarViewController.setLauncherIconAlignment(
-                alignment, mLauncher.getDeviceProfile());
-
-        mTaskbarBackgroundAlpha.updateValue(1 - alignment);
-
-        setIconAlpha(state, alignment);
+        return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
     }
 
     /**
@@ -358,20 +172,6 @@
         return mControllers.taskbarActivityContext.getDragLayer();
     }
 
-    private void setIconAlpha(LauncherState state, float progress) {
-        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
-            // If the hotseat icons are visible, then switch taskbar in last frame
-            setTaskbarViewVisible(progress < 1);
-        } else {
-            mIconAlphaForHome.setValue(1 - progress);
-        }
-    }
-
-    private void setTaskbarViewVisible(boolean isVisible) {
-        mIconAlphaForHome.setValue(isVisible ? 1 : 0);
-        mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
-    }
-
     @Override
     protected void onStashedInAppChanged() {
         onStashedInAppChanged(mLauncher.getDeviceProfile());
@@ -451,35 +251,4 @@
         mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item,
                 instanceId);
     }
-
-    private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener {
-        private final RecentsAnimationCallbacks mCallbacks;
-
-        TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
-            mCallbacks = callbacks;
-        }
-
-        @Override
-        public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
-            endGestureStateOverride(true);
-        }
-
-        @Override
-        public void onRecentsAnimationFinished(RecentsAnimationController controller) {
-            endGestureStateOverride(!controller.getFinishTargetIsLauncher());
-        }
-
-        private void endGestureStateOverride(boolean finishedToApp) {
-            mCallbacks.removeListener(this);
-            mIsAnimatingToLauncherViaGesture = false;
-
-            mIconAlignmentForGestureState
-                    .animateToValue(0)
-                    .start();
-
-            TaskbarStashController controller = mControllers.taskbarStashController;
-            controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
-            controller.applyState();
-        }
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
new file mode 100644
index 0000000..2693bc3
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2021 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.launcher3.taskbar;
+
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
+import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
+import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.HashMap;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
+ * the task bar accordingly.
+ */
+ public class TaskbarLauncherStateController {
+
+    public static final int FLAG_RESUMED = 1 << 0;
+    public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
+    public static final int FLAG_TRANSITION_STATE_START_STASHED = 1 << 2;
+    public static final int FLAG_TRANSITION_STATE_COMMITTED_STASHED = 1 << 3;
+
+    private final AnimatedFloat mIconAlignmentForResumedState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForGestureState =
+            new AnimatedFloat(this::onIconAlignmentRatioChanged);
+    private final AnimatedFloat mIconAlignmentForLauncherState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
+
+    private TaskbarControllers mControllers;
+    private AnimatedFloat mTaskbarBackgroundAlpha;
+    private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
+    private BaseQuickstepLauncher mLauncher;
+
+    private int mPrevState;
+    private int mState;
+
+    private LauncherState mTargetStateOverride = null;
+    private LauncherState mTargetStateOverrideForStateTransition = null;
+
+    private boolean mIsAnimatingToLauncherViaGesture;
+    private boolean mIsAnimatingToLauncherViaResume;
+
+    private final StateManager.StateListener<LauncherState> mStateListener =
+            new StateManager.StateListener<LauncherState>() {
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                    mTargetStateOverrideForStateTransition = toState;
+                    updateStateForFlag(FLAG_TRANSITION_STATE_START_STASHED,
+                            toState.isTaskbarStashed());
+                    applyState();
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    updateStateForFlag(FLAG_TRANSITION_STATE_COMMITTED_STASHED,
+                            finalState.isTaskbarStashed());
+                    applyState();
+                }
+            };
+
+    public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
+        mControllers = controllers;
+        mLauncher = launcher;
+
+        mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
+                .getTaskbarBackgroundAlpha();
+        MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
+        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
+        mIconAlphaForHome.setConsumer(
+                (Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
+
+        mIconAlignmentForResumedState.finishAnimation();
+        onIconAlignmentRatioChanged();
+
+        mLauncher.getStateManager().addStateListener(mStateListener);
+    }
+
+    public void onDestroy() {
+        mIconAlignmentForResumedState.finishAnimation();
+        mIconAlignmentForGestureState.finishAnimation();
+        mIconAlignmentForLauncherState.finishAnimation();
+
+        mLauncher.getHotseat().setIconsAlpha(1f);
+        mLauncher.getStateManager().removeStateListener(mStateListener);
+    }
+
+    public Animator createAnimToLauncher(@NonNull LauncherState toState,
+            @NonNull RecentsAnimationCallbacks callbacks, long duration) {
+        // If going to overview, stash the task bar
+        // If going home, align the icons to hotseat
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                toState.isTaskbarStashed());
+        stashController.updateStateForFlag(FLAG_IN_APP, false);
+
+        updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
+        animatorSet.play(stashController.applyStateWithoutStart(duration));
+        animatorSet.play(applyState(duration, false));
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                mTargetStateOverride = null;
+                animator.removeListener(this);
+            }
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                mTargetStateOverride = toState;
+            }
+        });
+
+        TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
+        callbacks.addListener(listener);
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        recentsView.setTaskLaunchListener(() -> {
+            listener.endGestureStateOverride(true);
+            callbacks.removeListener(listener);
+        });
+        return animatorSet;
+    }
+
+    public boolean isAnimatingToLauncher() {
+        return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+    }
+
+    /**
+     * Updates the proper flag to change the state of the task bar.
+     *
+     * Note that this only updates the flag. {@link #applyState()} needs to be called separately.
+     *
+     * @param flag The flag to update.
+     * @param enabled Whether to enable the flag
+     */
+    public void updateStateForFlag(int flag, boolean enabled) {
+        if (enabled) {
+            mState |= flag;
+        } else {
+            mState &= ~flag;
+        }
+    }
+
+    private boolean hasAnyFlag(int flagMask) {
+        return hasAnyFlag(mState, flagMask);
+    }
+
+    private boolean hasAnyFlag(int flags, int flagMask) {
+        return (flags & flagMask) != 0;
+    }
+
+    public void applyState() {
+        applyState(TASKBAR_STASH_DURATION);
+    }
+
+    public void applyState(long duration) {
+        applyState(duration, true);
+    }
+
+    public Animator applyState(boolean start) {
+        return applyState(TASKBAR_STASH_DURATION, start);
+    }
+
+    public Animator applyState(long duration, boolean start) {
+        Animator animator = null;
+        if (mPrevState != mState) {
+            int changedFlags = mPrevState ^ mState;
+            animator = onStateChangeApplied(changedFlags, duration, start);
+            mPrevState = mState;
+        }
+        return animator;
+    }
+
+    private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
+        AnimatorSet animatorSet = new AnimatorSet();
+        if (hasAnyFlag(changedFlags, FLAG_RESUMED)) {
+            boolean isResumed = isResumed();
+            ObjectAnimator anim = mIconAlignmentForResumedState
+                    .animateToValue(getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
+                    .setDuration(duration);
+
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mIsAnimatingToLauncherViaResume = false;
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mIsAnimatingToLauncherViaResume = isResumed;
+
+                    TaskbarStashController stashController = mControllers.taskbarStashController;
+                    stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
+                    stashController.applyState(duration);
+                }
+            });
+            animatorSet.play(anim);
+        }
+
+        if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) {
+            boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
+            Animator animator = mIconAlignmentForGestureState
+                    .animateToValue(isRecentsAnimationRunning ? 1 : 0);
+            if (isRecentsAnimationRunning) {
+                animator.setDuration(duration);
+            }
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mIsAnimatingToLauncherViaGesture = false;
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
+                }
+            });
+            animatorSet.play(animator);
+        }
+
+        if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_START_STASHED)) {
+            playStateTransitionAnim(isTransitionStateStartStashed(), animatorSet, duration,
+                    false /* committed */);
+        }
+
+        if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_COMMITTED_STASHED)) {
+            playStateTransitionAnim(isTransitionStateCommittedStashed(), animatorSet, duration,
+                    true /* committed */);
+        }
+
+        if (start) {
+            animatorSet.start();
+        }
+        return animatorSet;
+    }
+
+    private void playStateTransitionAnim(boolean isTransitionStateStashed,
+            AnimatorSet animatorSet, long duration, boolean committed) {
+        TaskbarStashController controller = mControllers.taskbarStashController;
+        controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                isTransitionStateStashed);
+        Animator stashAnimator = controller.applyStateWithoutStart(duration);
+        if (stashAnimator != null) {
+            stashAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (isTransitionStateStashed && committed) {
+                        // Reset hotseat alpha to default
+                        mLauncher.getHotseat().setIconsAlpha(1);
+                    }
+                    mTargetStateOverrideForStateTransition = null;
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+                }
+            });
+            animatorSet.play(stashAnimator);
+            animatorSet.play(mIconAlignmentForLauncherState.animateToValue(
+                    getCurrentIconAlignmentRatioForLauncherState(),
+                    isTransitionStateStashed ? 0 : 1));
+        } else {
+            mTargetStateOverrideForStateTransition = null;
+        }
+    }
+
+    private boolean isResumed() {
+        return (mState & FLAG_RESUMED) != 0;
+    }
+
+    private boolean isRecentsAnimationRunning() {
+        return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
+    }
+
+    private boolean isTransitionStateStartStashed() {
+        return (mState & FLAG_TRANSITION_STATE_START_STASHED) != 0;
+    }
+
+    private boolean isTransitionStateCommittedStashed() {
+        return (mState & FLAG_TRANSITION_STATE_COMMITTED_STASHED) != 0;
+    }
+
+    private void onIconAlignmentRatioChangedForStateTransition() {
+        onIconAlignmentRatioChanged(
+                mTargetStateOverrideForStateTransition != null
+                        ? mTargetStateOverrideForStateTransition
+                        : mLauncher.getStateManager().getState(),
+                this::getCurrentIconAlignmentRatioForLauncherState);
+    }
+
+    private void onIconAlignmentRatioChanged() {
+        onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
+                : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
+    }
+
+    private void onIconAlignmentRatioChanged(LauncherState state,
+            Supplier<Float> alignmentSupplier) {
+        if (mControllers == null) {
+            return;
+        }
+        float alignment = alignmentSupplier.get();
+        mControllers.taskbarViewController.setLauncherIconAlignment(
+                alignment, mLauncher.getDeviceProfile());
+
+        mTaskbarBackgroundAlpha.updateValue(1 - alignment);
+
+        setIconAlpha(state, alignment);
+    }
+
+    private float getCurrentIconAlignmentRatio() {
+        return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
+    }
+
+    private float getCurrentIconAlignmentRatioForLauncherState() {
+        return mIconAlignmentForLauncherState.value;
+    }
+
+    private void setIconAlpha(LauncherState state, float progress) {
+        if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+            // If the hotseat icons are visible, then switch taskbar in last frame
+            setTaskbarViewVisible(progress < 1);
+        } else {
+            mIconAlphaForHome.setValue(1 - progress);
+        }
+    }
+
+    private void setTaskbarViewVisible(boolean isVisible) {
+        mIconAlphaForHome.setValue(isVisible ? 1 : 0);
+    }
+
+    private final class TaskBarRecentsAnimationListener implements
+            RecentsAnimationCallbacks.RecentsAnimationListener {
+        private final RecentsAnimationCallbacks mCallbacks;
+
+        TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
+            mCallbacks = callbacks;
+        }
+
+        @Override
+        public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
+            endGestureStateOverride(true);
+        }
+
+        @Override
+        public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+            endGestureStateOverride(!controller.getFinishTargetIsLauncher());
+        }
+
+        private void endGestureStateOverride(boolean finishedToApp) {
+            mCallbacks.removeListener(this);
+            updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
+            applyState();
+
+            TaskbarStashController controller = mControllers.taskbarStashController;
+            controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+            controller.applyState();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 26d935d..75e8dd1 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -117,11 +117,12 @@
         mPendingCommands.clear();
     }
 
+    @Nullable
     private TaskView getNextTask(RecentsView view) {
         final TaskView runningTaskView = view.getRunningTaskView();
 
         if (runningTaskView == null) {
-            return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
+            return view.getTaskViewAt(0);
         } else {
             final TaskView nextTask = view.getNextTaskView();
             return nextTask != null ? nextTask : runningTaskView;
@@ -256,8 +257,8 @@
                 // Ensure that recents view has focus so that it receives the followup key inputs
                 TaskView taskView = rv.getNextTaskView();
                 if (taskView == null) {
-                    if (rv.getTaskViewCount() > 0) {
-                        taskView = rv.getTaskViewAt(0);
+                    taskView = rv.getTaskViewAt(0);
+                    if (taskView != null) {
                         taskView.requestFocus();
                     } else {
                         rv.requestFocus();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 223ce1c..92f1a67 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -383,7 +383,9 @@
 
     protected final RecentsOrientedState mOrientationState;
     protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
+    @Nullable
     protected RecentsAnimationController mRecentsAnimationController;
+    @Nullable
     protected SurfaceTransactionApplier mSyncTransactionApplier;
     protected int mTaskWidth;
     protected int mTaskHeight;
@@ -394,12 +396,15 @@
     // mTaskGridVerticalDiff and mTopBottomRowHeightDiff summed together provides the top
     // position for bottom row of grid tasks.
 
+    @Nullable
     protected RemoteTargetHandle[] mRemoteTargetHandles;
     protected final Rect mLastComputedTaskSize = new Rect();
     protected final Rect mLastComputedGridSize = new Rect();
     protected final Rect mLastComputedGridTaskSize = new Rect();
     // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
+    @Nullable
     protected Float mLastComputedTaskStartPushOutDistance = null;
+    @Nullable
     protected Float mLastComputedTaskEndPushOutDistance = null;
     protected boolean mEnableDrawingLiveTile = false;
     protected final Rect mTempRect = new Rect();
@@ -454,11 +459,13 @@
     private final IntSet mTopRowIdSet = new IntSet();
 
     // The GestureEndTarget that is still in progress.
+    @Nullable
     protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
 
     // TODO(b/187528071): Remove these and replace with a real scrim.
     private float mColorTint;
     private final int mTintingColor;
+    @Nullable
     private ObjectAnimator mTintingAnimator;
 
     private int mOverScrollShift = 0;
@@ -542,6 +549,7 @@
     private int mTaskViewIdCount;
     private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
     protected boolean mRunningTaskTileHidden;
+    @Nullable
     private Task[] mTmpRunningTasks;
     protected int mFocusedTaskViewId = -1;
 
@@ -556,7 +564,9 @@
     private int mDownX;
     private int mDownY;
 
+    @Nullable
     private PendingAnimation mPendingAnimation;
+    @Nullable
     private LayoutTransition mLayoutTransition;
 
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -581,7 +591,9 @@
     private final Point mLastMeasureSize = new Point();
     private final int mEmptyMessagePadding;
     private boolean mShowEmptyMessage;
+    @Nullable
     private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
+    @Nullable
     private Layout mEmptyTextLayout;
 
     /**
@@ -596,8 +608,11 @@
      * ensure this View doesn't go back into the {@link #mTaskViewPool},
      * see {@link #onViewRemoved(View)}
      */
+    @Nullable
     private TaskView mSplitHiddenTaskView;
+    @Nullable
     private TaskView mSecondSplitHiddenTaskView;
+    @Nullable
     private StagedSplitBounds mSplitBoundsConfig;
     private final Toast mSplitToast = Toast.makeText(getContext(),
             R.string.toast_split_select_app, Toast.LENGTH_SHORT);
@@ -613,12 +628,15 @@
      * removed from recentsView
      */
     private int mSplitHiddenTaskViewIndex;
+    @Nullable
     private FloatingTaskView mFirstFloatingTaskView;
+    @Nullable
     private FloatingTaskView mSecondFloatingTaskView;
 
     /**
      * The task to be removed and immediately re-added. Should not be added to task pool.
      */
+    @Nullable
     private TaskView mMovingTaskView;
 
     private OverviewActionsView mActionsView;
@@ -638,10 +656,12 @@
                 }
             };
 
+    @Nullable
     private RunnableList mSideTaskLaunchCallback;
+    @Nullable
     private TaskLaunchListener mTaskLaunchListener;
 
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
+    public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             BaseActivityInterface sizeStrategy) {
         super(context, attrs, defStyleAttr);
         setEnableFreeScroll(true);
@@ -782,6 +802,7 @@
     }
 
     @Override
+    @Nullable
     public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
         if (mHandleTaskStackChanges) {
             TaskView taskView = getTaskViewByTaskId(taskId);
@@ -1017,6 +1038,7 @@
         }
     }
 
+    @Nullable
     private TaskView getLastGridTaskView() {
         return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
     }
@@ -1070,6 +1092,10 @@
         return getScrollForPage(taskIndex) == getPagedOrientationHandler().getPrimaryScroll(this);
     }
 
+    /**
+     * Returns a {@link TaskView} that has taskId matching {@code taskId} or null if no match.
+     */
+    @Nullable
     public TaskView getTaskViewByTaskId(int taskId) {
         if (taskId == -1) {
             return null;
@@ -1920,6 +1946,7 @@
         return getTaskViewFromTaskViewId(mFocusedTaskViewId);
     }
 
+    @Nullable
     private TaskView getTaskViewFromTaskViewId(int taskViewId) {
         if (taskViewId == -1) {
             return null;
@@ -4371,12 +4398,15 @@
         }
     }
 
-    public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
+    /**
+     * Finish recents animation.
+     */
+    public void finishRecentsAnimation(boolean toRecents, @Nullable Runnable onFinishComplete) {
         finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
     }
 
     public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
-            Runnable onFinishComplete) {
+            @Nullable Runnable onFinishComplete) {
         // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
         cleanupRemoteTargets();
         if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
@@ -4939,10 +4969,13 @@
 
     private static class PinnedStackAnimationListener<T extends BaseActivity> extends
             IPipAnimationListener.Stub {
+        @Nullable
         private T mActivity;
+        @Nullable
         private RecentsView mRecentsView;
 
-        public void setActivityAndRecentsView(T activity, RecentsView recentsView) {
+        public void setActivityAndRecentsView(@Nullable T activity,
+                @Nullable RecentsView recentsView) {
             mActivity = activity;
             mRecentsView = recentsView;
         }
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index bd39391..326141d 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -24,6 +24,7 @@
 import com.android.launcher3.anim.AlphaUpdateListener;
 
 import java.util.Arrays;
+import java.util.function.Consumer;
 
 /**
  * Utility class to handle separating a single value as a factor of multiple values
@@ -85,6 +86,8 @@
         // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
         private float mOthers = 1;
 
+        private Consumer<Float> mConsumer;
+
         AlphaProperty(int myMask) {
             mMyMask = myMask;
         }
@@ -109,16 +112,24 @@
             mValidMask = mMyMask;
             mValue = value;
 
-            mView.setAlpha(mOthers * mValue);
+            final float alpha = mOthers * mValue;
+            mView.setAlpha(alpha);
             if (mUpdateVisibility) {
                 AlphaUpdateListener.updateVisibility(mView);
             }
+            if (mConsumer != null) {
+                mConsumer.accept(mValue);
+            }
         }
 
         public float getValue() {
             return mValue;
         }
 
+        public void setConsumer(Consumer<Float> consumer) {
+            mConsumer = consumer;
+        }
+
         @Override
         public String toString() {
             return Float.toString(mValue);