Merge "Fix NPE of BaseContainerInterface#calculateTaskSizeInternal" into main
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index cc3b30e..46c1332 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -28,7 +28,7 @@
     launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
     launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
 
-    <include layout="@layout/task_thumbnail" />
+    <include layout="@layout/task_thumbnail_deprecated" />
 
     <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
          separately through RecentsView#resetFromSplitSelectionState() -->
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index 87a0f70..708aa3c 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -33,9 +33,9 @@
     launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
     launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
 
-    <include layout="@layout/task_thumbnail"/>
+    <include layout="@layout/task_thumbnail_deprecated"/>
 
-    <include layout="@layout/task_thumbnail"
+    <include layout="@layout/task_thumbnail_deprecated"
         android:id="@+id/bottomright_snapshot" />
 
     <!-- Filtering affects only alpha instead of the visibility since visibility can be altered
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index f1a3d62..34640e6 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -13,8 +13,29 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.TaskThumbnailViewDeprecated
+<com.android.quickstep.task.thumbnail.TaskThumbnailView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/snapshot"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/task_thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        android:visibility="gone"/>
+
+    <com.android.quickstep.task.thumbnail.LiveTileView
+        android:id="@+id/task_thumbnail_live_tile"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"/>
+
+    <View
+        android:id="@+id/task_thumbnail_scrim"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/overview_foreground_scrim_color"
+        android:alpha="0" />
+
+</com.android.quickstep.task.thumbnail.TaskThumbnailView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_thumbnail_deprecated.xml b/quickstep/res/layout/task_thumbnail_deprecated.xml
new file mode 100644
index 0000000..f1a3d62
--- /dev/null
+++ b/quickstep/res/layout/task_thumbnail_deprecated.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2024 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.
+-->
+<com.android.quickstep.views.TaskThumbnailViewDeprecated
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/snapshot"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 8984086..44d8a5c 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -20,6 +20,7 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
 
+import static com.android.launcher3.Flags.enablePredictiveBackGesture;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
@@ -33,6 +34,9 @@
 import android.view.View;
 import android.view.WindowInsetsController;
 import android.view.WindowManager;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -124,6 +128,8 @@
     /** A set of user ids that should be filtered out from the selected widgets. */
     @NonNull
     Set<Integer> mFilteredUserIds = new HashSet<>();
+    @Nullable
+    private WidgetsFullSheet mWidgetSheet;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -148,6 +154,18 @@
         refreshAndBindWidgets();
     }
 
+    @Override
+    protected void registerBackDispatcher() {
+        if (!enablePredictiveBackGesture()) {
+            super.registerBackDispatcher();
+            return;
+        }
+
+        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                new BackAnimationCallback());
+    }
+
     private void parseIntentExtras() {
         mTitle = getIntent().getStringExtra(EXTRA_PICKER_TITLE);
         mDescription = getIntent().getStringExtra(EXTRA_PICKER_DESCRIPTION);
@@ -293,12 +311,12 @@
         MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
     }
 
-    private void openWidgetsSheet() {
+   private void openWidgetsSheet() {
         MAIN_EXECUTOR.execute(() -> {
-            WidgetsFullSheet widgetSheet = WidgetsFullSheet.show(this, true);
-            widgetSheet.mayUpdateTitleAndDescription(mTitle, mDescription);
-            widgetSheet.disableNavBarScrim(true);
-            widgetSheet.addOnCloseListener(this::finish);
+            mWidgetSheet = WidgetsFullSheet.show(this, true);
+            mWidgetSheet.mayUpdateTitleAndDescription(mTitle, mDescription);
+            mWidgetSheet.disableNavBarScrim(true);
+            mWidgetSheet.addOnCloseListener(this::finish);
         });
     }
 
@@ -317,6 +335,51 @@
         }
     }
 
