Merge "Use drawable states for list positions instead of swapping drawables" into sc-dev
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index c6b70aa..4f70a05 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<TableLayout
+<com.android.launcher3.widget.picker.WidgetsListTableView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/widgets_table"
     android:layout_width="match_parent"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e8d3212..96b7d26 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -140,7 +140,6 @@
 
     <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
     <dimen name="widget_list_content_corner_radius">4dp</dimen>
-    <dimen name="widget_list_content_joined_corner_radius">0dp</dimen>
 
     <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
     <dimen name="widget_list_entry_bottom_margin">2dp</dimen>
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 84a03d5..4e2a508 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -107,7 +107,10 @@
                 /* iconClickListener= */ view -> {},
                 /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
+                LayoutInflater.from(mTestActivity),
+                mOnHeaderClickListener,
+                new WidgetsListDrawableFactory(mTestActivity),
+                widgetsListAdapter);
     }
 
     @After
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index 075c58d..d6aea55 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -107,7 +107,10 @@
                 /* iconClickListener= */ view -> {},
                 /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
+                LayoutInflater.from(mTestActivity),
+                mOnHeaderClickListener,
+                new WidgetsListDrawableFactory(mTestActivity),
+                widgetsListAdapter);
     }
 
     @After
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 0c6e717..2f1326f 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -118,6 +118,7 @@
                 mOnIconClickListener,
                 mOnLongClickListener,
                 mWidgetPreviewLoader,
+                new WidgetsListDrawableFactory(mTestActivity),
                 widgetsListAdapter);
     }
 
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index e89aea7..6863c60 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -103,18 +103,25 @@
             OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
         mLauncher = Launcher.getLauncher(context);
         mDiffReporter = new WidgetsDiffReporter(iconCache, this);
+        WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
         mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
                 layoutInflater, iconClickListener, iconLongClickListener,
