Merge "Creating provider for test gesture dimensions." into ub-launcher3-master
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index 6c50950..ff3dcef9 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -24,5 +24,15 @@
         android:id="@+id/recent_task_recycler_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:scrollbars="none"/>
+        android:scrollbars="none"
+        android:visibility="gone"/>
+    <TextView
+        android:id="@+id/recent_task_empty_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="@string/recents_empty_message"
+        android:textColor="@android:color/white"
+        android:textSize="25sp"
+        android:visibility="gone"/>
 </com.android.quickstep.views.IconRecentsView>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index 90940c4..ee67d49 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -19,12 +19,24 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal">
-    <ImageView
+    <FrameLayout
         android:id="@+id/task_icon_and_thumbnail"
-        android:layout_width="@dimen/task_thumbnail_icon_size"
-        android:layout_height="@dimen/task_thumbnail_icon_size"
+        android:layout_width="@dimen/task_item_height"
+        android:layout_height="@dimen/task_item_height"
         android:layout_gravity="center_vertical"
-        android:layout_marginHorizontal="8dp"/>
+        android:layout_marginHorizontal="8dp"
+        android:layout_marginVertical="@dimen/task_item_half_vert_margin">
+        <ImageView
+            android:id="@+id/task_thumbnail"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="top|start"/>
+        <ImageView
+            android:id="@+id/task_icon"
+            android:layout_width="@dimen/task_icon_size"
+            android:layout_height="@dimen/task_icon_size"
+            android:layout_gravity="bottom|end"/>
+    </FrameLayout>
     <TextView
         android:id="@+id/task_label"
         android:layout_width="wrap_content"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
new file mode 100644
index 0000000..28cc1eb
--- /dev/null
+++ b/go/quickstep/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+    <dimen name="task_item_height">60dp</dimen>
+    <dimen name="task_item_half_vert_margin">8dp</dimen>
+    <dimen name="task_icon_size">36dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
index abb9242..bba08a4 100644
--- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -26,7 +26,6 @@
 
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.IconRecentsView;
 
@@ -110,11 +109,6 @@
     }
 
     @Override
-    public AlphaProperty getAlphaProperty(RecentsActivity activity) {
-        return activity.getDragLayer().getAlphaProperty(0);
-    }
-
-    @Override
     public int getContainerType() {
         return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
     }
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 76685f3..d55d2e5 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -22,9 +22,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.views.IconRecentsView;
 
 import java.util.function.BiPredicate;
@@ -94,11 +92,6 @@
     }
 
     @Override