+    /**
+     * Animation callback for different predictive back animation states for the widget picker.
+     */
+    private class BackAnimationCallback implements OnBackAnimationCallback {
+        @Nullable
+        OnBackAnimationCallback mActiveOnBackAnimationCallback;
+
+        @Override
+        public void onBackStarted(@NonNull BackEvent backEvent) {
+            if (mActiveOnBackAnimationCallback != null) {
+                mActiveOnBackAnimationCallback.onBackCancelled();
+            }
+            if (mWidgetSheet != null) {
+                mActiveOnBackAnimationCallback = mWidgetSheet;
+                mActiveOnBackAnimationCallback.onBackStarted(backEvent);
+            }
+        }
+
+        @Override
+        public void onBackInvoked() {
+            if (mActiveOnBackAnimationCallback == null) {
+                return;
+            }
+            mActiveOnBackAnimationCallback.onBackInvoked();
+            mActiveOnBackAnimationCallback = null;
+        }
+
+        @Override
+        public void onBackProgressed(@NonNull BackEvent backEvent) {
+            if (mActiveOnBackAnimationCallback == null) {
+                return;
+            }
+            mActiveOnBackAnimationCallback.onBackProgressed(backEvent);
+        }
+
+        @Override
+        public void onBackCancelled() {
+            if (mActiveOnBackAnimationCallback == null) {
+                return;
+            }
+            mActiveOnBackAnimationCallback.onBackCancelled();
+            mActiveOnBackAnimationCallback = null;
+        }
+    };
+
     private WidgetAcceptabilityVerdict isWidgetAcceptable(WidgetItem widget) {
         final AppWidgetProviderInfo info = widget.widgetInfo;
         if (info == null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 4c55495..fd989b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -46,7 +46,6 @@
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.taskbar.bubbles.animation.BubbleAnimator;
 import com.android.launcher3.util.DisplayController;
-import com.android.wm.shell.Flags;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 
 import java.io.PrintWriter;
@@ -260,10 +259,6 @@
         if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) {
             return;
         }
-        if (!Flags.animateBubbleSizeChange()) {
-            setIconSizeAndPadding(newIconSize, newBubbleBarPadding);
-            return;
-        }
         if (mScalePaddingAnimator != null && mScalePaddingAnimator.isRunning()) {
             mScalePaddingAnimator.cancel();
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 0e26c54..4c468bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -20,6 +20,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Outline;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -47,7 +48,7 @@
 
     private final ImageView mBubbleIcon;
     private final ImageView mAppIcon;
-    private final int mBubbleSize;
+    private int mBubbleSize;
 
     private float mDragTranslationX;
     private float mOffsetX;
@@ -89,8 +90,6 @@
         setLayoutDirection(LAYOUT_DIRECTION_LTR);
 
         LayoutInflater.from(context).inflate(R.layout.bubble_view, this);
-
-        mBubbleSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
         mBubbleIcon = findViewById(R.id.icon_view);
         mAppIcon = findViewById(R.id.app_icon_view);
 
@@ -107,11 +106,21 @@
     }
 
     private void getOutline(Outline outline) {
+        updateBubbleSizeAndDotRender();
         final int normalizedSize = IconNormalizer.getNormalizedCircleSize(mBubbleSize);
         final int inset = (mBubbleSize - normalizedSize) / 2;
         outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
     }
 
+    private void updateBubbleSizeAndDotRender() {
+        int updatedBubbleSize = Math.min(getWidth(), getHeight());
+        if (updatedBubbleSize == mBubbleSize) return;
+        mBubbleSize = updatedBubbleSize;
+        if (mBubble == null || mBubble instanceof BubbleBarOverflow) return;
+        Path dotPath = ((BubbleBarBubble) mBubble).getDotPath();
+        mDotRenderer = new DotRenderer(mBubbleSize, dotPath, DEFAULT_PATH_SIZE);
+    }
+
     /**
      * Set translation-x while this bubble is being dragged.
      * Translation applied to the view is a sum of {@code translationX} and offset defined by
@@ -141,6 +150,12 @@
         applyDragTranslation();
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateBubbleSizeAndDotRender();
+    }
+
     private void applyDragTranslation() {
         setTranslationX(mDragTranslationX + mOffsetX);
     }
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index b3a9199..1f6c02c 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -33,6 +33,7 @@
 import android.text.TextUtils;
 import android.util.SparseArray;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.R;
@@ -48,6 +49,7 @@
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.task.thumbnail.data.TaskIconDataSource;
 import com.android.quickstep.util.TaskKeyLruCache;
 import com.android.quickstep.util.TaskVisualsChangeListener;
 import com.android.systemui.shared.recents.model.Task;
@@ -59,7 +61,7 @@
 /**
  * Manages the caching of task icons and related data.
  */
-public class TaskIconCache implements DisplayInfoChangeListener {
+public class TaskIconCache implements TaskIconDataSource, DisplayInfoChangeListener {
 
     private final Executor mBgExecutor;
 
@@ -102,7 +104,8 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public CancellableTask getIconInBackground(Task task, GetTaskIconCallback callback) {
+    @Override
+    public CancellableTask getIconInBackground(Task task, @NonNull GetTaskIconCallback callback) {
         Preconditions.assertUIThread();
         if (task.icon != null) {
             // Nothing to load, the icon is already loaded
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
index 4d6dfc3..f73db5a 100644
--- a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -16,7 +16,8 @@
 
 package com.android.quickstep.recents.data
 
-import com.android.quickstep.TaskIconCache
+import android.graphics.drawable.Drawable
+import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
 import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
 import com.android.quickstep.util.GroupTask
 import com.android.systemui.shared.recents.model.Task
@@ -38,7 +39,7 @@
 class TasksRepository(
     private val recentsModel: RecentTasksDataSource,
     private val taskThumbnailDataSource: TaskThumbnailDataSource,
-    private val taskIconCache: TaskIconCache,
+    private val taskIconDataSource: TaskIconDataSource,
 ) : RecentTasksRepository {
     private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
     private val _taskData =
@@ -46,10 +47,19 @@
     private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
 
     private val taskData: Flow<List<Task>> =
-        combine(_taskData, getThumbnailQueryResults()) { tasks, results ->
+        combine(_taskData, getThumbnailQueryResults(), getIconQueryResults()) {
+            tasks,
+            thumbnailQueryResults,
+            iconQueryResults ->
             tasks.forEach { task ->
                 // Add retrieved thumbnails + remove unnecessary thumbnails
-                task.thumbnail = results[task.key.id]
+                task.thumbnail = thumbnailQueryResults[task.key.id]
+
+                // TODO(b/352331675) don't load icons for DesktopTaskView
+                // Add retrieved icons + remove unnecessary icons
+                task.icon = iconQueryResults[task.key.id]?.icon
+                task.titleDescription = iconQueryResults[task.key.id]?.contentDescription
+                task.title = iconQueryResults[task.key.id]?.title
             }
             tasks
         }
@@ -79,7 +89,6 @@
                     suspendCancellableCoroutine { continuation ->
                         val cancellableTask =
                             taskThumbnailDataSource.getThumbnailInBackground(task) {
-                                task.thumbnail = it
                                 continuation.resume(it)
                             }
                         continuation.invokeOnCancellation { cancellableTask?.cancel() }
@@ -109,6 +118,59 @@
             }
         }
     }
+
+    /** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
+    private fun getIconDataRequest(task: Task): IconDataRequest =
+        flow {
+                emit(task.key.id to task.getTaskIconQueryResponse())
+                val iconDataResponse: TaskIconQueryResponse? =
+                    suspendCancellableCoroutine { continuation ->
+                        val cancellableTask =
+                            taskIconDataSource.getIconInBackground(task) {
+                                icon,
+                                contentDescription,
+                                title ->
+                                continuation.resume(
+                                    TaskIconQueryResponse(icon, contentDescription, title)
+                                )
+                            }
+                        continuation.invokeOnCancellation { cancellableTask?.cancel() }
+                    }
+                emit(task.key.id to iconDataResponse)
+            }
+            .distinctUntilChanged()
+
+    private fun getIconQueryResults(): Flow<Map<Int, TaskIconQueryResponse?>> {
+        val visibleTasks =
+            combine(_taskData, visibleTaskIds) { tasks, visibleIds ->
+                tasks.filter { it.key.id in visibleIds }
+            }
+        val visibleIconDataRequests: Flow<List<IconDataRequest>> =
+            visibleTasks.map { visibleTasksList -> visibleTasksList.map(::getIconDataRequest) }
+        return visibleIconDataRequests.flatMapLatest { iconRequestFlows: List<IconDataRequest> ->
+            if (iconRequestFlows.isEmpty()) {
+                flowOf(emptyMap())
+            } else {
+                combine(iconRequestFlows) { it.toMap() }
+            }
+        }
+    }
 }
 
-typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
+private data class TaskIconQueryResponse(
+    val icon: Drawable,
+    val contentDescription: String,
+    val title: String
+)
+
+private fun Task.getTaskIconQueryResponse(): TaskIconQueryResponse? {
+    val iconVal = icon ?: return null
+    val titleDescriptionVal = titleDescription ?: return null
+    val titleVal = title ?: return null
+
+    return TaskIconQueryResponse(iconVal, titleDescriptionVal, titleVal)
+}
+
+private typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
+
+private typealias IconDataRequest = Flow<Pair<Int, TaskIconQueryResponse?>>
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt
new file mode 100644
index 0000000..45b3687
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/LiveTileView.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.task.thumbnail
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.util.AttributeSet
+import android.view.View
+
+class LiveTileView : View {
+    constructor(context: Context) : super(context)
+
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+    constructor(
+        context: Context,
+        attrs: AttributeSet?,
+        defStyleAttr: Int,
+    ) : super(context, attrs, defStyleAttr)
+
+    override fun onDraw(canvas: Canvas) {
+        canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
+    }
+
+    companion object {
+        private val CLEAR_PAINT =
+            Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 22d49c1..c71b9e7 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -18,17 +18,17 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Outline
-import android.graphics.Paint
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffXfermode
 import android.graphics.Rect
 import android.util.AttributeSet
 import android.view.View
 import android.view.ViewOutlineProvider
+import android.widget.FrameLayout
+import android.widget.ImageView
 import androidx.annotation.ColorInt
+import androidx.core.view.isVisible
+import com.android.launcher3.R
 import com.android.launcher3.Utilities
 import com.android.launcher3.util.ViewPool
 import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
@@ -43,7 +43,7 @@
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.launch
 
-class TaskThumbnailView : View, ViewPool.Reusable {
+class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
     // TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped
     //  to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
     //  This is using a lazy for now because the dependencies cannot be obtained without DI.
@@ -59,12 +59,12 @@
         )
     }
 
-    private var uiState: TaskThumbnailUiState = Uninitialized
-    private var inheritedScale: Float = 1f
-    private var dimProgress: Float = 0f
+    private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
+    private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
+    private val thumbnail: ImageView by lazy { findViewById(R.id.task_thumbnail) }
 
-    private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
-    private val scrimPaint = Paint().apply { color = Color.BLACK }
+    private var inheritedScale: Float = 1f
+
     private val _measuredBounds = Rect()
     private val measuredBounds: Rect
         get() {
@@ -75,12 +75,12 @@
     private var overviewCornerRadius: Float = TaskCornerRadius.get(context)
     private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
 
-    constructor(context: Context?) : super(context)
+    constructor(context: Context) : super(context)
 
-    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
 
     constructor(
-        context: Context?,
+        context: Context,
         attrs: AttributeSet?,
         defStyleAttr: Int,
     ) : super(context, attrs, defStyleAttr)
@@ -90,15 +90,19 @@
         // TODO(b/335396935) replace MainScope with shorter lifecycle.
         MainScope().launch {
             viewModel.uiState.collect { viewModelUiState ->
-                uiState = viewModelUiState
-                invalidate()
+                resetViews()
+                when (viewModelUiState) {
+                    is Uninitialized -> {}
+                    is LiveTile -> drawLiveWindow()
+                    is Snapshot -> drawSnapshot(viewModelUiState)
+                    is BackgroundOnly -> drawBackground(viewModelUiState.backgroundColor)
+                }
             }
         }
         MainScope().launch {
             viewModel.dimProgress.collect { dimProgress ->
                 // TODO(b/348195366) Add fade in/out for scrim
-                this@TaskThumbnailView.dimProgress = dimProgress
-                invalidate()
+                scrimView.alpha = dimProgress * MAX_SCRIM_ALPHA
             }
         }
         MainScope().launch { viewModel.cornerRadiusProgress.collect { invalidateOutline() } }
@@ -120,25 +124,6 @@
 
     override fun onRecycle() {
         // Do nothing
-        uiState = Uninitialized
-    }
-
-    override fun onDraw(canvas: Canvas) {
-        when (val uiStateVal = uiState) {
-            is Uninitialized -> drawBackgroundOnly(canvas, Color.BLACK)
-            is LiveTile -> drawTransparentUiState(canvas)
-            is Snapshot -> drawSnapshotState(canvas, uiStateVal)
-            is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor)
-        }
-
-        if (dimProgress > 0) {
-            drawScrim(canvas)
-        }
-    }
-
-    private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) {
-        backgroundPaint.color = backgroundColor
-        canvas.drawRect(measuredBounds, backgroundPaint)
     }
 
     override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -149,18 +134,25 @@
         invalidateOutline()
     }
 
-    private fun drawTransparentUiState(canvas: Canvas) {
-        canvas.drawRect(measuredBounds, CLEAR_PAINT)
+    private fun resetViews() {
+        liveTileView.isVisible = false
+        thumbnail.isVisible = false
+        scrimView.alpha = 0f
+        setBackgroundColor(Color.BLACK)
     }
 
-    private fun drawSnapshotState(canvas: Canvas, snapshot: Snapshot) {
-        drawBackgroundOnly(canvas, snapshot.backgroundColor)
-        canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null)
+    private fun drawBackground(@ColorInt background: Int) {
+        setBackgroundColor(background)
     }
 
-    private fun drawScrim(canvas: Canvas) {
-        scrimPaint.alpha = (dimProgress * MAX_SCRIM_ALPHA).toInt()
-        canvas.drawRect(measuredBounds, scrimPaint)
+    private fun drawLiveWindow() {
+        liveTileView.isVisible = true
+    }
+
+    private fun drawSnapshot(snapshot: Snapshot) {
+        drawBackground(snapshot.backgroundColor)
+        thumbnail.setImageBitmap(snapshot.bitmap)
+        thumbnail.isVisible = true
     }
 
     private fun getCurrentCornerRadius() =
@@ -170,9 +162,7 @@
             fullscreenCornerRadius
         ) / inheritedScale
 
-    companion object {
-        private val CLEAR_PAINT =
-            Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
-        private const val MAX_SCRIM_ALPHA = (0.4f * 255).toInt()
+    private companion object {
+        const val MAX_SCRIM_ALPHA = 0.4f
     }
 }
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt
new file mode 100644
index 0000000..ab699c6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskIconDataSource.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.task.thumbnail.data
+
+import com.android.launcher3.util.CancellableTask
+import com.android.quickstep.TaskIconCache.GetTaskIconCallback
+import com.android.systemui.shared.recents.model.Task
+
+interface TaskIconDataSource {
+    fun getIconInBackground(task: Task, callback: GetTaskIconCallback): CancellableTask<*>?
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 4333c8b..9ce2277 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -24,6 +24,7 @@
 import android.graphics.drawable.shapes.RoundRectShape
 import android.util.AttributeSet
 import android.util.Log
+import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.core.view.updateLayoutParams
@@ -36,7 +37,6 @@
 import com.android.launcher3.util.rects.set
 import com.android.quickstep.BaseContainerInterface
 import com.android.quickstep.TaskOverlayFactory
-import com.android.quickstep.task.thumbnail.TaskThumbnailView
 import com.android.quickstep.util.RecentsOrientedState
 import com.android.systemui.shared.recents.model.Task
 
@@ -54,7 +54,7 @@
         ViewPool<TaskThumbnailViewDeprecated>(
             context,
             this,
-            R.layout.task_thumbnail,
+            R.layout.task_thumbnail_deprecated,
             VIEW_POOL_MAX_SIZE,
             VIEW_POOL_INITIAL_SIZE
         )
@@ -108,22 +108,21 @@
             tasks.map { task ->
                 val snapshotView =
                     if (enableRefactorTaskThumbnail()) {
-                            TaskThumbnailView(context)
-                        } else {
-                            taskThumbnailViewDeprecatedPool.view
-                        }
-                        .also { snapshotView ->
-                            addView(
-                                snapshotView,
-                                // Add snapshotView to the front after initial views e.g. icon and
-                                // background.
-                                childCountAtInflation,
-                                LayoutParams(
-                                    ViewGroup.LayoutParams.WRAP_CONTENT,
-                                    ViewGroup.LayoutParams.WRAP_CONTENT
-                                )
-                            )
-                        }
+                        LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
+                    } else {
+                        taskThumbnailViewDeprecatedPool.view
+                    }
+
+                addView(
+                    snapshotView,
+                    // Add snapshotView to the front after initial views e.g. icon and
+                    // background.
+                    childCountAtInflation,
+                    LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                )
                 TaskContainer(
                     this,
                     task,
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 4e19d34..004003c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -31,6 +31,7 @@
 import android.util.FloatProperty
 import android.util.Log
 import android.view.Display
+import android.view.LayoutInflater
 import android.view.MotionEvent
 import android.view.View
 import android.view.View.OnClickListener
@@ -666,9 +667,8 @@
             if (enableRefactorTaskThumbnail()) {
                 thumbnailViewDeprecated.visibility = GONE
                 val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
-                TaskThumbnailView(context).apply {
-                    layoutParams = thumbnailViewDeprecated.layoutParams
-                    addView(this, indexOfSnapshotView)
+                LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also {
+                    addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams)
                 }
             } else {
                 thumbnailViewDeprecated
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
new file mode 100644
index 0000000..242bc73
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskIconDataSource.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.recents.data
+
+import android.graphics.drawable.Drawable
+import com.android.launcher3.util.CancellableTask
+import com.android.quickstep.TaskIconCache
+import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
+import com.android.systemui.shared.recents.model.Task
+import com.google.common.truth.Truth.assertThat
+import org.mockito.kotlin.mock
+
+class FakeTaskIconDataSource : TaskIconDataSource {
+
+    val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mock() }
+    val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
+    var shouldLoadSynchronously: Boolean = true
+
+    /** Retrieves and sets an icon on [task] from [taskIdToDrawable]. */
+    override fun getIconInBackground(
+        task: Task,
+        callback: TaskIconCache.GetTaskIconCallback
+    ): CancellableTask<*>? {
+        val wrappedCallback = {
+            callback.onTaskIconReceived(
+                taskIdToDrawable.getValue(task.key.id),
+                "content desc ${task.key.id}",
+                "title ${task.key.id}"
+            )
+        }
+        if (shouldLoadSynchronously) {
+            wrappedCallback()
+        } else {
+            taskIdToUpdatingTask[task.key.id] = wrappedCallback
+        }
+        return null
+    }
+}
+
+fun Task.assertHasIconDataFromSource(fakeTaskIconDataSource: FakeTaskIconDataSource) {
+    assertThat(icon).isEqualTo(fakeTaskIconDataSource.taskIdToDrawable[key.id])
+    assertThat(titleDescription).isEqualTo("content desc ${key.id}")
+    assertThat(title).isEqualTo("title ${key.id}")
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index c28a85a..88fa190 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.ComponentName
 import android.content.Intent
-import com.android.quickstep.TaskIconCache
 import com.android.quickstep.util.DesktopTask
 import com.android.quickstep.util.GroupTask
 import com.android.systemui.shared.recents.model.Task
@@ -31,7 +30,6 @@
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
-import org.mockito.kotlin.mock
 
 @OptIn(ExperimentalCoroutinesApi::class)
 class TasksRepositoryTest {
@@ -44,10 +42,10 @@
         )
     private val recentsModel = FakeRecentTasksDataSource()
     private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
-    private val taskIconCache = mock<TaskIconCache>()
+    private val taskIconDataSource = FakeTaskIconDataSource()
 
     private val systemUnderTest =
-        TasksRepository(recentsModel, taskThumbnailDataSource, taskIconCache)
+        TasksRepository(recentsModel, taskThumbnailDataSource, taskIconDataSource)
 
     @Test
     fun getAllTaskDataReturnsFlattenedListOfTasks() = runTest {
@@ -81,6 +79,22 @@
     }
 
     @Test
+    fun setVisibleTasksPopulatesIcons() = runTest {
+        recentsModel.seedTasks(defaultTaskList)
+        systemUnderTest.getAllTaskData(forceRefresh = true)
+
+        systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+        // .drop(1) to ignore initial null content before from thumbnail was loaded.
+        systemUnderTest
+            .getTaskDataById(1)
+            .drop(1)
+            .first()!!
+            .assertHasIconDataFromSource(taskIconDataSource)
+        systemUnderTest.getTaskDataById(2).first()!!.assertHasIconDataFromSource(taskIconDataSource)
+    }
+
+    @Test
     fun changingVisibleTasksContainsAlreadyPopulatedThumbnails() = runTest {
         recentsModel.seedTasks(defaultTaskList)
         val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
@@ -101,7 +115,28 @@
     }
 
     @Test
-    fun retrievedThumbnailsAreDiscardedWhenTaskBecomesInvisible() = runTest {
+    fun changingVisibleTasksContainsAlreadyPopulatedIcons() = runTest {
+        recentsModel.seedTasks(defaultTaskList)
+        systemUnderTest.getAllTaskData(forceRefresh = true)
+
+        systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+        // .drop(1) to ignore initial null content before from icon was loaded.
+        systemUnderTest
+            .getTaskDataById(2)
+            .drop(1)
+            .first()!!
+            .assertHasIconDataFromSource(taskIconDataSource)
+
+        // Prevent new loading of Drawables
+        taskThumbnailDataSource.shouldLoadSynchronously = false
+        systemUnderTest.setVisibleTasks(listOf(2, 3))
+
+        systemUnderTest.getTaskDataById(2).first()!!.assertHasIconDataFromSource(taskIconDataSource)
+    }
+
+    @Test
+    fun retrievedImagesAreDiscardedWhenTaskBecomesInvisible() = runTest {
         recentsModel.seedTasks(defaultTaskList)
         val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
         systemUnderTest.getAllTaskData(forceRefresh = true)
@@ -109,14 +144,20 @@
         systemUnderTest.setVisibleTasks(listOf(1, 2))
 
         // .drop(1) to ignore initial null content before from thumbnail was loaded.
-        assertThat(systemUnderTest.getTaskDataById(2).drop(1).first()!!.thumbnail!!.thumbnail)
-            .isEqualTo(bitmap2)
+        val task2 = systemUnderTest.getTaskDataById(2).drop(1).first()!!
+        assertThat(task2.thumbnail!!.thumbnail).isEqualTo(bitmap2)
+        task2.assertHasIconDataFromSource(taskIconDataSource)
 
         // Prevent new loading of Bitmaps
         taskThumbnailDataSource.shouldLoadSynchronously = false
+        taskIconDataSource.shouldLoadSynchronously = false
         systemUnderTest.setVisibleTasks(listOf(0, 1))
 
-        assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail).isNull()
+        val task2AfterVisibleTasksChanged = systemUnderTest.getTaskDataById(2).first()!!
+        assertThat(task2AfterVisibleTasksChanged.thumbnail).isNull()
+        assertThat(task2AfterVisibleTasksChanged.icon).isNull()
+        assertThat(task2AfterVisibleTasksChanged.titleDescription).isNull()
+        assertThat(task2AfterVisibleTasksChanged.title).isNull()
     }
 
     @Test
diff --git a/res/anim-v33/shared_x_axis_activity_close_enter.xml b/res/anim-v33/shared_x_axis_activity_close_enter.xml
deleted file mode 100644
index 3d7ad2b..0000000
--- a/res/anim-v33/shared_x_axis_activity_close_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:showBackdrop="true">
-
-    <alpha
-        android:fromAlpha="0.0"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/standard_decelerate_interpolator"
-        android:startOffset="100"
-        android:duration="350" />
-
-    <translate
-        android:fromXDelta="-25%"
-        android:toXDelta="0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/emphasized_interpolator"
-        android:startOffset="0"
-        android:duration="450" />
-
-</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_close_exit.xml b/res/anim-v33/shared_x_axis_activity_close_exit.xml
deleted file mode 100644
index fb63602..0000000
--- a/res/anim-v33/shared_x_axis_activity_close_exit.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="0.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/standard_accelerate_interpolator"
-        android:startOffset="0"
-        android:duration="100" />
-
-    <translate
-        android:fromXDelta="0"
-        android:toXDelta="25%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/emphasized_interpolator"
-        android:startOffset="0"
-        android:duration="450" />
-
-</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_open_enter.xml b/res/anim-v33/shared_x_axis_activity_open_enter.xml
deleted file mode 100644
index cba74ba..0000000
--- a/res/anim-v33/shared_x_axis_activity_open_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:showBackdrop="true">
-
-    <alpha
-        android:fromAlpha="0.0"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/standard_decelerate_interpolator"
-        android:startOffset="100"
-        android:duration="350" />
-
-    <translate
-        android:fromXDelta="25%"
-        android:toXDelta="0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/emphasized_interpolator"
-        android:startOffset="0"
-        android:duration="450" />
-
-</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_open_exit.xml b/res/anim-v33/shared_x_axis_activity_open_exit.xml
deleted file mode 100644
index 22e878d..0000000
--- a/res/anim-v33/shared_x_axis_activity_open_exit.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="0.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/standard_accelerate_interpolator"
-        android:startOffset="0"
-        android:duration="100" />
-
-    <translate
-        android:fromXDelta="0"
-        android:toXDelta="-25%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/emphasized_interpolator"
-        android:startOffset="0"
-        android:duration="450" />
-
-</set>
\ No newline at end of file
diff --git a/res/values-v33/style.xml b/res/values-v33/style.xml
deleted file mode 100644
index 1261b23..0000000
--- a/res/values-v33/style.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2022 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <style name="HomeSettings.Theme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:listPreferredItemPaddingEnd">16dp</item>
-        <item name="android:listPreferredItemPaddingStart">24dp</item>
-        <item name="android:navigationBarColor">@android:color/transparent</item>
-        <item name="android:statusBarColor">@android:color/transparent</item>
-        <item name="android:switchStyle">@style/SwitchStyle</item>
-        <item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
-        <item name="android:windowActionBar">false</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
-        <item name="android:windowAnimationStyle">@style/Animation.SharedBackground</item>
-    </style>
-
-    <style name="Animation.SharedBackground" parent="@android:style/Animation.Activity">
-        <item name="android:activityOpenEnterAnimation">@anim/shared_x_axis_activity_open_enter</item>
-        <item name="android:activityOpenExitAnimation">@anim/shared_x_axis_activity_open_exit</item>
-        <item name="android:activityCloseEnterAnimation">@anim/shared_x_axis_activity_close_enter</item>
-        <item name="android:activityCloseExitAnimation">@anim/shared_x_axis_activity_close_exit</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index ae8f1d5..6088941 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -508,6 +508,7 @@
                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
             CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
             View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false);
+            // TODO: set bgHandler on qsb when it is BaseTemplateCard, which requires API changes.
             CellLayoutLayoutParams lp = new CellLayoutLayoutParams(
                     0, 0, firstScreen.getCountX(), 1);
             lp.canReorder = false;