| package com.android.launcher3.folder; |
| |
| public class ClippedFolderIconLayoutRule { |
| |
| public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4; |
| private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2; |
| |
| private static final float MIN_SCALE = 0.48f; |
| private static final float MAX_SCALE = 0.58f; |
| private static final float MAX_RADIUS_DILATION = 0.15f; |
| private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f; |
| |
| public static final int EXIT_INDEX = -2; |
| public static final int ENTER_INDEX = -3; |
| |
| private float[] mTmpPoint = new float[2]; |
| |
| private float mAvailableSpace; |
| private float mRadius; |
| private float mIconSize; |
| private boolean mIsRtl; |
| private float mBaselineIconScale; |
| |
| public void init(int availableSpace, float intrinsicIconSize, boolean rtl) { |
| mAvailableSpace = availableSpace; |
| mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f; |
| mIconSize = intrinsicIconSize; |
| mIsRtl = rtl; |
| mBaselineIconScale = availableSpace / (intrinsicIconSize * 1f); |
| } |
| |
| public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, |
| PreviewItemDrawingParams params) { |
| float totalScale = scaleForItem(curNumItems); |
| float transX; |
| float transY; |
| float overlayAlpha = 0; |
| |
| if (index == EXIT_INDEX) { |
| // 0 1 * <-- Exit position (row 0, col 2) |
| // 2 3 |
| getGridPosition(0, 2, mTmpPoint); |
| } else if (index == ENTER_INDEX) { |
| // 0 1 |
| // 2 3 * <-- Enter position (row 1, col 2) |
| getGridPosition(1, 2, mTmpPoint); |
| } else if (index >= MAX_NUM_ITEMS_IN_PREVIEW) { |
| // Items beyond those displayed in the preview are animated to the center |
| mTmpPoint[0] = mTmpPoint[1] = mAvailableSpace / 2 - (mIconSize * totalScale) / 2; |
| } else { |
| getPosition(index, curNumItems, mTmpPoint); |
| } |
| |
| transX = mTmpPoint[0]; |
| transY = mTmpPoint[1]; |
| |
| if (params == null) { |
| params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); |
| } else { |
| params.update(transX, transY, totalScale); |
| params.overlayAlpha = overlayAlpha; |
| } |
| return params; |
| } |
| |
| /** |
| * Builds a grid based on the positioning of the items when there are |
| * {@link #MAX_NUM_ITEMS_IN_PREVIEW} in the preview. |
| * |
| * Positions in the grid: 0 1 // 0 is row 0, col 1 |
| * 2 3 // 3 is row 1, col 1 |
| */ |
| private void getGridPosition(int row, int col, float[] result) { |
| // We use position 0 and 3 to calculate the x and y distances between items. |
| getPosition(0, 4, result); |
| float left = result[0]; |
| float top = result[1]; |
| |
| getPosition(3, 4, result); |
| float dx = result[0] - left; |
| float dy = result[1] - top; |
| |
| result[0] = left + (col * dx); |
| result[1] = top + (row * dy); |
| } |
| |
| private void getPosition(int index, int curNumItems, float[] result) { |
| // The case of two items is homomorphic to the case of one. |
| curNumItems = Math.max(curNumItems, 2); |
| |
| // We model the preview as a circle of items starting in the appropriate piece of the |
| // upper left quadrant (to achieve horizontal and vertical symmetry). |
| double theta0 = mIsRtl ? 0 : Math.PI; |
| |
| // In RTL we go counterclockwise |
| int direction = mIsRtl ? 1 : -1; |
| |
| double thetaShift = 0; |
| if (curNumItems == 3) { |
| thetaShift = Math.PI / 6; |
| } else if (curNumItems == 4) { |
| thetaShift = Math.PI / 4; |
| } |
| theta0 += direction * thetaShift; |
| |
| // We want the items to appear in reading order. For the case of 1, 2 and 3 items, this |
| // is natural for the circular model. With 4 items, however, we need to swap the 3rd and |
| // 4th indices to achieve reading order. |
| if (curNumItems == 4 && index == 3) { |
| index = 2; |
| } else if (curNumItems == 4 && index == 2) { |
| index = 3; |
| } |
| |
| // We bump the radius up between 0 and MAX_RADIUS_DILATION % as the number of items increase |
| float radius = mRadius * (1 + MAX_RADIUS_DILATION * (curNumItems - |
| MIN_NUM_ITEMS_IN_PREVIEW) / (MAX_NUM_ITEMS_IN_PREVIEW - MIN_NUM_ITEMS_IN_PREVIEW)); |
| double theta = theta0 + index * (2 * Math.PI / curNumItems) * direction; |
| |
| float halfIconSize = (mIconSize * scaleForItem(curNumItems)) / 2; |
| |
| // Map the location along the circle, and offset the coordinates to represent the center |
| // of the icon, and to be based from the top / left of the preview area. The y component |
| // is inverted to match the coordinate system. |
| result[0] = mAvailableSpace / 2 + (float) (radius * Math.cos(theta) / 2) - halfIconSize; |
| result[1] = mAvailableSpace / 2 + (float) (- radius * Math.sin(theta) / 2) - halfIconSize; |
| |
| } |
| |
| public float scaleForItem(int numItems) { |
| // Scale is determined by the number of items in the preview. |
| final float scale; |
| if (numItems <= 2) { |
| scale = MAX_SCALE; |
| } else if (numItems == 3) { |
| scale = (MAX_SCALE + MIN_SCALE) / 2; |
| } else { |
| scale = MIN_SCALE; |
| } |
| return scale * mBaselineIconScale; |
| } |
| |
| public float getIconSize() { |
| return mIconSize; |
| } |
| } |