Optimizing some layouts in taskview

> Recycling DigitalWellBeingToast so that the view is not inflated everytime
> Simplifying DigitalWellBeingToast to use a single text view
> Adding support for footers in taskView without creating additional layout

Bug: 122345781
Change-Id: Ia889819b93eb8644532ea95c6767554874d5e2d1
diff --git a/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml b/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml
deleted file mode 100644
index be3f17a..0000000
--- a/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<com.android.quickstep.hints.ProactiveHintsContainer
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal|bottom"
-    android:gravity="center_horizontal">
-</com.android.quickstep.hints.ProactiveHintsContainer>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 5aab944..7fac813 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -18,9 +18,11 @@
 
 import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
 
+import static com.android.launcher3.Utilities.prefixTextWithIcon;
+
+import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.AppUsageLimit;
@@ -28,16 +30,16 @@
 import android.icu.text.MeasureFormat.FormatWidth;
 import android.icu.util.Measure;
 import android.icu.util.MeasureUnit;
+import android.os.Build;
 import android.os.UserHandle;
-import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.annotation.StringRes;
 
 import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -46,45 +48,72 @@
 import java.time.Duration;
 import java.util.Locale;
 
-public final class DigitalWellBeingToast extends LinearLayout {
+@TargetApi(Build.VERSION_CODES.Q)
+public final class DigitalWellBeingToast {
     static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
     static final int MINUTE_MS = 60000;
-    private final LauncherApps mLauncherApps;
-
-    public interface InitializeCallback {
-        void call(String contentDescription);
-    }
 
     private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
 
+    private final BaseDraggingActivity mActivity;
+    private final TaskView mTaskView;
+    private final LauncherApps mLauncherApps;
+
     private Task mTask;
-    private TextView mText;
+    private boolean mHasLimit;
+    private long mAppRemainingTimeMs;
 
-    public DigitalWellBeingToast(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutDirection(Utilities.isRtl(getResources()) ?
-                View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        setOnClickListener((view) -> openAppUsageSettings());
-        mLauncherApps = context.getSystemService(LauncherApps.class);
+    public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
+        mActivity = activity;
+        mTaskView = taskView;
+        mLauncherApps = activity.getSystemService(LauncherApps.class);
     }
 
-    public TextView getTextView() {
-        return mText;
+    private void setTaskFooter(View view) {
+        View oldFooter = mTaskView.setFooter(TaskView.INDEX_DIGITAL_WELLBEING_TOAST, view);
+        if (oldFooter != null) {
+            oldFooter.setOnClickListener(null);
+            mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, oldFooter);
+        }
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mText = findViewById(R.id.digital_well_being_remaining_time);
+    private void setNoLimit() {
+        mHasLimit = false;
+        mTaskView.setContentDescription(mTask.titleDescription);
+        setTaskFooter(null);
+        mAppRemainingTimeMs = 0;
     }
 
-    public void initialize(Task task, InitializeCallback callback) {
+    private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
+        mAppRemainingTimeMs = appRemainingTimeMs;
+        mHasLimit = true;
+        TextView toast = mActivity.getViewCache().getView(R.layout.digital_wellbeing_toast,
+                mActivity, mTaskView);
+        toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText()));
+        toast.setOnClickListener(this::openAppUsageSettings);
+        setTaskFooter(toast);
+
+        mTaskView.setContentDescription(
+                getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
+        RecentsView rv = mTaskView.getRecentsView();
+        if (rv != null) {
+            rv.onDigitalWellbeingToastShown();
+        }
+    }
+
+    public String getText() {
+        return getText(mAppRemainingTimeMs);
+    }
+
+    public boolean hasLimit() {
+        return mHasLimit;
+    }
+
+    public void initialize(Task task) {
         mTask = task;
 
         if (task.key.userId != UserHandle.myUserId()) {
-            setVisibility(GONE);
-            callback.call(task.titleDescription);
+            setNoLimit();
             return;
         }
 
@@ -98,16 +127,12 @@
             final long appRemainingTimeMs =
                     usageLimit != null ? usageLimit.getUsageRemaining() : -1;
 
-            post(() -> {
+            mTaskView.post(() -> {
                 if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
-                    setVisibility(GONE);
+                    setNoLimit();
                 } else {
-                    setVisibility(VISIBLE);
-                    mText.setText(getText(appRemainingTimeMs));
+                    setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
                 }
-
-                callback.call(getContentDescriptionForTask(
-                        task, appUsageLimitTimeMs, appRemainingTimeMs));
             });
         });
     }
@@ -146,7 +171,7 @@
 
         // Use a specific string for usage less than one minute but non-zero.
         if (duration.compareTo(Duration.ZERO) > 0) {
-            return getResources().getString(durationLessThanOneMinuteStringId);
+            return mActivity.getString(durationLessThanOneMinuteStringId);
         }
 
         // Otherwise, return 0-minute string.
@@ -176,24 +201,24 @@
     }
 
     private String getText(long remainingTime) {
-        return getResources().getString(
+        return mActivity.getString(
                 R.string.time_left_for_app,
                 getRoundedUpToMinuteReadableDuration(remainingTime));
     }
 
-    public void openAppUsageSettings() {
+    public void openAppUsageSettings(View view) {
         final Intent intent = new Intent(OPEN_APP_USAGE_SETTINGS_TEMPLATE)
                 .putExtra(Intent.EXTRA_PACKAGE_NAME,
                         mTask.getTopComponent().getPackageName()).addFlags(
                         Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         try {
-            final BaseActivity activity = BaseActivity.fromContext(getContext());
+            final BaseActivity activity = BaseActivity.fromContext(view.getContext());
             final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(
-                    this, 0, 0,
-                    getWidth(), getHeight());
+                    view, 0, 0,
+                    view.getWidth(), view.getHeight());
             activity.startActivity(intent, options.toBundle());
             activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
-                    LauncherLogProto.ControlType.APP_USAGE_SETTINGS, this);
+                    LauncherLogProto.ControlType.APP_USAGE_SETTINGS, view);
         } catch (ActivityNotFoundException e) {
             Log.e(TAG, "Failed to open app usage settings for task "
                     + mTask.getTopComponent().getPackageName(), e);
@@ -203,7 +228,7 @@
     private String getContentDescriptionForTask(
             Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
         return appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0 ?
-                getResources().getString(
+                mActivity.getString(
                         R.string.task_contents_description_with_remaining_time,
                         task.titleDescription,
                         getText(appRemainingTimeMs)) :
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index f66e401..9058e7e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -351,6 +351,9 @@
                 .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
         setWillNotDraw(false);
         updateEmptyMessage();
+
+        // Initialize quickstep specific cache params here, as this is constructed only once
+        mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
     }
 
     public OverScroller getScroller() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 694d501..e7e4189 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -27,6 +27,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.res.Resources;
@@ -39,6 +41,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -145,14 +148,12 @@
             };
 
     private final TaskOutlineProvider mOutlineProvider;
-    private final FooterOutlineProvider mFooterOutlineProvider;
 
     private Task mTask;
     private TaskThumbnailView mSnapshotView;
     private TaskMenuView mMenuView;
     private IconView mIconView;
-    private View mTaskFooterContainer;
-    private DigitalWellBeingToast mDigitalWellBeingToast;
+    private final DigitalWellBeingToast mDigitalWellBeingToast;
     private float mCurveScale;
     private float mFullscreenProgress;
     private final FullscreenDrawParams mCurrentFullscreenParams;
@@ -171,6 +172,14 @@
     private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
     private TaskIconCache.IconLoadRequest mIconLoadRequest;
 
+    // Order in which the footers appear. Lower order appear below higher order.
+    public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
+    public static final int INDEX_PROACTIVE_SUGGEST = 1;
+    private final FooterWrapper[] mFooters = new FooterWrapper[2];
+    private float mFooterVerticalOffset = 0;
+    private float mFooterAlpha = 1;
+    private int mStackHeight;
+
     public TaskView(Context context) {
         this(context, null);
     }
@@ -208,8 +217,9 @@
         mCornerRadius = TaskCornerRadius.get(context);
         mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
         mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
+        mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
+
         mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
-        mFooterOutlineProvider = new FooterOutlineProvider(mCurrentFullscreenParams);
         setOutlineProvider(mOutlineProvider);
     }
 
@@ -218,10 +228,6 @@
         super.onFinishInflate();
         mSnapshotView = findViewById(R.id.snapshot);
         mIconView = findViewById(R.id.icon);
-        mDigitalWellBeingToast = findViewById(R.id.digital_well_being_toast);
-        mTaskFooterContainer = findViewById(R.id.task_footer_container);
-        mTaskFooterContainer.setOutlineProvider(mFooterOutlineProvider);
-        mTaskFooterContainer.setClipToOutline(true);
     }
 
     public TaskMenuView getMenuView() {
@@ -357,15 +363,7 @@
                         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
                             getRecentsView().updateLiveTileIcon(task.icon);
                         }
-                        mDigitalWellBeingToast.initialize(
-                                mTask,
-                                contentDescription -> {
-                                    setContentDescription(contentDescription);
-                                    if (mDigitalWellBeingToast.getVisibility() == VISIBLE
-                                            && getRecentsView() != null) {
-                                        getRecentsView().onDigitalWellbeingToastShown();
-                                    }
-                                });
+                        mDigitalWellBeingToast.initialize(mTask);
                     });
         } else {
             mSnapshotView.setThumbnail(null, null);
@@ -424,14 +422,12 @@
         mIconView.setScaleX(scale);
         mIconView.setScaleY(scale);
 
-        int footerVerticalOffset = (int) (mTaskFooterContainer.getHeight() * (1.0f - scale));
-        mTaskFooterContainer.setTranslationY(
-                mCurrentFullscreenParams.mCurrentDrawnInsets.bottom +
-                mCurrentFullscreenParams.mCurrentDrawnInsets.top +
-                footerVerticalOffset);
-        mFooterOutlineProvider.setFullscreenDrawParams(
-                mCurrentFullscreenParams, footerVerticalOffset);
-        mTaskFooterContainer.invalidateOutline();
+        mFooterVerticalOffset = 1.0f - scale;
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.updateFooterOffset();
+            }
+        }
     }
 
     public void setIconScaleAnimStartProgress(float startProgress) {
@@ -505,8 +501,13 @@
         mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
         setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
 
-        float fade = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
-        mTaskFooterContainer.setAlpha(fade);
+        mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.mView.setAlpha(mFooterAlpha);
+            }
+        }
+
         if (mMenuView != null) {
             mMenuView.setPosition(getX() - getRecentsView().getScrollX(), getY());
             mMenuView.setScaleX(getScaleX());
@@ -514,6 +515,56 @@
         }
     }
 
