Animate taskbar background alpha and visibility alpha

Setup codepath to animate the Taskbar when going to and from Launcher,
primarily by listening for pause/resume signals but also hints from
gesture nav and AppToOverviewAnimationProvider.

Additionally, add TaskbarStateHandler to listen for Launcher state
changes if Taskbar is enabled. Combined, the end behavior is:

- Background alpha is 0 when Launcher is resumed, and 1 when Launcher
  is paused (we can make this animation more interesting later).
- Taskbar is always visible when Launcher is paused, otherwise its
  visibility is determined by multiple factors: LauncherState and
  whether the IME is showing.

Bug: 171917176
Change-Id: I7856fc979931c9d12d714dee11d179fd1b5a6968
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2518f42..09a3cfd 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.taskbar.TaskbarContainerView;
 import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.TaskbarStateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.UiThreadHelper;
@@ -82,6 +83,7 @@
     private OverviewActionsView mActionsView;
 
     private @Nullable TaskbarController mTaskbarController;
+    private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -245,13 +247,23 @@
                 getWorkspace(),
                 getDepthController(),
                 new RecentsViewStateController(this),
-                new BackButtonAlphaHandler(this)};
+                new BackButtonAlphaHandler(this),
+                getTaskbarStateHandler(),
+        };
     }
 
     public DepthController getDepthController() {
         return mDepthController;
     }
 
+    public @Nullable TaskbarController getTaskbarController() {
+        return mTaskbarController;
+    }
+
+    public TaskbarStateHandler getTaskbarStateHandler() {
+        return mTaskbarStateHandler;
+    }
+
     @Override
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
         QuickstepAppTransitionManagerImpl appTransitionManager =
@@ -296,6 +308,12 @@
             mDepthController.setActivityStarted(isStarted());
         }
 
+        if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+            if (mTaskbarController != null) {
+                mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
+            }
+        }
+
         super.onActivityFlagsChanged(changeBits);
     }
 
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 470a442..36c8bb8 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -139,7 +139,7 @@
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
-    protected static final int CONTENT_ALPHA_DURATION = 217;
+    public static final int CONTENT_ALPHA_DURATION = 217;
     protected static final int CONTENT_TRANSLATION_DURATION = 350;
 
     // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 7be1b92..bdf7f8a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -22,13 +22,22 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
+import android.animation.Animator;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.view.Gravity;
 import android.view.WindowManager;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.AnimatedFloat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
@@ -44,7 +53,10 @@
     private final WindowManager mWindowManager;
     // Layout width and height of the Taskbar in the default state.
     private final Point mTaskbarSize;
+    private final TaskbarStateHandler mTaskbarStateHandler;
+    private final TaskbarVisibilityController mTaskbarVisibilityController;
 
+    // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
 
     public TaskbarController(BaseQuickstepLauncher launcher,
@@ -55,6 +67,24 @@
         mWindowManager = mLauncher.getWindowManager();
         mTaskbarSize = new Point(MATCH_PARENT,
                 mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size));
+        mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
+        mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher,
+                createTaskbarVisibilityControllerCallbacks());
+    }
+
+    private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() {
+        return new TaskbarVisibilityControllerCallbacks() {
+            @Override
+            public void updateTaskbarBackgroundAlpha(float alpha) {
+                mTaskbarView.setBackgroundAlpha(alpha);
+            }
+
+            @Override
+            public void updateTaskbarVisibilityAlpha(float alpha) {
+                mTaskbarContainerView.setAlpha(alpha);
+                AlphaUpdateListener.updateVisibility(mTaskbarContainerView);
+            }
+        };
     }
 
     /**
@@ -62,6 +92,17 @@
      */
     public void init() {
         addToWindowManager();
+        mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
+        mTaskbarVisibilityController.init();
+    }
+
+    private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
+        return new TaskbarStateHandlerCallbacks() {
+            @Override
+            public AnimatedFloat getAlphaTarget() {
+                return mTaskbarVisibilityController.getTaskbarVisibilityForLauncherState();
+            }
+        };
     }
 
     /**
@@ -69,6 +110,8 @@
      */
     public void cleanup() {
         removeFromWindowManager();
+        mTaskbarStateHandler.setTaskbarCallbacks(null);
+        mTaskbarVisibilityController.cleanup();
     }
 
     private void removeFromWindowManager() {
@@ -108,4 +151,58 @@
 
         mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
     }