-                widgetPreviewLoader, /* listAdapter= */ this);
+                widgetPreviewLoader, listDrawableFactory, /* listAdapter= */ this);
         mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_HEADER,
                 new WidgetsListHeaderViewHolderBinder(
-                        layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this));
+                        layoutInflater,
+                        /* onHeaderClickListener= */ this,
+                        listDrawableFactory,
+                        /* listAdapter= */ this));
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_SEARCH_HEADER,
                 new WidgetsListSearchHeaderViewHolderBinder(
-                        layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this));
+                        layoutInflater,
+                        /* onHeaderClickListener= */ this,
+                        listDrawableFactory,
+                        /* listAdapter= */ this));
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
new file mode 100644
index 0000000..c61e3a4
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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.launcher3.widget.picker;
+
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST_EXPANDED;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE_EXPANDED;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.SINGLE;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.StateListDrawable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+/** Factory for creating drawables to use as background for list elements. */
+final class WidgetsListDrawableFactory {
+
+    private final float mTopBottomCornerRadius;
+    private final float mMiddleCornerRadius;
+    private final ColorStateList mSurfaceColor;
+    private final ColorStateList mRippleColor;
+
+    WidgetsListDrawableFactory(Context context) {
+        Resources res = context.getResources();
+        mTopBottomCornerRadius = res.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
+        mMiddleCornerRadius = res.getDimension(R.dimen.widget_list_content_corner_radius);
+        mSurfaceColor = context.getColorStateList(R.color.surface);
+        mRippleColor = ColorStateList.valueOf(
+                Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
+    }
+
+    /**
+     * Creates a drawable for widget header list items. This drawable supports all positions
+     * in {@link WidgetsListDrawableState}.
+     */
+    Drawable createHeaderBackgroundDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(
+                SINGLE.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, mTopBottomCornerRadius));
+        stateList.addState(
+                FIRST_EXPANDED.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, 0));
+        stateList.addState(
+                FIRST.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, mMiddleCornerRadius));
+        stateList.addState(
+                MIDDLE_EXPANDED.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, 0));
+        stateList.addState(
+                MIDDLE.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, mMiddleCornerRadius));
+        stateList.addState(
+                LAST.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius));
+        return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+    }
+
+    /**
+     * Creates a drawable for widget content list items. This state list supports the middle and
+     * last states.
+     */
+    Drawable createContentBackgroundDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(
+                MIDDLE.mStateSet,
+                createRoundedRectDrawable(0, mMiddleCornerRadius));
+        stateList.addState(
+                LAST.mStateSet,
+                createRoundedRectDrawable(0, mTopBottomCornerRadius));
+        return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+    }
+
+    /** Creates a rounded-rect drawable with the specified radii. */
+    private Drawable createRoundedRectDrawable(float topRadius, float bottomRadius) {
+        GradientDrawable backgroundMask = new GradientDrawable();
+        backgroundMask.setColor(mSurfaceColor);
+        backgroundMask.setShape(GradientDrawable.RECTANGLE);
+        backgroundMask.setCornerRadii(
+                new float[]{
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius
+                });
+        return backgroundMask;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
new file mode 100644
index 0000000..94f292b
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.launcher3.widget.picker;
+
+/**
+ * Different possible list position states for an item in the widgets list to have. Note that only
+ * headers use the expanded state.
+ */
+enum WidgetsListDrawableState {
+    FIRST(new int[]{android.R.attr.state_first}),
+    FIRST_EXPANDED(new int[]{android.R.attr.state_first, android.R.attr.state_expanded}),
+    MIDDLE(new int[]{android.R.attr.state_middle}),
+    MIDDLE_EXPANDED(new int[]{android.R.attr.state_middle, android.R.attr.state_expanded}),
+    LAST(new int[]{android.R.attr.state_last}),
+    SINGLE(new int[]{android.R.attr.state_single});
+
+    final int[] mStateSet;
+
+    WidgetsListDrawableState(int[] stateSet) {
+        mStateSet = stateSet;
+    }
+
+    static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast, boolean isExpanded) {
+        if (isFirst && isLast) return SINGLE;
+        if (isFirst && isExpanded) return FIRST_EXPANDED;
+        if (isFirst) return FIRST;
+        if (isLast) return LAST;
+        if (isExpanded) return MIDDLE_EXPANDED;
+        return MIDDLE;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java
deleted file mode 100644
index b3bb544..0000000
--- a/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 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.launcher3.widget.picker;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
-
-/** Helper class for creating drawables to use as background for list elements. */
-final class WidgetsListDrawables {
-
-    private WidgetsListDrawables() {}
-
-    /** Creates a list background drawable with the specified radii. */
-    static Drawable createListBackgroundDrawable(
-            Context context,
-            float topRadius,
-            float bottomRadius) {
-        GradientDrawable backgroundMask = new GradientDrawable();
-        backgroundMask.setColor(context.getColorStateList(R.color.surface));
-        backgroundMask.setShape(GradientDrawable.RECTANGLE);
-
-        backgroundMask.setCornerRadii(
-                new float[]{
-                        topRadius,
-                        topRadius,
-                        topRadius,
-                        topRadius,
-                        bottomRadius,
-                        bottomRadius,
-                        bottomRadius,
-                        bottomRadius
-                });
-
-        return new RippleDrawable(
-                /* color= */ ColorStateList.valueOf(
-                        Themes.getAttrColor(context, android.R.attr.colorControlHighlight)),
-                /* content= */ backgroundMask,
-                /* mask= */ backgroundMask);
-    }
-
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index fece359..cdab964 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -60,9 +60,6 @@
     @Nullable private Drawable mIconDrawable;
     private final int mIconSize;
     private final int mBottomMarginSize;
-    private final float mTopBottomCornerRadius;
-    private final float mMiddleCornerRadius;
-    private final float mJoinedCornerRadius;
 
     private ImageView mAppIcon;
     private TextView mTitle;
@@ -70,6 +67,7 @@
 
     private CheckBox mExpandToggle;
     private boolean mIsExpanded = false;
+    @Nullable private WidgetsListDrawableState mListDrawableState;
 
     public WidgetsListHeader(Context context) {
         this(context, /* attrs= */ null);
@@ -90,12 +88,6 @@
                 grid.iconSizePx);
         mBottomMarginSize =
                 getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
-        mTopBottomCornerRadius =
-                getResources().getDimension(R.dimen.widget_list_top_bottom_corner_radius);
-        mMiddleCornerRadius =
-                getResources().getDimension(R.dimen.widget_list_content_corner_radius);
-        mJoinedCornerRadius =
-                getResources().getDimension(R.dimen.widget_list_content_joined_corner_radius);
     }
 
     @Override
@@ -163,6 +155,14 @@
         }
     }
 