-    public AlphaProperty getAlphaProperty(Launcher activity) {
-        return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
-    }
-
-    @Override
     public int getContainerType() {
         final Launcher launcher = getVisibleLauncher();
         return launcher != null ? launcher.getStateManager().getState().containerType
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 99446d0..4f3d1e4 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -25,7 +25,8 @@
 import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
 
-import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
  * appropriate {@link Task} from the recents task list.
@@ -56,7 +57,7 @@
 
     @Override
     public void onBindViewHolder(TaskHolder holder, int position) {
-        ArrayList<Task> tasks = mLoader.getCurrentTaskList();
+        List<Task> tasks = mLoader.getCurrentTaskList();
         if (position >= tasks.size()) {
             // Task list has updated.
             return;
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 67e8ece..8d5e4d5 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -45,6 +45,7 @@
         mTask = task;
         mTaskItemView.setLabel(task.titleDescription);
         mTaskItemView.setIcon(task.icon);
+        mTaskItemView.setThumbnail(task.thumbnail.thumbnail);
     }
 
     /**
diff --git a/go/quickstep/src/com/android/quickstep/TaskInputController.java b/go/quickstep/src/com/android/quickstep/TaskInputController.java
index 66c4496..d97ac8d 100644
--- a/go/quickstep/src/com/android/quickstep/TaskInputController.java
+++ b/go/quickstep/src/com/android/quickstep/TaskInputController.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 /**
@@ -22,9 +23,11 @@
  */
 public final class TaskInputController {
 
-    TaskAdapter mAdapter;
+    private final TaskListLoader mLoader;
+    private final TaskAdapter mAdapter;
 
-    public TaskInputController(TaskAdapter adapter) {
+    public TaskInputController(TaskListLoader loader,TaskAdapter adapter) {
+        mLoader = loader;
         mAdapter = adapter;
     }
 
@@ -39,7 +42,14 @@
                 null /* options */, null /* resultCallback */, null /* resultCallbackHandler */);
     }
 
-    // TODO: Implement swipe to delete and notify adapter that data has updated
+    public void onTaskSwiped(TaskHolder viewHolder) {
+        int position = viewHolder.getAdapterPosition();
+        Task task = viewHolder.getTask();
+        ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+        mLoader.removeTask(task);
+        mAdapter.notifyItemRemoved(position);
+    }
 
     // TODO: Implement "Clear all" and notify adapter that data has updated
+
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index c798cef..e6d1a22 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -24,6 +24,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
@@ -45,13 +46,13 @@
 
     /**
      * Returns the current task list as of the last completed load (see
-     * {@link #loadTaskList}). This list of tasks is guaranteed to always have all its task
-     * content loaded.
+     * {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have
+     * all its task content loaded.
      *
      * @return the current list of tasks w/ all content loaded
      */
-    public ArrayList<Task> getCurrentTaskList() {
-        return mTaskList;
+    public List<Task> getCurrentTaskList() {
+        return Collections.unmodifiableList(mTaskList);
     }
 
     /**
@@ -85,6 +86,13 @@
     }
 
     /**
+     * Removes the task from the current task list.
+     */
+    void removeTask(Task task) {
+        mTaskList.remove(task);
+    }
+
+    /**
      * Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
      * that isn't cached, load the content asynchronously in the background.
      *
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
new file mode 100644
index 0000000..2a53917
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package com.android.quickstep;
+
+import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Callback for swipe input on {@link TaskHolder} views in the recents view.
+ */
+public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
+
+    private final TaskInputController mTaskInputController;
+
+    public TaskSwipeCallback(TaskInputController inputController) {
+        super(0 /* dragDirs */, RIGHT);
+        mTaskInputController = inputController;
+    }
+
+    @Override
+    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
+            ViewHolder target) {
+        return false;
+    }
+
+    @Override
+    public void onSwiped(ViewHolder viewHolder, int direction) {
+        if (direction == RIGHT) {
+            mTaskInputController.onTaskSwiped((TaskHolder) viewHolder);
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e8a915f..8c7177b 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -17,19 +17,25 @@
 
 import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.view.View;
 import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
+import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
 
 import com.android.launcher3.R;
 import com.android.quickstep.TaskAdapter;
 import com.android.quickstep.TaskInputController;
 import com.android.quickstep.TaskListLoader;
+import com.android.quickstep.TaskSwipeCallback;
 
 /**
  * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
@@ -68,6 +74,7 @@
                     return ALPHA.get(view);
                 }
             };
+    private static final long CROSSFADE_DURATION = 300;
 
     /**
      * A ratio representing the view's relative placement within its padded space. For example, 0
@@ -76,29 +83,48 @@
     @ViewDebug.ExportedProperty(category = "launcher")
 
     private final Context mContext;
+    private final TaskListLoader mTaskLoader;
+    private final TaskAdapter mTaskAdapter;
+    private final TaskInputController mTaskInputController;
 
     private float mTranslationYFactor;
-    private TaskAdapter mTaskAdapter;
     private RecyclerView mTaskRecyclerView;
-    private TaskInputController mTaskInputController;
-    private TaskListLoader mTaskLoader;
+    private View mEmptyView;
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
+        mTaskLoader = new TaskListLoader(mContext);
+        mTaskAdapter = new TaskAdapter(mTaskLoader);
+        mTaskInputController = new TaskInputController(mTaskLoader, mTaskAdapter);
+        mTaskAdapter.setInputController(mTaskInputController);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mTaskLoader = new TaskListLoader(mContext);
-        mTaskAdapter = new TaskAdapter(mTaskLoader);
-        mTaskInputController = new TaskInputController(mTaskAdapter);
-        mTaskAdapter.setInputController(mTaskInputController);
-        mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
-        mTaskRecyclerView.setAdapter(mTaskAdapter);
-        mTaskRecyclerView.setLayoutManager(
-                new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+        if (mTaskRecyclerView == null) {
+            mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
+            mTaskRecyclerView.setAdapter(mTaskAdapter);
+            mTaskRecyclerView.setLayoutManager(
+                    new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
+            ItemTouchHelper helper = new ItemTouchHelper(
+                    new TaskSwipeCallback(mTaskInputController));
+            helper.attachToRecyclerView(mTaskRecyclerView);
+
+            mEmptyView = findViewById(R.id.recent_task_empty_view);
+            mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
+                @Override
+                public void onChanged() {
+                    updateContentViewVisibility();
+                }
+
+                @Override
+                public void onItemRangeRemoved(int positionStart, int itemCount) {
+                    updateContentViewVisibility();
+                }
+            });
+        }
     }
 
     /**
@@ -125,4 +151,44 @@
     private float computeTranslationYForFactor(float translationYFactor) {
         return translationYFactor * (getPaddingBottom() - getPaddingTop());
     }
+
+    /**
+     * Update the content view so that the appropriate view is shown based off the current list
+     * of tasks.
+     */
+    private void updateContentViewVisibility() {
+        int taskListSize = mTaskLoader.getCurrentTaskList().size();
+        if (mEmptyView.getVisibility() != VISIBLE && taskListSize == 0) {
+            crossfadeViews(mEmptyView, mTaskRecyclerView);
+            // TODO: Go to home.
+        }
+        if (mTaskRecyclerView.getVisibility() != VISIBLE && taskListSize > 0) {
+            crossfadeViews(mTaskRecyclerView, mEmptyView);
+        }
+    }
+
+    /**
+     * Animate views so that one view fades in while the other fades out.
+     *
+     * @param fadeInView view that should fade in
+     * @param fadeOutView view that should fade out
+     */
+    private void crossfadeViews(View fadeInView, View fadeOutView) {
+        fadeInView.setVisibility(VISIBLE);
+        fadeInView.setAlpha(0f);
+        fadeInView.animate()
+                .alpha(1f)
+                .setDuration(CROSSFADE_DURATION)
+                .setListener(null);
+
+        fadeOutView.animate()
+                .alpha(0f)
+                .setDuration(CROSSFADE_DURATION)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        fadeOutView.setVisibility(GONE);
+                    }
+                });
+    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index ce3947d..3818965 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.views;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageView;
@@ -31,6 +32,7 @@
 
     private TextView mLabelView;
     private ImageView mIconView;
+    private ImageView mThumbnailView;
 
     public TaskItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -40,7 +42,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mLabelView = findViewById(R.id.task_label);
-        mIconView = findViewById(R.id.task_icon_and_thumbnail);
+        mThumbnailView = findViewById(R.id.task_thumbnail);
+        mIconView = findViewById(R.id.task_icon);
     }
 
     /**
@@ -58,7 +61,19 @@
      * @param icon task icon
      */
     public void setIcon(Drawable icon) {
+        // TODO: Scale the icon up based off the padding on the side
+        // The icon proper is actually smaller than the drawable and has "padding" on the side for
+        // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
+        // view if we want the icon to be flush with the bottom of the thumbnail.
         mIconView.setImageDrawable(icon);
-        // TODO: Add in combination drawable for icon + thumbnail
+    }
+
+    /**
+     * Set the task thumbnail for the task.
+     *
+     * @param thumbnail task thumbnail for the task
+     */
+    public void setThumbnail(Bitmap thumbnail) {
+        mThumbnailView.setImageBitmap(thumbnail);
     }
 }
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 0992849..62d0500 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -39,7 +39,7 @@
         <service
             android:name="com.android.quickstep.TouchInteractionService"
             android:permission="android.permission.STATUS_BAR_SERVICE"