+
+    /**
+     * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
+     */
+    public void onLauncherResumedOrPaused(boolean isResumed) {
+        long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
+        final Animator anim;
+        if (isResumed) {
+            anim = createAnimToLauncher(null, duration);
+        } else {
+            anim = createAnimToApp(duration);
+        }
+        anim.start();
+    }
+
+    /**
+     * Create Taskbar animation when going from an app to Launcher.
+     * @param toState If known, the state we will end up in when reaching Launcher.
+     */
+    public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
+        PendingAnimation anim = new PendingAnimation(duration);
+        anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(0, duration));
+        if (toState != null) {
+            mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
+        }
+        return anim.buildAnim();
+    }
+
+    private Animator createAnimToApp(long duration) {
+        return mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration);
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+     */
+    public void setIsImeVisible(boolean isImeVisible) {
+        mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+    }
+
+    /**
+     * Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
+     */
+    protected interface TaskbarStateHandlerCallbacks {
+        AnimatedFloat getAlphaTarget();
+    }
+
+    /**
+     * Contains methods that TaskbarVisibilityController can call to interface with
+     * TaskbarController.
+     */
+    protected interface TaskbarVisibilityControllerCallbacks {
+        void updateTaskbarBackgroundAlpha(float alpha);
+        void updateTaskbarVisibilityAlpha(float alpha);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
new file mode 100644
index 0000000..b4b5d8b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.TASKBAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.AnimatedFloat;
+
+/**
+ * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
+ * isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
+ */
+public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
+
+    private final BaseQuickstepLauncher mLauncher;
+
+    // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
+    private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
+
+    public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
+        mLauncher = launcher;
+    }
+
+    public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
+        mTaskbarCallbacks = callbacks;
+    }
+
+    @Override
+    public void setState(LauncherState state) {
+        if (mTaskbarCallbacks == null) {
+            return;
+        }
+
+        AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+        boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
+        alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
+    }
+
+    @Override
+    public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+            PendingAnimation animation) {
+        if (mTaskbarCallbacks == null) {
+            return;
+        }
+        if (config.hasAnimationFlag(SKIP_TASKBAR)) {
+            return;
+        }
+
+        AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+        boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
+        animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f,
+                config.getInterpolator(ANIM_TASKBAR_FADE, Interpolators.LINEAR));
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 5df8d5f..bf6e946 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
 import android.widget.LinearLayout;
 
