Merge "Merging IconShape with ThemeManager" into main
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index a1963c9..a9dbbf2 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -215,13 +215,13 @@
         if (enableOverviewIconMenu()) {
             builder.add(
                 ObjectAnimator.ofFloat(
-                    (iconView as IconAppChipView).splitTranslationX,
+                    (iconView as IconAppChipView).getSplitTranslationX(),
                     MULTI_PROPERTY_VALUE,
                     0f,
                 )
             )
             builder.add(
-                ObjectAnimator.ofFloat(iconView.splitTranslationY, MULTI_PROPERTY_VALUE, 0f)
+                ObjectAnimator.ofFloat(iconView.getSplitTranslationY(), MULTI_PROPERTY_VALUE, 0f)
             )
         }
 
@@ -985,12 +985,11 @@
         val splitTree: Pair<Change, List<Change>>? = extractTopParentAndChildren(transitionInfo)
         check(splitTree != null) { "Could not find a split root candidate" }
         val rootCandidate = splitTree.first
-        val stageRootTaskIds: Set<Int> = splitTree.second
-            .map { it.taskInfo!!.taskId }
-            .toSet()
-        val leafTasks: List<Change> = transitionInfo.changes
-            .filter { it.taskInfo != null && it.taskInfo!!.parentTaskId in stageRootTaskIds}
-            .toList()
+        val stageRootTaskIds: Set<Int> = splitTree.second.map { it.taskInfo!!.taskId }.toSet()
+        val leafTasks: List<Change> =
+            transitionInfo.changes
+                .filter { it.taskInfo != null && it.taskInfo!!.parentTaskId in stageRootTaskIds }
+                .toList()
 
         // Starting position is a 34% size tile centered in the middle of the screen.
         // Ending position is the full device screen.
@@ -1031,8 +1030,13 @@
                         val endAbsBounds = leaf.endAbsBounds
 
                         t.setAlpha(leaf.leash, 1f)
-                        t.setCrop(leaf.leash, 0f, 0f,
-                            endAbsBounds.width().toFloat(), endAbsBounds.height().toFloat())
+                        t.setCrop(
+                            leaf.leash,
+                            0f,
+                            0f,
+                            endAbsBounds.width().toFloat(),
+                            endAbsBounds.height().toFloat(),
+                        )
                         t.setPosition(leaf.leash, 0f, 0f)
                     }
 
@@ -1040,10 +1044,18 @@
                         val endAbsBounds = stageRoot.endAbsBounds
 
                         t.setAlpha(stageRoot.leash, 1f)
-                        t.setCrop(stageRoot.leash, 0f, 0f,
-                            endAbsBounds.width().toFloat(), endAbsBounds.height().toFloat())
-                        t.setPosition(stageRoot.leash, endAbsBounds.left.toFloat(),
-                            endAbsBounds.top.toFloat())
+                        t.setCrop(
+                            stageRoot.leash,
+                            0f,
+                            0f,
+                            endAbsBounds.width().toFloat(),
+                            endAbsBounds.height().toFloat(),
+                        )
+                        t.setPosition(
+                            stageRoot.leash,
+                            endAbsBounds.left.toFloat(),
+                            endAbsBounds.top.toFloat(),
+                        )
                     }
                     t.apply()
                 }
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 229c8f5..2abfb13 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -171,12 +171,10 @@
                 val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
                 // setMaxWidth() needs to be called before mIconView.setIconOrientation which is
                 // called in the super below.