-            android:directBootAware="false" >
+            android:directBootAware="true" >
             <intent-filter>
                 <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
             </intent-filter>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
index 3602508..f4ea9f9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
 import android.graphics.Rect;
@@ -30,8 +31,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
@@ -139,6 +140,8 @@
     public void onBackPressed(Launcher launcher) {
         TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
         if (taskView != null) {
+            launcher.getUserEventDispatcher().logActionCommand(Action.Command.BACK,
+                    newContainerTarget(ContainerType.OVERVIEW));
             taskView.launchTask(true);
         } else {
             super.onBackPressed(launcher);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index 1ed1353..d61ed72 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -182,11 +182,6 @@
     }
 
     @Override
-    public AlphaProperty getAlphaProperty(RecentsActivity activity) {
-        return activity.getDragLayer().getAlphaProperty(0);
-    }
-
-    @Override
     public int getContainerType() {
         return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index eee0344..e95e2a0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -394,11 +394,6 @@
     }
 
     @Override
-    public AlphaProperty getAlphaProperty(Launcher activity) {
-        return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
-    }
-
-    @Override
     public int getContainerType() {
         final Launcher launcher = getVisibleLauncher();
         return launcher != null ? launcher.getStateManager().getState().containerType
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
index 9fceab4..357c9fc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
@@ -18,6 +18,8 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.launcher3.config.FeatureFlags;
+
 import java.util.StringJoiner;
 import java.util.function.Consumer;
 
@@ -101,6 +103,9 @@
      * The callback is only run once.
      */
     public void addCallback(int stateMask, Runnable callback) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && mCallbacks.get(stateMask) != null) {
+            throw new IllegalStateException("Multiple callbacks on same state");
+        }
         mCallbacks.put(stateMask, callback);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 7bea593..00dbdff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -40,7 +40,6 @@
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
@@ -78,8 +77,6 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.FloatingIconView;
@@ -113,7 +110,7 @@
         implements SwipeAnimationListener, OnApplyWindowInsetsListener {
     private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
 
-    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[19] : null;
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
 
     private static int getFlagForIndex(int index, String name) {
         if (DEBUG_STATES) {
@@ -126,47 +123,44 @@
     private static final int STATE_LAUNCHER_PRESENT = getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
     private static final int STATE_LAUNCHER_STARTED = getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
     private static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
-    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE =
-            getFlagForIndex(3, "STATE_ACTIVITY_MULTIPLIER_COMPLETE");
 
     // Internal initialization states
     private static final int STATE_APP_CONTROLLER_RECEIVED =
-            getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
+            getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
 
     // Interaction finish states
     private static final int STATE_SCALED_CONTROLLER_HOME =
-            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
+            getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
     private static final int STATE_SCALED_CONTROLLER_RECENTS =
-            getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
+            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
     private static final int STATE_SCALED_CONTROLLER_LAST_TASK =
-            getFlagForIndex(7, "STATE_SCALED_CONTROLLER_LAST_TASK");
+            getFlagForIndex(6, "STATE_SCALED_CONTROLLER_LAST_TASK");
 
     private static final int STATE_HANDLER_INVALIDATED =
-            getFlagForIndex(8, "STATE_HANDLER_INVALIDATED");
+            getFlagForIndex(7, "STATE_HANDLER_INVALIDATED");
     private static final int STATE_GESTURE_STARTED =
-            getFlagForIndex(9, "STATE_GESTURE_STARTED");
+            getFlagForIndex(8, "STATE_GESTURE_STARTED");
     private static final int STATE_GESTURE_CANCELLED =
-            getFlagForIndex(10, "STATE_GESTURE_CANCELLED");
+            getFlagForIndex(9, "STATE_GESTURE_CANCELLED");
     private static final int STATE_GESTURE_COMPLETED =
-            getFlagForIndex(11, "STATE_GESTURE_COMPLETED");
+            getFlagForIndex(10, "STATE_GESTURE_COMPLETED");
 
     private static final int STATE_CAPTURE_SCREENSHOT =
-            getFlagForIndex(12, "STATE_CAPTURE_SCREENSHOT");
+            getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT");
     private static final int STATE_SCREENSHOT_CAPTURED =
-            getFlagForIndex(13, "STATE_SCREENSHOT_CAPTURED");
+            getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED");
     private static final int STATE_SCREENSHOT_VIEW_SHOWN =
-            getFlagForIndex(14, "STATE_SCREENSHOT_VIEW_SHOWN");
+            getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN");
 
     private static final int STATE_RESUME_LAST_TASK =
-            getFlagForIndex(15, "STATE_RESUME_LAST_TASK");
+            getFlagForIndex(14, "STATE_RESUME_LAST_TASK");
     private static final int STATE_START_NEW_TASK =
-            getFlagForIndex(16, "STATE_START_NEW_TASK");
+            getFlagForIndex(15, "STATE_START_NEW_TASK");
     private static final int STATE_CURRENT_TASK_FINISHED =
-            getFlagForIndex(17, "STATE_CURRENT_TASK_FINISHED");
+            getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED");
 
     private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
-            | STATE_LAUNCHER_STARTED;
+            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
 
     // For debugging, keep in sync with above states
 
@@ -226,7 +220,6 @@
     // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
     // visible.
     private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
-    private boolean mDispatchedDownEvent;
     private boolean mContinuingLastGesture;
     // To avoid UI jump when gesture is started, we offset the animation by the threshold.
     private float mShiftAtGestureStart = 0;
@@ -238,7 +231,6 @@
     private final ActivityInitListener mActivityInitListener;
 
     private final int mRunningTaskId;
-    private final RunningTaskInfo mRunningTaskInfo;
     private ThumbnailData mTaskSnapshot;
 
     private MultiStateCallback mStateCallback;
@@ -265,7 +257,6 @@
             long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
             InputConsumerController inputConsumer) {
         mContext = context;
-        mRunningTaskInfo = runningTaskInfo;
         mRunningTaskId = runningTaskInfo.id;
         mTouchTimeMs = touchTimeMs;
         mActivityControlHelper = controller;
@@ -312,8 +303,7 @@
                 this::startNewTask);
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE
-                        | STATE_CAPTURE_SCREENSHOT,
+                        | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
                 this::switchToScreenshot);
 
         mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
@@ -322,13 +312,13 @@
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_COMPLETED
                         | STATE_SCALED_CONTROLLER_HOME | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
+                        | STATE_LAUNCHER_DRAWN,
                 this::finishCurrentTransitionToHome);
         mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
                 this::reset);
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
+                        | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
                         | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
                         | STATE_GESTURE_STARTED,
                 this::setupLauncherUiAfterSwipeUpAnimation);