@@ -26,6 +27,9 @@
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
 public class TaskbarView extends LinearLayout {
+
+    private final ColorDrawable mBackgroundDrawable;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -42,5 +46,14 @@
     public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mBackgroundDrawable = (ColorDrawable) getBackground();
+    }
+
+    /**
+     * Sets the alpha of the background color behind all the Taskbar contents.
+     * @param alpha 0 is fully transparent, 1 is fully opaque.
+     */
+    public void setBackgroundAlpha(float alpha) {
+        mBackgroundDrawable.setAlpha((int) (alpha * 255));
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
new file mode 100644
index 0000000..4cf55d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
@@ -0,0 +1,95 @@
+/*
+ * 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.TASKBAR;
+
+import android.animation.Animator;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's alpha based on LauncherState, whether
+ * Launcher is in the foreground, etc.
+ */
+public class TaskbarVisibilityController {
+
+    private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
+
+    private final BaseQuickstepLauncher mLauncher;
+    private final TaskbarController.TaskbarVisibilityControllerCallbacks mTaskbarCallbacks;
+
+    // Background alpha.
+    private AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
+            this::onTaskbarBackgroundAlphaChanged);
+
+    // Overall visibility.
+    private AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
+            this::updateVisibilityAlpha);
+    private AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
+            this::updateVisibilityAlpha);
+
+    public TaskbarVisibilityController(BaseQuickstepLauncher launcher,
+            TaskbarController.TaskbarVisibilityControllerCallbacks taskbarCallbacks) {
+        mLauncher = launcher;
+        mTaskbarCallbacks = taskbarCallbacks;
+    }
+
+    protected void init() {
+        mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
+        boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
+                .getVisibleElements(mLauncher) & TASKBAR) != 0;
+        mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
+        boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
+                & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
+        mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
+    }
+
+    protected void cleanup() {
+    }
+
+    protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
+        return mTaskbarVisibilityAlphaForLauncherState;
+    }
+
+    protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
+        return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
+                .setDuration(duration);
+    }
+
+    protected void animateToVisibilityForIme(float toAlpha) {
+        mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
+                .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
+    }
+
+    private void onTaskbarBackgroundAlphaChanged() {
+        mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
+        updateVisibilityAlpha();
+    }
+
+    private void updateVisibilityAlpha() {
+        // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
+        // assumption being that Taskbar should always be visible regardless of the current
+        // LauncherState if Launcher is paused.
+        float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
+                mTaskbarVisibilityAlphaForLauncherState.value);
+        float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
+        mTaskbarCallbacks.updateTaskbarVisibilityAlpha(alphaDueToLauncher * alphaDueToOther);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4b4f955..2cf65af 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,7 +69,9 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         return super.getVisibleElements(launcher)
-                & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
+                & ~OVERVIEW_BUTTONS
+                & ~VERTICAL_SWIPE_INDICATOR
+                | TASKBAR;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 51e72da..965f474 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -40,6 +40,6 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return NONE;
+        return TASKBAR;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index efb91c6..69b8aca 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -36,6 +36,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
@@ -80,6 +81,7 @@
         if (toState == NORMAL && fromState == OVERVIEW) {
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+            config.setInterpolator(ANIM_TASKBAR_FADE, ACCEL);
             config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
             config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
@@ -138,6 +140,7 @@
             config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_TASKBAR_FADE, OVERSHOOT_1_2);
         } else if (fromState == HINT_STATE && toState == NORMAL) {
             config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
             if (mHintToNormalDuration == -1) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 39a3a7c..6bcc4bf 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1062,6 +1062,8 @@
         if (mGestureState.getEndTarget().isLauncher) {
             ActivityManagerWrapper.getInstance().registerTaskStackListener(
                     mActivityRestartListener);
+
+            mActivityInterface.onAnimateToLauncher(mGestureState.getEndTarget(), duration);
         }
 
         if (mGestureState.getEndTarget() == HOME) {
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index efd4530..d159fa0 100644
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -119,6 +120,11 @@
                     OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
         }
 
+        TaskbarController taskbarController = mActivityInterface.getTaskbarController();
+        if (taskbarController != null) {
+            pa.add(taskbarController.createAnimToLauncher(OVERVIEW, getRecentsLaunchDuration()));
+        }
+
         RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
                 wallpaperTargets, MODE_CLOSING);
 
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 5bed929..5f6e59f 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.SysUINavigationMode.Mode;
@@ -121,6 +122,11 @@
         return null;
     }
 
+    @Nullable
+    public TaskbarController getTaskbarController() {
+        return null;
+    }
+
     public final boolean isResumed() {
         ACTIVITY_TYPE activity = getCreatedActivity();
         return activity != null && activity.hasBeenResumed();
@@ -276,6 +282,20 @@
         return overviewActionsHeight;
     }
 