+
+    /**
+     * Sets the footer at the specific index and returns the previously set footer.
+     */
+    public View setFooter(int index, View view) {
+        View oldFooter = null;
+
+        // If the footer are is already collapsed, do not animate entry
+        boolean shouldAnimateEntry = mFooterVerticalOffset <= 0;
+
+        if (mFooters[index] != null) {
+            oldFooter = mFooters[index].mView;
+            mFooters[index].release();
+            removeView(oldFooter);
+
+            // If we are replacing an existing footer, do not animate entry
+            shouldAnimateEntry = false;
+        }
+        if (view != null) {
+            int indexToAdd = getChildCount();
+            for (int i = index - 1; i >= 0; i--) {
+                if (mFooters[i] != null) {
+                    indexToAdd = indexOfChild(mFooters[i].mView);
+                    break;
+                }
+            }
+
+            addView(view, indexToAdd);
+            ((LayoutParams) view.getLayoutParams()).gravity =
+                    Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+            view.setAlpha(mFooterAlpha);
+            mFooters[index] = new FooterWrapper(view);
+            if (shouldAnimateEntry) {
+                mFooters[index].animateEntry();
+            }
+        } else {
+            mFooters[index] = null;
+        }
+
+        mStackHeight = 0;
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.setVerticalShift(mStackHeight);
+                mStackHeight += footer.mExpectedHeight;
+            }
+        }
+
+        return oldFooter;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -523,6 +574,18 @@
             SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
             setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
         }