+    /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
+    @UiThread
+    public void setListDrawableState(WidgetsListDrawableState state) {
+        if (state == mListDrawableState) return;
+        this.mListDrawableState = state;
+        refreshDrawableState();
+    }
+
     /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
     @UiThread
     public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
@@ -263,20 +263,6 @@
         verifyHighRes();
     }
 
-    /** Updates the list to have a background drawable with the appropriate corner radii. */
-    @UiThread
-    public void updateListBackground(boolean isFirst, boolean isLast, boolean isExpanded) {
-        float topRadius = isFirst ? mTopBottomCornerRadius : mMiddleCornerRadius;
-        float bottomRadius = isLast
-                ? mTopBottomCornerRadius
-                : isExpanded
-                        ? mJoinedCornerRadius
-                        : mMiddleCornerRadius;
-        setBackground(
-                WidgetsListDrawables.createListBackgroundDrawable(
-                        getContext(), topRadius, bottomRadius));
-    }
-
     private void setTitles(WidgetsListSearchHeaderEntry entry) {
         mTitle.setText(entry.mPkgItem.title);
 
@@ -300,6 +286,17 @@
         }
     }
 
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
+        // Augment the state set from the super implementation with the custom states from
+        // mListDrawableState.
+        int[] drawableState =
+                super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
+        mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+        return drawableState;
+    }
+
     /** Verifies that the current icon is high-res otherwise posts a request to load the icon. */
     public void verifyHighRes() {
         if (mIconLoadRequest != null) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index 22d6d22..2f8f1ba 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -30,13 +30,16 @@
         ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
+    private final WidgetsListDrawableFactory mListDrawableFactory;
     private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
             OnHeaderClickListener onHeaderClickListener,
+            WidgetsListDrawableFactory listDrawableFactory,
             WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
+        mListDrawableFactory = listDrawableFactory;
         mWidgetsListAdapter = listAdapter;
     }
 
@@ -44,7 +47,7 @@
     public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
         WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
                 R.layout.widgets_list_row_header, parent, false);
-
+        header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
         return new WidgetsListHeaderHolder(header);
     }
 
@@ -52,12 +55,13 @@
     public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
             int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        widgetsListHeader.updateListBackground(
-                /* isFirst= */ position == 0,
-                /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
-                /* isExpanded= */ data.isWidgetListShown());
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
+        widgetsListHeader.setListDrawableState(
+                WidgetsListDrawableState.obtain(
+                        /* isFirst= */ position == 0,
+                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        /* isExpanded= */ data.isWidgetListShown()));
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
                 mOnHeaderClickListener.onHeaderClicked(
                         isExpanded,
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index d5e03a4..31dd9ee 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -31,13 +31,16 @@
         ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
+    private final WidgetsListDrawableFactory mListDrawableFactory;
     private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
             OnHeaderClickListener onHeaderClickListener,
+            WidgetsListDrawableFactory listDrawableFactory,
             WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
+        mListDrawableFactory = listDrawableFactory;
         mWidgetsListAdapter = listAdapter;
     }
 
@@ -45,7 +48,7 @@
     public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) {
         WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
                 R.layout.widgets_list_row_header, parent, false);
-
+        header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
         return new WidgetsListSearchHeaderHolder(header);
     }
 
@@ -53,12 +56,13 @@
     public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
             WidgetsListSearchHeaderEntry data, int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
