Fix icon and DWB toast focus transition

- icon and DWB toast now transition in with animation when swipe up from apps to Overview
- Refacotred focusTransition into MultiProperty, with separate control from fullScreenProgress and scaleAndDim, and get rid of "invert" parameter and iconScaleAnimStartProgress variable which is very confusing
- Added a SCALE_AND_DIM_OUT property for use with PendingAnmation during dismiss, as PendingAnimation does not support per animator interpolator
- Use height from resource in DWB toast when calcualting translation, so translation can be properly set before view is measured
- Always set bannerOffsetPercentage regardless if banner is added

Fix: 344786723
Fix: 315972057
Fix: 313644427
Flag: EXEMPT bugfix
Test: Quick switch from home, icon and DWB toast don't transition in until gesture release
Test: Quick switch from app, icon and DWB toast don't transition in until gesture release to Overview
Test: Dismiss focus task, icon and DWB toast transition in after transition settle
Test: Swipe down task, icon and DWB toast transition out with finger
Test: Launcher central task, icon and DWB toast transition out
Change-Id: Ie16a2ceff10967de38b5f66c52f4be00d0051c5f
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index 42cddbf..9144c7f 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -19,7 +19,7 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     style="@style/TextTitle"
     android:layout_width="match_parent"
-    android:layout_height="48dp"
+    android:layout_height="@dimen/digital_wellbeing_toast_height"
     android:background="@drawable/bg_wellbeing_toast"
     android:forceHasOverlappingRendering="false"
     android:gravity="center"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 08d36d8..6bbf7f6 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -487,4 +487,7 @@
     <dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
     <dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
 
+    <!-- Digital Wellbeing -->
+    <dimen name="digital_wellbeing_toast_height">48dp</dimen>
+
 </resources>
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 6377bf4..4a5b9e4 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -90,6 +90,8 @@
     private final TaskView mTaskView;
     private final LauncherApps mLauncherApps;
 
+    private final int mBannerHeight;
+
     private Task mTask;
     private boolean mHasLimit;
 
@@ -109,6 +111,8 @@
         mContainer = container;
         mTaskView = taskView;
         mLauncherApps = container.asContext().getSystemService(LauncherApps.class);
+        mBannerHeight = container.asContext().getResources().getDimensionPixelSize(
+                R.dimen.digital_wellbeing_toast_height);
     }
 
     private void setNoLimit() {
@@ -359,10 +363,12 @@
     }
 
     void updateBannerOffset(float offsetPercentage) {
-        if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
+        if (mBannerOffsetPercentage != offsetPercentage) {
             mBannerOffsetPercentage = offsetPercentage;
-            updateTranslationY();
-            mBanner.invalidateOutline();
+            if (mBanner != null) {
+                updateTranslationY();
+                mBanner.invalidateOutline();
+            }
         }
     }
 
@@ -372,7 +378,7 @@
         }
 
         mBanner.setTranslationY(
-                (mBannerOffsetPercentage * mBanner.getHeight()) + mSplitOffsetTranslationY);
+                (mBannerOffsetPercentage * mBannerHeight) + mSplitOffsetTranslationY);
     }
 
     private void updateTranslationX() {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 4804e56..e116e87 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2955,7 +2955,6 @@
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = requireTaskViewAt(i);
-            taskView.setIconScaleAnimStartProgress(0f);
             taskView.animateIconScaleAndDimIntoView();
         }
     }
@@ -3788,7 +3787,7 @@
                     anim.setFloat(taskView, taskView.getSecondaryDismissTranslationProperty(),
                             secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
                                     dismissTranslationInterpolationEnd));
-                    anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
+                    anim.setFloat(taskView, TaskView.SCALE_AND_DIM_OUT, 0f,
                             clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
                 } else {
                     float primaryTranslation =
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index cae23a1..4fd686d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -52,7 +52,6 @@
 import com.android.launcher3.Flags.enableRefactorTaskThumbnail
 import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
 import com.android.launcher3.LauncherSettings
-import com.android.launcher3.LauncherState
 import com.android.launcher3.R
 import com.android.launcher3.Utilities
 import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
@@ -65,6 +64,8 @@
 import com.android.launcher3.testing.shared.TestProtocol
 import com.android.launcher3.util.CancellableTask
 import com.android.launcher3.util.Executors
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
 import com.android.launcher3.util.RunnableList
 import com.android.launcher3.util.SafeCloseable
 import com.android.launcher3.util.SplitConfigurationOptions
@@ -265,12 +266,14 @@
             field = Utilities.boundToRange(value, 0f, 1f)
             onFullscreenProgressChanged(field)
         }
+
     // gridProgress 0 = carousel; 1 = 2 row grid.
     protected var gridProgress = 0f
         set(value) {
             field = value
             onGridProgressChanged()
         }
