Animate icon dimming in taskbar when force showing in immersive mode.

Test: manual
Bug: 203748818
Change-Id: Iab3c65db502e7dbde3141429e9ae70eec1fabde1
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 224c05e..15db49c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -106,6 +106,10 @@
 
     private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
 
+    public static final int ALPHA_INDEX_IMMERSIVE_MODE = 0;
+    public static final int ALPHA_INDEX_KEYGUARD_OR_DISABLE = 1;
+    private static final int NUM_ALPHA_CHANNELS = 2;
+
     private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
     private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
     private int mState;
@@ -140,6 +144,8 @@
     private int mSysuiStateFlags;
     private View mBackButton;
     private View mHomeButton;
+    private MultiValueAlpha mBackButtonAlpha;
+    private MultiValueAlpha mHomeButtonAlpha;
     private FloatingRotationButton mFloatingRotationButton;
 
     // Variables for moving nav buttons to a separate window above IME
@@ -180,8 +186,7 @@
                 mControllers.taskbarViewController.getTaskbarIconAlpha()
                         .getProperty(ALPHA_INDEX_KEYGUARD),
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
-                        && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0,
-                MultiValueAlpha.VALUE, 1, 0));
+                        && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0));
 
         mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
                 .getKeyguardBgTaskbar(),
@@ -347,7 +352,10 @@
 
         mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
                 mNavButtonContainer, mControllers.navButtonController, R.id.back);
-        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+        mBackButtonAlpha = new MultiValueAlpha(mBackButton, NUM_ALPHA_CHANNELS);
+        mBackButtonAlpha.setUpdateVisibility(true);
+        mPropertyHolders.add(new StatePropertyHolder(
+                mBackButtonAlpha.getProperty(ALPHA_INDEX_KEYGUARD_OR_DISABLE),
                 flags -> {
                     // Show only if not disabled, and if not on the keyguard or otherwise only when
                     // the bouncer or a lockscreen app is showing above the keyguard
@@ -373,7 +381,11 @@
         // home and recents buttons
         mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
                 navButtonController, R.id.home);
-        mPropertyHolders.add(new StatePropertyHolder(mHomeButton,
+        mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS);
+        mHomeButtonAlpha.setUpdateVisibility(true);
+        mPropertyHolders.add(
+                new StatePropertyHolder(mHomeButtonAlpha.getProperty(
+                        ALPHA_INDEX_KEYGUARD_OR_DISABLE),
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
                         (flags & FLAG_DISABLE_HOME) == 0));
         View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
@@ -486,6 +498,20 @@
         }
     }
 
+    /**
+     * Returns multi-value alpha controller for back button.
+     */
+    public MultiValueAlpha getBackButtonAlpha() {
+        return mBackButtonAlpha;
+    }
+
+    /**
+     * Returns multi-value alpha controller for home button.
+     */
+    public MultiValueAlpha getHomeButtonAlpha() {
+        return mHomeButtonAlpha;
+    }
+
     /** Use to set the translationY for the all nav+contextual buttons */
     public AnimatedFloat getTaskbarNavButtonTranslationY() {
         return mTaskbarNavButtonTranslationY;
@@ -778,6 +804,11 @@
             mAnimator.addListener(new AlphaUpdateListener(view));
         }
 
+        StatePropertyHolder(MultiValueAlpha.AlphaProperty alphaProperty,
+                IntPredicate enableCondition) {
+            this(alphaProperty, enableCondition, MultiValueAlpha.VALUE, 1, 0);
+        }
+
         <T> StatePropertyHolder(T target, IntPredicate enabledCondition,
                 Property<T, Float> property, float enabledValue, float disabledValue) {
             mEnableCondition = enabledCondition;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e37bd21..6a59bc2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -188,7 +188,8 @@
                 new TaskbarStashController(this),
                 new TaskbarEduController(this),
                 new TaskbarAutohideSuspendController(this),
-                new TaskbarPopupController(this));
+                new TaskbarPopupController(this),
+                new TaskbarForceVisibleImmersiveController(this));
     }
 
     public void init(TaskbarSharedState sharedState) {
@@ -417,6 +418,7 @@
         mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
                 fromInit);
         mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index f491669..8364137 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -47,6 +47,7 @@
     public final TaskbarEduController taskbarEduController;
     public final TaskbarAutohideSuspendController taskbarAutohideSuspendController;
     public final TaskbarPopupController taskbarPopupController;
+    public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController;
 
     @Nullable private LoggableTaskbarController[] mControllersToLog = null;
 
@@ -70,7 +71,8 @@
             TaskbarStashController taskbarStashController,
             TaskbarEduController taskbarEduController,
             TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
