Merge "Update widget picker row logic to resolve cropping" into tm-qpr-dev
This commit is contained in:
@@ -33,6 +33,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -61,7 +62,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
implements OnClickListener, OnLongClickListener, DragSource,
|
||||
PopupDataProvider.PopupDataChangeListener, Insettable {
|
||||
/** The default number of cells that can fit horizontally in a widget sheet. */
|
||||
protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
|
||||
public static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4;
|
||||
|
||||
protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
|
||||
"launcher.widgets_education_tip_seen";
|
||||
@@ -70,15 +71,18 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
/* Touch handling related member variables. */
|
||||
private Toast mWidgetInstructionToast;
|
||||
|
||||
private int mContentHorizontalMarginInPx;
|
||||
@Px protected int mContentHorizontalMargin;
|
||||
@Px protected int mWidgetCellHorizontalPadding;
|
||||
|
||||
protected int mNavBarScrimHeight;
|
||||
private final Paint mNavBarScrimPaint;
|
||||
|
||||
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContentHorizontalMarginInPx = getResources().getDimensionPixelSize(
|
||||
mContentHorizontalMargin = getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_list_horizontal_margin);
|
||||
mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_cell_horizontal_padding);
|
||||
mNavBarScrimPaint = new Paint();
|
||||
mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
|
||||
}
|
||||
@@ -138,11 +142,11 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
mInsets.set(insets);
|
||||
int contentHorizontalMarginInPx = getResources().getDimensionPixelSize(
|
||||
@Px int contentHorizontalMargin = getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_list_horizontal_margin);
|
||||
if (contentHorizontalMarginInPx != mContentHorizontalMarginInPx) {
|
||||
onContentHorizontalMarginChanged(contentHorizontalMarginInPx);
|
||||
mContentHorizontalMarginInPx = contentHorizontalMarginInPx;
|
||||
if (contentHorizontalMargin != mContentHorizontalMargin) {
|
||||
onContentHorizontalMarginChanged(contentHorizontalMargin);
|
||||
mContentHorizontalMargin = contentHorizontalMargin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,19 +202,6 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
|
||||
MeasureSpec.getSize(heightMeasureSpec));
|
||||
}
|
||||
|
||||
/** Returns the number of cells that can fit horizontally in a given {@code content}. */
|
||||
protected int computeMaxHorizontalSpans(View content, int contentHorizontalPaddingPx) {
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
int availableWidth = content.getMeasuredWidth()
|
||||
- contentHorizontalPaddingPx
|
||||
- (2 * mContentHorizontalMarginInPx);
|
||||
Point cellSize = deviceProfile.getCellSize();
|
||||
if (cellSize.x > 0) {
|
||||
return availableWidth / cellSize.x;
|
||||
}
|
||||
return DEFAULT_MAX_HORIZONTAL_SPANS;
|
||||
}
|
||||
|
||||
private boolean beginDraggingWidget(WidgetCell v) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_DROP_TARGET, "2");
|
||||
|
||||
@@ -36,6 +36,8 @@ import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Px;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
@@ -69,8 +71,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
private static final long EDUCATION_TIP_DELAY_MS = 300;
|
||||
|
||||
private ItemInfo mOriginalItemInfo;
|
||||
private int mMaxHorizontalSpan = DEFAULT_MAX_HORIZONTAL_SPANS;
|
||||
private final int mWidgetCellHorizontalPadding;
|
||||
@Px private int mMaxHorizontalSpan;
|
||||
|
||||
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
|
||||
new OnLayoutChangeListener() {
|
||||
@@ -111,8 +112,6 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
if (!hasSeenEducationTip()) {
|
||||
addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
|
||||
}
|
||||
mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_cell_horizontal_padding);
|
||||
setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet));
|
||||
}
|
||||
|
||||
@@ -134,7 +133,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
private boolean updateMaxSpansPerRow() {
|
||||
if (getMeasuredWidth() == 0) return false;
|
||||
|
||||
int maxHorizontalSpan = computeMaxHorizontalSpans(mContent, mWidgetCellHorizontalPadding);
|
||||
@Px int maxHorizontalSpan = mContent.getMeasuredWidth() - (2 * mContentHorizontalMargin);
|
||||
if (mMaxHorizontalSpan != maxHorizontalSpan) {
|
||||
// Ensure the table layout is showing widgets in the right column after measure.
|
||||
mMaxHorizontalSpan = maxHorizontalSpan;
|
||||
@@ -184,7 +183,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
|
||||
TableLayout widgetsTable = findViewById(R.id.widgets_table);
|
||||
widgetsTable.removeAllViews();
|
||||
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(widgets, mMaxHorizontalSpan)
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgets, mActivityContext,
|
||||
mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
|
||||
mWidgetCellHorizontalPadding)
|
||||
.forEach(row -> {
|
||||
TableRow tableRow = new TableRow(getContext());
|
||||
tableRow.setGravity(Gravity.TOP);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.widget.model;
|
||||
|
||||
import androidx.annotation.Px;
|
||||
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
|
||||
@@ -26,7 +28,7 @@ import java.util.List;
|
||||
*/
|
||||
public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
|
||||
|
||||
private final int mMaxSpanSizeInCells;
|
||||
@Px private final int mMaxSpanSize;
|
||||
|
||||
/**
|
||||
* Constructor for {@link WidgetsListContentEntry}.
|
||||
@@ -37,7 +39,7 @@ public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
|
||||
*/
|
||||
public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
|
||||
List<WidgetItem> items) {
|
||||
this(pkgItem, titleSectionName, items, /* maxSpanSizeInCells= */ 0);
|
||||
this(pkgItem, titleSectionName, items, /* maxSpanSize= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,43 +48,43 @@ public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
|
||||
* @param pkgItem package info associated with the entry
|
||||
* @param titleSectionName title section name associated with the entry.
|
||||
* @param items list of widgets for the package.
|
||||
* @param maxSpanSizeInCells the max horizontal span in cells that is allowed for grouping more
|
||||
* @param maxSpanSize the max horizontal span in pixels that is allowed for grouping more
|
||||
* than one widgets in a table row.
|
||||
*/
|
||||
public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
|
||||
List<WidgetItem> items, int maxSpanSizeInCells) {
|
||||
List<WidgetItem> items, @Px int maxSpanSize) {
|
||||
super(pkgItem, titleSectionName, items);
|
||||
mMaxSpanSizeInCells = maxSpanSizeInCells;
|
||||
mMaxSpanSize = maxSpanSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSizeInCells: "
|
||||
+ mMaxSpanSizeInCells;
|
||||
return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSize: "
|
||||
+ mMaxSpanSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this {@link WidgetsListContentEntry} with updated
|
||||
* {@param maxSpanSizeInCells}.
|
||||
* Returns a copy of this {@link WidgetsListContentEntry} with updated {@code maxSpanSize}.
|
||||
*
|
||||
* @param maxSpanSizeInCells the maximum horizontal span in cells that is allowed for grouping
|
||||
* @param maxSpanSize the maximum horizontal span in pixels that is allowed for grouping
|
||||
* more than one widgets in a table row.
|
||||
*/
|
||||
public WidgetsListContentEntry withMaxSpanSize(int maxSpanSizeInCells) {
|
||||
if (mMaxSpanSizeInCells == maxSpanSizeInCells) return this;
|
||||
public WidgetsListContentEntry withMaxSpanSize(@Px int maxSpanSize) {
|
||||
if (mMaxSpanSize == maxSpanSize) return this;
|
||||
return new WidgetsListContentEntry(
|
||||
mPkgItem,
|
||||
mTitleSectionName,
|
||||
mWidgets,
|
||||
/* maxSpanSizeInCells= */ maxSpanSizeInCells);
|
||||
/* maxSpanSize= */ maxSpanSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max horizontal span size in cells that is allowed for grouping more than one
|
||||
* Returns the max horizontal span size in pixels that is allowed for grouping more than one
|
||||
* widget in a table row.
|
||||
*/
|
||||
public int getMaxSpanSizeInCells() {
|
||||
return mMaxSpanSizeInCells;
|
||||
@Px
|
||||
public int getMaxSpanSize() {
|
||||
return mMaxSpanSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,6 +93,6 @@ public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
|
||||
WidgetsListContentEntry otherEntry = (WidgetsListContentEntry) obj;
|
||||
return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
|
||||
&& mTitleSectionName.equals(otherEntry.mTitleSectionName)
|
||||
&& mMaxSpanSizeInCells == otherEntry.mMaxSpanSizeInCells;
|
||||
&& mMaxSpanSize == otherEntry.mMaxSpanSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -181,14 +182,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
}
|
||||
};
|
||||
|
||||
private final int mTabsHeight;
|
||||
private final int mWidgetSheetContentHorizontalPadding;
|
||||
@Px private final int mTabsHeight;
|
||||
|
||||
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
|
||||
@Nullable private PersonalWorkPagedView mViewPager;
|
||||
private boolean mIsInSearchMode;
|
||||
private boolean mIsNoWidgetsViewNeeded;
|
||||
private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
|
||||
@Px private int mMaxSpanPerRow;
|
||||
private TextView mNoWidgetsView;
|
||||
|
||||
private StickyHeaderLayout mSearchScrollView;
|
||||
@@ -224,8 +224,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
mTabsHeight = mHasWorkProfile
|
||||
? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
|
||||
: 0;
|
||||
mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
|
||||
R.dimen.widget_cell_horizontal_padding);
|
||||
|
||||
mUserManagerState.init(UserCache.INSTANCE.get(context),
|
||||
context.getSystemService(UserManager.class));
|
||||
@@ -337,7 +335,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
: mSearchScrollView.findViewById(R.id.title);
|
||||
mRightPane = mIsTwoPane ? mContent.findViewById(R.id.right_pane) : null;
|
||||
mWidgetsListTableViewHolderBinder =
|
||||
new WidgetsListTableViewHolderBinder(layoutInflater, this, this);
|
||||
new WidgetsListTableViewHolderBinder(mActivityContext, layoutInflater, this, this);
|
||||
onRecommendedWidgetsBound();
|
||||
onWidgetsBound();
|
||||
|
||||
@@ -536,22 +534,20 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
View content = mHasWorkProfile
|
||||
? mViewPager
|
||||
: mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
|
||||
|
||||
if (mIsTwoPane && mRightPane != null) {
|
||||
content = mRightPane;
|
||||
}
|
||||
|
||||
int maxHorizontalSpans = computeMaxHorizontalSpans(content,
|
||||
mWidgetSheetContentHorizontalPadding);
|
||||
if (mMaxSpansPerRow != maxHorizontalSpans) {
|
||||
mMaxSpansPerRow = maxHorizontalSpans;
|
||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
|
||||
mMaxSpansPerRow);
|
||||
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
|
||||
mMaxSpansPerRow);
|
||||
@Px int maxHorizontalSpan = content.getMeasuredWidth() - (2 * mContentHorizontalMargin);
|
||||
if (mMaxSpanPerRow != maxHorizontalSpan) {
|
||||
mMaxSpanPerRow = maxHorizontalSpan;
|
||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
|
||||
maxHorizontalSpan);
|
||||
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
|
||||
maxHorizontalSpan);
|
||||
if (mHasWorkProfile) {
|
||||
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
|
||||
mMaxSpansPerRow);
|
||||
mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(
|
||||
maxHorizontalSpan);
|
||||
}
|
||||
onRecommendedWidgetsBound();
|
||||
return true;
|
||||
@@ -700,8 +696,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
- noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
|
||||
|
||||
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
|
||||
recommendedWidgets, mMaxSpansPerRow);
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(
|
||||
recommendedWidgets,
|
||||
mActivityContext,
|
||||
mActivityContext.getDeviceProfile(),
|
||||
mMaxSpanPerRow,
|
||||
mWidgetCellHorizontalPadding);
|
||||
mRecommendedWidgetsTable.setRecommendedWidgets(
|
||||
recommendedWidgetsInTable, maxTableHeight);
|
||||
} else {
|
||||
@@ -1051,7 +1051,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
if (mAdapterType == PRIMARY || mAdapterType == WORK) {
|
||||
mWidgetsRecyclerView.addOnAttachStateChangeListener(mBindScrollbarInSearchMode);
|
||||
}
|
||||
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
|
||||
mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(mMaxSpanPerRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_DEFAULT;
|
||||
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST;
|
||||
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
|
||||
import static com.android.launcher3.widget.BaseWidgetSheet.DEFAULT_MAX_HORIZONTAL_SPANS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
@@ -32,6 +33,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.DiffUtil.DiffResult;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@@ -49,6 +51,7 @@ import com.android.launcher3.widget.model.WidgetListSpaceEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||
import com.android.launcher3.widget.util.WidgetSizes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -99,7 +102,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
|
||||
@Nullable private RecyclerView mRecyclerView;
|
||||
@Nullable private PackageUserKey mPendingClickHeader;
|
||||
private int mMaxSpanSize = 4;
|
||||
@Px private int mMaxHorizontalSpan;
|
||||
|
||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||
IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener,
|
||||
@@ -107,11 +110,14 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
WidgetsFullSheet.HeaderChangeListener headerChangeListener) {
|
||||
mHeaderChangeListener = headerChangeListener;
|
||||
mContext = context;
|
||||
mMaxHorizontalSpan = WidgetSizes.getWidgetSizePx(
|
||||
ActivityContext.lookupContext(context).getDeviceProfile(),
|
||||
DEFAULT_MAX_HORIZONTAL_SPANS, 1).getWidth();
|
||||
|
||||
mViewHolderBinders.put(
|
||||
VIEW_TYPE_WIDGETS_LIST,
|
||||
new WidgetsListTableViewHolderBinder(
|
||||
layoutInflater, iconClickListener, iconLongClickListener));
|
||||
mContext, layoutInflater, iconClickListener, iconLongClickListener));
|
||||
mViewHolderBinders.put(
|
||||
VIEW_TYPE_WIDGETS_HEADER,
|
||||
new WidgetsListHeaderViewHolderBinder(
|
||||
@@ -199,7 +205,8 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
} else if (entry instanceof WidgetsListContentEntry) {
|
||||
// Adjust the original content entries to accommodate for the current
|
||||
// maxSpanSize.
|
||||
return ((WidgetsListContentEntry) entry).withMaxSpanSize(mMaxSpanSize);
|
||||
return ((WidgetsListContentEntry) entry).withMaxSpanSize(
|
||||
mMaxHorizontalSpan);
|
||||
}
|
||||
return entry;
|
||||
})
|
||||
@@ -407,11 +414,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max horizontal span in cells that is allowed for grouping more than one widget in a
|
||||
* Sets the max horizontal span in pixels that is allowed for grouping more than one widget in a
|
||||
* table row.
|
||||
*/
|
||||
public void setMaxHorizontalSpansPerRow(int maxHorizontalSpans) {
|
||||
mMaxSpanSize = maxHorizontalSpans;
|
||||
public void setMaxHorizontalSpansPxPerRow(@Px int maxHorizontalSpan) {
|
||||
mMaxHorizontalSpan = maxHorizontalSpan;
|
||||
updateVisibleEntries();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
@@ -27,9 +28,13 @@ import android.view.ViewGroup;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Px;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.recyclerview.ViewHolderBinder;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||
import com.android.launcher3.widget.util.WidgetsTableUtils;
|
||||
@@ -47,13 +52,21 @@ public final class WidgetsListTableViewHolderBinder
|
||||
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final OnClickListener mIconClickListener;
|
||||
private @NonNull final Context mContext;
|
||||
private @NonNull final ActivityContext mActivityContext;
|
||||
@Px private final int mCellPadding;
|
||||
private final OnLongClickListener mIconLongClickListener;
|
||||
|
||||
public WidgetsListTableViewHolderBinder(
|
||||
@NonNull Context context,
|
||||
LayoutInflater layoutInflater,
|
||||
OnClickListener iconClickListener,
|
||||
OnLongClickListener iconLongClickListener) {
|
||||
mLayoutInflater = layoutInflater;
|
||||
mContext = context;
|
||||
mActivityContext = ActivityContext.lookupContext(context);
|
||||
mCellPadding = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.widget_cell_horizontal_padding);
|
||||
mIconClickListener = iconClickListener;
|
||||
mIconLongClickListener = iconLongClickListener;
|
||||
}
|
||||
@@ -87,8 +100,11 @@ public final class WidgetsListTableViewHolderBinder
|
||||
(position & POSITION_LAST) != 0));
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemsTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
|
||||
entry.mWidgets, entry.getMaxSpanSizeInCells());
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(entry.mWidgets,
|
||||
mContext,
|
||||
mActivityContext.getDeviceProfile(),
|
||||
entry.getMaxSpanSize(),
|
||||
mCellPadding);
|
||||
recycleTableBeforeBinding(table, widgetItemsTable);
|
||||
|
||||
// Bind the widget items.
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
*/
|
||||
package com.android.launcher3.widget.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Px;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -49,34 +54,41 @@ public final class WidgetsTableUtils {
|
||||
* Groups {@code widgetItems} items into a 2D array which matches their appearance in a UI
|
||||
* table. This takes liberty to rearrange widgets to make the table visually appealing.
|
||||
*/
|
||||
public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithReordering(
|
||||
List<WidgetItem> widgetItems, final int maxSpansPerRow) {
|
||||
public static List<ArrayList<WidgetItem>> groupWidgetItemsUsingRowPxWithReordering(
|
||||
List<WidgetItem> widgetItems, Context context, final DeviceProfile dp,
|
||||
final @Px int rowPx, final @Px int cellPadding) {
|
||||
List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
|
||||
.collect(Collectors.toList());
|
||||
return groupWidgetItemsIntoTableWithoutReordering(sortedWidgetItems, maxSpansPerRow);
|
||||
return groupWidgetItemsUsingRowPxWithoutReordering(sortedWidgetItems, context, dp, rowPx,
|
||||
cellPadding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups {@code widgetItems} into a 2D array which matches their appearance in a UI table while
|
||||
* maintaining their order.
|
||||
* maintaining their order. This function is a variant of
|
||||
* {@code groupWidgetItemsIntoTableWithoutReordering} in that this uses widget pixels for
|
||||
* calculation.
|
||||
*
|
||||
* <p>Grouping:
|
||||
* 1. Widgets and shortcuts never group together in the same row.
|
||||
* 2. The ordered widgets are grouped together in the same row until their total horizontal
|
||||
* spans exceed the {@code maxSpansPerRow} - 1.
|
||||
* 3. The order shortcuts are grouped together in the same row until their total horizontal
|
||||
* spans exceed the {@code maxSpansPerRow} - 1.
|
||||
* 4. If there is only one widget in a row, its width may exceed the {@code maxSpansPerRow}.
|
||||
* 2. The ordered widgets are grouped together in the same row until their individual occupying
|
||||
* pixels exceed the total allowed pixels for the cell.
|
||||
* 3. The ordered shortcuts are grouped together in the same row until their individual
|
||||
* occupying pixels exceed the total allowed pixels for the cell.
|
||||
* 4. If there is only one widget in a row, its width may exceed the {@code rowPx}.
|
||||
*
|
||||
* <p>Let's say the {@code maxSpansPerRow} is set to 6. Widgets can be grouped in the same row
|
||||
* if their total horizontal spans added don't exceed 5.
|
||||
* Example 1: Row 1: 2x2, 2x3, 1x1. Total horizontal spans is 5. This is okay.
|
||||
* Example 2: Row 1: 2x2, 4x3, 1x1. the total horizontal spans is 7. This is wrong. 4x3 and 1x1
|
||||
* should be moved to a new row.
|
||||
* Example 3: Row 1: 6x4. This is okay because this is the only item in the row.
|
||||
* <p>Let's say the {@code rowPx} is set to 600 and we have 5 widgets. Widgets can be grouped
|
||||
* in the same row if each of their individual occupying pixels does not exceed
|
||||
* {@code rowPx} / 5 - 2 * {@code cellPadding}.
|
||||
* Example 1: Row 1: 200x200, 200x300, 100x100. Average horizontal pixels is 200 and no widgets
|
||||
* exceed that width. This is okay.
|
||||
* Example 2: Row 1: 200x200, 400x300, 100x100. Average horizontal pixels is 200 and one widget
|
||||
* exceed that width. This is not allowed.
|
||||
* Example 3: Row 1: 700x400. This is okay because this is the only item in the row.
|
||||
*/
|
||||
public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTableWithoutReordering(
|
||||
List<WidgetItem> widgetItems, final int maxSpansPerRow) {
|
||||
public static List<ArrayList<WidgetItem>> groupWidgetItemsUsingRowPxWithoutReordering(
|
||||
List<WidgetItem> widgetItems, Context context, final DeviceProfile dp,
|
||||
final @Px int rowPx, final @Px int cellPadding) {
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
|
||||
ArrayList<WidgetItem> widgetItemsAtRow = null;
|
||||
@@ -86,23 +98,28 @@ public final class WidgetsTableUtils {
|
||||
widgetItemsTable.add(widgetItemsAtRow);
|
||||
}
|
||||
int numOfWidgetItems = widgetItemsAtRow.size();
|
||||
int totalHorizontalSpan = widgetItemsAtRow.stream().map(item -> item.spanX)
|
||||
.reduce(/* default= */ 0, Integer::sum);
|
||||
int totalHorizontalSpanAfterAddingWidget = widgetItem.spanX + totalHorizontalSpan;
|
||||
@Px int individualSpan = (rowPx / (numOfWidgetItems + 1)) - (2 * cellPadding);
|
||||
if (numOfWidgetItems == 0) {
|
||||
widgetItemsAtRow.add(widgetItem);
|
||||
} else if (
|
||||
// The max spans per row is reduced by 1 to ensure we don't pack too many
|
||||
// 1xn widgets on the same row, which may reduce the space for rendering a
|
||||
// widget's description.
|
||||
totalHorizontalSpanAfterAddingWidget <= maxSpansPerRow - 1
|
||||
&& widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))) {
|
||||
// Since the size of the widget cell is determined by dividing the maximum span
|
||||
// pixels evenly, making sure that each widget would have enough span pixels to
|
||||
// show their contents.
|
||||
widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))
|
||||
&& widgetItemsAtRow.stream().allMatch(
|
||||
item -> WidgetSizes.getWidgetItemSizePx(context, dp, item)
|
||||
.getWidth() <= individualSpan)
|
||||
&& WidgetSizes.getWidgetItemSizePx(context, dp, widgetItem)
|
||||
.getWidth() <= individualSpan) {
|
||||
// Group items in the same row if
|
||||
// 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
|
||||
// never a mix of both.
|
||||
// 2. the total number of horizontal spans are smaller than or equal to
|
||||
// MAX_SPAN_PER_ROW. If an item has a horizontal span > MAX_SPAN_PER_ROW, we just
|
||||
// place it in its own row regardless of the horizontal span limit.
|
||||
// 2. Each widget will have horizontal cell span pixels that is at least as large as
|
||||
// it is required to fit in the horizontal content, unless the widget horizontal
|
||||
// span pixels is larger than the maximum allowed.
|
||||
// If an item has horizontal span pixels larger than the maximum allowed pixels
|
||||
// per row, we just place it in its own row regardless of the horizontal span
|
||||
// limit.
|
||||
widgetItemsAtRow.add(widgetItem);
|
||||
} else {
|
||||
widgetItemsAtRow = new ArrayList<>();
|
||||
|
||||
+1
@@ -94,6 +94,7 @@ public final class WidgetsListTableViewHolderBinderTest {
|
||||
}).when(mIconCache).getTitleNoCache(any());
|
||||
|
||||
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
|
||||
mContext,
|
||||
LayoutInflater.from(mContext),
|
||||
mOnIconClickListener,
|
||||
mOnLongClickListener);
|
||||
|
||||
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
@@ -35,11 +36,13 @@ import android.os.UserHandle;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.icons.ComponentWithLabel;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
|
||||
import com.android.launcher3.util.ActivityContextWrapper;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.widget.util.WidgetsTableUtils;
|
||||
|
||||
@@ -57,11 +60,19 @@ import java.util.List;
|
||||
public final class WidgetsTableUtilsTest {
|
||||
private static final String TEST_PACKAGE = "com.google.test";
|
||||
|
||||
private static final int SPACE_SIZE = 10;
|
||||
private static final int CELL_SIZE = 50;
|
||||
private static final int NUM_OF_COLS = 5;
|
||||
private static final int NUM_OF_ROWS = 5;
|
||||
|
||||
@Mock
|
||||
private IconCache mIconCache;
|
||||
|
||||
@Mock
|
||||
private DeviceProfile mTestDeviceProfile;
|
||||
|
||||
private Context mContext;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
private InvariantDeviceProfile mTestInvariantProfile;
|
||||
private WidgetItem mWidget1x1;
|
||||
private WidgetItem mWidget2x2;
|
||||
private WidgetItem mWidget2x3;
|
||||
@@ -76,12 +87,13 @@ public final class WidgetsTableUtilsTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = getApplicationContext();
|
||||
mContext = new ActivityContextWrapper(getApplicationContext());
|
||||
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
mTestInvariantProfile = new InvariantDeviceProfile();
|
||||
mTestInvariantProfile.numColumns = NUM_OF_COLS;
|
||||
mTestInvariantProfile.numRows = NUM_OF_ROWS;
|
||||
|
||||
initDP();
|
||||
initTestWidgets();
|
||||
initTestShortcuts();
|
||||
|
||||
@@ -92,17 +104,17 @@ public final class WidgetsTableUtilsTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
|
||||
public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding0_shouldGroupWidgetsInTable() {
|
||||
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
|
||||
mWidget2x2);
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
|
||||
widgetItems, /* maxSpansPerRow= */ 5);
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
|
||||
mTestDeviceProfile, 220, 0);
|
||||
|
||||
// Row 0: 1x1, 2x2
|
||||
// Row 1: 2x3, 2x4
|
||||
// Row 2: 4x4
|
||||
// Row 0: 1x1(50px), 2x2(110px)
|
||||
// Row 1: 2x3(110px), 2x4(110px)
|
||||
// Row 2: 4x4(230px)
|
||||
assertThat(widgetItemInTable).hasSize(3);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
|
||||
@@ -110,65 +122,91 @@ public final class WidgetsTableUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
|
||||
public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding10_shouldGroupWidgetsInTable() {
|
||||
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
|
||||
mWidget2x2);
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
|
||||
widgetItems, /* maxSpansPerRow= */ 4);
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
|
||||
mTestDeviceProfile, 220, 10);
|
||||
|
||||
// Row 0: 1x1, 2x2
|
||||
// Row 1: 2x3,
|
||||
// Row 2: 2x4,
|
||||
// Row 3: 4x4
|
||||
assertThat(widgetItemInTable).hasSize(4);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
|
||||
assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
|
||||
// Row 0: 1x1(50px), 2x2(110px)
|
||||
// Row 1: 2x3(110px), 2x4(110px)
|
||||
// Row 2: 4x4(230px)
|
||||
assertThat(widgetItemInTable).hasSize(5);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3);
|
||||
assertThat(widgetItemInTable.get(3)).containsExactly(mWidget2x4);
|
||||
assertThat(widgetItemInTable.get(4)).containsExactly(mWidget4x4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpansPerRow4_shouldGroupWidgetsInTable() {
|
||||
public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
|
||||
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
|
||||
mWidget2x2);
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
|
||||
mTestDeviceProfile, 350, 0);
|
||||
|
||||
// Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
|
||||
// Row 1: 2x4(110px)
|
||||
// Row 2: 4x4(230px)
|
||||
assertThat(widgetItemInTable).hasSize(3);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
|
||||
List<WidgetItem> widgetItems = List.of(mWidget4x4, mShortcut3, mWidget2x3, mShortcut1,
|
||||
mWidget1x1, mShortcut2, mWidget2x4, mWidget2x2);
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
|
||||
widgetItems, /* maxSpansPerRow= */ 4);
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
|
||||
mTestDeviceProfile, 350, 0);
|
||||
|
||||
// Row 0: 1x1, 2x2
|
||||
// Row 1: 2x3,
|
||||
// Row 2: 2x4,
|
||||
// Row 3: 4x4
|
||||
// Row 4: shortcut3, shortcut1, shortcut2
|
||||
assertThat(widgetItemInTable).hasSize(5);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
|
||||
assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
|
||||
assertThat(widgetItemInTable.get(4)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
|
||||
// Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
|
||||
// Row 1: 2x4(110px),
|
||||
// Row 2: 4x4(230px)
|
||||
// Row 3: shortcut3(50px), shortcut1(50px), shortcut2(50px)
|
||||
assertThat(widgetItemInTable).hasSize(4);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
|
||||
assertThat(widgetItemInTable.get(3)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupWidgetItemsIntoTableWithoutReordering_shouldMaintainTheOrder() {
|
||||
public void groupWidgetItemsIntoTableWithoutReordering_maxSpanPxPerRow220_cellPadding0_shouldMaintainTheOrder() {
|
||||
List<WidgetItem> widgetItems =
|
||||
List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4, mWidget2x2);
|
||||
|
||||
List<ArrayList<WidgetItem>> widgetItemInTable =
|
||||
WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
|
||||
widgetItems, /* maxSpansPerRow= */ 5);
|
||||
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(widgetItems, mContext,
|
||||
mTestDeviceProfile, 220, 0);
|
||||
|
||||
// Row 0: 4x4
|
||||
// Row 1: 2x3, 1x1
|
||||
// Row 2: 2x4, 2x2
|
||||
// Row 0: 4x4(230px)
|
||||
// Row 1: 2x3(110px), 1x1(50px)
|
||||
// Row 2: 2x4(110px), 2x2(110px)
|
||||
assertThat(widgetItemInTable).hasSize(3);
|
||||
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget4x4);
|
||||
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget1x1);
|
||||
assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4, mWidget2x2);
|
||||
}
|
||||
|
||||
private void initDP() {
|
||||
doAnswer(i -> {
|
||||
((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
|
||||
return null;
|
||||
}).when(mTestDeviceProfile).getCellSize(any(Point.class));
|
||||
when(mTestDeviceProfile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
|
||||
mTestDeviceProfile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE);
|
||||
when(mTestDeviceProfile.shouldInsetWidgets()).thenReturn(false);
|
||||
}
|
||||
|
||||
private void initTestWidgets() {
|
||||
List<Point> widgetSizes = List.of(new Point(1, 1), new Point(2, 2), new Point(2, 3),
|
||||
new Point(2, 4), new Point(4, 4));
|
||||
@@ -184,7 +222,7 @@ public final class WidgetsTableUtilsTest {
|
||||
LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
|
||||
widgetInfo.spanX = widgetSize.x;
|
||||
widgetInfo.spanY = widgetSize.y;
|
||||
widgetItems.add(new WidgetItem(widgetInfo, mTestProfile, mIconCache));
|
||||
widgetItems.add(new WidgetItem(widgetInfo, mTestInvariantProfile, mIconCache));
|
||||
}
|
||||
);
|
||||
mWidget1x1 = widgetItems.get(0);
|
||||
|
||||
Reference in New Issue
Block a user