Update widget picker row logic to resolve cropping
Using pixels instead of cell spans for calculating the widget picker row
binding logic.
Before: https://screenshot.googleplex.com/3uXX2m7xWh3om5t
Now: https://screenshot.googleplex.com/C593mrQfqTahNrQ
Test: Manual
Fix: 269790954
Change-Id: Ib591a6f6d3cf8b72766e0714b9bc8ceb86b2ba4f
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 2ac1e94..7066fb5 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -33,6 +33,7 @@
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 @@
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 @@
/* 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 @@
@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 @@
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");
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 4099302..06c622d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -36,6 +36,8 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
index 626e0b9..d709196 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -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 @@
*/
public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
- private final int mMaxSpanSizeInCells;
+ @Px private final int mMaxSpanSize;
/**
* Constructor for {@link WidgetsListContentEntry}.
@@ -37,7 +39,7 @@
*/
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 @@
* @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 @@
WidgetsListContentEntry otherEntry = (WidgetsListContentEntry) obj;
return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
&& mTitleSectionName.equals(otherEntry.mTitleSectionName)
- && mMaxSpanSizeInCells == otherEntry.mMaxSpanSizeInCells;
+ && mMaxSpanSize == otherEntry.mMaxSpanSize;
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index f8068aa..5c86a66 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -53,6 +53,7 @@
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 @@
}
};
- 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;
@@ -221,8 +221,6 @@
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));
@@ -334,7 +332,7 @@
: 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();
@@ -533,22 +531,20 @@
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;
@@ -697,8 +693,12 @@
- 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 {
@@ -1046,7 +1046,7 @@
if (mAdapterType == PRIMARY || mAdapterType == WORK) {
mWidgetsRecyclerView.addOnAttachStateChangeListener(mBindScrollbarInSearchMode);
}
- mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
+ mWidgetsListAdapter.setMaxHorizontalSpansPxPerRow(mMaxSpanPerRow);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index c28402e..c89eea8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -19,6 +19,7 @@
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 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.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 @@
@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 @@
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 @@
} 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 @@
}
/**
- * 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();
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 2e8f0ab..c7d2aa3 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -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.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 @@
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 @@
(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.
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
index 72e27bf..74d3062 100644
--- a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -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 @@
* 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 @@
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<>();
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 9dc46f1..e0101f5 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -94,6 +94,7 @@
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
+ mContext,
LayoutInflater.from(mContext),
mOnIconClickListener,
mOnLongClickListener);
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 715dcca..d2c2fd7 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -23,6 +23,7 @@
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 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 @@
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 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 @@
@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 @@
}
@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 @@
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);