+
     /**
      * The modalness of this view is how it should be displayed when it is shown on its own in the
      * modal state of overview. 0 being in context with other tasks, 1 being shown on its own.
@@ -337,12 +340,14 @@
             field = value
             applyTranslationY()
         }
+
     // The following translation variables should only be used in the same orientation as Launcher.
     private var boxTranslationY = 0f
         set(value) {
             field = value
             applyTranslationY()
         }
+
     // The following grid translations scales with mGridProgress.
     protected var gridTranslationX = 0f
         set(value) {
@@ -355,12 +360,14 @@
             field = value
             applyTranslationY()
         }
+
     // The following grid translation is used to animate closing the gap between grid and clear all.
     private var gridEndTranslationX = 0f
         set(value) {
             field = value
             applyTranslationX()
         }
+
     // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
     // switch.
     protected var nonGridTranslationX = 0f
@@ -374,6 +381,7 @@
             field = value
             applyTranslationX()
         }
+
     // Used when in SplitScreenSelectState
     private var splitSelectTranslationY = 0f
         set(value) {
@@ -395,6 +403,7 @@
 
     protected var shouldShowScreenshot = false
         get() = !isRunningTask || field
+
     /** Enable or disable showing border on hover and focus change */
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     var borderEnabled = false
@@ -409,8 +418,35 @@
             focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
         }
 
-    protected var iconScaleAnimStartProgress = 0f
     private var focusTransitionProgress = 1f
+        set(value) {
+            field = value
+            onFocusTransitionProgressUpdated(field)
+        }
+
+    private val focusTransitionPropertyFactory =
+        MultiPropertyFactory(
+            this,
+            FOCUS_TRANSITION,
+            FOCUS_TRANSITION_INDEX_COUNT,
+            { x: Float, y: Float -> x * y },
+            1f
+        )
+    private val focusTransitionFullscreen =
+        focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_FULLSCREEN)
+    private val focusTransitionScaleAndDim =
+        focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_SCALE_AND_DIM)
+    /**
+     * Variant of [focusTransitionScaleAndDim] that has a built-in interpolator, to be used with
+     * [com.android.launcher3.anim.PendingAnimation] via [SCALE_AND_DIM_OUT] only. PendingAnimation
+     * doesn't support interpolator per animation, so we'll have to interpolate inside the property.
+     */
+    private var focusTransitionScaleAndDimOut = focusTransitionScaleAndDim.value
+        set(value) {
+            field = value
+            focusTransitionScaleAndDim.value =
+                FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(field)
+        }
 
     private var iconAndDimAnimator: ObjectAnimator? = null
     // The current background requests to load the task thumbnail and icon
@@ -1301,43 +1337,23 @@
         }
     }
 
-    protected open fun setIconsAndBannersFullscreenProgress(progress: Float) {
-        // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
-        // oversized and banner would look disproportionately large.
-        if (recentsView?.stateManager?.state == LauncherState.BACKGROUND_APP) {
-            return
-        }
-        setIconsAndBannersTransitionProgress(progress, invert = true)
-    }
-
     /**
      * Called to animate a smooth transition when going directly from an app into Overview (and vice
      * versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
      */
