cache) {
DotRenderer renderer = cache.get(size);
if (renderer == null) {
@@ -733,8 +828,18 @@ private static DotRenderer createDotRenderer(int size,
}
/**
- * QSB width is always calculated because when in 3 button nav the width doesn't
- * follow the
+ * Return maximum of all apps row count displayed on screen. Note that 1) Partially displayed
+ * row is counted as 1 row, and 2) we don't exclude the space of floating search bar. This
+ * method is used for calculating number of {@link BubbleTextView} we need to pre-inflate. Thus
+ * reasonable over estimation is fine.
+ */
+ public int getMaxAllAppsRowCount() {
+ return (int) (Math.ceil((availableHeightPx - allAppsPadding.top)
+ / (float) allAppsCellHeightPx));
+ }
+
+ /**
+ * QSB width is always calculated because when in 3 button nav the width doesn't follow the
* width of the hotseat.
*/
private int calculateQsbWidth(int hotseatBorderSpace) {
@@ -757,7 +862,7 @@ private int getIconToIconWidthForColumns(int columns) {
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
if (mIsResponsiveGrid) {
- return mResponsiveWidthSpec.getStartPaddingPx();
+ return mResponsiveWorkspaceWidthSpec.getStartPaddingPx();
}
if (isVerticalBarLayout()) {
@@ -770,14 +875,13 @@ private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
}
private void calculateAndSetWorkspaceVerticalPadding(Context context,
- InvariantDeviceProfile inv,
- int extraSpace) {
+ InvariantDeviceProfile inv,
+ int extraSpace) {
if (mIsResponsiveGrid) {
- workspaceTopPadding = mResponsiveHeightSpec.getStartPaddingPx();
- workspaceBottomPadding = mResponsiveHeightSpec.getEndPaddingPx();
+ workspaceTopPadding = mResponsiveWorkspaceHeightSpec.getStartPaddingPx();
+ workspaceBottomPadding = mResponsiveWorkspaceHeightSpec.getEndPaddingPx();
} else if (mIsScalableGrid && inv.devicePaddingId != INVALID_RESOURCE_HANDLE) {
- // Paddings were created assuming no scaling, so we first unscale the extra
- // space.
+ // Paddings were created assuming no scaling, so we first unscale the extra space.
int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit);
DevicePaddings devicePaddings = new DevicePaddings(context, inv.devicePaddingId);
DevicePadding padding = devicePaddings.getDevicePadding(unscaledExtraSpace);
@@ -793,8 +897,7 @@ private void calculateAndSetWorkspaceVerticalPadding(Context context,
/** Updates hotseatCellHeightPx and hotseatBarSizePx */
private void updateHotseatSizes(int hotseatIconSizePx) {
- // Ensure there is enough space for folder icons, which have a slightly larger
- // radius.
+ // Ensure there is enough space for folder icons, which have a slightly larger radius.
hotseatCellHeightPx = getIconSizeWithOverlap(hotseatIconSizePx * 2) - hotseatIconSizePx / 2;
var space = Math.abs(hotseatCellHeightPx / 2) - 16;
@@ -803,8 +906,8 @@ private void updateHotseatSizes(int hotseatIconSizePx) {
.firstBlocking(preferenceManager2.getHotseatBottomFactor());
if (isVerticalBarLayout()) {
- hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
- + hotseatBarSidePaddingEndPx + space;
+ hotseatBarSizePx = hotseatIconSizePx + mHotseatBarEdgePaddingPx
+ + mHotseatBarWorkspaceSpacePx + space;
} else if (isQsbInline) {
hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight)
+ hotseatBarBottomSpacePx + space;
@@ -822,13 +925,11 @@ private void updateHotseatSizes(int hotseatIconSizePx) {
}
/**
- * Calculates the width of the hotseat, changing spaces between the icons and
- * removing icons if
+ * Calculates the width of the hotseat, changing spaces between the icons and removing icons if
* necessary.
*/
public void recalculateHotseatWidthAndBorderSpace() {
- if (!mIsScalableGrid)
- return;
+ if (!mIsScalableGrid || isTablet) return;
int columns = inv.hotseatColumnSpan[mTypeIndex];
float hotseatWidthPx = getIconToIconWidthForColumns(columns);
@@ -839,8 +940,7 @@ public void recalculateHotseatWidthAndBorderSpace() {
return;
}
- // The side space with inline buttons should be what is defined in
- // InvariantDeviceProfile
+ // The side space with inline buttons should be what is defined in InvariantDeviceProfile
int sideSpacePx = inlineNavButtonsEndSpacingPx;
int maxHotseatWidthPx = availableWidthPx - sideSpacePx - hotseatBarEndOffset;
int maxHotseatIconsWidthPx = maxHotseatWidthPx - (isQsbInline ? hotseatQsbWidth : 0);
@@ -885,8 +985,8 @@ private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale)
int verticalSpacePx = 0;
if (mIsResponsiveGrid) {
- horizontalSpacePx = mResponsiveWidthSpec.getGutterPx();
- verticalSpacePx = mResponsiveHeightSpec.getGutterPx();
+ horizontalSpacePx = mResponsiveWorkspaceWidthSpec.getGutterPx();
+ verticalSpacePx = mResponsiveWorkspaceHeightSpec.getGutterPx();
} else if (mIsScalableGrid) {
horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics, scale);
verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics, scale);
@@ -930,10 +1030,8 @@ public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowB
.setMultiWindowMode(true)
.build();
- // We use these scales to measure and layout the widgets using their full
- // invariant profile
- // sizes and then draw them scaled and centered to fit in their multi-window
- // mode cellspans.
+ // We use these scales to measure and layout the widgets using their full invariant profile
+ // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
if (appWidgetScaleX != 1 || appWidgetScaleY != 1) {
@@ -951,8 +1049,7 @@ public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowB
/**
* Checks if there is enough space for labels on the workspace.
* If there is not, labels on the Workspace are hidden.
- * It is important to call this method after the All Apps variables have been
- * set.
+ * It is important to call this method after the All Apps variables have been set.
*/
private void hideWorkspaceLabelsIfNotEnoughSpace() {
float iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
@@ -960,14 +1057,11 @@ private void hideWorkspaceLabelsIfNotEnoughSpace() {
- iconTextHeight;
if (mIsResponsiveGrid) {
- // Hide text only if doesn't fit inside the cell for responsive grid
- if (workspaceCellPaddingY < 0) {
- iconTextSizePx = 0;
- iconDrawablePaddingPx = 0;
- int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx);
- cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2;
- autoResizeAllAppsCells();
- }
+ iconTextSizePx = 0;
+ iconDrawablePaddingPx = 0;
+ int iconSizeWithOverlap = getIconSizeWithOverlap(iconSizePx);
+ cellYPaddingPx = Math.max(0, getCellSize().y - iconSizeWithOverlap) / 2;
+ autoResizeAllAppsCells();
return;
}
@@ -985,19 +1079,25 @@ private void hideWorkspaceLabelsIfNotEnoughSpace() {
* Returns the amount of extra (or unused) vertical space.
*/
private int updateAvailableDimensions(Resources res) {
+ iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
+
+ if (mIsResponsiveGrid) {
+ iconSizePx = mResponsiveWorkspaceCellSpec.getIconSize();
+ iconTextSizePx = mResponsiveWorkspaceCellSpec.getIconTextSize();
+ mIconDrawablePaddingOriginalPx = mResponsiveWorkspaceCellSpec.getIconDrawablePadding();
+ updateIconSize(1f, res);
+ updateWorkspacePadding();
+ return 0;
+ }
+
float invIconSizeDp = inv.iconSize[mTypeIndex];
float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics));
iconTextSizePx = pxFromSp(invIconTextSizeSp, mMetrics);
- iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
updateIconSize(1f, res);
updateWorkspacePadding();
- if (mIsResponsiveGrid) {
- return 0;
- }
-
// Check to see if the icons fit within the available height.
float usedHeight = getCellLayoutHeightSpecification();
final int maxHeight = getCellLayoutHeight();
@@ -1008,12 +1108,11 @@ private int updateAvailableDimensions(Resources res) {
float scaleX = 1f;
if (mIsScalableGrid) {
// We scale to fit the cellWidth and cellHeight in the available space.
- // The benefit of scalable grids is that we can get consistent aspect ratios
- // between
+ // The benefit of scalable grids is that we can get consistent aspect ratios between
// devices.
- float usedWidth = getCellLayoutWidthSpecification() + (desiredWorkspaceHorizontalMarginPx * 2);
- // We do not subtract padding here, as we also scale the workspace padding if
- // needed.
+ float usedWidth =
+ getCellLayoutWidthSpecification() + (desiredWorkspaceHorizontalMarginPx * 2);
+ // We do not subtract padding here, as we also scale the workspace padding if needed.
scaleX = availableWidthPx / usedWidth;
shouldScale = true;
}
@@ -1038,12 +1137,16 @@ private int getCellLayoutWidthSpecification() {
+ cellLayoutPaddingPx.left + cellLayoutPaddingPx.right;
}
- private int getNormalizedIconDrawablePadding() {
+ private int getNormalizedIconDrawablePadding(int iconSizePx, int iconDrawablePadding) {
// TODO(b/235886078): workaround needed because of this bug
// Icons are 10% larger on XML than their visual size,
// so remove that extra space to get labels closer to the correct padding
- int iconVisibleSizePx = (int) Math.round(ICON_VISIBLE_AREA_FACTOR * iconSizePx);
- return Math.max(0, mIconDrawablePaddingOriginalPx - ((iconSizePx - iconVisibleSizePx) / 2));
+ int iconVisibleSizePx = Math.round(ICON_VISIBLE_AREA_FACTOR * iconSizePx);
+ return Math.max(0, iconDrawablePadding - ((iconSizePx - iconVisibleSizePx) / 2));
+ }
+
+ private int getNormalizedIconDrawablePadding() {
+ return getNormalizedIconDrawablePadding(iconSizePx, mIconDrawablePaddingOriginalPx);
}
private int getNormalizedFolderChildDrawablePaddingPx(int textHeight) {
@@ -1062,11 +1165,9 @@ private int getIconSizeWithOverlap(int iconSize) {
}
/**
- * Updating the iconSize affects many aspects of the launcher layout, such as:
- * iconSizePx,
+ * Updating the iconSize affects many aspects of the launcher layout, such as: iconSizePx,
* iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
- * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and
- * folderIconOffsetYPx.
+ * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
*/
public void updateIconSize(float scale, Resources res) {
// Icon scale should never exceed 1, otherwise pixellation may occur.
@@ -1078,8 +1179,8 @@ public void updateIconSize(float scale, Resources res) {
cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv, scale);
if (mIsResponsiveGrid) {
- cellWidthPx = mResponsiveWidthSpec.getCellSizePx();
- cellHeightPx = mResponsiveHeightSpec.getCellSizePx();
+ cellWidthPx = mResponsiveWorkspaceWidthSpec.getCellSizePx();
+ cellHeightPx = mResponsiveWorkspaceHeightSpec.getCellSizePx();
if (cellWidthPx < iconSizePx) {
// get a smaller icon size
@@ -1087,20 +1188,22 @@ public void updateIconSize(float scale, Resources res) {
}
iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
- int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
- int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
-
- while (iconSizePx > mIconSizeSteps.minimumIconSize()
- && cellContentHeight > cellHeightPx) {
- iconDrawablePaddingPx -= cellContentHeight - cellHeightPx;
- if (iconDrawablePaddingPx < 0) {
- // get a smaller icon size
- iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
- iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
+
+ CellContentDimensions cellContentDimensions = new CellContentDimensions(iconSizePx,
+ iconDrawablePaddingPx,
+ iconTextSizePx);
+ if (isVerticalLayout) {
+ if (cellHeightPx < iconSizePx) {
+ cellContentDimensions.setIconSizePx(
+ mIconSizeSteps.getIconSmallerThan(cellHeightPx));
}
- // calculate new cellContentHeight
- cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
+ } else {
+ cellContentDimensions.resizeToFitCellHeight(cellHeightPx, mIconSizeSteps);
}
+ iconSizePx = cellContentDimensions.getIconSizePx();
+ iconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+ iconTextSizePx = cellContentDimensions.getIconTextSizePx();
+ int cellContentHeight = cellContentDimensions.getCellContentHeight();
cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
} else if (mIsScalableGrid) {
@@ -1109,8 +1212,7 @@ public void updateIconSize(float scale, Resources res) {
cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
if (cellWidthPx < iconSizePx) {
- // If cellWidth no longer fit iconSize, reduce borderSpace to make cellWidth
- // bigger.
+ // If cellWidth no longer fit iconSize, reduce borderSpace to make cellWidth bigger.
int numColumns = getPanelCount() * inv.numColumns;
int numBorders = numColumns - 1;
int extraWidthRequired = (iconSizePx - cellWidthPx) * numColumns;
@@ -1127,7 +1229,8 @@ public void updateIconSize(float scale, Resources res) {
}
}
- int cellTextAndPaddingHeight = iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ int cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
if (cellHeightPx < cellContentHeight) {
// If cellHeight no longer fit iconSize, reduce borderSpace to make cellHeight
@@ -1154,12 +1257,14 @@ public void updateIconSize(float scale, Resources res) {
iconSizePx = (int) (iconSizePx * ratio);
iconTextSizePx = (int) (iconTextSizePx * ratio);
}
- cellTextAndPaddingHeight = iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
+ cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
}
cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
}
cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
- desiredWorkspaceHorizontalMarginPx = (int) (desiredWorkspaceHorizontalMarginOriginalPx * scale);
+ desiredWorkspaceHorizontalMarginPx =
+ (int) (desiredWorkspaceHorizontalMarginOriginalPx * scale);
} else {
iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale);
cellWidthPx = iconSizePx + iconDrawablePaddingPx;
@@ -1169,8 +1274,7 @@ public void updateIconSize(float scale, Resources res) {
int cellPaddingY = (getCellSize().y - cellHeightPx) / 2;
if (iconDrawablePaddingPx > cellPaddingY && !isVerticalLayout
&& !isMultiWindowMode) {
- // Ensures that the label is closer to its corresponding icon. This is not an
- // issue
+ // Ensures that the label is closer to its corresponding icon. This is not an issue
// with vertical bar layout or multi-window mode since the issue is handled
// separately with their calls to {@link #adjustToHideWorkspaceLabels}.
cellHeightPx -= (iconDrawablePaddingPx - cellPaddingY);
@@ -1187,9 +1291,13 @@ public void updateIconSize(float scale, Resources res) {
updateAllAppsIconSize(scale, res);
}
updateAllAppsContainerWidth();
- if (isVerticalBarLayout()) {
+ if (isVerticalBarLayout() || PreferenceExtensionsKt.firstBlocking(preferenceManager2.getShowIconLabelsOnHomeScreen())) {
hideWorkspaceLabelsIfNotEnoughSpace();
}
+ if (FeatureFlags.twoLineAllApps(LawnchairApp.Companion.getInstance())) {
+ // Add extra textHeight to the existing allAppsCellHeight.
+ allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+ }
updateHotseatSizes(iconSizePx);
@@ -1201,24 +1309,24 @@ public void updateIconSize(float scale, Resources res) {
float minSpacing = pxFromDp(MIN_WIDGET_PADDING_DP, mMetrics);
if (cellLayoutBorderSpacePx.x < minSpacing
|| cellLayoutBorderSpacePx.y < minSpacing) {
- widgetPadding.left = widgetPadding.right = Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.x));
- widgetPadding.top = widgetPadding.bottom = Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.y));
+ widgetPadding.left = widgetPadding.right =
+ Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.x));
+ widgetPadding.top = widgetPadding.bottom =
+ Math.round(Math.max(0, minSpacing - cellLayoutBorderSpacePx.y));
} else {
widgetPadding.setEmpty();
}
}
/**
- * This method calculates the space between the icons to achieve a certain
- * width.
+ * This method calculates the space between the icons to achieve a certain width.
*/
private int calculateHotseatBorderSpace(float hotseatWidthPx, int numExtraBorder) {
int numBorders = (numShownHotseatIcons - 1 + numExtraBorder);
- if (numBorders <= 0)
- return 0;
+ if (numBorders <= 0) return 0;
+
float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
- int hotseatBorderSpacePx = (int) (hotseatWidthPx - hotseatIconsTotalPx)
- / numBorders;
+ int hotseatBorderSpacePx = (int) (hotseatWidthPx - hotseatIconsTotalPx) / numBorders;
return Math.min(hotseatBorderSpacePx, mMaxHotseatIconSpacePx);
}
@@ -1243,11 +1351,11 @@ private void updateAllAppsIconSize(float scale, Resources res) {
allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
if (allAppsCellWidthPx < allAppsIconSizePx) {
- // If allAppsCellWidth no longer fit allAppsIconSize, reduce allAppsBorderSpace
- // to
+ // If allAppsCellWidth no longer fit allAppsIconSize, reduce allAppsBorderSpace to
// make allAppsCellWidth bigger.
int numBorders = inv.numAllAppsColumns - 1;
- int extraWidthRequired = (allAppsIconSizePx - allAppsCellWidthPx) * inv.numAllAppsColumns;
+ int extraWidthRequired =
+ (allAppsIconSizePx - allAppsCellWidthPx) * inv.numAllAppsColumns;
if (allAppsBorderSpacePx.x * numBorders >= extraWidthRequired) {
allAppsCellWidthPx = allAppsIconSizePx;
allAppsBorderSpacePx.x -= extraWidthRequired / numBorders;
@@ -1268,28 +1376,61 @@ private void updateAllAppsIconSize(float scale, Resources res) {
allAppsCellHeightPx = cellContentHeight;
}
} else {
- float invIconSizeDp = inv.allAppsIconSize[INDEX_DEFAULT];
+ float invIconSizeDp = inv.allAppsIconSize[mTypeIndex];
float invIconTextSizeSp = inv.allAppsIconTextSize[mTypeIndex];
allAppsIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale);
+ allAppsIconDrawablePaddingPx =
+ res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
allAppsIconTextSizePx *= mTextFactors.getAllAppsIconTextSizeFactor();
- allAppsIconDrawablePaddingPx = res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
}
}
private void updateAllAppsWithResponsiveMeasures() {
- allAppsIconSizePx = iconSizePx;
- allAppsIconTextSizePx = iconTextSizePx;
- allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
-
+ allAppsIconSizePx = mResponsiveAllAppsCellSpec.getIconSize();
+ allAppsIconTextSizePx = mResponsiveAllAppsCellSpec.getIconTextSize();
+ allAppsIconDrawablePaddingPx = getNormalizedIconDrawablePadding(allAppsIconSizePx,
+ mResponsiveAllAppsCellSpec.getIconDrawablePadding());
allAppsBorderSpacePx = new Point(
- mAllAppsResponsiveWidthSpec.getGutterPx(),
- mAllAppsResponsiveHeightSpec.getGutterPx());
- allAppsCellHeightPx = mAllAppsResponsiveHeightSpec.getCellSizePx()
- + mAllAppsResponsiveHeightSpec.getGutterPx();
- allAppsCellWidthPx = mAllAppsResponsiveWidthSpec.getCellSizePx();
- allAppsLeftRightPadding = mAllAppsResponsiveWidthSpec.getStartPaddingPx();
+ mResponsiveAllAppsWidthSpec.getGutterPx(),
+ mResponsiveAllAppsHeightSpec.getGutterPx()
+ );
+ allAppsCellHeightPx = mResponsiveAllAppsHeightSpec.getCellSizePx();
+ allAppsCellWidthPx = mResponsiveAllAppsWidthSpec.getCellSizePx();
+
+ // This workaround is needed to align AllApps icons with Workspace icons
+ // since AllApps doesn't have borders between cells
+ int halfBorder = allAppsBorderSpacePx.x / 2;
+ allAppsPadding.left = mResponsiveAllAppsWidthSpec.getStartPaddingPx() - halfBorder;
+ allAppsPadding.right = mResponsiveAllAppsWidthSpec.getEndPaddingPx() - halfBorder;
+
+
+ // Reduce the size of the app icon if it doesn't fit
+ if (allAppsCellWidthPx < allAppsIconSizePx) {
+ // get a smaller icon size
+ allAppsIconSizePx = mIconSizeSteps.getIconSmallerThan(allAppsCellWidthPx);
+ }
+
+ CellContentDimensions cellContentDimensions = new CellContentDimensions(
+ allAppsIconSizePx, allAppsIconDrawablePaddingPx, (int) allAppsIconTextSizePx);
+
+ if (allAppsCellHeightPx < cellContentDimensions.getCellContentHeight()) {
+ if (isVerticalBarLayout()) {
+ if (allAppsCellHeightPx < iconSizePx) {
+ cellContentDimensions.setIconSizePx(
+ mIconSizeSteps.getIconSmallerThan(allAppsCellHeightPx));
+ }
+ } else {
+ cellContentDimensions.resizeToFitCellHeight(allAppsCellHeightPx,
+ mIconSizeSteps);
+ }
+ allAppsIconSizePx = cellContentDimensions.getIconSizePx();
+ allAppsIconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+ allAppsIconTextSizePx = cellContentDimensions.getIconTextSizePx();
+ }
+
+ allAppsCellHeightPx += mResponsiveAllAppsHeightSpec.getGutterPx();
}
/**
@@ -1303,21 +1444,24 @@ public void autoResizeAllAppsCells() {
}
private void updateAllAppsContainerWidth() {
- int cellLayoutHorizontalPadding = (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2;
+ int cellLayoutHorizontalPadding =
+ (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2;
if (isTablet) {
int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
+ (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1))
- + allAppsLeftRightPadding * 2;
+ + allAppsPadding.left + allAppsPadding.right;
allAppsLeftRightMargin = Math.max(1, (availableWidthPx - usedWidth) / 2);
- } else {
- allAppsLeftRightPadding = Math.max(1, desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding
- - (allAppsBorderSpacePx.x / 2));
+ } else if (!mIsResponsiveGrid) {
+ allAppsPadding.left = allAppsPadding.right =
+ Math.max(0, desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding
+ - (allAppsBorderSpacePx.x / 2));
}
var allAppLeftRightMarginMultiplier = PreferenceExtensionsKt
.firstBlocking(preferenceManager2.getDrawerLeftRightMarginFactor());
var marginMultiplier = allAppLeftRightMarginMultiplier * (!isTablet ? 100 : 2);
- allAppsLeftRightMargin *= marginMultiplier;
- allAppsLeftRightPadding *= marginMultiplier;
+ allAppsLeftRightMargin = (int) (allAppsLeftRightMargin * marginMultiplier);
+ allAppsPadding.left = (int) (allAppsPadding.left * marginMultiplier);
+ allAppsPadding.right = (int) (allAppsPadding.right * marginMultiplier);
}
private void setupAllAppsStyle(Context context) {
@@ -1325,7 +1469,7 @@ private void setupAllAppsStyle(Context context) {
inv.allAppsStyle != INVALID_RESOURCE_HANDLE ? inv.allAppsStyle
: R.style.AllAppsStyleDefault, R.styleable.AllAppsStyle);
- allAppsLeftRightPadding = allAppsStyle.getDimensionPixelSize(
+ allAppsPadding.left = allAppsPadding.right = allAppsStyle.getDimensionPixelSize(
R.styleable.AllAppsStyle_horizontalPadding, 0);
allAppsStyle.recycle();
}
@@ -1334,23 +1478,22 @@ private void updateAvailableFolderCellDimensions(Resources res) {
updateFolderCellSize(1f, res);
// Responsive grid doesn't need to scale the folder
- if (mIsResponsiveGrid)
- return;
+ if (mIsResponsiveGrid) return;
// For usability we can't have the folder use the whole width of the screen
Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the folder fit within the available height.
- float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
- + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacePx.y)
+ float contentUsedHeight = folderCellHeightPx * numFolderRows
+ + ((numFolderRows - 1) * folderCellLayoutBorderSpacePx.y)
+ folderFooterHeightPx
+ folderContentPaddingTop;
int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y;
float scaleY = contentMaxHeight / contentUsedHeight;
// Check if the folder fit within the available width.
- float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
- + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x)
+ float contentUsedWidth = folderCellWidthPx * numFolderColumns
+ + ((numFolderColumns - 1) * folderCellLayoutBorderSpacePx.x)
+ folderContentPaddingLeftRight * 2;
int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x;
float scaleX = contentMaxWidth / contentUsedWidth;
@@ -1362,18 +1505,15 @@ private void updateAvailableFolderCellDimensions(Resources res) {
}
private void updateFolderCellSize(float scale, Resources res) {
- float invIconSizeDp = inv.iconSize[mTypeIndex];
- folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
- folderChildTextSizePx = pxFromSp(inv.iconTextSize[mTypeIndex], mMetrics, scale);
- folderLabelTextSizePx = Math.max(pxFromSp(MIN_FOLDER_TEXT_SIZE_SP, mMetrics, scale),
- (int) (folderChildTextSizePx * folderLabelTextScale));
-
- int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
-
+ int minLabelTextSize = pxFromSp(MIN_FOLDER_TEXT_SIZE_SP, mMetrics, scale);
if (mIsResponsiveGrid) {
- folderCellWidthPx = mResponsiveFolderWidthSpec.getCellSizePx();
+ folderChildIconSizePx = mResponsiveWorkspaceCellSpec.getIconSize();
+ folderChildTextSizePx = mResponsiveWorkspaceCellSpec.getIconTextSize();
+ folderLabelTextSizePx = Math.max(minLabelTextSize,
+ (int) (folderChildTextSizePx * folderLabelTextScale));
+ int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
- // Height
+ folderCellWidthPx = mResponsiveFolderWidthSpec.getCellSizePx();
folderCellHeightPx = mResponsiveFolderHeightSpec.getCellSizePx();
folderContentPaddingTop = mResponsiveFolderHeightSpec.getStartPaddingPx();
folderFooterHeightPx = mResponsiveFolderHeightSpec.getEndPaddingPx();
@@ -1389,25 +1529,30 @@ private void updateFolderCellSize(float scale, Resources res) {
}
// Recalculating padding and cell height
- folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
- int folderCellContentHeight = folderChildIconSizePx + folderChildDrawablePaddingPx
- + textHeight;
-
- // Reduce the icon in height when it's taller than the expected cell height
- while (folderChildIconSizePx > mIconSizeSteps.minimumIconSize()
- && folderCellContentHeight > folderCellHeightPx) {
- folderChildDrawablePaddingPx -= folderCellContentHeight - folderCellHeightPx;
- if (folderChildDrawablePaddingPx < 0) {
- // get a smaller icon size
- folderChildIconSizePx = mIconSizeSteps.getNextLowerIconSize(
- folderChildIconSizePx);
- folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
- }
- // calculate new cellContentHeight
- folderCellContentHeight = folderChildIconSizePx + folderChildDrawablePaddingPx
- + textHeight;
- }
- } else if (mIsScalableGrid) {
+ folderChildDrawablePaddingPx = mResponsiveWorkspaceCellSpec.getIconDrawablePadding();
+
+ CellContentDimensions cellContentDimensions = new CellContentDimensions(
+ folderChildIconSizePx,
+ folderChildDrawablePaddingPx,
+ folderChildTextSizePx);
+ cellContentDimensions.resizeToFitCellHeight(folderCellHeightPx, mIconSizeSteps);
+ folderChildIconSizePx = cellContentDimensions.getIconSizePx();
+ folderChildDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+ folderChildTextSizePx = cellContentDimensions.getIconTextSizePx();
+ folderLabelTextSizePx = Math.max(minLabelTextSize,
+ (int) (folderChildTextSizePx * folderLabelTextScale));
+ return;
+ }
+
+ float invIconSizeDp = inv.iconSize[mTypeIndex];
+ float invIconTextSizeDp = inv.iconTextSize[mTypeIndex];
+ folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
+ folderChildTextSizePx = pxFromSp(invIconTextSizeDp, mMetrics, scale);
+ folderLabelTextSizePx = Math.max(minLabelTextSize,
+ (int) (folderChildTextSizePx * folderLabelTextScale));
+ int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
+
+ if (mIsScalableGrid) {
if (inv.folderStyle == INVALID_RESOURCE_HANDLE) {
folderCellWidthPx = roundPxValueFromFloat(getCellSize().x * scale);
folderCellHeightPx = roundPxValueFromFloat(getCellSize().y * scale);
@@ -1415,16 +1560,25 @@ private void updateFolderCellSize(float scale, Resources res) {
folderCellWidthPx = roundPxValueFromFloat(folderCellWidthPx * scale);
folderCellHeightPx = roundPxValueFromFloat(folderCellHeightPx * scale);
}
+ // Recalculating padding and cell height
+ folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
+
+ CellContentDimensions cellContentDimensions = new CellContentDimensions(
+ folderChildIconSizePx,
+ folderChildDrawablePaddingPx,
+ folderChildTextSizePx);
+ cellContentDimensions.resizeToFitCellHeight(folderCellHeightPx, mIconSizeSteps);
+ folderChildIconSizePx = cellContentDimensions.getIconSizePx();
+ folderChildDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+ folderChildTextSizePx = cellContentDimensions.getIconTextSizePx();
folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale);
folderCellLayoutBorderSpacePx = new Point(
roundPxValueFromFloat(folderCellLayoutBorderSpacePx.x * scale),
- roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale));
+ roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale)
+ );
folderFooterHeightPx = roundPxValueFromFloat(folderFooterHeightPx * scale);
-
folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x;
-
- folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
} else {
int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
* scale);
@@ -1434,10 +1588,12 @@ private void updateFolderCellSize(float scale, Resources res) {
folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale);
- folderContentPaddingLeftRight = res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
- folderFooterHeightPx = roundPxValueFromFloat(
- res.getDimensionPixelSize(R.dimen.folder_footer_height_default)
- * scale);
+ folderContentPaddingLeftRight =
+ res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right);
+ folderFooterHeightPx =
+ roundPxValueFromFloat(
+ res.getDimensionPixelSize(R.dimen.folder_footer_height_default)
+ * scale);
folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
}
@@ -1451,10 +1607,8 @@ public void updateInsets(Rect insets) {
}
/**
- * The current device insets. This is generally same as the insets being
- * dispatched to
- * {@link Insettable} elements, but can differ if the element is using a
- * different profile.
+ * The current device insets. This is generally same as the insets being dispatched to
+ * {@link Insettable} elements, but can differ if the element is using a different profile.
*/
public Rect getInsets() {
return mInsets;
@@ -1469,20 +1623,19 @@ public Point getCellSize(Point result) {
result = new Point();
}
- int shortcutAndWidgetContainerWidth = getCellLayoutWidth()
- - (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right);
+ int shortcutAndWidgetContainerWidth =
+ getCellLayoutWidth() - (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right);
result.x = calculateCellWidth(shortcutAndWidgetContainerWidth, cellLayoutBorderSpacePx.x,
inv.numColumns);
- int shortcutAndWidgetContainerHeight = getCellLayoutHeight()
- - (cellLayoutPaddingPx.top + cellLayoutPaddingPx.bottom);
+ int shortcutAndWidgetContainerHeight =
+ getCellLayoutHeight() - (cellLayoutPaddingPx.top + cellLayoutPaddingPx.bottom);
result.y = calculateCellHeight(shortcutAndWidgetContainerHeight, cellLayoutBorderSpacePx.y,
inv.numRows);
return result;
}
/**
- * Returns the left and right space on the cell, which is the cell width - icon
- * size
+ * Returns the left and right space on the cell, which is the cell width - icon size
*/
public int getCellHorizontalSpace() {
return getCellSize().x - iconSizePx;
@@ -1496,8 +1649,7 @@ public int getPanelCount() {
}
/**
- * Gets the space in px from the bottom of last item in the vertical-bar hotseat
- * to the
+ * Gets the space in px from the bottom of last item in the vertical-bar hotseat to the
* bottom of the screen.
*/
private int getVerticalHotseatLastItemBottomOffset(Context context) {
@@ -1518,22 +1670,21 @@ public float getCellLayoutSpringLoadShrunkTop() {
}
/**
- * Gets the scaled bottom of the workspace in px for the spring-loaded edit
- * state.
+ * Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
*/
public float getCellLayoutSpringLoadShrunkBottom(Context context) {
int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
return heightPx - (isVerticalBarLayout()
- ? getVerticalHotseatLastItemBottomOffset(context)
- : topOfHotseat);
+ ? getVerticalHotseatLastItemBottomOffset(context) : topOfHotseat);
}
/**
* Gets the scale of the workspace for the spring-loaded edit state.
*/
public float getWorkspaceSpringLoadScale(Context context) {
- float scale = (getCellLayoutSpringLoadShrunkBottom(context) - getCellLayoutSpringLoadShrunkTop())
- / getCellLayoutHeight();
+ float scale =
+ (getCellLayoutSpringLoadShrunkBottom(context) - getCellLayoutSpringLoadShrunkTop())
+ / getCellLayoutHeight();
scale = Math.min(scale, 1f);
// Reduce scale if next pages would not be visible after scaling the workspace.
@@ -1547,12 +1698,9 @@ public float getWorkspaceSpringLoadScale(Context context) {
}
/**
- * Gets the width of a single Cell Layout, aka a single panel within a
- * Workspace.
+ * Gets the width of a single Cell Layout, aka a single panel within a Workspace.
*
- *
- * This is the width of a Workspace, less its horizontal padding. Note that
- * two-panel
+ *
This is the width of a Workspace, less its horizontal padding. Note that two-panel
* layouts have two Cell Layouts per workspace.
*/
public int getCellLayoutWidth() {
@@ -1560,11 +1708,9 @@ public int getCellLayoutWidth() {
}
/**
- * Gets the height of a single Cell Layout, aka a single panel within a
- * Workspace.
+ * Gets the height of a single Cell Layout, aka a single panel within a Workspace.
*
- *
- * This is the height of a Workspace, less its vertical padding.
+ *
This is the height of a Workspace, less its vertical padding.
*/
public int getCellLayoutHeight() {
return availableHeightPx - getTotalWorkspacePadding().y;
@@ -1576,31 +1722,33 @@ public Point getTotalWorkspacePadding() {
}
/**
- * Updates {@link #workspacePadding} as a result of any internal value change to
- * reflect the
+ * Updates {@link #workspacePadding} as a result of any internal value change to reflect the
* new workspace padding
*/
private void updateWorkspacePadding() {
Rect padding = workspacePadding;
if (isVerticalBarLayout()) {
if (mIsResponsiveGrid) {
- padding.top = mResponsiveHeightSpec.getStartPaddingPx();
- padding.bottom = mResponsiveHeightSpec.getEndPaddingPx();
+ padding.top = mResponsiveWorkspaceHeightSpec.getStartPaddingPx();
+ padding.bottom = Math.max(0,
+ mResponsiveWorkspaceHeightSpec.getEndPaddingPx() - mInsets.bottom);
if (isSeascape()) {
- padding.left = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx();
- padding.right = mResponsiveWidthSpec.getStartPaddingPx();
+ padding.left =
+ hotseatBarSizePx + mResponsiveWorkspaceWidthSpec.getEndPaddingPx();
+ padding.right = mResponsiveWorkspaceWidthSpec.getStartPaddingPx();
} else {
- padding.left = mResponsiveWidthSpec.getStartPaddingPx();
- padding.right = hotseatBarSizePx + mResponsiveWidthSpec.getEndPaddingPx();
+ padding.left = mResponsiveWorkspaceWidthSpec.getStartPaddingPx();
+ padding.right =
+ hotseatBarSizePx + mResponsiveWorkspaceWidthSpec.getEndPaddingPx();
}
} else {
padding.top = 0;
padding.bottom = edgeMarginPx;
if (isSeascape()) {
padding.left = hotseatBarSizePx;
- padding.right = hotseatBarSidePaddingStartPx;
+ padding.right = mHotseatBarEdgePaddingPx;
} else {
- padding.left = hotseatBarSidePaddingStartPx;
+ padding.left = mHotseatBarEdgePaddingPx;
padding.right = hotseatBarSizePx;
}
}
@@ -1609,7 +1757,8 @@ private void updateWorkspacePadding() {
// and leave a bit of space in case a widget go all the way down
int paddingBottom = hotseatBarSizePx + workspaceBottomPadding - mInsets.bottom;
if (!mIsResponsiveGrid) {
- paddingBottom += workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace;
+ paddingBottom +=
+ workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace;
}
int paddingTop = workspaceTopPadding + (mIsScalableGrid ? 0 : edgeMarginPx);
int paddingSide = desiredWorkspaceHorizontalMarginPx;
@@ -1633,6 +1782,32 @@ private void insetPadding(Rect paddings, Rect insets) {
paddings.bottom -= insets.bottom;
}
+
+ /**
+ * Returns the new border space that should be used between hotseat icons after adjusting it to
+ * the bubble bar.
+ *
+ *
If there's no adjustment needed, this method returns {@code 0}.
+ */
+ public float getHotseatAdjustedBorderSpaceForBubbleBar(Context context) {
+ // only need to adjust when QSB is on top of the hotseat.
+ if (isQsbInline) {
+ return 0;
+ }
+
+ // no need to adjust if there's enough space for the bubble bar to the right of the hotseat.
+ if (getHotseatLayoutPadding(context).right > mBubbleBarSpaceThresholdPx) {
+ return 0;
+ }
+
+ // The adjustment is shrinking the hotseat's width by 1 icon on either side.
+ int iconsWidth =
+ iconSizePx * numShownHotseatIcons + hotseatBorderSpace * (numShownHotseatIcons - 1);
+ int newWidth = iconsWidth - 2 * iconSizePx;
+ // Evenly space the icons within the boundaries of the new width.
+ return (float) (newWidth - iconSizePx * numShownHotseatIcons) / (numShownHotseatIcons - 1);
+ }
+
/**
* Returns the padding for hotseat view
*/
@@ -1642,8 +1817,7 @@ public Rect getHotseatLayoutPadding(Context context) {
// The hotseat icons will be placed in the middle of the hotseat cells.
// Changing the hotseatCellHeightPx is not affecting hotseat icon positions
// in vertical bar layout.
- // Workspace icons are moved up by a small factor. The variable
- // diffOverlapFactor
+ // Workspace icons are moved up by a small factor. The variable diffOverlapFactor
// is set to account for that difference.
float diffOverlapFactor = mIsResponsiveGrid ? 0
: iconSizePx * (ICON_OVERLAP_FACTOR - 1) / 2;
@@ -1654,16 +1828,17 @@ public Rect getHotseatLayoutPadding(Context context) {
+ diffOverlapFactor), 0);
if (isSeascape()) {
- hotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
- hotseatBarSidePaddingEndPx, paddingBottom);
+ hotseatBarPadding.set(mInsets.left + mHotseatBarEdgePaddingPx, paddingTop,
+ mHotseatBarWorkspaceSpacePx, paddingBottom);
} else {
- hotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
- mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
+ hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
+ mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
}
- } else if (isTaskbarPresent) {
+ } else if (isTaskbarPresent || isTablet) {
// Center the QSB vertically with hotseat
int hotseatBarBottomPadding = getHotseatBarBottomPadding();
- int hotseatBarTopPadding = hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
+ int hotseatBarTopPadding =
+ hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
int hotseatWidth = getHotseatRequiredWidth();
int startSpacing;
@@ -1696,12 +1871,9 @@ public Rect getHotseatLayoutPadding(Context context) {
sideSpacing,
getHotseatBarBottomPadding());
} else {
- // We want the edges of the hotseat to line up with the edges of the workspace,
- // but the
- // icons in the hotseat are a different size, and so don't line up perfectly. To
- // account
- // for this, we pad the left and right of the hotseat with half of the
- // difference of a
+ // We want the edges of the hotseat to line up with the edges of the workspace, but the
+ // icons in the hotseat are a different size, and so don't line up perfectly. To account
+ // for this, we pad the left and right of the hotseat with half of the difference of a
// workspace cell vs a hotseat cell.
float workspaceCellWidth = (float) widthPx / inv.numColumns;
float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
@@ -1718,13 +1890,14 @@ public Rect getHotseatLayoutPadding(Context context) {
}
/** The margin between the edge of all apps and the edge of the first icon. */
- public int getAllAppsIconStartMargin() {
+ public int getAllAppsIconStartMargin(Context context) {
int allAppsSpacing;
if (isVerticalBarLayout()) {
// On phones, the landscape layout uses a different setup.
allAppsSpacing = workspacePadding.left + workspacePadding.right;
} else {
- allAppsSpacing = allAppsLeftRightPadding * 2 + allAppsLeftRightMargin * 2;
+ allAppsSpacing =
+ allAppsPadding.left + allAppsPadding.right + allAppsLeftRightMargin * 2;
}
int cellWidth = DeviceProfile.calculateCellWidth(
@@ -1733,7 +1906,9 @@ public int getAllAppsIconStartMargin() {
numShownAllAppsColumns);
int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * allAppsIconSizePx);
int iconAlignmentMargin = (cellWidth - iconVisibleSize) / 2;
- return allAppsLeftRightPadding + iconAlignmentMargin;
+
+ return (Utilities.isRtl(context.getResources()) ? allAppsPadding.right
+ : allAppsPadding.left) + iconAlignmentMargin;
}
private int getAdditionalQsbSpace() {
@@ -1751,8 +1926,7 @@ private int getHotseatRequiredWidth() {
}
/**
- * Returns the number of pixels the QSB is translated from the bottom of the
- * screen.
+ * Returns the number of pixels the QSB is translated from the bottom of the screen.
*/
public int getQsbOffsetY() {
if (isQsbInline) {
@@ -1765,8 +1939,7 @@ public int getQsbOffsetY() {
}
/**
- * Returns the number of pixels the hotseat is translated from the bottom of the
- * screen.
+ * Returns the number of pixels the hotseat is translated from the bottom of the screen.
*/
private int getHotseatBarBottomPadding() {
if (isTaskbarPresent) { // QSB on top or inline
@@ -1777,44 +1950,35 @@ private int getHotseatBarBottomPadding() {
}
/**
- * Returns the number of pixels the taskbar is translated from the bottom of the
- * screen.
+ * Returns the number of pixels the taskbar is translated from the bottom of the screen.
*/
public int getTaskbarOffsetY() {
int taskbarIconBottomSpace = (taskbarHeight - iconSizePx) / 2;
- int launcherIconBottomSpace = Math.min((hotseatCellHeightPx - iconSizePx) / 2, gridVisualizationPaddingY);
+ int launcherIconBottomSpace =
+ Math.min((hotseatCellHeightPx - iconSizePx) / 2, gridVisualizationPaddingY);
return getHotseatBarBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
}
- /**
- * Returns the number of pixels required below OverviewActions excluding insets.
- */
+ /** Returns the number of pixels required below OverviewActions. */
public int getOverviewActionsClaimedSpaceBelow() {
- if (isTaskbarPresent) {
- return taskbarHeight + taskbarBottomMargin * 2;
- }
- return mInsets.bottom;
+ return isTaskbarPresent ? mTransientTaskbarClaimedSpace : mInsets.bottom;
}
- /**
- * Gets the space that the overview actions will take, including bottom margin.
- */
+ /** Gets the space that the overview actions will take, including bottom margin. */
public int getOverviewActionsClaimedSpace() {
- int overviewActionsSpace = isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()
+ int overviewActionsSpace = isTablet
? 0
: (overviewActionsTopMarginPx + overviewActionsHeight);
return overviewActionsSpace + getOverviewActionsClaimedSpaceBelow();
}
/**
- * Takes the View and return the scales of width and height depending on the
- * DeviceProfile
+ * Takes the View and return the scales of width and height depending on the DeviceProfile
* specifications
*
* @param itemInfo The tag of the widget view
- * @return A PointF instance with the x set to be the scale of width, and y
- * being the scale of
- * height
+ * @return A PointF instance with the x set to be the scale of width, and y being the scale of
+ * height
*/
@NonNull
public PointF getAppWidgetScale(@Nullable final ItemInfo itemInfo) {
@@ -1826,8 +1990,7 @@ public PointF getAppWidgetScale(@Nullable final ItemInfo itemInfo) {
*/
public Rect getAbsoluteOpenFolderBounds() {
if (isVerticalBarLayout()) {
- // Folders should only appear right of the drop target bar and left of the
- // hotseat
+ // Folders should only appear right of the drop target bar and left of the hotseat
return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
mInsets.top,
mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx,
@@ -1852,10 +2015,8 @@ public static int calculateCellHeight(int height, int borderSpacing, int countY)
}
/**
- * When {@code true}, the device is in landscape mode and the hotseat is on the
- * right column.
- * When {@code false}, either device is in portrait mode or the device is in
- * landscape mode and
+ * When {@code true}, the device is in landscape mode and the hotseat is on the right column.
+ * When {@code false}, either device is in portrait mode or the device is in landscape mode and
* the hotseat is on the bottom row.
*/
public boolean isVerticalBarLayout() {
@@ -1863,8 +2024,7 @@ public boolean isVerticalBarLayout() {
}
/**
- * Updates orientation information and returns true if it has changed from the
- * previous value.
+ * Updates orientation information and returns true if it has changed from the previous value.
*/
public boolean updateIsSeascape(Context context) {
if (isVerticalBarLayout()) {
@@ -1926,6 +2086,7 @@ public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + "\tisLandscape:" + isLandscape);
writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
+ writer.println(prefix + "\tisLeftRightSplit:" + isLeftRightSplit);
writer.println(prefix + pxToDpStr("windowX", windowX));
writer.println(prefix + pxToDpStr("windowY", windowY));
@@ -1973,8 +2134,8 @@ public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx));
- writer.println(prefix + "\tinv.numFolderRows: " + inv.numFolderRows);
- writer.println(prefix + "\tinv.numFolderColumns: " + inv.numFolderColumns);
+ writer.println(prefix + "\tnumFolderRows: " + numFolderRows);
+ writer.println(prefix + "\tnumFolderColumns: " + numFolderColumns);
writer.println(prefix + pxToDpStr("folderCellWidthPx", folderCellWidthPx));
writer.println(prefix + pxToDpStr("folderCellHeightPx", folderCellHeightPx));
writer.println(prefix + pxToDpStr("folderChildIconSizePx", folderChildIconSizePx));
@@ -1997,7 +2158,6 @@ public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + "\tbottomSheetDepth: " + bottomSheetDepth);
writer.println(prefix + pxToDpStr("allAppsShiftRange", allAppsShiftRange));
- writer.println(prefix + pxToDpStr("allAppsTopPadding", allAppsTopPadding));
writer.println(prefix + "\tallAppsOpenDuration: " + allAppsOpenDuration);
writer.println(prefix + "\tallAppsCloseDuration: " + allAppsCloseDuration);
writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
@@ -2009,17 +2169,19 @@ public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", allAppsBorderSpacePx.x));
writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", allAppsBorderSpacePx.y));
writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
- writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding));
+ writer.println(prefix + pxToDpStr("allAppsPadding.top", allAppsPadding.top));
+ writer.println(prefix + pxToDpStr("allAppsPadding.left", allAppsPadding.left));
+ writer.println(prefix + pxToDpStr("allAppsPadding.right", allAppsPadding.right));
writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx));
- writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
- hotseatBarSidePaddingStartPx));
- writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
- hotseatBarSidePaddingEndPx));
+ writer.println(prefix + pxToDpStr("mHotseatBarEdgePaddingPx",
+ mHotseatBarEdgePaddingPx));
+ writer.println(prefix + pxToDpStr("mHotseatBarWorkspaceSpacePx",
+ mHotseatBarWorkspaceSpacePx));
writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset));
writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace));
writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
@@ -2068,6 +2230,8 @@ public void dump(Context context, String prefix, PrintWriter writer) {
overviewTaskIconDrawableSizePx));
writer.println(prefix + pxToDpStr("overviewTaskIconDrawableSizeGridPx",
overviewTaskIconDrawableSizeGridPx));
+ writer.println(prefix + pxToDpStr("overviewTaskIconAppChipMenuDrawableSizePx",
+ overviewTaskIconAppChipMenuDrawableSizePx));
writer.println(prefix + pxToDpStr("overviewTaskThumbnailTopMarginPx",
overviewTaskThumbnailTopMarginPx));
writer.println(prefix + pxToDpStr("overviewActionsTopMarginPx",
@@ -2098,15 +2262,20 @@ public void dump(Context context, String prefix, PrintWriter writer) {
writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight()));
writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth()));
if (mIsResponsiveGrid) {
- writer.println(prefix + "\tmResponsiveHeightSpec:" + mResponsiveHeightSpec.toString());
- writer.println(prefix + "\tmResponsiveWidthSpec:" + mResponsiveWidthSpec.toString());
- writer.println(prefix + "\tmAllAppsResponsiveHeightSpec:"
- + mAllAppsResponsiveHeightSpec.toString());
- writer.println(prefix + "\tmAllAppsResponsiveWidthSpec:"
- + mAllAppsResponsiveWidthSpec.toString());
+ writer.println(prefix + "\tmResponsiveWorkspaceHeightSpec:"
+ + mResponsiveWorkspaceHeightSpec.toString());
+ writer.println(prefix + "\tmResponsiveWorkspaceWidthSpec:"
+ + mResponsiveWorkspaceWidthSpec.toString());
+ writer.println(prefix + "\tmResponsiveAllAppsHeightSpec:"
+ + mResponsiveAllAppsHeightSpec.toString());
+ writer.println(prefix + "\tmResponsiveAllAppsWidthSpec:"
+ + mResponsiveAllAppsWidthSpec.toString());
writer.println(prefix + "\tmResponsiveFolderHeightSpec:" + mResponsiveFolderHeightSpec);
writer.println(prefix + "\tmResponsiveFolderWidthSpec:" + mResponsiveFolderWidthSpec);
writer.println(prefix + "\tmResponsiveHotseatSpec:" + mResponsiveHotseatSpec);
+ writer.println(prefix + "\tmResponsiveWorkspaceCellSpec:"
+ + mResponsiveWorkspaceCellSpec);
+ writer.println(prefix + "\tmResponsiveAllAppsCellSpec:" + mResponsiveAllAppsCellSpec);
}
}
@@ -2129,17 +2298,14 @@ private static Context getContext(Context c, Info info, int orientation, WindowB
}
/**
- * Callback when a component changes the DeviceProfile associated with it, as a
- * result of
+ * Callback when a component changes the DeviceProfile associated with it, as a result of
* configuration change
*/
public interface OnDeviceProfileChangeListener {
/**
- * Called when the device profile is reassigned. Note that for layout and
- * measurements, it
- * is sufficient to listen for inset changes. Use this callback when you need to
- * perform
+ * Called when the device profile is reassigned. Note that for layout and measurements, it
+ * is sufficient to listen for inset changes. Use this callback when you need to perform
* a one time operation.
*/
void onDeviceProfileChanged(DeviceProfile dp);
@@ -2154,9 +2320,8 @@ public interface ViewScaleProvider {
* Get the scales from the view
*
* @param itemInfo The tag of the widget view
- * @return PointF instance containing the scale information, or null if using
- * the default
- * app widget scale of this device profile.
+ * @return PointF instance containing the scale information, or null if using the default
+ * app widget scale of this device profile.
*/
@NonNull
PointF getScaleFromItemInfo(@Nullable ItemInfo itemInfo);
@@ -2179,10 +2344,13 @@ public static class Builder {
private Consumer mOverrideProvider;
+ private boolean mIsTransientTaskbar;
+
public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context;
mInv = inv;
mInfo = info;
+ mIsTransientTaskbar = info.isTransientTaskbar();
}
public Builder setMultiWindowMode(boolean isMultiWindowMode) {
@@ -2233,6 +2401,15 @@ public Builder setViewScaleProvider(@Nullable ViewScaleProvider viewScaleProvide
return this;
}
+ /**
+ * Set the isTransientTaskbar for the builder
+ * @return This Builder
+ */
+ public Builder setIsTransientTaskbar(boolean isTransientTaskbar) {
+ mIsTransientTaskbar = isTransientTaskbar;
+ return this;
+ }
+
public DeviceProfile build() {
if (mWindowBounds == null) {
throw new IllegalArgumentException("Window bounds not set");
@@ -2254,8 +2431,7 @@ public DeviceProfile build() {
}
return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mDotRendererCache,
mIsMultiWindowMode, mTransposeLayoutWithOrientation, mIsMultiDisplay,
- mIsGestureMode, mViewScaleProvider, mOverrideProvider);
+ mIsGestureMode, mViewScaleProvider, mOverrideProvider, mIsTransientTaskbar);
}
}
-
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index fea87a83002..f156bb9055f 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -211,6 +211,14 @@ public class InvariantDeviceProfile {
@XmlRes
public int hotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int workspaceCellSpecsId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int workspaceCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int allAppsCellSpecsId = INVALID_RESOURCE_HANDLE;
+ @XmlRes
+ public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
public int demoModeLayoutId;
public boolean[] inlineQsb = new boolean[COUNT_SIZES];
@@ -386,7 +394,7 @@ private void initGrid(Context context, Info displayInfo, DisplayOption displayOp
closestProfile = displayOption.grid;
numRows = dbGridInfo.getNumRows();
numColumns = dbGridInfo.getNumColumns();
- numSearchContainerColumns = dbGridInfo.getNumHotseatColumns();
+ numSearchContainerColumns = closestProfile.numSearchContainerColumns;
dbFile = dbGridInfo.getDbFile();
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -407,6 +415,10 @@ private void initGrid(Context context, Info displayInfo, DisplayOption displayOp
folderSpecsTwoPanelId = closestProfile.mFolderSpecsTwoPanelId;
hotseatSpecsId = closestProfile.mHotseatSpecsId;
hotseatSpecsTwoPanelId = closestProfile.mHotseatSpecsTwoPanelId;
+ workspaceCellSpecsId = closestProfile.mWorkspaceCellSpecsId;
+ workspaceCellSpecsTwoPanelId = closestProfile.mWorkspaceCellSpecsTwoPanelId;
+ allAppsCellSpecsId = closestProfile.mAllAppsCellSpecsId;
+ allAppsCellSpecsTwoPanelId = closestProfile.mAllAppsCellSpecsTwoPanelId;
this.deviceType = deviceType;
@@ -428,7 +440,8 @@ private void initGrid(Context context, Info displayInfo, DisplayOption displayOp
horizontalMargin = displayOption.horizontalMargin;
- numShownHotseatIcons = numSearchContainerColumns;
+ numShownHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
+ ? closestProfile.numHotseatIcons : dbGridInfo.getNumHotseatColumns();
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : numShownHotseatIcons;
hotseatColumnSpan = closestProfile.hotseatColumnSpan;
@@ -500,9 +513,6 @@ private void initGrid(Context context, Info displayInfo, DisplayOption displayOp
deviceProfile.numShownHotseatIcons = numMinShownHotseatIconsForTablet;
deviceProfile.recalculateHotseatWidthAndBorderSpace();
});
-
- ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
- defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
}
public void addOnChangeListener(OnIDPChangeListener listener) {
@@ -870,6 +880,10 @@ public static final class GridOption {
private final int mFolderSpecsTwoPanelId;
private final int mHotseatSpecsId;
private final int mHotseatSpecsTwoPanelId;
+ private final int mWorkspaceCellSpecsId;
+ private final int mWorkspaceCellSpecsTwoPanelId;
+ private final int mAllAppsCellSpecsId;
+ private final int mAllAppsCellSpecsTwoPanelId;
private final boolean isScalable;
private final int devicePaddingId;
@@ -955,6 +969,18 @@ public GridOption(Context context, AttributeSet attrs) {
mHotseatSpecsTwoPanelId = a.getResourceId(
R.styleable.GridDisplayOption_hotseatSpecsTwoPanelId,
INVALID_RESOURCE_HANDLE);
+ mWorkspaceCellSpecsId = a.getResourceId(
+ R.styleable.GridDisplayOption_workspaceCellSpecsId,
+ INVALID_RESOURCE_HANDLE);
+ mWorkspaceCellSpecsTwoPanelId = a.getResourceId(
+ R.styleable.GridDisplayOption_workspaceCellSpecsTwoPanelId,
+ INVALID_RESOURCE_HANDLE);
+ mAllAppsCellSpecsId = a.getResourceId(
+ R.styleable.GridDisplayOption_allAppsCellSpecsId,
+ INVALID_RESOURCE_HANDLE);
+ mAllAppsCellSpecsTwoPanelId = a.getResourceId(
+ R.styleable.GridDisplayOption_allAppsCellSpecsTwoPanelId,
+ INVALID_RESOURCE_HANDLE);
} else {
mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
mWorkspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
@@ -964,6 +990,10 @@ public GridOption(Context context, AttributeSet attrs) {
mFolderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
mHotseatSpecsId = INVALID_RESOURCE_HANDLE;
mHotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+ mWorkspaceCellSpecsId = INVALID_RESOURCE_HANDLE;
+ mWorkspaceCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+ mAllAppsCellSpecsId = INVALID_RESOURCE_HANDLE;
+ mAllAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
}
int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 830709b79d5..6c660df61e1 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -544,17 +544,12 @@ public void onActivePageChanged(int currentActivePage) {
// Will be called at the end of the animation.
return;
}
- if (currentActivePage != SEARCH) {
- mActivityContext.hideKeyboard();
- }
if (mAH.get(currentActivePage).mRecyclerView != null) {
mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar(mFastScroller);
}
// Header keeps track of active recycler view to properly render header
// protection.
mHeader.setActiveRV(currentActivePage);
- reset(true /* animate */, !isSearching() /* exitSearch */);
-
mWorkManager.onActivePageChanged(currentActivePage);
}
@@ -846,7 +841,7 @@ public int getFloatingSearchBarRestingMarginBottom() {
*/
public int getFloatingSearchBarRestingMarginStart() {
DeviceProfile dp = mActivityContext.getDeviceProfile();
- return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(mActivityContext);
}
/**
@@ -862,7 +857,7 @@ public int getFloatingSearchBarRestingMarginStart() {
*/
public int getFloatingSearchBarRestingMarginEnd() {
DeviceProfile dp = mActivityContext.getDeviceProfile();
- return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(mActivityContext);
}
private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) {
@@ -1165,7 +1160,7 @@ public void setInsets(Rect insets) {
if (grid.isVerticalBarLayout()) {
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
- int topPadding = grid.allAppsTopPadding;
+ int topPadding = grid.allAppsPadding.top;
if (isSearchBarFloating() && !grid.isTablet) {
topPadding += getResources().getDimensionPixelSize(
R.dimen.all_apps_additional_top_padding_floating_search);
@@ -1227,7 +1222,7 @@ private void applyAdapterSideAndBottomPaddings(DeviceProfile grid) {
int bottomPadding = Math.max(mInsets.bottom, mNavBarScrimHeight);
mAH.forEach(adapterHolder -> {
adapterHolder.mPadding.bottom = bottomPadding;
- adapterHolder.mPadding.left = adapterHolder.mPadding.right = grid.allAppsLeftRightPadding;
+ adapterHolder.mPadding.left = adapterHolder.mPadding.right = grid.allAppsPadding.left + grid.allAppsPadding.right;
adapterHolder.applyPadding();
});
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 846c8620e0e..74742cf0181 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -460,8 +460,8 @@ public boolean hasOverlappingRendering() {
@Override
public void setInsets(Rect insets) {
- int leftRightPadding = ActivityContext.lookupContext(getContext())
- .getDeviceProfile().allAppsLeftRightPadding;
+ var dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+ int leftRightPadding = dp.allAppsPadding.left + dp.allAppsPadding.right;
setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index a054280dfe3..ef363333b52 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -131,7 +131,8 @@ public void setInsets(Rect insets) {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
View parent = (View) getParent();
- int allAppsLeftRightPadding = mActivityContext.getDeviceProfile().allAppsLeftRightPadding;
+ var dp = mActivityContext.getDeviceProfile();
+ int allAppsLeftRightPadding = dp.allAppsPadding.left + dp.allAppsPadding.right;
int size = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight()
- 2 * allAppsLeftRightPadding;
int tabWidth = getTabWidth(getContext(), size);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e5d9b7d3ed4..9df38c13c67 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -429,7 +429,7 @@ public static boolean twoLineAllApps(Context context) {
// TODO(Block 32): Empty block
public static final BooleanFlag ENABLE_RESPONSIVE_WORKSPACE = getDebugFlag(241386436,
- "ENABLE_RESPONSIVE_WORKSPACE", DISABLED,
+ "ENABLE_RESPONSIVE_WORKSPACE", ENABLED,
"Enables new workspace grid calculations method.");
// TODO(Block 33): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355,
diff --git a/src/com/android/launcher3/responsive/AllAppsSpecs.kt b/src/com/android/launcher3/responsive/AllAppsSpecs.kt
deleted file mode 100644
index 8ed3ffc1bf1..00000000000
--- a/src/com/android/launcher3/responsive/AllAppsSpecs.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2023 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.responsive
-
-import android.content.res.TypedArray
-import com.android.launcher3.R
-import com.android.launcher3.responsive.ResponsiveSpec.SpecType
-import com.android.launcher3.util.ResourceHelper
-
-class AllAppsSpecs(widthSpecs: List, heightSpecs: List) :
- ResponsiveSpecs(widthSpecs, heightSpecs) {
-
- fun getCalculatedWidthSpec(
- columns: Int,
- availableWidth: Int,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
- ): CalculatedAllAppsSpec {
- check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) {
- "Invalid specType for CalculatedWorkspaceSpec. " +
- "Expected: ${SpecType.WIDTH} - " +
- "Found: ${calculatedWorkspaceSpec.spec.specType}}"
- }
-
- val spec = getWidthSpec(availableWidth)
- return CalculatedAllAppsSpec(availableWidth, columns, spec, calculatedWorkspaceSpec)
- }
-
- fun getCalculatedHeightSpec(
- rows: Int,
- availableHeight: Int,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
- ): CalculatedAllAppsSpec {
- check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) {
- "Invalid specType for CalculatedWorkspaceSpec. " +
- "Expected: ${SpecType.HEIGHT} - " +
- "Found: ${calculatedWorkspaceSpec.spec.specType}}"
- }
-
- val spec = getHeightSpec(availableHeight)
- return CalculatedAllAppsSpec(availableHeight, rows, spec, calculatedWorkspaceSpec)
- }
-
- companion object {
- private const val XML_ALL_APPS_SPEC = "allAppsSpec"
-
- @JvmStatic
- fun create(resourceHelper: ResourceHelper): AllAppsSpecs {
- val parser = ResponsiveSpecsParser(resourceHelper)
- val specs = parser.parseXML(XML_ALL_APPS_SPEC, ::AllAppsSpec)
- val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
- return AllAppsSpecs(widthSpecs, heightSpecs)
- }
- }
-}
-
-data class AllAppsSpec(
- override val maxAvailableSize: Int,
- override val specType: SpecType,
- override val startPadding: SizeSpec,
- override val endPadding: SizeSpec,
- override val gutter: SizeSpec,
- override val cellSize: SizeSpec
-) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
-
- init {
- check(isValid()) { "Invalid AllAppsSpec found." }
- }
-
- constructor(
- attrs: TypedArray,
- specs: Map
- ) : this(
- maxAvailableSize =
- attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
- specType =
- SpecType.values()[
- attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
- startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
- endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
- gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
- cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
- )
-}
-
-class CalculatedAllAppsSpec(
- availableSpace: Int,
- cells: Int,
- spec: AllAppsSpec,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
-) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec)
diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt
deleted file mode 100644
index bc2db28ef18..00000000000
--- a/src/com/android/launcher3/responsive/FolderSpecs.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2023 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.responsive
-
-import android.content.res.TypedArray
-import com.android.launcher3.R
-import com.android.launcher3.responsive.ResponsiveSpec.SpecType
-import com.android.launcher3.util.ResourceHelper
-
-class FolderSpecs(widthSpecs: List, heightSpecs: List) :
- ResponsiveSpecs(widthSpecs, heightSpecs) {
-
- fun getCalculatedWidthSpec(
- columns: Int,
- availableWidth: Int,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
- ): CalculatedFolderSpec {
- check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) {
- "Invalid specType for CalculatedWorkspaceSpec. " +
- "Expected: ${SpecType.WIDTH} - " +
- "Found: ${calculatedWorkspaceSpec.spec.specType}}"
- }
-
- val spec = getWidthSpec(availableWidth)
- return CalculatedFolderSpec(availableWidth, columns, spec, calculatedWorkspaceSpec)
- }
-
- fun getCalculatedHeightSpec(
- rows: Int,
- availableHeight: Int,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
- ): CalculatedFolderSpec {
- check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) {
- "Invalid specType for CalculatedWorkspaceSpec. " +
- "Expected: ${SpecType.HEIGHT} - " +
- "Found: ${calculatedWorkspaceSpec.spec.specType}}"
- }
-
- val spec = getHeightSpec(availableHeight)
- return CalculatedFolderSpec(availableHeight, rows, spec, calculatedWorkspaceSpec)
- }
-
- companion object {
-
- private const val XML_FOLDER_SPEC = "folderSpec"
-
- @JvmStatic
- fun create(resourceHelper: ResourceHelper): FolderSpecs {
- val parser = ResponsiveSpecsParser(resourceHelper)
- val specs = parser.parseXML(XML_FOLDER_SPEC, ::FolderSpec)
- val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
- return FolderSpecs(widthSpecs, heightSpecs)
- }
- }
-}
-
-data class FolderSpec(
- override val maxAvailableSize: Int,
- override val specType: SpecType,
- override val startPadding: SizeSpec,
- override val endPadding: SizeSpec,
- override val gutter: SizeSpec,
- override val cellSize: SizeSpec
-) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
-
- init {
- check(isValid()) { "Invalid FolderSpec found." }
- }
-
- constructor(
- attrs: TypedArray,
- specs: Map
- ) : this(
- maxAvailableSize =
- attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
- specType =
- SpecType.values()[
- attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
- startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
- endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
- gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
- cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
- )
-}
-
-class CalculatedFolderSpec(
- availableSpace: Int,
- cells: Int,
- spec: FolderSpec,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
-) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec)
diff --git a/src/com/android/launcher3/responsive/HotseatSpecs.kt b/src/com/android/launcher3/responsive/HotseatSpecs.kt
deleted file mode 100644
index 482508d0855..00000000000
--- a/src/com/android/launcher3/responsive/HotseatSpecs.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2023 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.responsive
-
-import android.content.res.TypedArray
-import android.util.Log
-import com.android.launcher3.R
-import com.android.launcher3.util.ResourceHelper
-
-class HotseatSpecs(val specs: List) {
-
- fun getCalculatedHeightSpec(availableHeight: Int): CalculatedHotseatSpec {
- val spec = specs.firstOrNull { availableHeight <= it.maxAvailableSize }
- check(spec != null) { "No available height spec found within $availableHeight." }
- return CalculatedHotseatSpec(availableHeight, spec)
- }
-
- companion object {
- private const val XML_HOTSEAT_SPEC = "hotseatSpec"
-
- @JvmStatic
- fun create(resourceHelper: ResourceHelper): HotseatSpecs {
- val parser = ResponsiveSpecsParser(resourceHelper)
- val specs = parser.parseXML(XML_HOTSEAT_SPEC, ::HotseatSpec)
- return HotseatSpecs(specs.filter { it.specType == ResponsiveSpec.SpecType.HEIGHT })
- }
- }
-}
-
-data class HotseatSpec(
- val maxAvailableSize: Int,
- val specType: ResponsiveSpec.SpecType,
- val hotseatQsbSpace: SizeSpec
-) {
-
- init {
- check(isValid()) { "Invalid HotseatSpec found." }
- }
-
- constructor(
- attrs: TypedArray,
- specs: Map
- ) : this(
- maxAvailableSize =
- attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
- specType =
- ResponsiveSpec.SpecType.values()[
- attrs.getInt(
- R.styleable.ResponsiveSpec_specType,
- ResponsiveSpec.SpecType.HEIGHT.ordinal
- )],
- hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE)
- )
-
- fun isValid(): Boolean {
- if (maxAvailableSize <= 0) {
- Log.e(LOG_TAG, "${this::class.simpleName}#isValid - maxAvailableSize <= 0")
- return false
- }
-
- // All specs need to be individually valid
- if (!allSpecsAreValid()) {
- Log.e(LOG_TAG, "${this::class.simpleName}#isValid - !allSpecsAreValid()")
- return false
- }
-
- return true
- }
-
- private fun allSpecsAreValid(): Boolean {
- return hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize()
- }
-
- companion object {
- private const val LOG_TAG = "HotseatSpec"
- }
-}
-
-class CalculatedHotseatSpec(val availableSpace: Int, val spec: HotseatSpec) {
-
- var hotseatQsbSpace: Int = 0
- private set
-
- init {
- hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace)
- }
-
- override fun hashCode(): Int {
- var result = availableSpace.hashCode()
- result = 31 * result + hotseatQsbSpace.hashCode()
- result = 31 * result + spec.hashCode()
- return result
- }
-
- override fun equals(other: Any?): Boolean {
- return other is CalculatedHotseatSpec &&
- availableSpace == other.availableSpace &&
- hotseatQsbSpace == other.hotseatQsbSpace &&
- spec == other.spec
- }
-
- override fun toString(): String {
- return "${this::class.simpleName}(" +
- "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " +
- "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
- ")"
- }
-}
diff --git a/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt
new file mode 100644
index 00000000000..7502a43ff96
--- /dev/null
+++ b/src/com/android/launcher3/responsive/HotseatSpecsProvider.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2023 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.responsive
+
+import android.content.res.TypedArray
+import android.util.Log
+import com.android.launcher3.R
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
+import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
+import com.android.launcher3.util.ResourceHelper
+
+class HotseatSpecsProvider(groupOfSpecs: List>) {
+
+ private val groupOfSpecs: List>
+
+ init {
+ this.groupOfSpecs = groupOfSpecs.sortedBy { it.aspectRatio }
+ }
+
+ fun getSpecsByAspectRatio(aspectRatio: Float): ResponsiveSpecGroup {
+ check(aspectRatio > 0f) { "Invalid aspect ratio! The value should be bigger than 0." }
+
+ val specsGroup = groupOfSpecs.firstOrNull { aspectRatio <= it.aspectRatio }
+ check(specsGroup != null) { "No available spec with aspectRatio within $aspectRatio." }
+
+ return specsGroup
+ }
+
+ private fun getSpecIgnoringDimensionType(
+ availableSize: Int,
+ specsGroup: ResponsiveSpecGroup
+ ): HotseatSpec? {
+ val specWidth = specsGroup.widthSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
+ val specHeight = specsGroup.heightSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
+ return specWidth ?: specHeight
+ }
+
+ fun getCalculatedSpec(
+ aspectRatio: Float,
+ dimensionType: DimensionType,
+ availableSpace: Int,
+ ): CalculatedHotseatSpec {
+ val specsGroup = getSpecsByAspectRatio(aspectRatio)
+
+ // TODO(b/315548992): Ignore the dimension type to prevent crash before launcher
+ // data migration is finished. The restore process allows the initialization of
+ // an invalid or disabled grid until the data is restored and migrated.
+ val spec = getSpecIgnoringDimensionType(availableSpace, specsGroup)
+ check(spec != null) { "No available spec found within $availableSpace. $specsGroup" }
+ // val spec = specsGroup.getSpec(dimensionType, availableSpace)
+ return CalculatedHotseatSpec(availableSpace, spec)
+ }
+
+ companion object {
+ @JvmStatic
+ fun create(resourceHelper: ResourceHelper): HotseatSpecsProvider {
+ val parser = ResponsiveSpecsParser(resourceHelper)
+ val specs = parser.parseXML(ResponsiveSpecType.Hotseat, ::HotseatSpec)
+ return HotseatSpecsProvider(specs)
+ }
+ }
+}
+
+data class HotseatSpec(
+ override val maxAvailableSize: Int,
+ override val dimensionType: DimensionType,
+ override val specType: ResponsiveSpecType,
+ val hotseatQsbSpace: SizeSpec,
+ val edgePadding: SizeSpec
+) : IResponsiveSpec {
+ init {
+ check(isValid()) { "Invalid HotseatSpec found." }
+ }
+
+ constructor(
+ responsiveSpecType: ResponsiveSpecType,
+ attrs: TypedArray,
+ specs: Map
+ ) : this(
+ maxAvailableSize =
+ attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
+ dimensionType =
+ DimensionType.entries[
+ attrs.getInt(
+ R.styleable.ResponsiveSpec_dimensionType,
+ DimensionType.HEIGHT.ordinal
+ )],
+ specType = responsiveSpecType,
+ hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE),
+ edgePadding = specs.getOrError(SizeSpec.XmlTags.EDGE_PADDING)
+ )
+
+ fun isValid(): Boolean {
+ if (maxAvailableSize <= 0) {
+ logError("The property maxAvailableSize must be higher than 0.")
+ return false
+ }
+
+ // All specs need to be individually valid
+ if (!allSpecsAreValid()) {
+ logError("One or more specs are invalid!")
+ return false
+ }
+
+ if (!isValidFixedSize()) {
+ logError("The total Fixed Size used must be lower or equal to $maxAvailableSize.")
+ return false
+ }
+
+ return true
+ }
+
+ private fun allSpecsAreValid(): Boolean {
+ return hotseatQsbSpace.isValid() &&
+ hotseatQsbSpace.onlyFixedSize() &&
+ edgePadding.isValid() &&
+ edgePadding.onlyFixedSize()
+ }
+
+ private fun isValidFixedSize() =
+ hotseatQsbSpace.fixedSize + edgePadding.fixedSize <= maxAvailableSize
+
+ private fun logError(message: String) {
+ Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+ }
+
+ companion object {
+ private const val LOG_TAG = "HotseatSpec"
+ }
+}
+
+class CalculatedHotseatSpec(val availableSpace: Int, val spec: HotseatSpec) {
+
+ var hotseatQsbSpace: Int = 0
+ private set
+
+ var edgePadding: Int = 0
+ private set
+
+ init {
+ hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace)
+ edgePadding = spec.edgePadding.getCalculatedValue(availableSpace)
+ }
+
+ override fun hashCode(): Int {
+ var result = availableSpace.hashCode()
+ result = 31 * result + hotseatQsbSpace.hashCode()
+ result = 31 * result + edgePadding.hashCode()
+ result = 31 * result + spec.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is CalculatedHotseatSpec &&
+ availableSpace == other.availableSpace &&
+ hotseatQsbSpace == other.hotseatQsbSpace &&
+ edgePadding == other.edgePadding &&
+ spec == other.spec
+ }
+
+ override fun toString(): String {
+ return "${this::class.simpleName}(" +
+ "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " +
+ "edgePadding=$edgePadding, " +
+ "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
+ ")"
+ }
+}
diff --git a/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt b/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt
new file mode 100644
index 00000000000..a4b25e529a3
--- /dev/null
+++ b/src/com/android/launcher3/responsive/ResponsiveCellSpecsProvider.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 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.responsive
+
+import android.content.res.TypedArray
+import android.util.Log
+import com.android.launcher3.R
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
+import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
+import com.android.launcher3.util.ResourceHelper
+
+class ResponsiveCellSpecsProvider(groupOfSpecs: List>) {
+ private val groupOfSpecs: List>
+
+ init {
+ this.groupOfSpecs =
+ groupOfSpecs
+ .onEach { group ->
+ check(group.widthSpecs.isEmpty() && group.heightSpecs.isNotEmpty()) {
+ "${this::class.simpleName} is invalid, only heightSpecs are allowed - " +
+ "width list size = ${group.widthSpecs.size}; " +
+ "height list size = ${group.heightSpecs.size}."
+ }
+ }
+ .sortedBy { it.aspectRatio }
+ }
+
+ fun getSpecsByAspectRatio(aspectRatio: Float): ResponsiveSpecGroup {
+ check(aspectRatio > 0f) { "Invalid aspect ratio! The value should be bigger than 0." }
+
+ val specsGroup = groupOfSpecs.firstOrNull { aspectRatio <= it.aspectRatio }
+ check(specsGroup != null) { "No available spec with aspectRatio within $aspectRatio." }
+
+ return specsGroup
+ }
+
+ fun getCalculatedSpec(aspectRatio: Float, availableHeightSpace: Int): CalculatedCellSpec {
+ val specsGroup = getSpecsByAspectRatio(aspectRatio)
+ val spec = specsGroup.getSpec(DimensionType.HEIGHT, availableHeightSpace)
+ return CalculatedCellSpec(availableHeightSpace, spec)
+ }
+
+ fun getCalculatedSpec(
+ aspectRatio: Float,
+ availableHeightSpace: Int,
+ workspaceCellSpec: CalculatedCellSpec
+ ): CalculatedCellSpec {
+ val specsGroup = getSpecsByAspectRatio(aspectRatio)
+ val spec = specsGroup.getSpec(DimensionType.HEIGHT, availableHeightSpace)
+ return CalculatedCellSpec(availableHeightSpace, spec, workspaceCellSpec)
+ }
+
+ companion object {
+ @JvmStatic
+ fun create(resourceHelper: ResourceHelper): ResponsiveCellSpecsProvider {
+ val parser = ResponsiveSpecsParser(resourceHelper)
+ val specs = parser.parseXML(ResponsiveSpecType.Cell, ::CellSpec)
+ return ResponsiveCellSpecsProvider(specs)
+ }
+ }
+}
+
+data class CellSpec(
+ override val maxAvailableSize: Int,
+ override val dimensionType: DimensionType,
+ override val specType: ResponsiveSpecType,
+ val iconSize: SizeSpec,
+ val iconTextSize: SizeSpec,
+ val iconDrawablePadding: SizeSpec
+) : IResponsiveSpec {
+ init {
+ check(isValid()) { "Invalid CellSpec found." }
+ }
+
+ constructor(
+ responsiveSpecType: ResponsiveSpecType,
+ attrs: TypedArray,
+ specs: Map
+ ) : this(
+ maxAvailableSize =
+ attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
+ dimensionType =
+ DimensionType.entries[
+ attrs.getInt(
+ R.styleable.ResponsiveSpec_dimensionType,
+ DimensionType.HEIGHT.ordinal
+ )],
+ specType = responsiveSpecType,
+ iconSize = specs.getOrError(SizeSpec.XmlTags.ICON_SIZE),
+ iconTextSize = specs.getOrError(SizeSpec.XmlTags.ICON_TEXT_SIZE),
+ iconDrawablePadding = specs.getOrError(SizeSpec.XmlTags.ICON_DRAWABLE_PADDING)
+ )
+
+ fun isValid(): Boolean {
+ if (maxAvailableSize <= 0) {
+ logError("The property maxAvailableSize must be higher than 0.")
+ return false
+ }
+
+ // All specs need to be individually valid
+ if (!allSpecsAreValid()) {
+ logError("Specs must be either Fixed Size or Match Workspace!")
+ return false
+ }
+
+ if (!isValidFixedSize()) {
+ logError("The total Fixed Size used must be lower or equal to $maxAvailableSize.")
+ return false
+ }
+
+ return true
+ }
+
+ private fun isValidFixedSize(): Boolean {
+ val totalSize = iconSize.fixedSize + iconTextSize.fixedSize + iconDrawablePadding.fixedSize
+ return totalSize <= maxAvailableSize
+ }
+
+ private fun allSpecsAreValid(): Boolean {
+ return (iconSize.fixedSize > 0f || iconSize.matchWorkspace) &&
+ (iconTextSize.fixedSize >= 0f || iconTextSize.matchWorkspace) &&
+ (iconDrawablePadding.fixedSize >= 0f || iconDrawablePadding.matchWorkspace)
+ }
+
+ private fun logError(message: String) {
+ Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+ }
+
+ companion object {
+ private const val LOG_TAG = "CellSpec"
+ }
+}
+
+data class CalculatedCellSpec(
+ val availableSpace: Int,
+ val spec: CellSpec,
+ val iconSize: Int,
+ val iconTextSize: Int,
+ val iconDrawablePadding: Int
+) {
+ constructor(
+ availableSpace: Int,
+ spec: CellSpec
+ ) : this(
+ availableSpace = availableSpace,
+ spec = spec,
+ iconSize = spec.iconSize.getCalculatedValue(availableSpace),
+ iconTextSize = spec.iconTextSize.getCalculatedValue(availableSpace),
+ iconDrawablePadding = spec.iconDrawablePadding.getCalculatedValue(availableSpace)
+ )
+
+ constructor(
+ availableSpace: Int,
+ spec: CellSpec,
+ workspaceCellSpec: CalculatedCellSpec
+ ) : this(
+ availableSpace = availableSpace,
+ spec = spec,
+ iconSize = getCalculatedValue(availableSpace, spec.iconSize, workspaceCellSpec.iconSize),
+ iconTextSize =
+ getCalculatedValue(availableSpace, spec.iconTextSize, workspaceCellSpec.iconTextSize),
+ iconDrawablePadding =
+ getCalculatedValue(
+ availableSpace,
+ spec.iconDrawablePadding,
+ workspaceCellSpec.iconDrawablePadding
+ )
+ )
+
+ companion object {
+ private fun getCalculatedValue(
+ availableSpace: Int,
+ spec: SizeSpec,
+ workspaceValue: Int
+ ): Int =
+ if (spec.matchWorkspace) workspaceValue else spec.getCalculatedValue(availableSpace)
+ }
+
+ override fun toString(): String {
+ return "${this::class.simpleName}(" +
+ "availableSpace=$availableSpace, iconSize=$iconSize, " +
+ "iconTextSize=$iconTextSize, iconDrawablePadding=$iconDrawablePadding, " +
+ "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
+ ")"
+ }
+}
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
similarity index 52%
rename from src/com/android/launcher3/responsive/ResponsiveSpecs.kt
rename to src/com/android/launcher3/responsive/ResponsiveSpec.kt
index 72a0ea4f721..b0e1b27b5bb 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpec.kt
@@ -16,81 +16,107 @@
package com.android.launcher3.responsive
+import android.content.res.TypedArray
import android.util.Log
+import com.android.launcher3.R
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
/**
- * Base class for responsive specs that holds a list of width and height specs.
+ * Interface for responsive grid specs
*
- * @param widthSpecs List of width responsive specifications
- * @param heightSpecs List of height responsive specifications
+ * @property maxAvailableSize indicates the breakpoint to use this specification.
+ * @property dimensionType indicates whether the paddings and gutters will be applied vertically or
+ * horizontally.
+ * @property specType a [ResponsiveSpecType] that indicates the type of the spec.
*/
-abstract class ResponsiveSpecs(
- val widthSpecs: List,
- val heightSpecs: List
-) {
-
- init {
- check(widthSpecs.isNotEmpty() && heightSpecs.isNotEmpty()) {
- "${this::class.simpleName} is incomplete - " +
- "width list size = ${widthSpecs.size}; " +
- "height list size = ${heightSpecs.size}."
- }
- }
-
- /**
- * Get a [ResponsiveSpec] for width within the breakpoint.
- *
- * @param availableWidth The width breakpoint for the spec
- * @return A [ResponsiveSpec] for width.
- */
- fun getWidthSpec(availableWidth: Int): T {
- val spec = widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
- check(spec != null) { "No available width spec found within $availableWidth." }
- return spec
- }
-
- /**
- * Get a [ResponsiveSpec] for height within the breakpoint.
- *
- * @param availableHeight The height breakpoint for the spec
- * @return A [ResponsiveSpec] for height.
- */
- fun getHeightSpec(availableHeight: Int): T {
- val spec = heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
- check(spec != null) { "No available height spec found within $availableHeight." }
- return spec
- }
+interface IResponsiveSpec {
+ val maxAvailableSize: Int
+ val dimensionType: ResponsiveSpec.DimensionType
+ val specType: ResponsiveSpecType
}
/**
- * Base class for a responsive specification that is used to calculate the paddings, gutter and cell
+ * Class for a responsive specification that is used to calculate the paddings, gutter and cell
* size.
*
* @param maxAvailableSize indicates the breakpoint to use this specification.
- * @param specType indicates whether the paddings and gutters will be applied vertically or
+ * @param dimensionType indicates whether the paddings and gutters will be applied vertically or
* horizontally.
+ * @param specType a [ResponsiveSpecType] that indicates the type of the spec.
* @param startPadding padding used at the top or left (right in RTL) in the workspace folder.
* @param endPadding padding used at the bottom or right (left in RTL) in the workspace folder.
- * @param gutter the space between the cells vertically or horizontally depending on the [specType].
- * @param cellSize height or width of the cell depending on the [specType].
+ * @param gutter the space between the cells vertically or horizontally depending on the
+ * [dimensionType].
+ * @param cellSize height or width of the cell depending on the [dimensionType].
*/
-abstract class ResponsiveSpec(
- open val maxAvailableSize: Int,
- open val specType: SpecType,
- open val startPadding: SizeSpec,
- open val endPadding: SizeSpec,
- open val gutter: SizeSpec,
- open val cellSize: SizeSpec
-) {
- open fun isValid(): Boolean {
+data class ResponsiveSpec(
+ override val maxAvailableSize: Int,
+ override val dimensionType: DimensionType,
+ override val specType: ResponsiveSpecType,
+ val startPadding: SizeSpec,
+ val endPadding: SizeSpec,
+ val gutter: SizeSpec,
+ val cellSize: SizeSpec,
+) : IResponsiveSpec {
+ init {
+ check(isValid()) { "Invalid ResponsiveSpec found." }
+ }
+
+ constructor(
+ responsiveSpecType: ResponsiveSpecType,
+ attrs: TypedArray,
+ specs: Map
+ ) : this(
+ maxAvailableSize =
+ attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
+ dimensionType =
+ DimensionType.entries[
+ attrs.getInt(
+ R.styleable.ResponsiveSpec_dimensionType,
+ DimensionType.HEIGHT.ordinal
+ )],
+ specType = responsiveSpecType,
+ startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
+ endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
+ gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
+ cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
+ )
+
+ fun isValid(): Boolean {
+ if (
+ (specType == ResponsiveSpecType.Workspace) &&
+ (startPadding.matchWorkspace ||
+ endPadding.matchWorkspace ||
+ gutter.matchWorkspace ||
+ cellSize.matchWorkspace)
+ ) {
+ logError("Workspace spec provided must not have any match workspace value.")
+ return false
+ }
+
if (maxAvailableSize <= 0) {
- Log.e(LOG_TAG, "${this::class.simpleName}#isValid - maxAvailableSize <= 0")
+ logError("The property maxAvailableSize must be higher than 0.")
return false
}
// All specs need to be individually valid
if (!allSpecsAreValid()) {
- Log.e(LOG_TAG, "${this::class.simpleName}#isValid - !allSpecsAreValid()")
+ logError("One or more specs are invalid!")
+ return false
+ }
+
+ if (!isValidRemainderSpace()) {
+ logError("The total Remainder Space used must be lower or equal to 100%.")
+ return false
+ }
+
+ if (!isValidAvailableSpace()) {
+ logError("The total Available Space used must be lower or equal to 100%.")
+ return false
+ }
+
+ if (!isValidFixedSize()) {
+ logError("The total Fixed Size used must be lower or equal to $maxAvailableSize.")
return false
}
@@ -104,13 +130,47 @@ abstract class ResponsiveSpec(
cellSize.isValid()
}
- enum class SpecType {
+ private fun isValidRemainderSpace(): Boolean {
+ // TODO(b/313621277): This validation must be update do accept only 0 or 1 instead of <= 1f.
+ return startPadding.ofRemainderSpace +
+ endPadding.ofRemainderSpace +
+ gutter.ofRemainderSpace +
+ cellSize.ofRemainderSpace <= 1f
+ }
+
+ private fun isValidAvailableSpace(): Boolean {
+ return startPadding.ofAvailableSpace +
+ endPadding.ofAvailableSpace +
+ gutter.ofAvailableSpace +
+ cellSize.ofAvailableSpace < 1f
+ }
+
+ private fun isValidFixedSize(): Boolean {
+ return startPadding.fixedSize +
+ endPadding.fixedSize +
+ gutter.fixedSize +
+ cellSize.fixedSize <= maxAvailableSize
+ }
+
+ private fun logError(message: String) {
+ Log.e(LOG_TAG, "${this::class.simpleName}#isValid - $message - $this")
+ }
+
+ enum class DimensionType {
HEIGHT,
WIDTH
}
companion object {
private const val LOG_TAG = "ResponsiveSpec"
+
+ enum class ResponsiveSpecType(val xmlTag: String) {
+ AllApps("allAppsSpec"),
+ Folder("folderSpec"),
+ Workspace("workspaceSpec"),
+ Hotseat("hotseatSpec"),
+ Cell("cellSpec")
+ }
}
}
@@ -118,7 +178,10 @@ abstract class ResponsiveSpec(
* Calculated responsive specs contains the final paddings, gutter and cell size in pixels after
* they are calculated from the available space, cells and workspace specs.
*/
-sealed class CalculatedResponsiveSpec {
+class CalculatedResponsiveSpec {
+ var aspectRatio: Float = Float.NaN
+ private set
+
var availableSpace: Int = 0
private set
@@ -141,11 +204,13 @@ sealed class CalculatedResponsiveSpec {
private set
constructor(
+ aspectRatio: Float,
availableSpace: Int,
cells: Int,
spec: ResponsiveSpec,
- calculatedWorkspaceSpec: CalculatedWorkspaceSpec
+ calculatedWorkspaceSpec: CalculatedResponsiveSpec
) {
+ this.aspectRatio = aspectRatio
this.availableSpace = availableSpace
this.cells = cells
this.spec = spec
@@ -165,7 +230,8 @@ sealed class CalculatedResponsiveSpec {
updateRemainderSpaces(availableSpace, cells, spec)
}
- constructor(availableSpace: Int, cells: Int, spec: ResponsiveSpec) {
+ constructor(aspectRatio: Float, availableSpace: Int, cells: Int, spec: ResponsiveSpec) {
+ this.aspectRatio = aspectRatio
this.availableSpace = availableSpace
this.cells = cells
this.spec = spec
@@ -179,6 +245,8 @@ sealed class CalculatedResponsiveSpec {
updateRemainderSpaces(availableSpace, cells, spec)
}
+ fun isResponsiveSpecType(type: ResponsiveSpecType) = spec.specType == type
+
private fun updateRemainderSpaces(availableSpace: Int, cells: Int, spec: ResponsiveSpec) {
val gutters = cells - 1
val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
@@ -213,10 +281,11 @@ sealed class CalculatedResponsiveSpec {
}
override fun toString(): String {
- return "${this::class.simpleName}(" +
+ return "Calculated${spec.specType}Spec(" +
"availableSpace=$availableSpace, cells=$cells, startPaddingPx=$startPaddingPx, " +
"endPaddingPx=$endPaddingPx, gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
- "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
+ "aspectRatio=${aspectRatio}, " +
+ "${spec.specType}Spec.maxAvailableSize=${spec.maxAvailableSize}" +
")"
}
}
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt b/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt
new file mode 100644
index 00000000000..a758be8e1c1
--- /dev/null
+++ b/src/com/android/launcher3/responsive/ResponsiveSpecGroup.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 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.responsive
+
+import android.content.res.TypedArray
+import com.android.launcher3.R
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
+import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
+
+/**
+ * Base class for responsive specs that holds a list of width and height specs.
+ *
+ * @param widthSpecs List of width responsive specifications
+ * @param heightSpecs List of height responsive specifications
+ */
+class ResponsiveSpecGroup(
+ val aspectRatio: Float,
+ widthSpecs: List,
+ heightSpecs: List
+) {
+ val widthSpecs: List
+ val heightSpecs: List
+
+ init {
+ check(aspectRatio > 0f) { "Invalid aspect ratio! Aspect ratio should be bigger than zero." }
+ this.widthSpecs = widthSpecs.sortedBy { it.maxAvailableSize }
+ this.heightSpecs = heightSpecs.sortedBy { it.maxAvailableSize }
+ }
+
+ /**
+ * Get a [ResponsiveSpec] within the breakpoint.
+ *
+ * @param type Type of the spec to be retrieved (width or height)
+ * @param availableSize The breakpoint for the spec
+ * @return A [ResponsiveSpec].
+ */
+ fun getSpec(type: DimensionType, availableSize: Int): T {
+ val spec =
+ if (type == DimensionType.WIDTH) {
+ widthSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
+ } else {
+ heightSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
+ }
+ check(spec != null) { "No available $type spec found within $availableSize. $this" }
+ return spec
+ }
+
+ override fun toString(): String {
+ fun printSpec(spec: IResponsiveSpec) =
+ when (spec.specType) {
+ ResponsiveSpecType.AllApps,
+ ResponsiveSpecType.Folder,
+ ResponsiveSpecType.Workspace -> (spec as ResponsiveSpec).toString()
+ ResponsiveSpecType.Hotseat -> (spec as HotseatSpec).toString()
+ ResponsiveSpecType.Cell -> (spec as CellSpec).toString()
+ }
+
+ val widthSpecsString = widthSpecs.joinToString(", ") { printSpec(it) }
+ val heightSpecsString = heightSpecs.joinToString(", ") { printSpec(it) }
+ return "ResponsiveSpecGroup(" +
+ "aspectRatio=${aspectRatio}, " +
+ "widthSpecs=[${widthSpecsString}], " +
+ "heightSpecs=[${heightSpecsString}]" +
+ ")"
+ }
+
+ companion object {
+ const val XML_GROUP_NAME = "specs"
+
+ fun create(
+ attrs: TypedArray,
+ specs: List
+ ): ResponsiveSpecGroup {
+ val (widthSpecs, heightSpecs) =
+ specs.partition { it.dimensionType == DimensionType.WIDTH }
+ val aspectRatio = attrs.getFloat(R.styleable.ResponsiveSpecGroup_maxAspectRatio, 0f)
+ return ResponsiveSpecGroup(aspectRatio, widthSpecs, heightSpecs)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt
index a89b619c00d..acb4226de1b 100644
--- a/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt
+++ b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt
@@ -20,49 +20,60 @@ import android.content.res.TypedArray
import android.content.res.XmlResourceParser
import android.util.Xml
import com.android.launcher3.R
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
import com.android.launcher3.util.ResourceHelper
-import java.io.IOException
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
+import java.io.IOException
class ResponsiveSpecsParser(private val resourceHelper: ResourceHelper) {
- private fun parseSizeSpecs(parser: XmlResourceParser): Map {
- val parentName = parser.name
- parser.next()
-
- val result = mutableMapOf()
- while (parser.eventType != XmlPullParser.END_DOCUMENT && parser.name != parentName) {
- if (parser.eventType == XmlResourceParser.START_TAG) {
- result[parser.name] = SizeSpec.create(resourceHelper, Xml.asAttributeSet(parser))
- }
- parser.next()
- }
-
- return result
- }
-
- fun parseXML(
- tagName: String,
- map: (attributes: TypedArray, sizeSpecs: Map) -> T
- ): List {
+ fun parseXML(
+ responsiveSpecType: ResponsiveSpecType,
+ map:
+ (
+ responsiveSpecType: ResponsiveSpecType,
+ attributes: TypedArray,
+ sizeSpecs: Map
+ ) -> T
+ ): List> {
val parser: XmlResourceParser = resourceHelper.getXml()
try {
- val list = mutableListOf()
+ val groups = mutableListOf>()
+ val specs = mutableListOf()
+ var groupAttrs: TypedArray? = null
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlResourceParser.START_TAG && parser.name == tagName) {
- val attrs =
- resourceHelper.obtainStyledAttributes(
- Xml.asAttributeSet(parser),
- R.styleable.ResponsiveSpec
- )
-
- val sizeSpecs = parseSizeSpecs(parser)
- list += map(attrs, sizeSpecs)
- attrs.recycle()
+ // Parsing Group
+ when {
+ parser starts ResponsiveSpecGroup.XML_GROUP_NAME -> {
+ groupAttrs =
+ resourceHelper.obtainStyledAttributes(
+ Xml.asAttributeSet(parser),
+ R.styleable.ResponsiveSpecGroup
+ )
+ }
+ parser ends ResponsiveSpecGroup.XML_GROUP_NAME -> {
+ checkNotNull(groupAttrs)
+ groups += ResponsiveSpecGroup.create(groupAttrs, specs)
+ specs.clear()
+ groupAttrs.recycle()
+ groupAttrs = null
+ }
+ // Mapping Spec to WorkspaceSpec, AllAppsSpec, FolderSpecs, HotseatSpec
+ parser starts responsiveSpecType.xmlTag -> {
+ val attrs =
+ resourceHelper.obtainStyledAttributes(
+ Xml.asAttributeSet(parser),
+ R.styleable.ResponsiveSpec
+ )
+
+ val sizeSpecs = parseSizeSpecs(parser)
+ specs += map(responsiveSpecType, attrs, sizeSpecs)
+ attrs.recycle()
+ }
}
eventType = parser.next()
@@ -70,7 +81,14 @@ class ResponsiveSpecsParser(private val resourceHelper: ResourceHelper) {
parser.close()
- return list
+ // All the specs should have been linked to a group, otherwise the XML is invalid
+ check(specs.isEmpty()) {
+ throw InvalidResponsiveGridSpec(
+ "Invalid XML. ${specs.size} specs not linked to a group."
+ )
+ }
+
+ return groups
} catch (e: Exception) {
when (e) {
is NoSuchFieldException,
@@ -83,8 +101,31 @@ class ResponsiveSpecsParser(private val resourceHelper: ResourceHelper) {
parser.close()
}
}
+
+ private fun parseSizeSpecs(parser: XmlResourceParser): Map {
+ val parentName = parser.name
+ parser.next()
+
+ val result = mutableMapOf()
+ while (parser.eventType != XmlPullParser.END_DOCUMENT && parser.name != parentName) {
+ if (parser.eventType == XmlResourceParser.START_TAG) {
+ result[parser.name] = SizeSpec.create(resourceHelper, Xml.asAttributeSet(parser))
+ }
+ parser.next()
+ }
+
+ return result
+ }
+
+ private infix fun XmlResourceParser.starts(tag: String): Boolean =
+ name == tag && eventType == XmlPullParser.START_TAG
+
+ private infix fun XmlResourceParser.ends(tag: String): Boolean =
+ name == tag && eventType == XmlPullParser.END_TAG
}
fun Map.getOrError(key: String): SizeSpec {
return this.getOrElse(key) { error("Attr '$key' must be defined.") }
}
+
+class InvalidResponsiveGridSpec(message: String) : Exception(message)
diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt b/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt
new file mode 100644
index 00000000000..67eaac051ef
--- /dev/null
+++ b/src/com/android/launcher3/responsive/ResponsiveSpecsProvider.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 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.responsive
+
+import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
+import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
+import com.android.launcher3.util.ResourceHelper
+
+/**
+ * A class to provide responsive grid specs for workspace, folder and all apps.
+ *
+ * This class is responsible for provide width and height [CalculatedResponsiveSpec] to be used for
+ * the correct placement of the workspace, all apps and folders.
+ *
+ * @param type A [ResponsiveSpecType] to indicates the type of the spec.
+ * @param groupOfSpecs Groups of responsive specifications
+ */
+class ResponsiveSpecsProvider(
+ val type: ResponsiveSpecType,
+ groupOfSpecs: List>
+) {
+ private val groupOfSpecs: List>
+
+ init {
+ this.groupOfSpecs =
+ groupOfSpecs
+ .onEach { group ->
+ check(group.widthSpecs.isNotEmpty() && group.heightSpecs.isNotEmpty()) {
+ "${this::class.simpleName} is incomplete - " +
+ "width list size = ${group.widthSpecs.size}; " +
+ "height list size = ${group.heightSpecs.size}."
+ }
+ }
+ .sortedBy { it.aspectRatio }
+ }
+
+ fun getSpecsByAspectRatio(aspectRatio: Float): ResponsiveSpecGroup {
+ check(aspectRatio > 0f) { "Invalid aspect ratio! The value should be bigger than 0." }
+
+ val specsGroup = groupOfSpecs.firstOrNull { aspectRatio <= it.aspectRatio }
+ checkNotNull(specsGroup) { "No available spec with aspectRatio within $aspectRatio." }
+
+ return specsGroup
+ }
+
+ /**
+ * Retrieves a responsive grid specification that matches the number of [numCells],
+ * * [availableSpace] and [aspectRatio].
+ *
+ * @param aspectRatio the device width divided by device height (aspect ratio) to filter the
+ * specifications
+ * @param dimensionType the grid axis of the spec width is x axis, height is y axis.
+ * @param numCells number of rows/columns in the grid
+ * @param availableSpace available width to filter the specifications
+ * @return A [CalculatedResponsiveSpec] that matches the parameters provided.
+ */
+ fun getCalculatedSpec(
+ aspectRatio: Float,
+ dimensionType: DimensionType,
+ numCells: Int,
+ availableSpace: Int,
+ ): CalculatedResponsiveSpec {
+ val specsGroup = getSpecsByAspectRatio(aspectRatio)
+ val spec = specsGroup.getSpec(dimensionType, availableSpace)
+ return CalculatedResponsiveSpec(aspectRatio, availableSpace, numCells, spec)
+ }
+
+ /**
+ * Retrieves a responsive grid specification that matches the number of [numCells],
+ * * [availableSpace] and [aspectRatio]. This function uses a [CalculatedResponsiveSpec] to
+ * match workspace when its true.
+ *
+ * @param aspectRatio the device width divided by device height (aspect ratio) to filter the
+ * specifications
+ * @param dimensionType the grid axis of the spec width is x axis, height is y axis.
+ * @param numCells number of rows/columns in the grid
+ * @param availableSpace available width to filter the specifications
+ * @param calculatedWorkspaceSpec the calculated workspace specification to use its values as
+ * base when matchWorkspace is true.
+ * @return A [CalculatedResponsiveSpec] that matches the parameters provided.
+ */
+ fun getCalculatedSpec(
+ aspectRatio: Float,
+ dimensionType: DimensionType,
+ numCells: Int,
+ availableSpace: Int,
+ calculatedWorkspaceSpec: CalculatedResponsiveSpec
+ ): CalculatedResponsiveSpec {
+ check(calculatedWorkspaceSpec.spec.dimensionType == dimensionType) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: $dimensionType - " +
+ "Found: ${calculatedWorkspaceSpec.spec.dimensionType}}"
+ }
+
+ check(calculatedWorkspaceSpec.isResponsiveSpecType(ResponsiveSpecType.Workspace)) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: ${ResponsiveSpecType.Workspace} - " +
+ "Found: ${calculatedWorkspaceSpec.spec.specType}}"
+ }
+
+ val specsGroup = getSpecsByAspectRatio(aspectRatio)
+ val spec = specsGroup.getSpec(dimensionType, availableSpace)
+ return CalculatedResponsiveSpec(
+ aspectRatio,
+ availableSpace,
+ numCells,
+ spec,
+ calculatedWorkspaceSpec
+ )
+ }
+
+ companion object {
+ @JvmStatic
+ fun create(
+ resourceHelper: ResourceHelper,
+ type: ResponsiveSpecType
+ ): ResponsiveSpecsProvider {
+ val parser = ResponsiveSpecsParser(resourceHelper)
+ val specs = parser.parseXML(type, ::ResponsiveSpec)
+ return ResponsiveSpecsProvider(type, specs)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
index c868c9f7be5..d14689853ba 100644
--- a/src/com/android/launcher3/responsive/SizeSpec.kt
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -121,6 +121,10 @@ data class SizeSpec(
const val GUTTER = "gutter"
const val CELL_SIZE = "cellSize"
const val HOTSEAT_QSB_SPACE = "hotseatQsbSpace"
+ const val EDGE_PADDING = "edgePadding"
+ const val ICON_SIZE = "iconSize"
+ const val ICON_TEXT_SIZE = "iconTextSize"
+ const val ICON_DRAWABLE_PADDING = "iconDrawablePadding"
}
companion object {
diff --git a/src/com/android/launcher3/responsive/WorkspaceSpecs.kt b/src/com/android/launcher3/responsive/WorkspaceSpecs.kt
deleted file mode 100644
index 0da7026bd6b..00000000000
--- a/src/com/android/launcher3/responsive/WorkspaceSpecs.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2023 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.responsive
-
-import android.content.res.TypedArray
-import android.util.Log
-import com.android.launcher3.R
-import com.android.launcher3.responsive.ResponsiveSpec.SpecType
-import com.android.launcher3.util.ResourceHelper
-
-private const val TAG = "WorkspaceSpecs"
-
-class WorkspaceSpecs(widthSpecs: List, heightSpecs: List) :
- ResponsiveSpecs(widthSpecs, heightSpecs) {
-
- fun getCalculatedWidthSpec(columns: Int, availableWidth: Int): CalculatedWorkspaceSpec {
- val spec = getWidthSpec(availableWidth)
- return CalculatedWorkspaceSpec(availableWidth, columns, spec)
- }
-
- fun getCalculatedHeightSpec(rows: Int, availableHeight: Int): CalculatedWorkspaceSpec {
- val spec = getHeightSpec(availableHeight)
- return CalculatedWorkspaceSpec(availableHeight, rows, spec)
- }
-
- companion object {
- private const val XML_WORKSPACE_SPEC = "workspaceSpec"
-
- @JvmStatic
- fun create(resourceHelper: ResourceHelper): WorkspaceSpecs {
- val parser = ResponsiveSpecsParser(resourceHelper)
- val specs = parser.parseXML(XML_WORKSPACE_SPEC, ::WorkspaceSpec)
- val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
- return WorkspaceSpecs(widthSpecs, heightSpecs)
- }
- }
-}
-
-data class WorkspaceSpec(
- override val maxAvailableSize: Int,
- override val specType: SpecType,
- override val startPadding: SizeSpec,
- override val endPadding: SizeSpec,
- override val gutter: SizeSpec,
- override val cellSize: SizeSpec
-) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
-
- init {
- check(isValid()) { "Invalid WorkspaceSpec found." }
- }
-
- constructor(
- attrs: TypedArray,
- specs: Map
- ) : this(
- maxAvailableSize =
- attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
- specType =
- SpecType.values()[
- attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
- startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
- endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
- gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
- cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
- )
-
- override fun isValid(): Boolean {
- // Workspace spec should not match workspace
- if (
- startPadding.matchWorkspace ||
- endPadding.matchWorkspace ||
- gutter.matchWorkspace ||
- cellSize.matchWorkspace
- ) {
- Log.e(TAG, "WorkspaceSpec#isValid - workspace shouldn't contain matchWorkspace!")
- return false
- }
-
- return super.isValid()
- }
-}
-
-class CalculatedWorkspaceSpec(availableSpace: Int, cells: Int, spec: WorkspaceSpec) :
- CalculatedResponsiveSpec(availableSpace, cells, spec)
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
index 1816e7be388..2796bb06b2a 100644
--- a/src/com/android/launcher3/settings/NotificationDotsPreference.java
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -155,7 +155,7 @@ public static class NotificationAccessConfirmation
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
- String msg = context.getString(R.string.msg_missing_notification_access,
+ String msg = context.getString(R.string.missing_notification_access_desc,
context.getString(R.string.derived_app_name));
return new AlertDialog.Builder(context)
.setTitle(R.string.title_missing_notification_access)
diff --git a/src/com/android/launcher3/util/CellContentDimensions.kt b/src/com/android/launcher3/util/CellContentDimensions.kt
new file mode 100644
index 00000000000..3c8e0c44ca0
--- /dev/null
+++ b/src/com/android/launcher3/util/CellContentDimensions.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 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.util
+
+import com.android.launcher3.Utilities
+import kotlin.math.max
+
+class CellContentDimensions(
+ var iconSizePx: Int,
+ var iconDrawablePaddingPx: Int,
+ var iconTextSizePx: Int
+) {
+ /**
+ * This method goes through some steps to reduce the padding between icon and label, icon size
+ * and then label size, until it can fit in the [cellHeightPx].
+ *
+ * @return the height of the content after being sized down.
+ */
+ fun resizeToFitCellHeight(cellHeightPx: Int, iconSizeSteps: IconSizeSteps): Int {
+ var cellContentHeight = getCellContentHeight()
+
+ // Step 1. Decrease drawable padding
+ if (cellContentHeight > cellHeightPx) {
+ val diff = cellContentHeight - cellHeightPx
+ iconDrawablePaddingPx = max(0, iconDrawablePaddingPx - diff)
+ cellContentHeight = getCellContentHeight()
+ }
+
+ while (
+ (iconTextSizePx > iconSizeSteps.minimumIconLabelSize ||
+ iconSizePx > iconSizeSteps.minimumIconSize()) && cellContentHeight > cellHeightPx
+ ) {
+ // Step 2. Decrease icon size
+ iconSizePx = iconSizeSteps.getNextLowerIconSize(iconSizePx)
+ cellContentHeight = getCellContentHeight()
+
+ // Step 3. Decrease label size
+ if (cellContentHeight > cellHeightPx) {
+ iconTextSizePx =
+ max(
+ iconSizeSteps.minimumIconLabelSize,
+ iconTextSizePx - IconSizeSteps.TEXT_STEP
+ )
+ cellContentHeight = getCellContentHeight()
+ }
+ }
+
+ return cellContentHeight
+ }
+
+ /** Calculate new cellContentHeight */
+ fun getCellContentHeight(): Int {
+ val iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx.toFloat())
+ return iconSizePx + iconDrawablePaddingPx + iconTextHeight
+ }
+}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 50807c8a085..fd0ba16171a 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -20,9 +20,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
+import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TRANSIENT_TASKBAR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
@@ -32,6 +31,7 @@
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
@@ -62,30 +62,31 @@
import java.util.StringJoiner;
/**
- * Utility class to cache properties of default display to avoid a system RPC on
- * every call.
+ * Utility class to cache properties of default display to avoid a system RPC on every call.
*/
@SuppressLint("NewApi")
public class DisplayController implements ComponentCallbacks, SafeCloseable {
private static final String TAG = "DisplayController";
private static final boolean DEBUG = false;
- private static boolean sTransientTaskbarStatusForTests;
+ private static boolean sTransientTaskbarStatusForTests = true;
// TODO(b/254119092) remove all logs with this tag
public static final String TASKBAR_NOT_DESTROYED_TAG = "b/254119092";
- public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(
- DisplayController::new);
+ public static final MainThreadInitializedObject INSTANCE =
+ new MainThreadInitializedObject<>(DisplayController::new);
public static final int CHANGE_ACTIVE_SCREEN = 1 << 0;
public static final int CHANGE_ROTATION = 1 << 1;
public static final int CHANGE_DENSITY = 1 << 2;
public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 3;
public static final int CHANGE_NAVIGATION_MODE = 1 << 4;
+ public static final int CHANGE_TASKBAR_PINNING = 1 << 5;
public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
- | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE;
+ | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS | CHANGE_NAVIGATION_MODE
+ | CHANGE_TASKBAR_PINNING;
private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
private static final String TARGET_OVERLAY_PACKAGE = "android";
@@ -96,8 +97,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
// Null for SDK < S
private final Context mWindowContext;
- // The callback in this listener updates DeviceProfile, which other listeners
- // might depend on
+ // The callback in this listener updates DeviceProfile, which other listeners might depend on
private DisplayInfoChangeListener mPriorityListener;
private final ArrayList mListeners = new ArrayList<>();
@@ -106,13 +106,17 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
private Info mInfo;
private boolean mDestroyed = false;
- private final LauncherPrefs mPrefs;
+ private SharedPreferences.OnSharedPreferenceChangeListener
+ mTaskbarPinningPreferenceChangeListener;
@VisibleForTesting
protected DisplayController(Context context) {
mContext = context;
mDM = context.getSystemService(DisplayManager.class);
- mPrefs = LauncherPrefs.get(context);
+
+ if (false) {
+ attachTaskbarPinningSharedPreferenceChangeListener(mContext);
+ }
Display display = mDM.getDisplay(DEFAULT_DISPLAY);
if (Utilities.ATLEAST_S) {
@@ -133,6 +137,21 @@ protected DisplayController(Context context) {
FileLog.i(TAG, "(CTOR) perDisplayBounds: " + mInfo.mPerDisplayBounds);
}
+ private void attachTaskbarPinningSharedPreferenceChangeListener(Context context) {
+ mTaskbarPinningPreferenceChangeListener =
+ (sharedPreferences, key) -> {
+ if (TASKBAR_PINNING_KEY.equals(key)
+ && mInfo.mIsTaskbarPinned != LauncherPrefs.get(mContext).get(
+ TASKBAR_PINNING)
+ ) {
+ handleInfoChange(mWindowContext.getDisplay());
+ }
+ };
+
+ LauncherPrefs.get(context).addListener(
+ mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
+ }
+
/**
* Returns the current navigation mode
*/
@@ -144,27 +163,7 @@ public static NavigationMode getNavigationMode(Context context) {
* Returns whether taskbar is transient.
*/
public static boolean isTransientTaskbar(Context context) {
- return INSTANCE.get(context).isTransientTaskbar();
- }
-
- /**
- * Returns whether taskbar is transient.
- */
- public boolean isTransientTaskbar() {
- // TODO(b/258604917): When running in test harness, use
- // !sTransientTaskbarStatusForTests
- // once tests are updated to expect new persistent behavior such as not allowing
- // long press
- // to stash.
- if (!Utilities.isRunningInTestHarness()
- && ENABLE_TASKBAR_PINNING.get()
- && mPrefs.get(TASKBAR_PINNING)) {
- return false;
- }
- return getInfo().navigationMode == NavigationMode.NO_BUTTON
- && (Utilities.isRunningInTestHarness()
- ? sTransientTaskbarStatusForTests
- : ENABLE_TRANSIENT_TASKBAR.get());
+ return INSTANCE.get(context).getInfo().isTransientTaskbar();
}
/**
@@ -178,6 +177,10 @@ public static void enableTransientTaskbarForTests(boolean enable) {
@Override
public void close() {
mDestroyed = true;
+ if (false) {
+ LauncherPrefs.get(mContext).removeListener(
+ mTaskbarPinningPreferenceChangeListener, TASKBAR_PINNING);
+ }
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
} else {
@@ -192,10 +195,9 @@ public interface DisplayInfoChangeListener {
/**
* Invoked when display info has changed.
- *
* @param context updated context associated with the display.
- * @param info updated display information.
- * @param flags bitmask indicating type of change.
+ * @param info updated display information.
+ * @param flags bitmask indicating type of change.
*/
void onDisplayInfoChanged(Context context, Info info, int flags);
}
@@ -232,14 +234,13 @@ public final void onConfigurationChanged(Configuration config) {
|| config.fontScale != mInfo.fontScale
|| display.getRotation() != mInfo.rotation
|| !mInfo.mScreenSizeDp.equals(
- new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
+ new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
handleInfoChange(display);
}
}
@Override
- public final void onLowMemory() {
- }
+ public final void onLowMemory() { }
public void setPriorityListener(DisplayInfoChangeListener listener) {
mPriorityListener = listener;
@@ -262,7 +263,8 @@ private Context getDisplayInfoContext(Display display) {
}
@AnyThread
- private void handleInfoChange(Display display) {
+ @VisibleForTesting
+ public void handleInfoChange(Display display) {
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
Info oldInfo = mInfo;
@@ -295,6 +297,9 @@ private void handleInfoChange(Display display) {
FileLog.w(TAG,
"(CHANGE_SUPPORTED_BOUNDS) perDisplayBounds: " + newInfo.mPerDisplayBounds);
}
+ if (newInfo.mIsTaskbarPinned != oldInfo.mIsTaskbarPinned) {
+ change |= CHANGE_TASKBAR_PINNING;
+ }
if (DEBUG) {
Log.d(TAG, "handleInfoChange - change: " + getChangeFlagsString(change));
}
@@ -334,7 +339,10 @@ public static class Info {
// WindowBounds
public final WindowBounds realBounds;
public final Set supportedBounds = new ArraySet<>();
- private final ArrayMap> mPerDisplayBounds = new ArrayMap<>();
+ private final ArrayMap> mPerDisplayBounds =
+ new ArrayMap<>();
+
+ private final boolean mIsTaskbarPinned;
public Info(Context displayInfoContext) {
/* don't need system overrides for external displays */
@@ -343,8 +351,8 @@ public Info(Context displayInfoContext) {
// Used for testing
public Info(Context displayInfoContext,
- WindowManagerProxy wmProxy,
- Map> perDisplayBoundsCache) {
+ WindowManagerProxy wmProxy,
+ Map> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
@@ -392,6 +400,27 @@ public Info(Context displayInfoContext,
Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo);
Log.d(TAG, "perDisplayBounds: " + mPerDisplayBounds);
}
+
+ mIsTaskbarPinned = LauncherPrefs.get(displayInfoContext).get(TASKBAR_PINNING);
+ }
+
+ /**
+ * Returns whether taskbar is transient.
+ */
+ public boolean isTransientTaskbar() {
+ if (navigationMode != NavigationMode.NO_BUTTON) {
+ return false;
+ }
+ if (Utilities.isRunningInTestHarness()) {
+ // TODO(b/258604917): Once ENABLE_TASKBAR_PINNING is enabled, remove usage of
+ // sTransientTaskbarStatusForTests and update test to directly
+ // toggle shared preference to switch transient taskbar on/off.
+ return sTransientTaskbarStatusForTests;
+ }
+ if (false) {
+ return !mIsTaskbarPinned;
+ }
+ return true;
}
/**
@@ -401,6 +430,11 @@ public boolean isTablet(WindowBounds bounds) {
return smallestSizeDp(bounds) >= MIN_TABLET_WIDTH;
}
+ /** Getter for {@link #navigationMode} to allow mocking. */
+ public NavigationMode getNavigationMode() {
+ return navigationMode;
+ }
+
/**
* Returns smallest size in dp for given bounds.
*/
@@ -422,7 +456,6 @@ public int getDensityDpi() {
/**
* Returns the given binary flags as a human-readable string.
- *
* @see #CHANGE_ALL
*/
public String getChangeFlagsString(int change) {
@@ -432,6 +465,7 @@ public String getChangeFlagsString(int change) {
appendFlag(result, change, CHANGE_DENSITY, "CHANGE_DENSITY");
appendFlag(result, change, CHANGE_SUPPORTED_BOUNDS, "CHANGE_SUPPORTED_BOUNDS");
appendFlag(result, change, CHANGE_NAVIGATION_MODE, "CHANGE_NAVIGATION_MODE");
+ appendFlag(result, change, CHANGE_TASKBAR_PINNING, "CHANGE_TASKBAR_VARIANT");
return result.toString();
}
@@ -446,9 +480,11 @@ public void dump(PrintWriter pw) {
pw.println(" fontScale=" + info.fontScale);
pw.println(" densityDpi=" + info.densityDpi);
pw.println(" navigationMode=" + info.navigationMode.name());
+ pw.println(" isTaskbarPinned=" + info.mIsTaskbarPinned);
pw.println(" currentSize=" + info.currentSize);
info.mPerDisplayBounds.forEach((key, value) -> pw.println(
" perDisplayBounds - " + key + ": " + value));
+ pw.println(" isTransientTaskbar=" + info.isTransientTaskbar());
}
/**
@@ -464,10 +500,8 @@ public PortraitSize(int w, int h) {
@Override
public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
PortraitSize that = (PortraitSize) o;
return width == that.width && height == that.height;
}
diff --git a/src/com/android/launcher3/util/IconSizeSteps.kt b/src/com/android/launcher3/util/IconSizeSteps.kt
index 2a5afe0ce2f..6128eb4dfbd 100644
--- a/src/com/android/launcher3/util/IconSizeSteps.kt
+++ b/src/com/android/launcher3/util/IconSizeSteps.kt
@@ -23,12 +23,14 @@ import kotlin.math.max
class IconSizeSteps(res: Resources) {
private val steps: List
+ val minimumIconLabelSize: Int
init {
steps =
res.obtainTypedArray(R.array.icon_size_steps).use {
(0 until it.length()).map { step -> it.getDimensionOrThrow(step).toInt() }.sorted()
}
+ minimumIconLabelSize = res.getDimensionPixelSize(R.dimen.minimum_icon_label_size)
}
fun minimumIconSize(): Int = steps[0]
@@ -37,11 +39,15 @@ class IconSizeSteps(res: Resources) {
return steps[max(0, getIndexForIconSize(iconSizePx) - 1)]
}
- fun getIconSmallerThan(cellWidth: Int): Int {
- return steps.lastOrNull { it <= cellWidth } ?: steps[0]
+ fun getIconSmallerThan(cellSize: Int): Int {
+ return steps.lastOrNull { it <= cellSize } ?: steps[0]
}
private fun getIndexForIconSize(iconSizePx: Int): Int {
return max(0, steps.indexOfFirst { iconSizePx <= it })
}
+
+ companion object {
+ internal const val TEXT_STEP = 1
+ }
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index d1a2f432e7c..ef3169d0ef5 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -18,7 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.Utilities.dpiFromPx;
-
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT;
import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
@@ -136,7 +135,7 @@ public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo
*/
@TargetApi(Build.VERSION_CODES.R)
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
- Rect outInsets) {
+ Rect outInsets) {
if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
@@ -156,10 +155,10 @@ public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInset
int bottomNav = isTablet
? 0
: (isPortrait
- ? getDimenByName(systemRes, NAVBAR_HEIGHT)
- : (isGesture
- ? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE)
- : 0));
+ ? getDimenByName(systemRes, NAVBAR_HEIGHT)
+ : (isGesture
+ ? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE)
+ : 0));
Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
@@ -205,7 +204,7 @@ protected int getStatusBarHeight(Context context, boolean isPortrait, int status
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
protected List estimateWindowBounds(Context context,
- CachedDisplayInfo displayInfo) {
+ CachedDisplayInfo displayInfo) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
int rotation = displayInfo.rotation;
Rect safeCutout = displayInfo.cutout;
@@ -236,14 +235,14 @@ protected List estimateWindowBounds(Context context,
navBarHeightPortrait = isTablet
? (mTaskbarDrawnInProcess
- ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
+ ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
: getDimenByName(systemRes, NAVBAR_HEIGHT);
navBarHeightLandscape = isTablet
? (mTaskbarDrawnInProcess
- ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
+ ? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
: (isTabletOrGesture
- ? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE) : 0);
+ ? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE) : 0);
navbarWidthLandscape = isTabletOrGesture
? 0
: getDimenByName(systemRes, NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
@@ -338,6 +337,20 @@ protected CachedDisplayInfo getDisplayInfo(WindowMetrics windowMetrics, int rota
return new CachedDisplayInfo(size, rotation, cutoutRect);
}
+ /**
+ * Returns bounds of the display associated with the context, or bounds of DEFAULT_DISPLAY
+ * if the context isn't associated with a display.
+ */
+ public Rect getCurrentBounds(Context displayInfoContext) {
+ Resources res = displayInfoContext.getResources();
+ Configuration config = res.getConfiguration();
+
+ float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
+ float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+
+ return new Rect(0, 0, (int) screenWidth, (int) screenHeight);
+ }
+
/**
* Returns rotation of the display associated with the context, or rotation of DEFAULT_DISPLAY
* if the context isn't associated with a display.