From 803927bdae8f4db9c8ede435d95388eaa58ecb89 Mon Sep 17 00:00:00 2001 From: Stevie Kideckel Date: Mon, 14 Jun 2021 17:27:58 +0000 Subject: [PATCH] Use drawable states for list positions instead of swapping drawables Replacing drawables interrupts touch feedback of ripples. Implementing custom state lists allows altering the corner radius without cutting off ripples and also means we reduce unnecessary object creation. Fix: 190467676 Test: locally Change-Id: I10eec042aae57d278f8254327d42df945767c7ac --- res/layout/widgets_table_container.xml | 2 +- res/values/dimens.xml | 1 - ...WidgetsListHeaderViewHolderBinderTest.java | 5 +- ...sListSearchHeaderViewHolderBinderTest.java | 5 +- .../WidgetsListTableViewHolderBinderTest.java | 1 + .../widget/picker/WidgetsListAdapter.java | 13 +- .../picker/WidgetsListDrawableFactory.java | 113 ++++++++++++++++++ .../picker/WidgetsListDrawableState.java | 44 +++++++ .../widget/picker/WidgetsListDrawables.java | 60 ---------- .../widget/picker/WidgetsListHeader.java | 43 ++++--- .../WidgetsListHeaderViewHolderBinder.java | 14 ++- ...dgetsListSearchHeaderViewHolderBinder.java | 14 ++- .../widget/picker/WidgetsListTableView.java | 59 +++++++++ .../WidgetsListTableViewHolderBinder.java | 38 +++--- .../widget/picker/WidgetsRowViewHolder.java | 7 +- 15 files changed, 292 insertions(+), 127 deletions(-) create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java delete mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawables.java create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListTableView.java diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml index c6b70aa4aa..4f70a05374 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. --> -28dp 4dp - 0dp 20dp 2dp 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 84a03d572c..4e2a508394 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 @@ public final class WidgetsListHeaderViewHolderBinderTest { /* 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 075c58db6b..d6aea554f2 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 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest { /* 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 0c6e71711d..2f1326fdc0 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 @@ public final class WidgetsListTableViewHolderBinderTest { 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 e89aea7507..6863c60a37 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -103,18 +103,25 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC 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 0000000000..c61e3a4349 --- /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 0000000000..94f292beb2 --- /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 b3bb544cd7..0000000000 --- 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 fece359607..cdab964279 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java @@ -60,9 +60,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd @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 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd private CheckBox mExpandToggle; private boolean mIsExpanded = false; + @Nullable private WidgetsListDrawableState mListDrawableState; public WidgetsListHeader(Context context) { this(context, /* attrs= */ null); @@ -90,12 +88,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd 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 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd } } + /** 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 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd 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 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd } } + @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 22d6d227c4..2f8f1bacfc 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java @@ -30,13 +30,16 @@ public final class WidgetsListHeaderViewHolderBinder implements ViewHolderBinder { 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 final class WidgetsListHeaderViewHolderBinder implements 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 final class WidgetsListHeaderViewHolderBinder implements 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 d5e03a4fe0..31dd9ee0ae 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java @@ -31,13 +31,16 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements ViewHolderBinder { 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 final class WidgetsListSearchHeaderViewHolderBinder implements 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 final class WidgetsListSearchHeaderViewHolderBinder implements 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 0000000000..d30e7b68ae --- /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 8e310c5d83..7e8c55bfe6 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 @@ public final class WidgetsListTableViewHolderBinder 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 @@ public final class WidgetsListTableViewHolderBinder 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 @@ public final class WidgetsListTableViewHolderBinder 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> 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 aef1103c2c..618e2cbbd6 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 @@ import com.android.launcher3.R; /** 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);