-    protected open fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
-        focusTransitionProgress = if (invert) 1 - progress else progress
-        getIconContentScale(invert).let { iconContentScale ->
-            taskContainers.forEach {
-                it.iconView.setContentAlpha(iconContentScale)
-                it.digitalWellBeingToast?.updateBannerOffset(1f - iconContentScale)
-            }
+    private fun onFocusTransitionProgressUpdated(focusTransitionProgress: Float) {
+        taskContainers.forEach {
+            it.iconView.setContentAlpha(focusTransitionProgress)
+            it.digitalWellBeingToast?.updateBannerOffset(1f - focusTransitionProgress)
         }
     }
 
-    private fun getIconContentScale(invert: Boolean): Float {
-        val iconScalePercentage = SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
-        val lowerClamp = if (invert) 1f - iconScalePercentage else 0f
-        val upperClamp = if (invert) 1f else iconScalePercentage
-        return Interpolators.clampToProgress(Interpolators.FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
-            .getInterpolation(focusTransitionProgress)
-    }
-
     fun animateIconScaleAndDimIntoView() {
         iconAndDimAnimator?.cancel()
         iconAndDimAnimator =
-            ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1f).apply {
-                setCurrentFraction(iconScaleAnimStartProgress)
-                setDuration(DIM_ANIM_DURATION).interpolator = Interpolators.LINEAR
+            ObjectAnimator.ofFloat(focusTransitionScaleAndDim, MULTI_PROPERTY_VALUE, 0f, 1f).apply {
+                duration = SCALE_ICON_DURATION
+                interpolator = Interpolators.LINEAR
                 addListener(
                     object : AnimatorListenerAdapter() {
                         override fun onAnimationEnd(animation: Animator) {
@@ -1351,7 +1367,7 @@
 
     fun setIconScaleAndDim(iconScale: Float) {
         iconAndDimAnimator?.cancel()
-        setIconsAndBannersTransitionProgress(iconScale, invert = false)
+        focusTransitionScaleAndDim.value = iconScale
     }
 
     /** Set a color tint on the snapshot and supporting views. */
@@ -1454,7 +1470,8 @@
             it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
             it.overlay.setFullscreenProgress(fullscreenProgress)
         }
-        setIconsAndBannersFullscreenProgress(fullscreenProgress)
+        focusTransitionFullscreen.value =
+            FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(1 - fullscreenProgress)
         updateSnapshotRadius()
     }
 
@@ -1638,21 +1655,43 @@
         const val FLAG_UPDATE_ALL =
             (FLAG_UPDATE_ICON or FLAG_UPDATE_THUMBNAIL or FLAG_UPDATE_CORNER_RADIUS)
 
+        const val FOCUS_TRANSITION_INDEX_FULLSCREEN = 0
+        const val FOCUS_TRANSITION_INDEX_SCALE_AND_DIM = 1
+        const val FOCUS_TRANSITION_INDEX_COUNT = 2
+
         /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
         const val MAX_PAGE_SCRIM_ALPHA = 0.4f
         const val SCALE_ICON_DURATION: Long = 120
         private const val DIM_ANIM_DURATION: Long = 700
+        private const val FOCUS_TRANSITION_THRESHOLD =
+            SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
+        val FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR =
+            Interpolators.clampToProgress(
+                Interpolators.FAST_OUT_SLOW_IN,
+                1f - FOCUS_TRANSITION_THRESHOLD,
+                1f
+            )!!
         private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())
 
-        @JvmField
-        val FOCUS_TRANSITION: FloatProperty<TaskView> =
+        private val FOCUS_TRANSITION: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("focusTransition") {
                 override fun setValue(taskView: TaskView, v: Float) {
-                    taskView.setIconsAndBannersTransitionProgress(v, false /* invert */)
+                    taskView.focusTransitionProgress = v
                 }
 
                 override fun get(taskView: TaskView) = taskView.focusTransitionProgress
             }
+
+        @JvmField
+        val SCALE_AND_DIM_OUT: FloatProperty<TaskView> =
+            object : FloatProperty<TaskView>("scaleAndDimFastOut") {
+                override fun setValue(taskView: TaskView, v: Float) {
+                    taskView.focusTransitionScaleAndDimOut = v
+                }
+
+                override fun get(taskView: TaskView) = taskView.focusTransitionScaleAndDimOut
+            }
+
         private val SPLIT_SELECT_TRANSLATION_X: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("splitSelectTranslationX") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1661,6 +1700,7 @@
 
                 override fun get(taskView: TaskView) = taskView.splitSelectTranslationX
             }
+
         private val SPLIT_SELECT_TRANSLATION_Y: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("splitSelectTranslationY") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1669,6 +1709,7 @@
 
                 override fun get(taskView: TaskView) = taskView.splitSelectTranslationY
             }
+
         private val DISMISS_TRANSLATION_X: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("dismissTranslationX") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1677,6 +1718,7 @@
 
                 override fun get(taskView: TaskView) = taskView.dismissTranslationX
             }
+
         private val DISMISS_TRANSLATION_Y: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("dismissTranslationY") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1685,6 +1727,7 @@
 
                 override fun get(taskView: TaskView) = taskView.dismissTranslationY
             }
+
         private val TASK_OFFSET_TRANSLATION_X: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("taskOffsetTranslationX") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1693,6 +1736,7 @@
 
                 override fun get(taskView: TaskView) = taskView.taskOffsetTranslationX
             }
+
         private val TASK_OFFSET_TRANSLATION_Y: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("taskOffsetTranslationY") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1701,6 +1745,7 @@
 
                 override fun get(taskView: TaskView) = taskView.taskOffsetTranslationY
             }
+
         private val TASK_RESISTANCE_TRANSLATION_X: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("taskResistanceTranslationX") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1709,6 +1754,7 @@
 
                 override fun get(taskView: TaskView) = taskView.taskResistanceTranslationX
             }
+
         private val TASK_RESISTANCE_TRANSLATION_Y: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("taskResistanceTranslationY") {
                 override fun setValue(taskView: TaskView, v: Float) {
@@ -1717,6 +1763,7 @@
 
                 override fun get(taskView: TaskView) = taskView.taskResistanceTranslationY
             }
+
         @JvmField
         val GRID_END_TRANSLATION_X: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("gridEndTranslationX") {
@@ -1726,6 +1773,7 @@
 
                 override fun get(taskView: TaskView) = taskView.gridEndTranslationX
             }
+
         @JvmField
         val DISMISS_SCALE: FloatProperty<TaskView> =
             object : FloatProperty<TaskView>("dismissScale") {