+    /**
+     * Called when the gesture ends and the animation starts towards the given target. No-op by
+     * default, but subclasses can override to add an additional animation with the same duration.
+     */
+    public void onAnimateToLauncher(GestureState.GestureEndTarget endTarget, long duration) {
+    }
+
+    /**
+     * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
+     * @param systemUiStateFlags The latest SystemUiStateFlags
+     */
+    public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+    }
+
     public interface AnimationFactory {
 
         void createActivityInterface(long transitionLength);
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7630bc4..deb86ec 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,9 +16,11 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -38,8 +40,10 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
 import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -156,6 +160,16 @@
 
     @Nullable
     @Override
+    public TaskbarController getTaskbarController() {
+        BaseQuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getTaskbarController();
+    }
+
+    @Nullable
+    @Override
     public RecentsView getVisibleRecentsView() {
         Launcher launcher = getVisibleLauncher();
         return launcher != null && launcher.getStateManager().getState().overviewUi
@@ -277,4 +291,24 @@
         if (activity == null) return;
         activity.getAppTransitionManager().registerRemoteTransitions();
     }
+
+    @Override
+    public void onAnimateToLauncher(GestureEndTarget endTarget, long duration) {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return;
+        }
+        LauncherState toState = endTarget == GestureEndTarget.RECENTS ? OVERVIEW : NORMAL;
+        taskbarController.createAnimToLauncher(toState, duration).start();
+    }
+
+    @Override
+    public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return;
+        }
+        boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+        taskbarController.setIsImeVisible(isImeVisible);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0f40937..a98fc1c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -370,12 +370,14 @@
     @UiThread
     private void onSystemUiFlagsChanged() {
         if (mDeviceState.isUserUnlocked()) {
-            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
-                    mDeviceState.getSystemUiStateFlags());
+            int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
+            mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
+                    systemUiStateFlags);
 
             // Update the tracing state
-            if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
+            if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
                 Log.d(TAG, "Starting tracing.");
                 ProtoTracer.INSTANCE.get(this).start();
             } else {
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 4120331..5bae3c7 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -158,7 +159,8 @@
      */
     private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
         StateAnimationConfig config = new StateAnimationConfig();
-        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER
+                | SKIP_TASKBAR;
         config.duration = 0;
         // setRecentsAttachedToAppWindow() will animate recents out.
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 79476fc..f9a1ded 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -57,6 +57,7 @@
     public static final int ALL_APPS_CONTENT = 1 << 4;
     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
     public static final int OVERVIEW_BUTTONS = 1 << 6;
+    public static final int TASKBAR = 1 << 7;
 
     /** Mask of all the items that are contained in the apps view. */
     public static final int APPS_VIEW_ITEM_MASK =
@@ -186,7 +187,7 @@
     }
 
     public int getVisibleElements(Launcher launcher) {
-        int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+        int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR | TASKBAR;
         if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
                 && !launcher.getDeviceProfile().isVerticalBarLayout()) {
             flags |= HOTSEAT_SEARCH_BOX;
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 45172b5..44bcc34 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -90,4 +90,9 @@
     public float getWorkspaceScrimAlpha(Launcher launcher) {
         return 0.3f;
     }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return super.getVisibleElements(launcher) & ~TASKBAR;
+    }
 }
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8b72177..ec949eb 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -37,7 +37,8 @@
             PLAY_ATOMIC_OVERVIEW_SCALE,
             PLAY_ATOMIC_OVERVIEW_PEEK,
             SKIP_OVERVIEW,
-            SKIP_DEPTH_CONTROLLER
+            SKIP_DEPTH_CONTROLLER,
+            SKIP_TASKBAR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationFlags {}
@@ -46,6 +47,7 @@
     public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
     public static final int SKIP_OVERVIEW = 1 << 3;
     public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+    public static final int SKIP_TASKBAR = 1 << 5;
 
     public long duration;
     public boolean userControlled;
@@ -72,6 +74,7 @@
             ANIM_OVERVIEW_MODAL,
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
+            ANIM_TASKBAR_FADE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -91,8 +94,9 @@
     public static final int ANIM_OVERVIEW_MODAL = 13;
     public static final int ANIM_DEPTH = 14;
     public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
+    public static final int ANIM_TASKBAR_FADE = 16;
 
-    private static final int ANIM_TYPES_COUNT = 16;
+    private static final int ANIM_TYPES_COUNT = 17;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];