+
+        mStackHeight = 0;
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                mStackHeight += footer.mView.getHeight();
+            }
+        }
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.updateFooterOffset();
+            }
+        }
     }
 
     public static float getCurveScaleForInterpolation(float linearInterpolation) {
@@ -581,26 +644,74 @@
         }
     }
 
-    private static final class FooterOutlineProvider extends ViewOutlineProvider {
+    private class FooterWrapper extends ViewOutlineProvider {
 
-        private FullscreenDrawParams mFullscreenDrawParams;
-        private int mVerticalOffset;
-        private final Rect mOutlineRect = new Rect();
+        final View mView;
+        final ViewOutlineProvider mOldOutlineProvider;
+        final ViewOutlineProvider mDelegate;
 
-        FooterOutlineProvider(FullscreenDrawParams params) {
-            mFullscreenDrawParams = params;
+        final int mExpectedHeight;
+        final int mOldPaddingBottom;
+
+        int mAnimationOffset = 0;
+        int mEntryAnimationOffset = 0;
+
+        public FooterWrapper(View view) {
+            mView = view;
+            mOldOutlineProvider = view.getOutlineProvider();
+            mDelegate = mOldOutlineProvider == null
+                    ? ViewOutlineProvider.BACKGROUND : mOldOutlineProvider;
+
+            int h = view.getLayoutParams().height;
+            if (h > 0) {
+                mExpectedHeight = h;
+            } else {
+                int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
+                view.measure(m, m);
+                mExpectedHeight = view.getMeasuredHeight();
+            }
+            mOldPaddingBottom = view.getPaddingBottom();
+
+            if (mOldOutlineProvider != null) {
+                view.setOutlineProvider(this);
+                view.setClipToOutline(true);
+            }
         }
 
-        void setFullscreenDrawParams(FullscreenDrawParams params, int verticalOffset) {
-            mFullscreenDrawParams = params;
-            mVerticalOffset = verticalOffset;
+        public void setVerticalShift(int shift) {
+            mView.setPadding(mView.getPaddingLeft(), mView.getPaddingTop(),
+                    mView.getPaddingRight(), mOldPaddingBottom + shift);
         }
 
         @Override
         public void getOutline(View view, Outline outline) {
-            mOutlineRect.set(0, 0, view.getWidth(), view.getHeight());
-            mOutlineRect.offset(0, -mVerticalOffset);
-            outline.setRoundRect(mOutlineRect, mFullscreenDrawParams.mCurrentDrawnCornerRadius);
+            mDelegate.getOutline(view, outline);
+            outline.offset(0, -mAnimationOffset - mEntryAnimationOffset);
+        }
+
+        void updateFooterOffset() {
+            mAnimationOffset = Math.round(mStackHeight * mFooterVerticalOffset);
+            mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset
+                    + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom
+                    + mCurrentFullscreenParams.mCurrentDrawnInsets.top);
+            mView.invalidateOutline();
+        }
+
+        void release() {
+            mView.setOutlineProvider(mOldOutlineProvider);
+            setVerticalShift(0);
+        }
+
+        void animateEntry() {
+            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+            animator.addUpdateListener(anim -> {
+               float factor = 1 - anim.getAnimatedFraction();
+               int totalShift = mExpectedHeight + mView.getPaddingBottom() - mOldPaddingBottom;
+                mEntryAnimationOffset = Math.round(factor * totalShift);
+                updateFooterOffset();
+            });
+            animator.setDuration(100);
+            animator.start();
         }
     }
 
@@ -624,7 +735,7 @@
             }
         }
 