-                (leftTopTaskContainer.iconView as IconAppChipView).setMaxWidth(
+                (leftTopTaskContainer.iconView as IconAppChipView).maxWidth =
                     groupedTaskViewSizes.first.x - iconMargins
-                )
-                (rightBottomTaskContainer.iconView as IconAppChipView).setMaxWidth(
+                (rightBottomTaskContainer.iconView as IconAppChipView).maxWidth =
                     groupedTaskViewSizes.second.x - iconMargins
-                )
             }
         }
         super.setOrientationState(orientationState)
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.java b/quickstep/src/com/android/quickstep/views/IconAppChipView.java
deleted file mode 100644
index 5270477..0000000
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright (C) 2023 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.quickstep.views;
-
-import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.MultiPropertyFactory;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
-import com.android.quickstep.util.RecentsOrientedState;
-
-/**
- * An icon app menu view which can be used in place of an IconView in overview TaskViews.
- */
-public class IconAppChipView extends FrameLayout implements TaskViewIcon {
-
-    private static final int MENU_BACKGROUND_REVEAL_DURATION = 417;
-    private static final int MENU_BACKGROUND_HIDE_DURATION = 333;
-
-    private static final int NUM_ALPHA_CHANNELS = 4;
-    private static final int INDEX_CONTENT_ALPHA = 0;
-    private static final int INDEX_COLOR_FILTER_ALPHA = 1;
-    private static final int INDEX_MODAL_ALPHA = 2;
-    /** Used to hide the app chip for 90:10 flex split. */
-    private static final int INDEX_MINIMUM_RATIO_ALPHA = 3;
-
-    private final MultiValueAlpha mMultiValueAlpha;
-
-    private View mMenuAnchorView;
-    private IconView mIconView;
-    // Two textview so we can ellipsize the collapsed view and crossfade on expand to the full name.
-    private TextView mIconTextCollapsedView;
-    private TextView mIconTextExpandedView;
-    private ImageView mIconArrowView;
-    private final Rect mBackgroundRelativeLtrLocation = new Rect();
-    final RectEvaluator mBackgroundAnimationRectEvaluator =
-            new RectEvaluator(mBackgroundRelativeLtrLocation);
-    private final int mCollapsedMenuDefaultWidth;
-    private final int mExpandedMenuDefaultWidth;
-    private final int mCollapsedMenuDefaultHeight;
-    private final int mExpandedMenuDefaultHeight;
-    private final int mIconMenuMarginTopStart;
-    private final int mMenuToChipGap;
-    private final int mBackgroundMarginTopStart;
-    private final int mAppNameHorizontalMargin;
-    private final int mIconViewMarginStart;
-    private final int mAppIconSize;
-    private final int mArrowSize;
-    private final int mIconViewDrawableExpandedSize;
-    private final int mArrowMarginEnd;
-    private AnimatorSet mAnimator;
-
-    private int mMaxWidth = Integer.MAX_VALUE;
-
-    private static final int INDEX_SPLIT_TRANSLATION = 0;
-    private static final int INDEX_MENU_TRANSLATION = 1;
-    private static final int INDEX_COUNT_TRANSLATION = 2;
-
-    private final MultiPropertyFactory<View> mViewTranslationX;
-    private final MultiPropertyFactory<View> mViewTranslationY;
-
-    /**
-     * Gets the view split x-axis translation
-     */
-    public MultiPropertyFactory<View>.MultiProperty getSplitTranslationX() {
-        return mViewTranslationX.get(INDEX_SPLIT_TRANSLATION);
-    }
-
-    /**
-     * Sets the view split x-axis translation
-     * @param translationX x-axis translation
-     */
-    public void setSplitTranslationX(float translationX) {
-        getSplitTranslationX().setValue(translationX);
-    }
-
-    /**
-     * Gets the view split y-axis translation
-     */
-    public MultiPropertyFactory<View>.MultiProperty getSplitTranslationY() {
-        return mViewTranslationY.get(INDEX_SPLIT_TRANSLATION);
-    }
-
-    /**
-     * Sets the view split y-axis translation
-     * @param translationY y-axis translation
-     */
-    public void setSplitTranslationY(float translationY) {
-        getSplitTranslationY().setValue(translationY);
-    }
-
-    /**
-     * Gets the menu x-axis translation for split task
-     */
-    public MultiPropertyFactory<View>.MultiProperty getMenuTranslationX() {
-        return mViewTranslationX.get(INDEX_MENU_TRANSLATION);
-    }
-
-    /**
-     * Gets the menu y-axis translation for split task
-     */
-    public MultiPropertyFactory<View>.MultiProperty getMenuTranslationY() {
-        return mViewTranslationY.get(INDEX_MENU_TRANSLATION);
-    }
-
-    public IconAppChipView(Context context) {
-        this(context, null);
-    }
-
-    public IconAppChipView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public IconAppChipView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public IconAppChipView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        Resources res = getResources();
-        mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS);
-        mMultiValueAlpha.setUpdateVisibility(/* updateVisibility= */ true);
-
-        // Menu dimensions
-        mCollapsedMenuDefaultWidth =
-                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_collapsed_width);
-        mExpandedMenuDefaultWidth =
-                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_width);
-        mCollapsedMenuDefaultHeight =
-                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_collapsed_height);
-        mExpandedMenuDefaultHeight =
-                res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_height);
-        mIconMenuMarginTopStart = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin);
-        mMenuToChipGap = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_expanded_gap);
-
-        // Background dimensions
-        mBackgroundMarginTopStart = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_background_margin_top_start);
-
-        // Contents dimensions
-        mAppNameHorizontalMargin = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_app_name_margin_horizontal_collapsed);
-        mArrowMarginEnd = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_arrow_margin);
-        mIconViewMarginStart = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_view_start_margin);
-        mAppIconSize = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_app_icon_collapsed_size);
-        mArrowSize = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_arrow_size);
-        mIconViewDrawableExpandedSize = res.getDimensionPixelSize(
-                R.dimen.task_thumbnail_icon_menu_app_icon_expanded_size);
-
-        mViewTranslationX = new MultiPropertyFactory<>(this, VIEW_TRANSLATE_X,
-                INDEX_COUNT_TRANSLATION,
-                Float::sum);
-        mViewTranslationY = new MultiPropertyFactory<>(this, VIEW_TRANSLATE_Y,
-                INDEX_COUNT_TRANSLATION,
-                Float::sum);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mIconView = findViewById(R.id.icon_view);
-        mIconTextCollapsedView = findViewById(R.id.icon_text_collapsed);
-        mIconTextExpandedView = findViewById(R.id.icon_text_expanded);
-        mIconArrowView = findViewById(R.id.icon_arrow);
-        mMenuAnchorView = findViewById(R.id.icon_view_menu_anchor);
-    }
-
-    protected IconView getIconView() {
-        return mIconView;
-    }
-
-    @Override
-    public void setText(CharSequence text) {
-        if (mIconTextCollapsedView != null) {
-            mIconTextCollapsedView.setText(text);
-        }
-        if (mIconTextExpandedView != null) {
-            mIconTextExpandedView.setText(text);
-        }
-    }
-
-    @Override
-    public Drawable getDrawable() {
-        return mIconView == null ? null : mIconView.getDrawable();
-    }
-
-    @Override
-    public void setDrawable(Drawable icon) {
-        if (mIconView != null) {
-            mIconView.setDrawable(icon);
-        }
-    }
-
-    @Override
-    public void setDrawableSize(int iconWidth, int iconHeight) {
-        if (mIconView != null) {
-            mIconView.setDrawableSize(iconWidth, iconHeight);
-        }
-    }
-
-    /**
-     * Sets the maximum width of this Icon Menu. This is usually used when space is limited for
-     * split screen.
-     */
-    public void setMaxWidth(int maxWidth) {
-        // Width showing only the app icon and arrow. Max width should not be set to less than this.
-        int minimumMaxWidth = mIconViewMarginStart + mAppIconSize + mArrowSize + mArrowMarginEnd;
-        mMaxWidth = Math.max(maxWidth, minimumMaxWidth);
-    }
-
-    @Override
-    public void setIconOrientation(RecentsOrientedState orientationState, boolean isGridTask) {
-        RecentsPagedOrientationHandler orientationHandler =
-                orientationState.getOrientationHandler();
-        // Layout params for anchor view
-        LayoutParams anchorLayoutParams = (LayoutParams) mMenuAnchorView.getLayoutParams();
-        anchorLayoutParams.topMargin = mExpandedMenuDefaultHeight + mMenuToChipGap;
-        mMenuAnchorView.setLayoutParams(anchorLayoutParams);
-
-        // Layout Params for the Menu View (this)
-        LayoutParams iconMenuParams = (LayoutParams) getLayoutParams();
-        iconMenuParams.width = mExpandedMenuDefaultWidth;
-        iconMenuParams.height = mExpandedMenuDefaultHeight;
-        orientationHandler.setIconAppChipMenuParams(this, iconMenuParams, mIconMenuMarginTopStart,
-                mIconMenuMarginTopStart);
-        setLayoutParams(iconMenuParams);
-
-        // Layout params for the background
-        Rect collapsedBackgroundBounds = getCollapsedBackgroundLtrBounds();
-        mBackgroundRelativeLtrLocation.set(collapsedBackgroundBounds);
-        setOutlineProvider(new ViewOutlineProvider() {
-            final Rect mRtlAppliedOutlineBounds = new Rect();
-            @Override
-            public void getOutline(View view, Outline outline) {
-                mRtlAppliedOutlineBounds.set(mBackgroundRelativeLtrLocation);
-                if (isLayoutRtl()) {
-                    int width = getWidth();
-                    mRtlAppliedOutlineBounds.left = width - mBackgroundRelativeLtrLocation.right;
-                    mRtlAppliedOutlineBounds.right = width - mBackgroundRelativeLtrLocation.left;
-                }
-                outline.setRoundRect(
-                        mRtlAppliedOutlineBounds, mRtlAppliedOutlineBounds.height() / 2f);
-            }
-        });
-
-        // Layout Params for the Icon View
-        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        int iconMarginStartRelativeToParent = mIconViewMarginStart + mBackgroundMarginTopStart;
-        orientationHandler.setIconAppChipChildrenParams(
-                iconParams, iconMarginStartRelativeToParent);
-
-        mIconView.setLayoutParams(iconParams);
-        mIconView.setDrawableSize(mAppIconSize, mAppIconSize);
-
-        // Layout Params for the collapsed Icon Text View
-        int textMarginStart =
-                iconMarginStartRelativeToParent + mAppIconSize + mAppNameHorizontalMargin;
-        LayoutParams iconTextCollapsedParams =
-                (LayoutParams) mIconTextCollapsedView.getLayoutParams();
-        orientationHandler.setIconAppChipChildrenParams(iconTextCollapsedParams, textMarginStart);
-        int collapsedTextWidth = collapsedBackgroundBounds.width() - mIconViewMarginStart
-                - mAppIconSize - mArrowSize - mAppNameHorizontalMargin - mArrowMarginEnd;
-        iconTextCollapsedParams.width = collapsedTextWidth;
-        mIconTextCollapsedView.setLayoutParams(iconTextCollapsedParams);
-        mIconTextCollapsedView.setAlpha(1f);
-
-        // Layout Params for the expanded Icon Text View
-        LayoutParams iconTextExpandedParams =
-                (LayoutParams) mIconTextExpandedView.getLayoutParams();
-        orientationHandler.setIconAppChipChildrenParams(iconTextExpandedParams, textMarginStart);
-        mIconTextExpandedView.setLayoutParams(iconTextExpandedParams);
-        mIconTextExpandedView.setAlpha(0f);
-        mIconTextExpandedView.setRevealClip(true, 0, mAppIconSize / 2f, collapsedTextWidth);
-
-        // Layout Params for the Icon Arrow View
-        LayoutParams iconArrowParams = (LayoutParams) mIconArrowView.getLayoutParams();
-        int arrowMarginStart = collapsedBackgroundBounds.right - mArrowMarginEnd - mArrowSize;
-        orientationHandler.setIconAppChipChildrenParams(iconArrowParams, arrowMarginStart);
-        mIconArrowView.setPivotY(iconArrowParams.height / 2f);
-        mIconArrowView.setLayoutParams(iconArrowParams);
-
-        // This method is called twice sometimes (like when rotating split tasks). It is called
-        // once before onMeasure and onLayout, and again after onMeasure but before onLayout with
-        // a new width. This happens because we update widths on rotation and on measure of
-        // grouped task views. Calling requestLayout() does not guarantee a call to onMeasure if
-        // it has just measured, so we explicitly call it here.
-        measure(MeasureSpec.makeMeasureSpec(getLayoutParams().width, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(getLayoutParams().height, MeasureSpec.EXACTLY));
-    }
-
-    @Override
-    public void setIconColorTint(int color, float amount) {
-        // RecentsView's COLOR_TINT animates between 0 and 0.5f, we want to hide the app chip menu.
-        float colorTintAlpha = Utilities.mapToRange(amount, 0f, 0.5f, 1f, 0f, LINEAR);
-        mMultiValueAlpha.get(INDEX_COLOR_FILTER_ALPHA).setValue(colorTintAlpha);
-    }
-
-    @Override
-    public void setContentAlpha(float alpha) {
-        mMultiValueAlpha.get(INDEX_CONTENT_ALPHA).setValue(alpha);
-    }
-
-    @Override
-    public void setModalAlpha(float alpha) {
-        mMultiValueAlpha.get(INDEX_MODAL_ALPHA).setValue(alpha);
-    }
-
-    @Override
-    public void setFlexSplitAlpha(float alpha) {
-        mMultiValueAlpha.get(INDEX_MINIMUM_RATIO_ALPHA).setValue(alpha);
-    }
-
-    @Override
-    public int getDrawableWidth() {
-        return mIconView == null ? 0 : mIconView.getDrawableWidth();
-    }
-
-    @Override
-    public int getDrawableHeight() {
-        return mIconView == null ? 0 : mIconView.getDrawableHeight();
-    }
-
-    protected void revealAnim(boolean isRevealing) {
-        cancelInProgressAnimations();
-        final Rect collapsedBackgroundBounds = getCollapsedBackgroundLtrBounds();
-        final Rect expandedBackgroundBounds = getExpandedBackgroundLtrBounds();
-        final Rect initialBackground = new Rect(mBackgroundRelativeLtrLocation);
-        mAnimator = new AnimatorSet();
-
-        if (isRevealing) {
-            boolean isRtl = isLayoutRtl();
-            bringToFront();
-            // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
-            Animator expandedTextRevealAnim = ViewAnimationUtils.createCircularReveal(
-                    mIconTextExpandedView, 0, mIconTextExpandedView.getHeight() / 2,
-                    mIconTextCollapsedView.getWidth(), mIconTextExpandedView.getWidth());
-            // Animate background clipping
-            ValueAnimator backgroundAnimator = ValueAnimator.ofObject(
-                    mBackgroundAnimationRectEvaluator,
-                    initialBackground,
-                    expandedBackgroundBounds);
-            backgroundAnimator.addUpdateListener(valueAnimator -> invalidateOutline());
-
-            float iconViewScaling = mIconViewDrawableExpandedSize / (float) mAppIconSize;
-            float arrowTranslationX =
-                    expandedBackgroundBounds.right - collapsedBackgroundBounds.right;
-            float iconCenterToTextCollapsed = mAppIconSize / 2f + mAppNameHorizontalMargin;
-            float iconCenterToTextExpanded =
-                    mIconViewDrawableExpandedSize / 2f + mAppNameHorizontalMargin;
-            float textTranslationX = iconCenterToTextExpanded - iconCenterToTextCollapsed;
-
-            float textTranslationXWithRtl = isRtl ? -textTranslationX : textTranslationX;
-            float arrowTranslationWithRtl = isRtl ? -arrowTranslationX : arrowTranslationX;
-
-            mAnimator.playTogether(
-                    expandedTextRevealAnim,
-                    backgroundAnimator,
-                    ObjectAnimator.ofFloat(mIconView, SCALE_X, iconViewScaling),
-                    ObjectAnimator.ofFloat(mIconView, SCALE_Y, iconViewScaling),
-                    ObjectAnimator.ofFloat(mIconTextCollapsedView, TRANSLATION_X,
-                            textTranslationXWithRtl),
-                    ObjectAnimator.ofFloat(mIconTextExpandedView, TRANSLATION_X,
-                            textTranslationXWithRtl),
-                    ObjectAnimator.ofFloat(mIconTextCollapsedView, ALPHA, 0),
-                    ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 1),
-                    ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X, arrowTranslationWithRtl),
-                    ObjectAnimator.ofFloat(mIconArrowView, SCALE_Y, -1));
-            mAnimator.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
-        } else {
-            // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
-            Animator expandedTextClipAnim = ViewAnimationUtils.createCircularReveal(
-                    mIconTextExpandedView, 0, mIconTextExpandedView.getHeight() / 2,
-                    mIconTextExpandedView.getWidth(), mIconTextCollapsedView.getWidth());
-
-            // Animate background clipping
-            ValueAnimator backgroundAnimator = ValueAnimator.ofObject(
-                    mBackgroundAnimationRectEvaluator,
-                    initialBackground,
-                    collapsedBackgroundBounds);
-            backgroundAnimator.addUpdateListener(valueAnimator -> invalidateOutline());
-
-            mAnimator.playTogether(
-                    expandedTextClipAnim,
-                    backgroundAnimator,
-                    ObjectAnimator.ofFloat(mIconView, SCALE_PROPERTY, 1),
-                    ObjectAnimator.ofFloat(mIconTextCollapsedView, TRANSLATION_X, 0),
-                    ObjectAnimator.ofFloat(mIconTextExpandedView, TRANSLATION_X, 0),
-                    ObjectAnimator.ofFloat(mIconTextCollapsedView, ALPHA, 1),
-                    ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 0),
-                    ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X, 0),
-                    ObjectAnimator.ofFloat(mIconArrowView, SCALE_Y, 1));
-            mAnimator.setDuration(MENU_BACKGROUND_HIDE_DURATION);
-        }
-
-        mAnimator.setInterpolator(EMPHASIZED);
-        mAnimator.start();
-    }
-
-    private Rect getCollapsedBackgroundLtrBounds() {
-        Rect bounds = new Rect(
-                0,
-                0,
-                Math.min(mMaxWidth, mCollapsedMenuDefaultWidth),
-                mCollapsedMenuDefaultHeight);
-        bounds.offset(mBackgroundMarginTopStart, mBackgroundMarginTopStart);
-        return bounds;
-    }
-
-    private Rect getExpandedBackgroundLtrBounds() {
-        return new Rect(0, 0, mExpandedMenuDefaultWidth, mExpandedMenuDefaultHeight);
-    }
-
-    private void cancelInProgressAnimations() {
-        // We null the `AnimatorSet` because it holds references to the `Animators` which aren't
-        // expecting to be mutable and will cause a crash if they are re-used.
-        if (mAnimator != null && mAnimator.isStarted()) {
-            mAnimator.cancel();
-            mAnimator = null;
-        }
-    }
-
-    @Override
-    public View asView() {
-        return this;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
new file mode 100644
index 0000000..85d14cc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2023 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.quickstep.views
+
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewAnimationUtils
+import android.view.ViewOutlineProvider
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.app.animation.Interpolators
+import com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY
+import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X
+import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.MultiPropertyFactory.FloatBiFunction
+import com.android.launcher3.util.MultiValueAlpha
+import com.android.quickstep.util.RecentsOrientedState
+import kotlin.math.max
+import kotlin.math.min
+
+/** An icon app menu view which can be used in place of an IconView in overview TaskViews. */
+class IconAppChipView
+@JvmOverloads
+constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttr, defStyleRes), TaskViewIcon {
+
+    private var iconView: IconView? = null
+    private var iconArrowView: ImageView? = null
+    private var menuAnchorView: View? = null
+    // Two textview so we can ellipsize the collapsed view and crossfade on expand to the full name.
+    private var iconTextCollapsedView: TextView? = null
+    private var iconTextExpandedView: TextView? = null
+
+    private val backgroundRelativeLtrLocation = Rect()
+    private val backgroundAnimationRectEvaluator = RectEvaluator(backgroundRelativeLtrLocation)
+
+    // Menu dimensions
+    private val collapsedMenuDefaultWidth: Int =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_collapsed_width)
+    private val expandedMenuDefaultWidth: Int =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_width)
+    private val collapsedMenuDefaultHeight =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_collapsed_height)
+    private val expandedMenuDefaultHeight =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_height)
+    private val iconMenuMarginTopStart =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin)
+    private val menuToChipGap: Int =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_expanded_gap)
+
+    // Background dimensions
+    private val backgroundMarginTopStart: Int =
+        resources.getDimensionPixelSize(
+            R.dimen.task_thumbnail_icon_menu_background_margin_top_start
+        )
+
+    // Contents dimensions
+    private val appNameHorizontalMargin =
+        resources.getDimensionPixelSize(
+            R.dimen.task_thumbnail_icon_menu_app_name_margin_horizontal_collapsed
+        )
+    private val arrowMarginEnd =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_arrow_margin)
+    private val iconViewMarginStart =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_view_start_margin)
+    private val appIconSize =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_app_icon_collapsed_size)
+    private val arrowSize =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_arrow_size)
+    private val iconViewDrawableExpandedSize =
+        resources.getDimensionPixelSize(R.dimen.task_thumbnail_icon_menu_app_icon_expanded_size)
+
+    private var animator: AnimatorSet? = null
+
+    private val multiValueAlpha: MultiValueAlpha =
+        MultiValueAlpha(this, NUM_ALPHA_CHANNELS).apply { setUpdateVisibility(true) }
+
+    private val viewTranslationX: MultiPropertyFactory<View> =
+        MultiPropertyFactory(this, VIEW_TRANSLATE_X, INDEX_COUNT_TRANSLATION, SUM_AGGREGATOR)
+
+    private val viewTranslationY: MultiPropertyFactory<View> =
+        MultiPropertyFactory(this, VIEW_TRANSLATE_Y, INDEX_COUNT_TRANSLATION, SUM_AGGREGATOR)
+
+    var maxWidth = Int.MAX_VALUE
+        /**
+         * Sets the maximum width of this Icon Menu. This is usually used when space is limited for
+         * split screen.
+         */
+        set(value) {
+            // Width showing only the app icon and arrow. Max width should not be set to less than
+            // this.
+            val minMaxWidth = iconViewMarginStart + appIconSize + arrowSize + arrowMarginEnd
+            field = max(value, minMaxWidth)
+        }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        iconView = findViewById(R.id.icon_view)
+        iconTextCollapsedView = findViewById(R.id.icon_text_collapsed)
+        iconTextExpandedView = findViewById(R.id.icon_text_expanded)
+        iconArrowView = findViewById(R.id.icon_arrow)
+        menuAnchorView = findViewById(R.id.icon_view_menu_anchor)
+    }
+
+    override fun setText(text: CharSequence?) {
+        iconTextCollapsedView?.text = text
+        iconTextExpandedView?.text = text
+    }
+
+    override fun getDrawable(): Drawable? = iconView?.drawable
+
+    override fun setDrawable(icon: Drawable?) {
+        iconView?.drawable = icon
+    }
+
+    override fun setDrawableSize(iconWidth: Int, iconHeight: Int) {
+        iconView?.setDrawableSize(iconWidth, iconHeight)
+    }
+
+    override fun setIconOrientation(orientationState: RecentsOrientedState, isGridTask: Boolean) {
+        val orientationHandler = orientationState.orientationHandler
+        // Layout params for anchor view
+        val anchorLayoutParams = menuAnchorView!!.layoutParams as LayoutParams
+        anchorLayoutParams.topMargin = expandedMenuDefaultHeight + menuToChipGap
+        menuAnchorView!!.layoutParams = anchorLayoutParams
+
+        // Layout Params for the Menu View (this)
+        val iconMenuParams = layoutParams as LayoutParams
+        iconMenuParams.width = expandedMenuDefaultWidth
+        iconMenuParams.height = expandedMenuDefaultHeight
+        orientationHandler.setIconAppChipMenuParams(
+            this,
+            iconMenuParams,
+            iconMenuMarginTopStart,
+            iconMenuMarginTopStart,
+        )
+        layoutParams = iconMenuParams
+
+        // Layout params for the background
+        val collapsedBackgroundBounds = getCollapsedBackgroundLtrBounds()
+        backgroundRelativeLtrLocation.set(collapsedBackgroundBounds)
+        outlineProvider =
+            object : ViewOutlineProvider() {
+                val mRtlAppliedOutlineBounds: Rect = Rect()
+
+                override fun getOutline(view: View, outline: Outline) {
+                    mRtlAppliedOutlineBounds.set(backgroundRelativeLtrLocation)
+                    if (isLayoutRtl) {
+                        val width = width
+                        mRtlAppliedOutlineBounds.left = width - backgroundRelativeLtrLocation.right
+                        mRtlAppliedOutlineBounds.right = width - backgroundRelativeLtrLocation.left
+                    }
+                    outline.setRoundRect(
+                        mRtlAppliedOutlineBounds,
+                        mRtlAppliedOutlineBounds.height() / 2f,
+                    )
+                }
+            }
+
+        // Layout Params for the Icon View
+        val iconParams = iconView!!.layoutParams as LayoutParams
+        val iconMarginStartRelativeToParent = iconViewMarginStart + backgroundMarginTopStart
+        orientationHandler.setIconAppChipChildrenParams(iconParams, iconMarginStartRelativeToParent)
+
+        iconView!!.layoutParams = iconParams
+        iconView!!.setDrawableSize(appIconSize, appIconSize)
+
+        // Layout Params for the collapsed Icon Text View
+        val textMarginStart =
+            iconMarginStartRelativeToParent + appIconSize + appNameHorizontalMargin
+        val iconTextCollapsedParams = iconTextCollapsedView!!.layoutParams as LayoutParams
+        orientationHandler.setIconAppChipChildrenParams(iconTextCollapsedParams, textMarginStart)
+        val collapsedTextWidth =
+            (collapsedBackgroundBounds.width() -
+                iconViewMarginStart -
+                appIconSize -
+                arrowSize -
+                appNameHorizontalMargin -
+                arrowMarginEnd)
+        iconTextCollapsedParams.width = collapsedTextWidth
+        iconTextCollapsedView!!.layoutParams = iconTextCollapsedParams
+        iconTextCollapsedView!!.alpha = 1f
+
+        // Layout Params for the expanded Icon Text View
+        val iconTextExpandedParams = iconTextExpandedView!!.layoutParams as LayoutParams
+        orientationHandler.setIconAppChipChildrenParams(iconTextExpandedParams, textMarginStart)
+        iconTextExpandedView!!.layoutParams = iconTextExpandedParams
+        iconTextExpandedView!!.alpha = 0f
+        iconTextExpandedView!!.setRevealClip(
+            true,
+            0f,
+            appIconSize / 2f,
+            collapsedTextWidth.toFloat(),
+        )
+
+        // Layout Params for the Icon Arrow View
+        val iconArrowParams = iconArrowView!!.layoutParams as LayoutParams
+        val arrowMarginStart = collapsedBackgroundBounds.right - arrowMarginEnd - arrowSize
+        orientationHandler.setIconAppChipChildrenParams(iconArrowParams, arrowMarginStart)
+        iconArrowView!!.pivotY = iconArrowParams.height / 2f
+        iconArrowView!!.layoutParams = iconArrowParams
+
+        // This method is called twice sometimes (like when rotating split tasks). It is called
+        // once before onMeasure and onLayout, and again after onMeasure but before onLayout with
+        // a new width. This happens because we update widths on rotation and on measure of
+        // grouped task views. Calling requestLayout() does not guarantee a call to onMeasure if
+        // it has just measured, so we explicitly call it here.
+        measure(
+            MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY),
+            MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY),
+        )
+    }
+
+    override fun setIconColorTint(color: Int, amount: Float) {
+        // RecentsView's COLOR_TINT animates between 0 and 0.5f, we want to hide the app chip menu.
+        val colorTintAlpha = Utilities.mapToRange(amount, 0f, 0.5f, 1f, 0f, Interpolators.LINEAR)
+        multiValueAlpha[INDEX_COLOR_FILTER_ALPHA].value = colorTintAlpha
+    }
+
+    override fun setContentAlpha(alpha: Float) {
+        multiValueAlpha[INDEX_CONTENT_ALPHA].value = alpha
+    }
+
+    override fun setModalAlpha(alpha: Float) {
+        multiValueAlpha[INDEX_MODAL_ALPHA].value = alpha
+    }
+
+    override fun setFlexSplitAlpha(alpha: Float) {
+        multiValueAlpha[INDEX_MINIMUM_RATIO_ALPHA].value = alpha
+    }
+
+    override fun getDrawableWidth(): Int = iconView?.drawableWidth ?: 0
+
+    override fun getDrawableHeight(): Int = iconView?.drawableHeight ?: 0
+
+    /** Gets the view split x-axis translation */
+    fun getSplitTranslationX(): MultiPropertyFactory<View>.MultiProperty =
+        viewTranslationX.get(INDEX_SPLIT_TRANSLATION)
+
+    /**
+     * Sets the view split x-axis translation
+     *
+     * @param value x-axis translation
+     */
+    fun setSplitTranslationX(value: Float) {
+        getSplitTranslationX().value = value
+    }
+
+    /** Gets the view split y-axis translation */
+    fun getSplitTranslationY(): MultiPropertyFactory<View>.MultiProperty =
+        viewTranslationY[INDEX_SPLIT_TRANSLATION]
+
+    /**
+     * Sets the view split y-axis translation
+     *
+     * @param value y-axis translation
+     */
+    fun setSplitTranslationY(value: Float) {
+        getSplitTranslationY().value = value
+    }
+
+    /** Gets the menu x-axis translation for split task */
+    fun getMenuTranslationX(): MultiPropertyFactory<View>.MultiProperty =
+        viewTranslationX[INDEX_MENU_TRANSLATION]
+
+    /** Gets the menu y-axis translation for split task */
+    fun getMenuTranslationY(): MultiPropertyFactory<View>.MultiProperty =
+        viewTranslationY[INDEX_MENU_TRANSLATION]
+
+    internal fun revealAnim(isRevealing: Boolean) {
+        cancelInProgressAnimations()
+        val collapsedBackgroundBounds = getCollapsedBackgroundLtrBounds()
+        val expandedBackgroundBounds = getExpandedBackgroundLtrBounds()
+        val initialBackground = Rect(backgroundRelativeLtrLocation)
+        animator = AnimatorSet()
+
+        if (isRevealing) {
+            val isRtl = isLayoutRtl
+            bringToFront()
+            // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
+            val expandedTextRevealAnim =
+                ViewAnimationUtils.createCircularReveal(
+                    iconTextExpandedView,
+                    0,
+                    iconTextExpandedView!!.height / 2,
+                    iconTextCollapsedView!!.width.toFloat(),
+                    iconTextExpandedView!!.width.toFloat(),
+                )
+            // Animate background clipping
+            val backgroundAnimator =
+                ValueAnimator.ofObject(
+                    backgroundAnimationRectEvaluator,
+                    initialBackground,
+                    expandedBackgroundBounds,
+                )
+            backgroundAnimator.addUpdateListener { invalidateOutline() }
+
+            val iconViewScaling = iconViewDrawableExpandedSize / appIconSize.toFloat()
+            val arrowTranslationX =
+                (expandedBackgroundBounds.right - collapsedBackgroundBounds.right).toFloat()
+            val iconCenterToTextCollapsed = appIconSize / 2f + appNameHorizontalMargin
+            val iconCenterToTextExpanded =
+                iconViewDrawableExpandedSize / 2f + appNameHorizontalMargin
+            val textTranslationX = iconCenterToTextExpanded - iconCenterToTextCollapsed
+
+            val textTranslationXWithRtl = if (isRtl) -textTranslationX else textTranslationX
+            val arrowTranslationWithRtl = if (isRtl) -arrowTranslationX else arrowTranslationX
+
+            animator!!.playTogether(
+                expandedTextRevealAnim,
+                backgroundAnimator,
+                ObjectAnimator.ofFloat(iconView, SCALE_X, iconViewScaling),
+                ObjectAnimator.ofFloat(iconView, SCALE_Y, iconViewScaling),
+                ObjectAnimator.ofFloat(
+                    iconTextCollapsedView,
+                    TRANSLATION_X,
+                    textTranslationXWithRtl,
+                ),
+                ObjectAnimator.ofFloat(
+                    iconTextExpandedView,
+                    TRANSLATION_X,
+                    textTranslationXWithRtl,
+                ),
+                ObjectAnimator.ofFloat(iconTextCollapsedView, ALPHA, 0f),
+                ObjectAnimator.ofFloat(iconTextExpandedView, ALPHA, 1f),
+                ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, arrowTranslationWithRtl),
+                ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, -1f),
+            )
+            animator!!.setDuration(MENU_BACKGROUND_REVEAL_DURATION.toLong())
+        } else {
+            // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
+            val expandedTextClipAnim =
+                ViewAnimationUtils.createCircularReveal(
+                    iconTextExpandedView,
+                    0,
+                    iconTextExpandedView!!.height / 2,
+                    iconTextExpandedView!!.width.toFloat(),
+                    iconTextCollapsedView!!.width.toFloat(),
+                )
+
+            // Animate background clipping
+            val backgroundAnimator =
+                ValueAnimator.ofObject(
+                    backgroundAnimationRectEvaluator,
+                    initialBackground,
+                    collapsedBackgroundBounds,
+                )
+            backgroundAnimator.addUpdateListener { valueAnimator: ValueAnimator? ->
+                invalidateOutline()
+            }
+
+            animator!!.playTogether(
+                expandedTextClipAnim,
+                backgroundAnimator,
+                ObjectAnimator.ofFloat(iconView, SCALE_PROPERTY, 1f),
+                ObjectAnimator.ofFloat(iconTextCollapsedView, TRANSLATION_X, 0f),
+                ObjectAnimator.ofFloat(iconTextExpandedView, TRANSLATION_X, 0f),
+                ObjectAnimator.ofFloat(iconTextCollapsedView, ALPHA, 1f),
+                ObjectAnimator.ofFloat(iconTextExpandedView, ALPHA, 0f),
+                ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, 0f),
+                ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f),
+            )
+            animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong())
+        }
+
+        animator!!.interpolator = Interpolators.EMPHASIZED
+        animator!!.start()
+    }
+
+    private fun getCollapsedBackgroundLtrBounds(): Rect {
+        val bounds =
+            Rect(0, 0, min(maxWidth, collapsedMenuDefaultWidth), collapsedMenuDefaultHeight)
+        bounds.offset(backgroundMarginTopStart, backgroundMarginTopStart)
+        return bounds
+    }
+
+    private fun getExpandedBackgroundLtrBounds() =
+        Rect(0, 0, expandedMenuDefaultWidth, expandedMenuDefaultHeight)
+
+    private fun cancelInProgressAnimations() {
+        // We null the `AnimatorSet` because it holds references to the `Animators` which aren't
+        // expecting to be mutable and will cause a crash if they are re-used.
+        if (animator != null && animator!!.isStarted) {
+            animator!!.cancel()
+            animator = null
+        }
+    }
+
+    override fun asView(): View = this
+
+    private companion object {
+        private val SUM_AGGREGATOR = FloatBiFunction { a: Float, b: Float -> a + b }
+
+        private const val MENU_BACKGROUND_REVEAL_DURATION = 417
+        private const val MENU_BACKGROUND_HIDE_DURATION = 333
+
+        private const val NUM_ALPHA_CHANNELS = 4
+        private const val INDEX_CONTENT_ALPHA = 0
+        private const val INDEX_COLOR_FILTER_ALPHA = 1
+        private const val INDEX_MODAL_ALPHA = 2
+
+        /** Used to hide the app chip for 90:10 flex split. */
+        private const val INDEX_MINIMUM_RATIO_ALPHA = 3
+
+        private const val INDEX_SPLIT_TRANSLATION = 0
+        private const val INDEX_MENU_TRANSLATION = 1
+        private const val INDEX_COUNT_TRANSLATION = 2
+    }
+}