@@ -440,11 +430,10 @@
         AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
 
         if (mWasLauncherAlreadyVisible) {
-            mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
+            mStateCallback.setState(STATE_LAUNCHER_DRAWN);
         } else {
             TraceHelper.beginSection("WTS-init");
             View dragLayer = activity.getDragLayer();
-            mActivityControlHelper.getAlphaProperty(activity).setValue(0);
             dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
 
                 @Override
@@ -477,25 +466,6 @@
     }
 
     private void launcherFrameDrawn() {
-        AlphaProperty property = mActivityControlHelper.getAlphaProperty(mActivity);
-        if (property.getValue() < 1) {
-            if (mGestureStarted) {
-                final MultiStateCallback callback = mStateCallback;
-                ObjectAnimator animator = ObjectAnimator.ofFloat(
-                        property, MultiValueAlpha.VALUE, 1);
-                animator.setDuration(getFadeInDuration()).addListener(
-                        new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                callback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
-                            }
-                        });
-                animator.start();
-            } else {
-                property.setValue(1);
-                mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
-            }
-        }
         mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
     }
 
@@ -1074,7 +1044,6 @@
 
     private void invalidateHandlerWithLauncher() {
         mLauncherTransitionController = null;
-        mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
 
         mRecentsView.setEnableFreeScroll(true);
         mRecentsView.setRunningTaskIconScaledDown(false);
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 2583ffb..252a957 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
@@ -1578,7 +1578,9 @@
     }
 
     public void updateLiveTileIcon(Drawable icon) {
-        mLiveTileOverlay.setIcon(icon);
+        if (mLiveTileOverlay != null) {
+            mLiveTileOverlay.setIcon(icon);
+        }
     }
 
     public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index eccef04..75be2e4 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -85,8 +84,6 @@
         return true;
     }
 
