Merge "Use drawable states for list positions instead of swapping drawables" into sc-dev

This commit is contained in:
Stevie Kideckel 2021-06-16 09:34:21 +00:00 committed by Android (Google) Code Review
commit 358411a96a
15 changed files with 292 additions and 127 deletions

View File

@ -13,7 +13,7 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<TableLayout <com.android.launcher3.widget.picker.WidgetsListTableView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widgets_table" android:id="@+id/widgets_table"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -140,7 +140,6 @@
<dimen name="widget_list_top_bottom_corner_radius">28dp</dimen> <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
<dimen name="widget_list_content_corner_radius">4dp</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_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_bottom_margin">2dp</dimen> <dimen name="widget_list_entry_bottom_margin">2dp</dimen>

View File

@ -107,7 +107,10 @@ public final class WidgetsListHeaderViewHolderBinderTest {
/* iconClickListener= */ view -> {}, /* iconClickListener= */ view -> {},
/* iconLongClickListener= */ view -> false); /* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder( mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter); LayoutInflater.from(mTestActivity),
mOnHeaderClickListener,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter);
} }
@After @After

View File

@ -107,7 +107,10 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest {
/* iconClickListener= */ view -> {}, /* iconClickListener= */ view -> {},
/* iconLongClickListener= */ view -> false); /* iconLongClickListener= */ view -> false);
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder( mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter); LayoutInflater.from(mTestActivity),
mOnHeaderClickListener,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter);
} }
@After @After

View File

@ -118,6 +118,7 @@ public final class WidgetsListTableViewHolderBinderTest {
mOnIconClickListener, mOnIconClickListener,
mOnLongClickListener, mOnLongClickListener,
mWidgetPreviewLoader, mWidgetPreviewLoader,
new WidgetsListDrawableFactory(mTestActivity),
widgetsListAdapter); widgetsListAdapter);
} }

View File

@ -103,18 +103,25 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) { OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
mLauncher = Launcher.getLauncher(context); mLauncher = Launcher.getLauncher(context);
mDiffReporter = new WidgetsDiffReporter(iconCache, this); mDiffReporter = new WidgetsDiffReporter(iconCache, this);
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context, mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
layoutInflater, iconClickListener, iconLongClickListener, layoutInflater, iconClickListener, iconLongClickListener,
widgetPreviewLoader, /* listAdapter= */ this); widgetPreviewLoader, listDrawableFactory, /* listAdapter= */ this);
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder); mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
mViewHolderBinders.put( mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER, VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder( new WidgetsListHeaderViewHolderBinder(
layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this)); layoutInflater,
/* onHeaderClickListener= */ this,
listDrawableFactory,
/* listAdapter= */ this));
mViewHolderBinders.put( mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SEARCH_HEADER, VIEW_TYPE_WIDGETS_SEARCH_HEADER,
new WidgetsListSearchHeaderViewHolderBinder( new WidgetsListSearchHeaderViewHolderBinder(
layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this)); layoutInflater,
/* onHeaderClickListener= */ this,
listDrawableFactory,
/* listAdapter= */ this));
} }
@Override @Override

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -60,9 +60,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
@Nullable private Drawable mIconDrawable; @Nullable private Drawable mIconDrawable;
private final int mIconSize; private final int mIconSize;
private final int mBottomMarginSize; private final int mBottomMarginSize;
private final float mTopBottomCornerRadius;
private final float mMiddleCornerRadius;
private final float mJoinedCornerRadius;
private ImageView mAppIcon; private ImageView mAppIcon;
private TextView mTitle; private TextView mTitle;
@ -70,6 +67,7 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
private CheckBox mExpandToggle; private CheckBox mExpandToggle;
private boolean mIsExpanded = false; private boolean mIsExpanded = false;
@Nullable private WidgetsListDrawableState mListDrawableState;
public WidgetsListHeader(Context context) { public WidgetsListHeader(Context context) {
this(context, /* attrs= */ null); this(context, /* attrs= */ null);
@ -90,12 +88,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
grid.iconSizePx); grid.iconSizePx);
mBottomMarginSize = mBottomMarginSize =
getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin); 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 @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}. */ /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
@UiThread @UiThread
public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) { public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
@ -263,20 +263,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
verifyHighRes(); 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) { private void setTitles(WidgetsListSearchHeaderEntry entry) {
mTitle.setText(entry.mPkgItem.title); 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. */ /** Verifies that the current icon is high-res otherwise posts a request to load the icon. */
public void verifyHighRes() { public void verifyHighRes() {
if (mIconLoadRequest != null) { if (mIconLoadRequest != null) {

View File

@ -30,13 +30,16 @@ public final class WidgetsListHeaderViewHolderBinder implements
ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> { ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
private final LayoutInflater mLayoutInflater; private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener; private final OnHeaderClickListener mOnHeaderClickListener;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter; private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater, public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
OnHeaderClickListener onHeaderClickListener, OnHeaderClickListener onHeaderClickListener,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) { WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater; mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener; mOnHeaderClickListener = onHeaderClickListener;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter; mWidgetsListAdapter = listAdapter;
} }
@ -44,7 +47,7 @@ public final class WidgetsListHeaderViewHolderBinder implements
public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) { public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
R.layout.widgets_list_row_header, parent, false); R.layout.widgets_list_row_header, parent, false);
header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
return new WidgetsListHeaderHolder(header); return new WidgetsListHeaderHolder(header);
} }
@ -52,12 +55,13 @@ public final class WidgetsListHeaderViewHolderBinder implements
public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data, public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
int position) { int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader; WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.updateListBackground(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown());
widgetsListHeader.applyFromItemInfoWithIcon(data); widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown()); widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded -> widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked( mOnHeaderClickListener.onHeaderClicked(
isExpanded, isExpanded,

View File

@ -31,13 +31,16 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> { ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
private final LayoutInflater mLayoutInflater; private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener; private final OnHeaderClickListener mOnHeaderClickListener;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter; private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater, public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
OnHeaderClickListener onHeaderClickListener, OnHeaderClickListener onHeaderClickListener,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) { WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater; mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener; mOnHeaderClickListener = onHeaderClickListener;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter; mWidgetsListAdapter = listAdapter;
} }
@ -45,7 +48,7 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) { public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) {
WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
R.layout.widgets_list_row_header, parent, false); R.layout.widgets_list_row_header, parent, false);
header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
return new WidgetsListSearchHeaderHolder(header); return new WidgetsListSearchHeaderHolder(header);
} }
@ -53,12 +56,13 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder, public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
WidgetsListSearchHeaderEntry data, int position) { WidgetsListSearchHeaderEntry data, int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader; WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.updateListBackground(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown());
widgetsListHeader.applyFromItemInfoWithIcon(data); widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown()); widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
/* isFirst= */ position == 0,
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded -> widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(isExpanded, mOnHeaderClickListener.onHeaderClicked(isExpanded,
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user))); new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));