-        if (mDigitalWellBeingToast.getVisibility() == VISIBLE) {
+        if (mDigitalWellBeingToast.hasLimit()) {
             info.addAction(
                     new AccessibilityNodeInfo.AccessibilityAction(
                             R.string.accessibility_app_usage_settings,
@@ -648,7 +759,7 @@
         }
 
         if (action == R.string.accessibility_app_usage_settings) {
-            mDigitalWellBeingToast.openAppUsageSettings();
+            mDigitalWellBeingToast.openAppUsageSettings(this);
             return true;
         }
 
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
new file mode 100644
index 0000000..83594e4
--- /dev/null
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 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.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:background="@drawable/bg_wellbeing_toast"
+    android:fontFamily="sans-serif"
+    android:forceHasOverlappingRendering="false"
+    android:gravity="center"
+    android:importantForAccessibility="noHideDescendants"
+    android:textColor="@android:color/white"
+    android:textSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 6a595a1..60cfa0c 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -25,7 +25,7 @@
         android:id="@+id/snapshot"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/task_thumbnail_top_margin" />
+        android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
 
     <com.android.quickstep.views.IconView
         android:id="@+id/icon"
@@ -33,47 +33,5 @@
         android:layout_height="@dimen/task_thumbnail_icon_size"
         android:layout_gravity="top|center_horizontal"
         android:focusable="false"
-        android:importantForAccessibility="no" />
-
-    <LinearLayout
-        android:id="@+id/task_footer_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:animateLayoutChanges="true"
-        android:forceHasOverlappingRendering="true"
-        android:layout_gravity="bottom|center_horizontal">
-        <FrameLayout
-            android:id="@+id/proactive_suggest_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:visibility="gone"
-            />
-        <com.android.quickstep.views.DigitalWellBeingToast
-            android:id="@+id/digital_well_being_toast"
-            android:layout_width="match_parent"
-            android:layout_height="48dp"
-            android:importantForAccessibility="noHideDescendants"
-            android:background="@drawable/bg_wellbeing_toast"
-            android:gravity="center"
-            android:visibility="gone">
-            <ImageView
-                android:id="@+id/digital_well_being_hourglass"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_marginEnd="8dp"
-                android:src="@drawable/ic_hourglass_top"
-            />
-            <TextView
-                android:id="@+id/digital_well_being_remaining_time"
-                android:layout_width="wrap_content"
-                android:layout_height="24dp"
-                android:fontFamily="sans-serif"
-                android:textSize="14sp"
-                android:textColor="@android:color/white"
-                android:gravity="center_vertical"
-            />
-        </com.android.quickstep.views.DigitalWellBeingToast>
-    </LinearLayout>
+        android:importantForAccessibility="no"/>
 </com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 70f9c90..0c5a6f5 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -51,15 +51,15 @@
             mLauncher.pressHome();
             final DigitalWellBeingToast toast = getToast();
 
-            assertTrue("Toast is not visible", toast.isShown());
-            assertEquals("Toast text: ", "5 minutes left today", toast.getTextView().getText());
+            assertTrue("Toast is not visible", toast.hasLimit());
+            assertEquals("Toast text: ", "5 minutes left today", toast.getText());
 
             // Unset time limit for app.
             runWithShellPermission(
                     () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
 
             mLauncher.pressHome();
-            assertFalse("Toast is visible", getToast().isShown());
+            assertFalse("Toast is visible", getToast().hasLimit());
         } finally {
             runWithShellPermission(
                     () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cc9bda7..7bdbb95 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -69,6 +69,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -562,6 +563,20 @@
         return spanned;
     }
 
+    /**
+     * Prefixes a text with the provided icon
+     */
+    public static CharSequence prefixTextWithIcon(Context context, int iconRes, CharSequence msg) {
+        // Update the hint to contain the icon.
+        // Prefix the original hint with two spaces. The first space gets replaced by the icon
+        // using span. The second space is used for a singe space character between the hint
+        // and the icon.
+        SpannableString spanned = new SpannableString("  " + msg);
+        spanned.setSpan(new TintedDrawableSpan(context, iconRes),
+                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+        return spanned;
+    }
+
     public static SharedPreferences getPrefs(Context context) {
         return context.getSharedPreferences(
                 LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 1ff484b..31fcc8c 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -20,6 +20,7 @@
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
+import static com.android.launcher3.Utilities.prefixTextWithIcon;
 import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.content.Context;
@@ -40,6 +41,7 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
@@ -89,14 +91,7 @@
         mFixedTranslationY = getTranslationY();
         mMarginTopAdjusting = mFixedTranslationY - getPaddingTop();
 
-        // Update the hint to contain the icon.
-        // Prefix the original hint with two spaces. The first space gets replaced by the icon
-        // using span. The second space is used for a singe space character between the hint
-        // and the icon.
-        SpannableString spanned = new SpannableString("  " + getHint());
-        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
-                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
-        setHint(spanned);
+        setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
     }
 
     @Override
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
index d719575..0bfc435 100644
--- a/src/com/android/launcher3/graphics/TintedDrawableSpan.java
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -32,7 +32,7 @@
 
     public TintedDrawableSpan(Context context, int resourceId) {
         super(ALIGN_BOTTOM);
-        mDrawable = context.getDrawable(resourceId);
+        mDrawable = context.getDrawable(resourceId).mutate();
         mOldTint = 0;
         mDrawable.setTint(0);
     }