-    AlphaProperty getAlphaProperty(T activity);
-
     /**
      * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
      */
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index fe789aa..6031dcd 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -37,6 +37,9 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
+
+    static final int STRESS_REPEAT_COUNT = 10;
+
     @Override
     @Before
     public void setUp() throws Exception {
@@ -79,4 +82,28 @@
         closeLauncherActivity();
         mLauncher.getBackground().switchToOverview();
     }
+
+    @Test
+    @QuickstepOnOff
+    public void testStressPressHome() {
+        for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+            // Destroy Launcher activity.
+            closeLauncherActivity();
+
+            // The test action.
+            mLauncher.pressHome();
+        }
+    }
+
+    @Test
+    @QuickstepOnOff
+    public void testStressSwipeToOverview() {
+        for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+            // Destroy Launcher activity.
+            closeLauncherActivity();
+
+            // The test action.
+            mLauncher.getBackground().switchToOverview();
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e699500..7036639 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -571,9 +571,11 @@
         final boolean isFolderIcon = v instanceof FolderIcon;
         final Rect rect = new Rect();
 
+        // Deep shortcut views have their icon drawn in a separate view.
         final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
-        if (fromDeepShortcutView) {
-            // Deep shortcut views have their icon drawn in a separate view.
+        if (v instanceof DeepShortcutView) {
+            dragLayer.getDescendantRectRelativeToSelf(((DeepShortcutView) v).getIconView(), rect);
+        } else if (fromDeepShortcutView) {
             DeepShortcutView view = (DeepShortcutView) v.getParent();
             dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
         } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 09eb6d9..9427675 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3084,6 +3084,10 @@
     }
 
     private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
+        // TODO(b/128460496) Potential race condition where layout is not yet loaded
+        if (layout == null) {
+            return false;
+        }
         ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
         // map over all the shortcuts on the workspace
         final int itemCount = container.getChildCount();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index f005ce7..6950a1f 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -63,8 +63,7 @@
     public static final int ALPHA_INDEX_OVERLAY = 0;
     public static final int ALPHA_INDEX_LAUNCHER_LOAD = 1;
     public static final int ALPHA_INDEX_TRANSITIONS = 2;
-    public static final int ALPHA_INDEX_SWIPE_UP = 3;
-    private static final int ALPHA_CHANNEL_COUNT = 4;
+    private static final int ALPHA_CHANNEL_COUNT = 3;
 
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 5e41bb7..0c5a1fc 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -19,11 +19,8 @@
 import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.ColorFilter;
 import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.Path;
-import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -49,11 +46,15 @@
 
     private final Drawable mBadge;
     private final Path mMask;
+    private final ConstantState mConstantState;
 
     private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
         super(bg, fg);
         mBadge = badge;
         mMask = mask;
+
+        mConstantState = new MyConstantState(bg.getConstantState(), fg.getConstantState(),
+                badge.getConstantState(), mask);
     }
 
     @Override