-        widgetsListHeader.updateListBackground(
-                /* isFirst= */ position == 0,
-                /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
-                /* isExpanded= */ data.isWidgetListShown());
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
+        widgetsListHeader.setListDrawableState(
+                WidgetsListDrawableState.obtain(
+                        /* isFirst= */ position == 0,
+                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        /* isExpanded= */ data.isWidgetListShown()));
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
                 mOnHeaderClickListener.onHeaderClicked(isExpanded,
                         new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableView.java b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java
new file mode 100644
index 0000000..d30e7b6
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.launcher3.widget.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TableLayout;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+/**
+ * Extension of {@link TableLayout} to support the drawable states used by
+ * {@link WidgetsListDrawableState}.
+ */
+public class WidgetsListTableView extends TableLayout {
+
+    @Nullable private WidgetsListDrawableState mListDrawableState;
+
+    public WidgetsListTableView(Context context) {
+        super(context);
+    }
+
+    public WidgetsListTableView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
+    @UiThread
+    public void setListDrawableState(WidgetsListDrawableState state) {
+        if (state == mListDrawableState) return;
+        mListDrawableState = state;
+        refreshDrawableState();
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
+        // Augment the state set from the super implementation with the custom states from
+        // mListDrawableState.
+        int[] drawableState =
+                super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
+        mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+        return drawableState;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 8e310c5..7e8c55b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,8 +15,10 @@
  */
 package com.android.launcher3.widget.picker;
 
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
+
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -51,10 +53,8 @@
     private final OnClickListener mIconClickListener;
     private final OnLongClickListener mIconLongClickListener;
     private final WidgetPreviewLoader mWidgetPreviewLoader;
+    private final WidgetsListDrawableFactory mListDrawableFactory;
     private final WidgetsListAdapter mWidgetsListAdapter;
-    private final float mTopBottomCornerRadius;
-    private final float mMiddleCornerRadius;
-    private final float mJoinedCornerRadius;
     private boolean mApplyBitmapDeferred = false;
 
     public WidgetsListTableViewHolderBinder(
@@ -63,19 +63,14 @@
             OnClickListener iconClickListener,
             OnLongClickListener iconLongClickListener,
             WidgetPreviewLoader widgetPreviewLoader,
+            WidgetsListDrawableFactory listDrawableFactory,
             WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mWidgetPreviewLoader = widgetPreviewLoader;
+        mListDrawableFactory = listDrawableFactory;
         mWidgetsListAdapter = listAdapter;
-        Resources resources = context.getResources();
-        mTopBottomCornerRadius =
-                resources.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
-        mMiddleCornerRadius =
-                resources.getDimension(R.dimen.widget_list_content_corner_radius);
-        mJoinedCornerRadius =
-                resources.getDimension(R.dimen.widget_list_content_joined_corner_radius);
     }
 
     /**
@@ -97,28 +92,25 @@
             Log.v(TAG, "\nonCreateViewHolder");
         }
 
-        ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
-                R.layout.widgets_table_container, parent, false);
-        return new WidgetsRowViewHolder(container);
+        WidgetsRowViewHolder viewHolder =
+                new WidgetsRowViewHolder(mLayoutInflater.inflate(
+                        R.layout.widgets_table_container, parent, false));
+        viewHolder.mTableContainer.setBackgroundDrawable(
+                mListDrawableFactory.createContentBackgroundDrawable());
+        return viewHolder;
     }
 
     @Override
     public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
             int position) {
-        TableLayout table = holder.mTableContainer;
+        WidgetsListTableView table = holder.mTableContainer;
         if (DEBUG) {
             Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
                     entry.mWidgets.size(), table.getChildCount()));
         }
 
-        // The content is always joined to an expanded header above.
-        float topRadius = mJoinedCornerRadius;
-        float bottomRadius = position == mWidgetsListAdapter.getItemCount() - 1
-                ? mTopBottomCornerRadius
-                : mMiddleCornerRadius;
-        table.setBackgroundDrawable(
-                WidgetsListDrawables.createListBackgroundDrawable(
-                        holder.itemView.getContext(), topRadius, bottomRadius));
+        table.setListDrawableState(
+                position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE);
 
         List<ArrayList<WidgetItem>> widgetItemsTable =
                 WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index aef1103..618e2cb 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -15,8 +15,7 @@
  */
 package com.android.launcher3.widget.picker;
 
-import android.view.ViewGroup;
-import android.widget.TableLayout;
+import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
@@ -25,9 +24,9 @@
 /** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
 public final class WidgetsRowViewHolder extends ViewHolder {
 
-    public final TableLayout mTableContainer;
+    public final WidgetsListTableView mTableContainer;
 
-    public WidgetsRowViewHolder(ViewGroup v) {
+    public WidgetsRowViewHolder(View v) {
         super(v);
 
         mTableContainer = v.findViewById(R.id.widgets_table);