diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index b329156cf3..b6d3146ffe 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -143,9 +143,10 @@ public class PredictionRowView @Override public int getExpectedHeight() { DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); - int iconHeight = deviceProfile.allAppsIconSizePx; - int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx; - int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx); + int iconHeight = deviceProfile.getAllAppsProfile().getIconSizePx(); + int iconPadding = deviceProfile.getAllAppsProfile().getIconDrawablePaddingPx(); + int textHeight = Utilities.calculateTextHeight( + deviceProfile.getAllAppsProfile().getIconTextSizePx()); int totalHeight = iconHeight + iconPadding + textHeight + mVerticalPadding * 2; // Prediction row height will be 4dp bigger than the regular apps in A-Z list when two line // is not enabled. Otherwise, the extra height will increase by just the textHeight. @@ -235,7 +236,11 @@ public class PredictionRowView lp.height = ViewGroup.LayoutParams.MATCH_PARENT; } else { // Ensure the all apps icon height matches the workspace icons in portrait mode. - lp.height = mActivityContext.getDeviceProfile().allAppsCellHeightPx; + lp.height = + mActivityContext + .getDeviceProfile() + .getAllAppsProfile() + .getCellHeightPx(); } lp.width = 0; lp.weight = 1; diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index a8460c9d7c..70cbd26122 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -34,7 +34,8 @@ public class LayoutUtils { * The height for the swipe up motion */ public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) { - float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + float swipeHeight = dp.getAllAppsProfile().getCellHeightPx() + - dp.getAllAppsProfile().getIconTextSizePx(); if (DisplayController.getNavigationMode(context) == NavigationMode.NO_BUTTON) { swipeHeight -= dp.getInsets().bottom; } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index c56f50e193..2b5c67b014 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -273,15 +273,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setCenterVertically(mDeviceProfile.iconCenterVertically); } else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW || mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW) { - setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.allAppsIconTextSizePx); - setCompoundDrawablePadding(mDeviceProfile.allAppsIconDrawablePaddingPx); - defaultIconSize = mDeviceProfile.allAppsIconSizePx; + setTextSize(TypedValue.COMPLEX_UNIT_PX, + mDeviceProfile.getAllAppsProfile().getIconTextSizePx()); + setCompoundDrawablePadding( + mDeviceProfile.getAllAppsProfile().getIconDrawablePaddingPx()); + defaultIconSize = mDeviceProfile.getAllAppsProfile().getIconSizePx(); } else if (mDisplay == DISPLAY_FOLDER) { setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.folderChildTextSizePx); setCompoundDrawablePadding(mDeviceProfile.folderChildDrawablePaddingPx); defaultIconSize = mDeviceProfile.folderChildIconSizePx; } else if (mDisplay == DISPLAY_SEARCH_RESULT) { - setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.allAppsIconTextSizePx); + setTextSize(TypedValue.COMPLEX_UNIT_PX, + mDeviceProfile.getAllAppsProfile().getIconTextSizePx()); defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_icon_size); } else if (mDisplay == DISPLAY_SEARCH_RESULT_SMALL) { defaultIconSize = getResources().getDimensionPixelSize( @@ -576,7 +579,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, switch (mDisplay) { case DISPLAY_ALL_APPS, DISPLAY_PREDICTION_ROW -> { - return mDeviceProfile.maxAllAppsTextLineCount; + return mDeviceProfile.getAllAppsProfile().getMaxAllAppsTextLineCount(); } case DISPLAY_WORKSPACE -> { return mDeviceProfile.maxIconTextLineCount; @@ -961,8 +964,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, // Only apply two line for all_apps and device search only if necessary. if (shouldUseTwoLine() && (mLastOriginalText != null)) { int allowedVerticalSpace = height - getPaddingTop() - getPaddingBottom() - - mDeviceProfile.allAppsIconSizePx - - mDeviceProfile.allAppsIconDrawablePaddingPx; + - mDeviceProfile.getAllAppsProfile().getIconSizePx() + - mDeviceProfile.getAllAppsProfile().getIconDrawablePaddingPx(); CharSequence modifiedString = modifyTitleToSupportMultiLine( MeasureSpec.getSize(widthMeasureSpec) - getCompoundPaddingLeft() - getCompoundPaddingRight(), diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 84c6c73889..d1a9a29b6e 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -52,6 +52,7 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.CellLayout.ContainerType; import com.android.launcher3.DevicePaddings.DevicePadding; import com.android.launcher3.InvariantDeviceProfile.DisplayOptionSpec; +import com.android.launcher3.deviceprofile.AllAppsProfile; import com.android.launcher3.deviceprofile.BottomSheetProfile; import com.android.launcher3.deviceprofile.DeviceProperties; import com.android.launcher3.deviceprofile.DropTargetProfile; @@ -217,20 +218,13 @@ public class DeviceProfile { // not enough space, the hotseat will adjust itself for the bubble bar. private final int mBubbleBarSpaceThresholdPx; - // All apps - public Point allAppsBorderSpacePx; + private AllAppsProfile mAllAppsProfile; public int allAppsShiftRange; public Rect allAppsPadding = new Rect(); public int allAppsOpenDuration; public int allAppsCloseDuration; - public int allAppsCellHeightPx; - public int allAppsCellWidthPx; - public int allAppsIconSizePx; - public int allAppsIconDrawablePaddingPx; public int allAppsLeftRightMargin; public final int numShownAllAppsColumns; - public float allAppsIconTextSizePx; - public int maxAllAppsTextLineCount; private final OverviewProfile overviewProfile; @@ -323,6 +317,7 @@ public class DeviceProfile { mViewScaleProvider = null; mDotRendererWorkSpace = null; mDotRendererAllApps = null; + mAllAppsProfile = new AllAppsProfile(new Point(0, 0), 0, 0, 0f, 0, 0, 0); } /** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */ @@ -441,9 +436,6 @@ public class DeviceProfile { folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_top_padding_default); } - allAppsBorderSpacePx = new Point( - pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics), - pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics)); setupAllAppsStyle(context); if (!mIsResponsiveGrid) { @@ -675,7 +667,8 @@ public class DeviceProfile { // This is done last, after iconSizePx is calculated above. mDotRendererWorkSpace = createDotRenderer(themeManager, iconSizePx, dotRendererCache); - mDotRendererAllApps = createDotRenderer(themeManager, allAppsIconSizePx, dotRendererCache); + mDotRendererAllApps = createDotRenderer(themeManager, + getAllAppsProfile().getIconSizePx(), dotRendererCache); } public DeviceProperties getDeviceProperties() { @@ -734,7 +727,7 @@ public class DeviceProfile { */ public int getMaxAllAppsRowCount() { return (int) (Math.ceil((mDeviceProperties.getAvailableHeightPx() - allAppsPadding.top) - / (float) allAppsCellHeightPx)); + / (float) getAllAppsProfile().getCellHeightPx())); } /** @@ -942,7 +935,7 @@ public class DeviceProfile { SparseArray dotRendererCache = new SparseArray<>(); dotRendererCache.put(iconSizePx, mDotRendererWorkSpace); - dotRendererCache.put(allAppsIconSizePx, mDotRendererAllApps); + dotRendererCache.put(getAllAppsProfile().getIconSizePx(), mDotRendererAllApps); return inv.newDPBuilder(context, mInfo) .setWindowBounds(bounds) @@ -998,7 +991,8 @@ public class DeviceProfile { iconDrawablePaddingPx = 0; cellHeightPx = getIconSizeWithOverlap(iconSizePx); maxIconTextLineCount = 0; - autoResizeAllAppsCells(); + // TODO(420933882) Group all modifications of AllAppsProfile in one place + mAllAppsProfile = AllAppsProfile.Factory.autoResizeAllAppsCells(getAllAppsProfile()); } } @@ -1217,17 +1211,38 @@ public class DeviceProfile { // All apps if (mIsResponsiveGrid) { + mAllAppsProfile = AllAppsProfile.Factory.createAllAppsWithResponsive( + mResponsiveAllAppsCellSpec, + mResponsiveAllAppsWidthSpec, + mResponsiveAllAppsHeightSpec, + mIconSizeSteps, + isVerticalBarLayout() + ); updateAllAppsWithResponsiveMeasures(); } else { - updateAllAppsIconSize(scale, context.getResources()); + mAllAppsProfile = AllAppsProfile.Factory.createAllAppsProfile( + context.getResources(), + inv, + mMetrics, + mIsScalableGrid, + mTypeIndex, + scale, + iconSizePx, + mIconDrawablePaddingOriginalPx + ); } updateAllAppsContainerWidth(); if (isVerticalLayout && !mIsResponsiveGrid) { hideWorkspaceLabelsIfNotEnoughSpace(); } - if (inv.enableTwoLinesInAllApps && !(mIsResponsiveGrid && maxAllAppsTextLineCount == 2)) { + + if (inv.enableTwoLinesInAllApps + && !(mIsResponsiveGrid && getAllAppsProfile().getMaxAllAppsTextLineCount() == 2)) { // Add extra textHeight to the existing allAppsCellHeight. - allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx); + mAllAppsProfile = getAllAppsProfile().copyWithCellHeightPx( + getAllAppsProfile().getCellHeightPx() + Utilities.calculateTextHeight( + getAllAppsProfile().getIconTextSizePx()) + ); } updateHotseatSizes(iconSizePx); @@ -1261,138 +1276,27 @@ public class DeviceProfile { return Math.min(hotseatBorderSpacePx, getHotseatProfile().getMaxIconSpacePx()); } - /** - * Updates the iconSize for allApps* variants. - */ - private void updateAllAppsIconSize(float scale, Resources res) { - allAppsBorderSpacePx = new Point( - pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics, scale), - pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics, scale)); - // AllApps cells don't have real space between cells, - // so we add the border space to the cell height - allAppsCellHeightPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].y, mMetrics) - + allAppsBorderSpacePx.y; - maxAllAppsTextLineCount = 1; - - // but width is just the cell, - // the border is added in #updateAllAppsContainerWidth - if (mIsScalableGrid) { - allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics); - allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics); - allAppsIconDrawablePaddingPx = getNormalizedIconDrawablePadding(); - allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale); - - if (allAppsCellWidthPx < allAppsIconSizePx) { - // If allAppsCellWidth no longer fit allAppsIconSize, reduce allAppsBorderSpace to - // make allAppsCellWidth bigger. - int numBorders = inv.numAllAppsColumns - 1; - int extraWidthRequired = - (allAppsIconSizePx - allAppsCellWidthPx) * inv.numAllAppsColumns; - if (allAppsBorderSpacePx.x * numBorders >= extraWidthRequired) { - allAppsCellWidthPx = allAppsIconSizePx; - allAppsBorderSpacePx.x -= extraWidthRequired / numBorders; - } else { - // If it still doesn't fit, set allAppsBorderSpace to 0 and distribute the space - // for allAppsCellWidth, and reduce allAppsIconSize. - allAppsCellWidthPx = (allAppsCellWidthPx * inv.numAllAppsColumns - + allAppsBorderSpacePx.x * numBorders) / inv.numAllAppsColumns; - allAppsIconSizePx = Math.min(allAppsIconSizePx, allAppsCellWidthPx); - allAppsBorderSpacePx.x = 0; - } - } - - int cellContentHeight = allAppsIconSizePx - + Utilities.calculateTextHeight(allAppsIconTextSizePx) + allAppsBorderSpacePx.y; - if (allAppsCellHeightPx < cellContentHeight) { - // Increase allAppsCellHeight to fit its content. - allAppsCellHeightPx = cellContentHeight; - } - } else { - 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); - allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx); - } - } - private void updateAllAppsWithResponsiveMeasures() { - allAppsIconSizePx = mResponsiveAllAppsCellSpec.getIconSize(); - allAppsIconTextSizePx = mResponsiveAllAppsCellSpec.getIconTextSize(); - allAppsIconDrawablePaddingPx = getNormalizedIconDrawablePadding(allAppsIconSizePx, - mResponsiveAllAppsCellSpec.getIconDrawablePadding()); - maxAllAppsTextLineCount = mResponsiveAllAppsCellSpec.getIconTextMaxLineCount(); - allAppsBorderSpacePx = new Point( - 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; + int halfBorder = getAllAppsProfile().getBorderSpacePx().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, - maxAllAppsTextLineCount); - if (allAppsCellHeightPx < cellContentDimensions.getCellContentHeight()) { - if (isVerticalBarLayout()) { - if (allAppsCellHeightPx < allAppsIconSizePx) { - cellContentDimensions.setIconSizePx( - mIconSizeSteps.getIconSmallerThan(allAppsCellHeightPx)); - } - } else { - cellContentDimensions.resizeToFitCellHeight(allAppsCellHeightPx, - mIconSizeSteps); - } - allAppsIconSizePx = cellContentDimensions.getIconSizePx(); - allAppsIconDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx(); - allAppsIconTextSizePx = cellContentDimensions.getIconTextSizePx(); - maxAllAppsTextLineCount = cellContentDimensions.getMaxLineCount(); - } - - allAppsCellHeightPx += mResponsiveAllAppsHeightSpec.getGutterPx(); - - if (isVerticalBarLayout()) { - autoResizeAllAppsCells(); - } } - /** - * Re-computes the all-apps cell size to be independent of workspace - */ - public void autoResizeAllAppsCells() { - int textHeight = - Utilities.calculateTextHeight(allAppsIconTextSizePx) * maxAllAppsTextLineCount; - int topBottomPadding = textHeight; - allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx - + textHeight + (topBottomPadding * 2); - } private void updateAllAppsContainerWidth() { int cellLayoutHorizontalPadding = (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right) / 2; if (mDeviceProperties.isTablet()) { - int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns) - + (allAppsBorderSpacePx.x * (numShownAllAppsColumns - 1)) + int usedWidth = (getAllAppsProfile().getCellWidthPx() * numShownAllAppsColumns) + + (getAllAppsProfile().getBorderSpacePx().x * (numShownAllAppsColumns - 1)) + allAppsPadding.left + allAppsPadding.right; allAppsLeftRightMargin = Math.max(1, (mDeviceProperties.getAvailableWidthPx() - usedWidth) / 2); } else if (!mIsResponsiveGrid) { allAppsPadding.left = allAppsPadding.right = Math.max(0, desiredWorkspaceHorizontalMarginPx + cellLayoutHorizontalPadding - - (allAppsBorderSpacePx.x / 2)); + - (getAllAppsProfile().getBorderSpacePx().x / 2)); } } @@ -1906,7 +1810,8 @@ public class DeviceProfile { mDeviceProperties.getAvailableWidthPx() - allAppsSpacing, 0 /* borderSpace */, numShownAllAppsColumns); - int iconAlignmentMargin = (cellWidth - getIconVisibleSizePx(allAppsIconSizePx)) / 2; + int iconAlignmentMargin = (cellWidth - getIconVisibleSizePx( + getAllAppsProfile().getIconSizePx())) / 2; return (Utilities.isRtl(context.getResources()) ? allAppsPadding.right : allAppsPadding.left) + iconAlignmentMargin; @@ -2194,14 +2099,20 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("allAppsShiftRange", allAppsShiftRange)); writer.println(prefix + "\tallAppsOpenDuration: " + allAppsOpenDuration); writer.println(prefix + "\tallAppsCloseDuration: " + allAppsCloseDuration); - writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx)); - writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx)); + writer.println(prefix + pxToDpStr("allAppsIconSizePx", + getAllAppsProfile().getIconSizePx())); + writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", + getAllAppsProfile().getIconTextSizePx())); writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx", - allAppsIconDrawablePaddingPx)); - writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx)); - writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx)); - writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", allAppsBorderSpacePx.x)); - writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", allAppsBorderSpacePx.y)); + getAllAppsProfile().getIconDrawablePaddingPx())); + writer.println(prefix + pxToDpStr("allAppsCellHeightPx", + getAllAppsProfile().getCellHeightPx())); + writer.println(prefix + pxToDpStr("allAppsCellWidthPx", + getAllAppsProfile().getCellWidthPx())); + writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", + getAllAppsProfile().getBorderSpacePx().x)); + writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", + getAllAppsProfile().getBorderSpacePx().y)); writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns); writer.println(prefix + pxToDpStr("allAppsPadding.top", allAppsPadding.top)); writer.println(prefix + pxToDpStr("allAppsPadding.left", allAppsPadding.left)); @@ -2377,6 +2288,14 @@ public class DeviceProfile { return mBottomSheetProfile; } + public AllAppsProfile getAllAppsProfile() { + return mAllAppsProfile; + } + + public void setAllAppsProfile(AllAppsProfile allAppsProfile) { + mAllAppsProfile = allAppsProfile; + } + /** * Callback when a component changes the DeviceProfile associated with it, as a result of * configuration change diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 3464e5abdb..881b331457 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -19,6 +19,7 @@ package com.android.launcher3; import static com.android.launcher3.Flags.enableMouseInteractionChanges; import static com.android.launcher3.graphics.ShapeDelegate.DEFAULT_PATH_SIZE; import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED; +import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; @@ -435,6 +436,25 @@ public final class Utilities { return (int) mapRange(interpolator.getInterpolation(progress), toMin, toMax); } + /** + * 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 + * some dimensions correct. + * + * When this bug is resolved this method will no longer be needed and we would be able to + * replace all instances where this method is called with iconSizePx. + */ + public static int getIconVisibleSizePx(int iconSizePx) { + return Math.round(ICON_VISIBLE_AREA_FACTOR * iconSizePx); + } + + public static int getNormalizedIconDrawablePadding(int iconSizePx, int iconDrawablePadding) { + return Math.max( + 0, + iconDrawablePadding - ((iconSizePx - getIconVisibleSizePx(iconSizePx)) / 2) + ); + } + /** Bounds t between a lower and upper bound and maps the result to a range. */ public static float mapBoundToRange(float t, float lowerBound, float upperBound, float toMin, float toMax, Interpolator interpolator) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b410c54929..d589167fa5 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -336,7 +336,7 @@ public class Workspace extends PagedView mLauncher = Launcher.getLauncher(context); mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this); mWallpaperManager = WallpaperManager.getInstance(context); - mAllAppsIconSize = mLauncher.getDeviceProfile().allAppsIconSizePx; + mAllAppsIconSize = mLauncher.getDeviceProfile().getAllAppsProfile().getIconSizePx(); mWallpaperOffset = new WallpaperOffsetInterpolator(this); setHapticFeedbackEnabled(false); diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index eb097dff5c..1b92cfad32 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -531,7 +531,7 @@ public class ActivityAllAppsContainerView getActiveAppsRecyclerView(), getPersonalAppList().getAdapterItems(), mPrivateProfileManager.getPsHeaderHeight(), - mActivityContext.getDeviceProfile().allAppsCellHeightPx); + mActivityContext.getDeviceProfile().getAllAppsProfile().getCellHeightPx()); } }); } diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index c72343fab7..f3a1a7cbc5 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -227,7 +227,7 @@ public abstract class BaseAllAppsAdapter ex icon.setOnLongClickListener(mOnIconLongClickListener); // Ensure the all apps icon height matches the workspace icons in portrait mode. icon.getLayoutParams().height = - mActivityContext.getDeviceProfile().allAppsCellHeightPx; + mActivityContext.getDeviceProfile().getAllAppsProfile().getCellHeightPx(); return new ViewHolder(icon); case VIEW_TYPE_EMPTY_SEARCH: return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java index c7c2bfe526..4e40664a95 100644 --- a/src/com/android/launcher3/allapps/PrivateProfileManager.java +++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java @@ -827,7 +827,7 @@ public class PrivateProfileManager extends UserProfileManager { DeviceProfile deviceProfile = ActivityContext.lookupContext(mAllApps.getContext()).getDeviceProfile(); scrollForHeaderToBeVisibleInContainer(mainAdapterHolder.mRecyclerView, adapterItems, - getPsHeaderHeight(), deviceProfile.allAppsCellHeightPx); + getPsHeaderHeight(), deviceProfile.getAllAppsProfile().getCellHeightPx()); updatePrivateStateAnimator(true); } Trace.endSection(); diff --git a/src/com/android/launcher3/deviceprofile/AllAppsProfile.kt b/src/com/android/launcher3/deviceprofile/AllAppsProfile.kt new file mode 100644 index 0000000000..b69e35d931 --- /dev/null +++ b/src/com/android/launcher3/deviceprofile/AllAppsProfile.kt @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2025 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.deviceprofile + +import android.content.res.Resources +import android.graphics.Point +import android.util.DisplayMetrics +import com.android.launcher3.InvariantDeviceProfile +import com.android.launcher3.R +import com.android.launcher3.Utilities +import com.android.launcher3.Utilities.getIconVisibleSizePx +import com.android.launcher3.Utilities.getNormalizedIconDrawablePadding +import com.android.launcher3.Utilities.pxFromSp +import com.android.launcher3.responsive.CalculatedCellSpec +import com.android.launcher3.responsive.CalculatedResponsiveSpec +import com.android.launcher3.testing.shared.ResourceUtils.pxFromDp +import com.android.launcher3.util.CellContentDimensions +import com.android.launcher3.util.IconSizeSteps +import kotlin.math.max +import kotlin.math.min + +data class AllAppsProfile( + val borderSpacePx: Point, + val cellHeightPx: Int, + val iconSizePx: Int, + val iconTextSizePx: Float, + val iconDrawablePaddingPx: Int, + val maxAllAppsTextLineCount: Int, + val cellWidthPx: Int, +) { + + /** + * Return a new AllAppsProfile with cellHeightPx changed. This function is needed for the java + * code. + */ + fun copyWithCellHeightPx(newCellHeightPx: Int): AllAppsProfile { + return copy(cellHeightPx = newCellHeightPx) + } + + companion object Factory { + + private fun calculateAllAppsBorderSpacePx( + inv: InvariantDeviceProfile, + metric: DisplayMetrics, + typeIndex: Int, + scale: Float, + ) = + Point( + pxFromDp(inv.allAppsBorderSpaces[typeIndex].x, metric, scale), + pxFromDp(inv.allAppsBorderSpaces[typeIndex].y, metric, scale), + ) + + fun createAllAppsProfileScalableGrid( + inv: InvariantDeviceProfile, + metric: DisplayMetrics, + typeIndex: Int, + scale: Float, + iconSizePx: Int, + iconDrawablePaddingOriginalPx: Int, + ): AllAppsProfile { + val allAppsBorderSpacePx = calculateAllAppsBorderSpacePx(inv, metric, typeIndex, scale) + var allAppsCellHeightPx = + (pxFromDp(inv.allAppsCellSize[typeIndex].y, metric) + allAppsBorderSpacePx.y) + var allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[typeIndex], metric) + val allAppsIconTextSizePx = + pxFromSp(inv.allAppsIconTextSize[typeIndex], metric).toFloat() + val allAppsIconDrawablePaddingPx = + max( + 0, + (iconDrawablePaddingOriginalPx - + ((iconSizePx - getIconVisibleSizePx(iconSizePx)) / 2)), + ) + var allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[typeIndex].x, metric, scale) + + if (allAppsCellWidthPx < allAppsIconSizePx) { + // If allAppsCellWidth no longer fit allAppsIconSize, reduce allAppsBorderSpace to + // make allAppsCellWidth bigger. + val numBorders: Int = inv.numAllAppsColumns - 1 + val extraWidthRequired: Int = + (allAppsIconSizePx - allAppsCellWidthPx) * inv.numAllAppsColumns + if (allAppsBorderSpacePx.x * numBorders >= extraWidthRequired) { + allAppsCellWidthPx = allAppsIconSizePx + allAppsBorderSpacePx.x -= extraWidthRequired / numBorders + } else { + // If it still doesn't fit, set allAppsBorderSpace to 0 and distribute the space + // for allAppsCellWidth, and reduce allAppsIconSize. + allAppsCellWidthPx = + (allAppsCellWidthPx * inv.numAllAppsColumns + + allAppsBorderSpacePx.x * numBorders) / inv.numAllAppsColumns + allAppsIconSizePx = + min(allAppsIconSizePx.toDouble(), allAppsCellWidthPx.toDouble()).toInt() + allAppsBorderSpacePx.x = 0 + } + } + + val cellContentHeight: Int = + (allAppsIconSizePx + + Utilities.calculateTextHeight(allAppsIconTextSizePx) + + allAppsBorderSpacePx.y) + if (allAppsCellHeightPx < cellContentHeight) { + // Increase allAppsCellHeight to fit its content. + allAppsCellHeightPx = cellContentHeight + } + + return AllAppsProfile( + borderSpacePx = allAppsBorderSpacePx, + cellHeightPx = allAppsCellHeightPx, + iconSizePx = allAppsIconSizePx, + iconTextSizePx = allAppsIconTextSizePx, + iconDrawablePaddingPx = allAppsIconDrawablePaddingPx, + maxAllAppsTextLineCount = 1, + cellWidthPx = allAppsCellWidthPx, + ) + } + + fun createAllAppsProfileNonScalable( + res: Resources, + inv: InvariantDeviceProfile, + metric: DisplayMetrics, + typeIndex: Int, + scale: Float, + ): AllAppsProfile { + val allAppsBorderSpacePx = calculateAllAppsBorderSpacePx(inv, metric, typeIndex, scale) + val allAppsIconSizePx = max(1, pxFromDp(inv.allAppsIconSize[typeIndex], metric, scale)) + val allAppsIconDrawablePaddingPx = + res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding) + return AllAppsProfile( + borderSpacePx = allAppsBorderSpacePx, + // AllApps cells don't have real space between cells, + // so we add the border space to the cell height + cellHeightPx = + (pxFromDp(inv.allAppsCellSize.get(typeIndex).y, metric) + + allAppsBorderSpacePx.y), + iconSizePx = allAppsIconSizePx, + // We need the double conversion to keep the original behaviour + iconTextSizePx = + (pxFromSp(inv.allAppsIconTextSize[typeIndex], metric).toFloat() * scale) + .toInt() + .toFloat(), + iconDrawablePaddingPx = allAppsIconDrawablePaddingPx, + maxAllAppsTextLineCount = 1, + cellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx), + ) + } + + fun createAllAppsWithResponsive( + responsiveAllAppsCellSpec: CalculatedCellSpec, + responsiveAllAppsWidthSpec: CalculatedResponsiveSpec, + responsiveAllAppsHeightSpec: CalculatedResponsiveSpec, + iconSizeSteps: IconSizeSteps, + isVerticalBarLayout: Boolean, + ): AllAppsProfile { + var allAppsIconSizePx = responsiveAllAppsCellSpec.iconSize + var allAppsIconTextSizePx: Float = responsiveAllAppsCellSpec.iconTextSize.toFloat() + var allAppsIconDrawablePaddingPx = + getNormalizedIconDrawablePadding( + allAppsIconSizePx, + responsiveAllAppsCellSpec.iconDrawablePadding, + ) + var maxAllAppsTextLineCount = responsiveAllAppsCellSpec.iconTextMaxLineCount + val allAppsBorderSpacePx = + Point(responsiveAllAppsWidthSpec.gutterPx, responsiveAllAppsHeightSpec.gutterPx) + var allAppsCellHeightPx = responsiveAllAppsHeightSpec.cellSizePx + var allAppsCellWidthPx = responsiveAllAppsWidthSpec.cellSizePx + + // Reduce the size of the app icon if it doesn't fit + if (allAppsCellWidthPx < allAppsIconSizePx) { + // get a smaller icon size + allAppsIconSizePx = iconSizeSteps.getIconSmallerThan(allAppsCellWidthPx) + } + + val cellContentDimensions = + CellContentDimensions( + allAppsIconSizePx, + allAppsIconDrawablePaddingPx, + allAppsIconTextSizePx.toInt(), + maxAllAppsTextLineCount, + ) + if (allAppsCellHeightPx < cellContentDimensions.getCellContentHeight()) { + if (isVerticalBarLayout) { + if (allAppsCellHeightPx < allAppsIconSizePx) { + cellContentDimensions.iconSizePx = + iconSizeSteps.getIconSmallerThan(allAppsCellHeightPx) + } + } else { + cellContentDimensions.resizeToFitCellHeight(allAppsCellHeightPx, iconSizeSteps) + } + allAppsIconSizePx = cellContentDimensions.iconSizePx + allAppsIconDrawablePaddingPx = cellContentDimensions.iconDrawablePaddingPx + allAppsIconTextSizePx = cellContentDimensions.iconTextSizePx.toFloat() + maxAllAppsTextLineCount = cellContentDimensions.maxLineCount + } + + allAppsCellHeightPx += responsiveAllAppsHeightSpec.gutterPx + + val allAppsProfile = + AllAppsProfile( + borderSpacePx = allAppsBorderSpacePx, + cellHeightPx = allAppsCellHeightPx, + iconSizePx = allAppsIconSizePx, + iconTextSizePx = allAppsIconTextSizePx, + iconDrawablePaddingPx = allAppsIconDrawablePaddingPx, + maxAllAppsTextLineCount = maxAllAppsTextLineCount, + cellWidthPx = allAppsCellWidthPx, + ) + + return when { + isVerticalBarLayout -> autoResizeAllAppsCells(allAppsProfile) + else -> allAppsProfile + } + } + + /** Re-computes the all-apps cell size to be independent of workspace */ + fun autoResizeAllAppsCells(allAppsProfile: AllAppsProfile): AllAppsProfile { + val textHeight: Int = + Utilities.calculateTextHeight(allAppsProfile.iconTextSizePx) * + allAppsProfile.maxAllAppsTextLineCount + return allAppsProfile.copy( + cellHeightPx = + allAppsProfile.iconSizePx + + allAppsProfile.iconDrawablePaddingPx + + textHeight + + (textHeight * 2) + ) + } + + fun createAllAppsProfile( + res: Resources, + inv: InvariantDeviceProfile, + metric: DisplayMetrics, + isScalableGrid: Boolean, + typeIndex: Int, + scale: Float, + iconSizePx: Int, + iconDrawablePaddingOriginalPx: Int, + ) = + when { + isScalableGrid -> { + createAllAppsProfileScalableGrid( + inv = inv, + metric = metric, + typeIndex = typeIndex, + scale = scale, + iconSizePx = iconSizePx, + iconDrawablePaddingOriginalPx = iconDrawablePaddingOriginalPx, + ) + } + + else -> { + createAllAppsProfileNonScalable(res, inv, metric, typeIndex, scale) + } + } + } +} diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 17bff899af..2f79f3cbf5 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -385,7 +385,8 @@ public class FolderIcon extends FrameLayout implements FloatingIconViewCompanion // Account for potentially different icon sizes with non-default grid settings if (d.dragSource instanceof ActivityAllAppsContainerView) { DeviceProfile grid = mActivity.getDeviceProfile(); - float containerScale = (1f * grid.iconSizePx / grid.allAppsIconSizePx); + float containerScale = (1f * grid.iconSizePx + / grid.getAllAppsProfile().getIconSizePx()); finalScale *= containerScale; } diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index 03b7eeed07..ae8252e114 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -43,6 +43,7 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore; +import com.android.launcher3.deviceprofile.AllAppsProfile; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DraggableView; @@ -99,7 +100,10 @@ public class SecondaryDisplayLauncher extends BaseActivity mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(this) .createDeviceProfileForSecondaryDisplay(this); - mDeviceProfile.autoResizeAllAppsCells(); + // TODO(b/420948290) Remove this! + mDeviceProfile.setAllAppsProfile(AllAppsProfile + .Factory + .autoResizeAllAppsCells(mDeviceProfile.getAllAppsProfile())); setContentView(R.layout.secondary_launcher); mDragLayer = findViewById(R.id.drag_layer); diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index 4d69482cc9..4b199ee5ac 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -139,11 +139,13 @@ public class SecondaryDragLayer extends BaseDragLayer grid.cellLayoutPaddingPx.top + grid.cellLayoutPaddingPx.bottom; int maxWidth = - grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding; + grid.getAllAppsProfile().getCellWidthPx() * grid.numShownAllAppsColumns + + horizontalPadding; int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth); int maxHeight = - grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding; + grid.getAllAppsProfile().getCellHeightPx() * grid.numShownAllAppsColumns + + verticalPadding; int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight); mAppsView.measure( @@ -229,7 +231,8 @@ public class SecondaryDragLayer extends BaseDragLayer DragOptions options = new DragOptions(); DeviceProfile grid = mContainer.getDeviceProfile(); - options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; + options.intrinsicIconScaleFactor = (float) grid.getAllAppsProfile().getIconSizePx() + / grid.iconSizePx; options.preDragCondition = container.createPreDragCondition(false); if (options.preDragCondition == null) { options.preDragCondition = new DragOptions.PreDragCondition() { diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index cb5725e89b..f6590464bd 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -194,7 +194,7 @@ public class TestInformationHandler implements ResourceBasedOverride { case TestProtocol.REQUEST_ICON_HEIGHT: { response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, - mDeviceProfile.allAppsCellHeightPx); + mDeviceProfile.getAllAppsProfile().getCellHeightPx()); return response; } diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java index 5b4b016773..55404d68dc 100644 --- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java +++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java @@ -272,7 +272,8 @@ public class DatabaseWidgetPreviewLoader { private Bitmap generateShortcutPreview( ShortcutConfigActivityInfo info, int maxWidth, int maxHeight) { - int iconSize = ActivityContext.lookupContext(mContext).getDeviceProfile().allAppsIconSizePx; + int iconSize = ActivityContext.lookupContext( + mContext).getDeviceProfile().getAllAppsProfile().getIconSizePx(); int padding = mContext.getResources() .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java index 4688359a0c..64f3ab0bbd 100644 --- a/src/com/android/launcher3/widget/util/WidgetSizes.java +++ b/src/com/android/launcher3/widget/util/WidgetSizes.java @@ -79,7 +79,7 @@ public final class WidgetSizes { public static Size getWidgetItemSizePx(Context context, DeviceProfile profile, WidgetItem widgetItem) { if (widgetItem.isShortcut()) { - int dimension = profile.allAppsIconSizePx + 2 * context.getResources() + int dimension = profile.getAllAppsProfile().getIconSizePx() + 2 * context.getResources() .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); return new Size(dimension, dimension); } diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java index 5fce8bfba3..31ff7e9190 100644 --- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java +++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java @@ -97,7 +97,8 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd */ public static int getTabWidth(Context context, int totalWidth) { DeviceProfile grid = ActivityContext.lookupContext(context).getDeviceProfile(); - int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx; + int iconPadding = totalWidth / grid.numShownAllAppsColumns + - grid.getAllAppsProfile().getIconSizePx(); return totalWidth - iconPadding; } diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java index a17e472d2f..1bcfe283bb 100644 --- a/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java +++ b/tests/multivalentTests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.deviceprofile.AllAppsProfile; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.icons.cache.CachedObject; @@ -245,6 +246,8 @@ public final class WidgetsTableUtilsTest { DeviceProfile dp = LauncherAppState.getIDP(mContext) .getDeviceProfile(mContext).copy(mContext); mTestDeviceProfile = Mockito.spy(dp); + AllAppsProfile testAllAppsProfile = Mockito.spy(mTestDeviceProfile.getAllAppsProfile()); + Mockito.doReturn(0).when(testAllAppsProfile).getIconSizePx(); doAnswer(i -> { ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE); @@ -253,7 +256,6 @@ public final class WidgetsTableUtilsTest { when(mTestDeviceProfile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE)); mTestDeviceProfile.cellLayoutBorderSpacePx = new Point(SPACE_SIZE, SPACE_SIZE); mTestDeviceProfile.widgetPadding.setEmpty(); - mTestDeviceProfile.allAppsIconSizePx = 0; } private void initTestWidgets() {