-            TaskbarPopupController taskbarPopupController) {
+            TaskbarPopupController taskbarPopupController,
+            TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController) {
         this.taskbarActivityContext = taskbarActivityContext;
         this.taskbarDragController = taskbarDragController;
         this.navButtonController = navButtonController;
@@ -86,6 +88,7 @@
         this.taskbarEduController = taskbarEduController;
         this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
         this.taskbarPopupController = taskbarPopupController;
+        this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
     }
 
     /**
@@ -108,6 +111,7 @@
         taskbarStashController.init(this, sharedState);
         taskbarEduController.init(this);
         taskbarPopupController.init(this);
+        taskbarForceVisibleImmersiveController.init(this);
 
         mControllersToLog = new LoggableTaskbarController[] {
                 taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -142,6 +146,7 @@
         stashedHandleViewController.onDestroy();
         taskbarAutohideSuspendController.onDestroy();
         taskbarPopupController.onDestroy();
+        taskbarForceVisibleImmersiveController.onDestroy();
 
         mControllersToLog = null;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index df004ef..4a80665 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
@@ -105,7 +104,7 @@
 
     @Override
     public void recreateControllers() {
-        mControllers = new TouchController[] {mActivity.getDragController()};
+        mControllers = mControllerCallbacks.getTouchControllers();
     }
 
     private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index fa40992..1bd76b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.util.TouchController;
 import com.android.quickstep.AnimatedFloat;
 import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
 
@@ -181,7 +182,8 @@
                 // Let touches pass through us.
                 insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
             } else if (mControllers.taskbarViewController.areIconsVisible()
-                    || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null) {
+                    || AbstractFloatingView.getOpenView(mActivity, TYPE_ALL) != null
+                    || mActivity.isNavBarKidsModeActive()) {
                 // Taskbar has some touchable elements, take over the full taskbar area
                 insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen()
                         ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT);
@@ -217,5 +219,13 @@
         public int getTaskbarBackgroundHeight() {
             return mActivity.getDeviceProfile().taskbarSize;
         }
+
+        /**
+         * Returns touch controllers.
+         */
+        public TouchController[] getTouchControllers() {
+            return new TouchController[]{mActivity.getDragController(),
+                    mControllers.taskbarForceVisibleImmersiveController};
+        }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
new file mode 100644
index 0000000..66a927f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.view.MotionEvent;
+
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.AnimatedFloat;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Controller for taskbar when force visible in immersive mode is set.
+ */
+public class TaskbarForceVisibleImmersiveController implements TouchController {
+    private static final int NAV_BAR_ICONS_DIM_ANIMATION_START_DELAY_MS = 4500;
+    private static final int NAV_BAR_ICONS_DIM_ANIMATION_DURATION_MS = 500;
+    private static final int NAV_BAR_ICONS_UNDIM_ANIMATION_DURATION_MS = 250;
+    private static final float NAV_BAR_ICONS_DIM_PCT = 0.15f;
+    private static final float NAV_BAR_ICONS_UNDIM_PCT = 1f;
+
+    private final TaskbarActivityContext mContext;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Runnable mDimmingRunnable = this::dimIcons;
+    private final Runnable mUndimmingRunnable = this::undimIcons;
+    private final AnimatedFloat mIconAlphaForDimming = new AnimatedFloat(
+            this::updateIconDimmingAlpha);
+    private final Consumer<MultiValueAlpha> mImmersiveModeAlphaUpdater = alpha -> alpha.getProperty(
+            ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    private boolean mIsImmersiveMode;
+
+    public TaskbarForceVisibleImmersiveController(TaskbarActivityContext context) {
+        mContext = context;
+    }
+
+    /**
+     * Initialize controllers.
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
+    /** Update values tracked via sysui flags. */
+    public void updateSysuiFlags(int sysuiFlags) {
+        mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_IMMERSIVE_MODE) != 0;
+        if (mContext.isNavBarKidsModeActive()) {
+            if (mIsImmersiveMode) {
+                startIconDimming();
+            } else {
+                startIconUndimming();
+            }
+        }
+    }
+
+    /** Clean up animations. */
+    public void onDestroy() {
+        startIconUndimming();
+    }
+
+    private void startIconUndimming() {
+        mHandler.removeCallbacks(mDimmingRunnable);
+        mHandler.removeCallbacks(mUndimmingRunnable);
+        mHandler.post(mUndimmingRunnable);
+    }
+
+    private void undimIcons() {
+        mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_UNDIM_PCT).setDuration(
+                NAV_BAR_ICONS_UNDIM_ANIMATION_DURATION_MS).start();
+    }
+
+    private void startIconDimming() {
+        mHandler.removeCallbacks(mDimmingRunnable);
+        mHandler.postDelayed(mDimmingRunnable, NAV_BAR_ICONS_DIM_ANIMATION_START_DELAY_MS);
+    }
+
+    private void dimIcons() {
+        mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_DIM_PCT).setDuration(
+                NAV_BAR_ICONS_DIM_ANIMATION_DURATION_MS).start();
+    }
+
+    /**
+     * Returns whether the taskbar is always visible in immersive mode.
+     */
+    private boolean isNavbarShownInImmersiveMode() {
+        return mIsImmersiveMode && mContext.isNavBarKidsModeActive();
+    }
+
+    private void updateIconDimmingAlpha() {
+        getBackButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
+        getHomeButtonAlphaOptional().ifPresent(mImmersiveModeAlphaUpdater);
+    }
+
+    private Optional<MultiValueAlpha> getBackButtonAlphaOptional() {
+        if (mControllers == null || mControllers.navbarButtonsViewController == null) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(mControllers.navbarButtonsViewController.getBackButtonAlpha());
+    }
+
+    private Optional<MultiValueAlpha> getHomeButtonAlphaOptional() {
+        if (mControllers == null || mControllers.navbarButtonsViewController == null) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(mControllers.navbarButtonsViewController.getHomeButtonAlpha());
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (!isNavbarShownInImmersiveMode()
+                || mControllers.taskbarStashController.supportsManualStashing()) {
+            return false;
+        }
+        return onControllerTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onControllerTouchEvent(MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                startIconUndimming();
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                startIconDimming();
+                break;
+        }
+        return false;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 748557b..014e172 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -197,7 +197,7 @@
     /**
      * Returns whether the user can manually stash the taskbar based on the current device state.
      */
-    private boolean supportsManualStashing() {
+    protected boolean supportsManualStashing() {
         return supportsVisualStashing()
                 && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
     }
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 6a7d066..6c7a885 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -98,6 +98,15 @@
         }
     }
 
+    /**
+     * Starts the animation.
+     */
+    public void startAnimation() {
+        if (mValueAnimator != null) {
+            mValueAnimator.start();
+        }
+    }
+
     public void cancelAnimation() {
         if (mValueAnimator != null) {
             mValueAnimator.cancel();