View File

@ -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;
}
}

View File

@ -15,8 +15,10 @@
*/ */
package com.android.launcher3.widget.picker; 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.Context;
import android.content.res.Resources;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -51,10 +53,8 @@ public final class WidgetsListTableViewHolderBinder
private final OnClickListener mIconClickListener; private final OnClickListener mIconClickListener;
private final OnLongClickListener mIconLongClickListener; private final OnLongClickListener mIconLongClickListener;
private final WidgetPreviewLoader mWidgetPreviewLoader; private final WidgetPreviewLoader mWidgetPreviewLoader;
private final WidgetsListDrawableFactory mListDrawableFactory;
private final WidgetsListAdapter mWidgetsListAdapter; private final WidgetsListAdapter mWidgetsListAdapter;
private final float mTopBottomCornerRadius;
private final float mMiddleCornerRadius;
private final float mJoinedCornerRadius;
private boolean mApplyBitmapDeferred = false; private boolean mApplyBitmapDeferred = false;
public WidgetsListTableViewHolderBinder( public WidgetsListTableViewHolderBinder(
@ -63,19 +63,14 @@ public final class WidgetsListTableViewHolderBinder
OnClickListener iconClickListener, OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener, OnLongClickListener iconLongClickListener,
WidgetPreviewLoader widgetPreviewLoader, WidgetPreviewLoader widgetPreviewLoader,
WidgetsListDrawableFactory listDrawableFactory,
WidgetsListAdapter listAdapter) { WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater; mLayoutInflater = layoutInflater;
mIconClickListener = iconClickListener; mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener; mIconLongClickListener = iconLongClickListener;
mWidgetPreviewLoader = widgetPreviewLoader; mWidgetPreviewLoader = widgetPreviewLoader;
mListDrawableFactory = listDrawableFactory;
mWidgetsListAdapter = listAdapter; 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"); Log.v(TAG, "\nonCreateViewHolder");
} }
ViewGroup container = (ViewGroup) mLayoutInflater.inflate( WidgetsRowViewHolder viewHolder =
R.layout.widgets_table_container, parent, false); new WidgetsRowViewHolder(mLayoutInflater.inflate(
return new WidgetsRowViewHolder(container); R.layout.widgets_table_container, parent, false));
viewHolder.mTableContainer.setBackgroundDrawable(
mListDrawableFactory.createContentBackgroundDrawable());
return viewHolder;
} }
@Override @Override
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry, public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
int position) { int position) {
TableLayout table = holder.mTableContainer; WidgetsListTableView table = holder.mTableContainer;
if (DEBUG) { if (DEBUG) {
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]", Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
entry.mWidgets.size(), table.getChildCount())); entry.mWidgets.size(), table.getChildCount()));
} }
// The content is always joined to an expanded header above. table.setListDrawableState(
float topRadius = mJoinedCornerRadius; position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE);
float bottomRadius = position == mWidgetsListAdapter.getItemCount() - 1
? mTopBottomCornerRadius
: mMiddleCornerRadius;
table.setBackgroundDrawable(
WidgetsListDrawables.createListBackgroundDrawable(
holder.itemView.getContext(), topRadius, bottomRadius));
List<ArrayList<WidgetItem>> widgetItemsTable = List<ArrayList<WidgetItem>> widgetItemsTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow); WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);

View File

@ -15,8 +15,7 @@
*/ */
package com.android.launcher3.widget.picker; package com.android.launcher3.widget.picker;
import android.view.ViewGroup; import android.view.View;
import android.widget.TableLayout;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; 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. */ /** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
public final class WidgetsRowViewHolder extends ViewHolder { public final class WidgetsRowViewHolder extends ViewHolder {
public final TableLayout mTableContainer; public final WidgetsListTableView mTableContainer;
public WidgetsRowViewHolder(ViewGroup v) { public WidgetsRowViewHolder(View v) {
super(v); super(v);
mTableContainer = v.findViewById(R.id.widgets_table); mTableContainer = v.findViewById(R.id.widgets_table);