@@ -134,4 +135,35 @@
 
         return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
     }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mConstantState;
+    }
+
+    private static class MyConstantState extends ConstantState {
+        private final ConstantState mBg;
+        private final ConstantState mFg;
+        private final ConstantState mBadge;
+        private final Path mMask;
+
+        MyConstantState(ConstantState bg, ConstantState fg, ConstantState badge, Path mask) {
+            mBg = bg;
+            mFg = fg;
+            mBadge = badge;
+            mMask = mask;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new FolderAdaptiveIcon(mBg.newDrawable(), mFg.newDrawable(),
+                    mBadge.newDrawable(), mMask);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mBg.getChangingConfigurations() & mFg.getChangingConfigurations()
+                    & mBadge.getChangingConfigurations();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
index 52d45bb..f8583b8 100644
--- a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
+++ b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
@@ -32,10 +32,14 @@
     private float mShiftX;
     private float mShiftY;
 
+    private final ConstantState mConstantState;
+
     public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
         mBitmap = bitmap;
         mShiftX = shiftX;
         mShiftY = shiftY;
+
+        mConstantState = new MyConstantState(mBitmap, mShiftX, mShiftY);
     }
 
     public float getShiftX() {
@@ -71,4 +75,31 @@
     public int getOpacity() {
         return PixelFormat.TRANSLUCENT;
     }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mConstantState;
+    }
+
+    private static class MyConstantState extends ConstantState {
+        private final Bitmap mBitmap;
+        private float mShiftX;
+        private float mShiftY;
+
+        MyConstantState(Bitmap bitmap, float shiftX, float shiftY) {
+            mBitmap = bitmap;
+            mShiftX = shiftX;
+            mShiftY = shiftY;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new ShiftedBitmapDrawable(mBitmap, mShiftX, mShiftY);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 5163e5f..e5c70da 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.graphics.ShiftedBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.shortcuts.DeepShortcutView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
@@ -67,7 +68,6 @@
     private Runnable mStartRunnable;
     private Runnable mEndRunnable;
 
-    private Drawable mDrawable;
     private int mOriginalHeight;
     private final int mBlurSizeOutline;
 
@@ -191,73 +191,85 @@
     private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs,
             float aspectRatio) {
         final LayoutParams lp = (LayoutParams) getLayoutParams();
-
+        Drawable drawable = null;
         boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
                 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
         if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
             // Similar to DragView, we simply use the BubbleTextView icon here.
-            mDrawable = ((BubbleTextView) v).getIcon();
+            drawable = ((BubbleTextView) v).getIcon();
         }
-        if (v instanceof ImageView && info instanceof SystemShortcut) {
-            mDrawable = ((ImageView) v).getDrawable();
+        if (info instanceof SystemShortcut) {
+            if (v instanceof ImageView) {
+                drawable = ((ImageView) v).getDrawable();
+            } else if (v instanceof DeepShortcutView) {
+                drawable = ((DeepShortcutView) v).getIconView().getBackground();
+            } else {
+                drawable = v.getBackground();
+            }
         }
-        if (mDrawable == null) {
-            mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
+        if (drawable == null) {
+            drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
                     useDrawableAsIs, new Object[1]);
         }
 
-        if (supportsAdaptiveIcons && mDrawable instanceof AdaptiveIconDrawable) {
-            mIsAdaptiveIcon = true;
-
-            AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) mDrawable;
-            Drawable background = adaptiveIcon.getBackground();
-            if (background == null) {
-                background = new ColorDrawable(Color.TRANSPARENT);
-            }
-            mBackground = background;
-            Drawable foreground = adaptiveIcon.getForeground();
-            if (foreground == null) {
-                foreground = new ColorDrawable(Color.TRANSPARENT);
-            }
-            mForeground = foreground;
-
-            int offset = getOffsetForAdaptiveIconBounds();
-            mFinalDrawableBounds.set(offset, offset, lp.width - offset, mOriginalHeight - offset);
-            if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
-                ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
-                ((FolderIcon) v).getPreviewBounds(sTmpRect);
-                sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
-                sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
-            }
-            mForeground.setBounds(mFinalDrawableBounds);
-            mBackground.setBounds(mFinalDrawableBounds);
-
-            int blurMargin = mBlurSizeOutline / 2;
-            mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
-                    mOriginalHeight - blurMargin);
-
-            if (aspectRatio > 0) {
-                lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
-                layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
-                        + lp.height);
-            }
-            mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
-            setBackgroundDrawableBounds(mBgDrawableStartScale);
-
-            // Set up outline
-            mOutline.set(0, 0, lp.width, lp.height);
-            setOutlineProvider(new ViewOutlineProvider() {
-                @Override
-                public void getOutline(View view, Outline outline) {
-                    outline.setRoundRect(mOutline, mTaskCornerRadius);
-                }
-            });
-            setClipToOutline(true);
-        } else {
-            setBackground(mDrawable);
-        }
+        Drawable finalDrawable = drawable == null ? null
+                : drawable.getConstantState().newDrawable();
+        boolean isAdaptiveIcon = supportsAdaptiveIcons
+                && finalDrawable instanceof AdaptiveIconDrawable;
+        int iconOffset = getOffsetForIconBounds(finalDrawable);
 
         new Handler(Looper.getMainLooper()).post(() -> {
+            if (isAdaptiveIcon) {
+                mIsAdaptiveIcon = true;
+
+                AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) finalDrawable;
+                Drawable background = adaptiveIcon.getBackground();
+                if (background == null) {
+                    background = new ColorDrawable(Color.TRANSPARENT);
+                }
+                mBackground = background;
+                Drawable foreground = adaptiveIcon.getForeground();
+                if (foreground == null) {
+                    foreground = new ColorDrawable(Color.TRANSPARENT);
+                }
+                mForeground = foreground;
+
+                mFinalDrawableBounds.set(iconOffset, iconOffset, lp.width -
+                        iconOffset, mOriginalHeight - iconOffset);
+                if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
+                    ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
+                    ((FolderIcon) v).getPreviewBounds(sTmpRect);
+                    sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
+                    sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
+                }
+                mForeground.setBounds(mFinalDrawableBounds);
+                mBackground.setBounds(mFinalDrawableBounds);
+
+                int blurMargin = mBlurSizeOutline / 2;
+                mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
+                        mOriginalHeight - blurMargin);
+
+                if (aspectRatio > 0) {
+                    lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+                    layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+                            + lp.height);
+                }
+                mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
+                setBackgroundDrawableBounds(mBgDrawableStartScale);
+
+                // Set up outline
+                mOutline.set(0, 0, lp.width, lp.height);
+                setOutlineProvider(new ViewOutlineProvider() {
+                    @Override
+                    public void getOutline(View view, Outline outline) {
+                        outline.setRoundRect(mOutline, mTaskCornerRadius);
+                    }
+                });
+                setClipToOutline(true);
+            } else {
+                setBackground(finalDrawable);
+            }
+
             invalidate();
             invalidateOutline();
         });
@@ -272,9 +284,10 @@
         mBackground.setBounds(mBgDrawableBounds);
     }
 
-    private int getOffsetForAdaptiveIconBounds() {
+    @WorkerThread
+    private int getOffsetForIconBounds(Drawable drawable) {
         if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O ||
-                !(mDrawable instanceof AdaptiveIconDrawable)) {
+                !(drawable instanceof AdaptiveIconDrawable)) {
             return 0;
         }
 
@@ -283,7 +296,7 @@
         bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
 
         try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) {
-            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(mDrawable, null));
+            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null));
         }
 
         bounds.inset(