diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java index 68bed443c7..3457804c3a 100644 --- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java +++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java @@ -6,9 +6,9 @@ public class ClippedFolderIconLayoutRule { private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2; private static final float MIN_SCALE = 0.44f; - private static final float MAX_SCALE = 0.54f; - private static final float MAX_RADIUS_DILATION = 0.10f; - private static final float ITEM_RADIUS_SCALE_FACTOR = 1.2f; + private static final float MAX_SCALE = 0.51f; + private static final float MAX_RADIUS_DILATION = 0.1f; + private static final float ITEM_RADIUS_SCALE_FACTOR = 1.15f; public static final int EXIT_INDEX = -2; public static final int ENTER_INDEX = -3; diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 17c1329ed9..dc188f27d4 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -743,7 +743,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mContent.completePendingPageChanges(); - mContent.snapToPageImmediately(pageNo); + mContent.setCurrentPage(pageNo); // This is set to true in close(), but isn't reset to false until onDropCompleted(). This // leads to an inconsistent state if you drag out of the folder and drag back in without diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index bd0dbfde08..e66e2f6fc1 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -16,6 +16,8 @@ package com.android.launcher3.folder; +import static android.view.View.ALPHA; + import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; @@ -57,6 +59,7 @@ import java.util.List; public class FolderAnimationManager { private static final int FOLDER_NAME_ALPHA_DURATION = 32; + private static final int LARGE_FOLDER_FOOTER_DURATION = 128; private Folder mFolder; private FolderPagedView mContent; @@ -214,7 +217,22 @@ public class FolderAnimationManager { play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f)); play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale)); play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale)); - play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening)); + + final int footerAlphaDuration; + final int footerStartDelay; + if (isLargeFolder()) { + if (mIsOpening) { + footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION; + footerStartDelay = mDuration - footerAlphaDuration; + } else { + footerAlphaDuration = 0; + footerStartDelay = 0; + } + } else { + footerStartDelay = 0; + footerAlphaDuration = mDuration; + } + play(a, getAnimator(mFolder.mFooter, ALPHA, 0, 1f), footerStartDelay, footerAlphaDuration); // Create reveal animator for the folder background play(a, getShape().createRevealAnimator( @@ -225,9 +243,13 @@ public class FolderAnimationManager { + mDeviceProfile.folderCellWidthPx * 2; int height = mContent.getPaddingTop() + mDeviceProfile.folderCellLayoutBorderSpacingPx + mDeviceProfile.folderCellHeightPx * 2; - Rect startRect2 = new Rect(0, 0, width, height); + int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage(); + int left = mContent.getPaddingLeft() + page * mContent.getWidth(); + Rect contentStart = new Rect(left, 0, left + width, height); + Rect contentEnd = new Rect(endRect.left + left, endRect.top, endRect.right + left, + endRect.bottom); play(a, getShape().createRevealAnimator( - mFolder.getContent(), startRect2, endRect, finalRadius, !mIsOpening)); + mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening)); // Fade in the folder name, as the text can overlap the icons when grid size is small. @@ -420,8 +442,12 @@ public class FolderAnimationManager { as.play(a); } + private boolean isLargeFolder() { + return mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW; + } + private TimeInterpolator getPreviewItemInterpolator() { - if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) { + if (isLargeFolder()) { // With larger folders, we want the preview items to reach their final positions faster // (when opening) and later (when closing) so that they appear aligned with the rest of // the folder items when they are both visible. diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 6b12d86d13..ed2d0a943a 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -627,6 +627,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { Rect iconBounds = mDotParams.iconBounds; BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx); + iconBounds.offset(0, mBackground.paddingY); float iconScale = (float) mBackground.previewSize / iconBounds.width(); Utilities.scaleRectAboutCenter(iconBounds, iconScale); diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java index 22f73330ef..edfd2ba35e 100644 --- a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java +++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java @@ -68,6 +68,7 @@ class FolderPreviewItemAnim { int duration, final Runnable onCompleteRunnable) { mItemManager = itemManager; mParams = params; + mParams.index = index1; mItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams); finalState = new float[] {sTmpParams.scale, sTmpParams.transX, sTmpParams.transY}; diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java index a14a0d8efc..5746be89d9 100644 --- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java +++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java @@ -23,6 +23,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; * Manages the parameters used to draw a Folder preview item. */ class PreviewItemDrawingParams { + float index; float transX; float transY; float scale; diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 6adef01514..baafbc92bc 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -28,6 +28,8 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.FloatProperty; @@ -81,6 +83,10 @@ public class PreviewItemManager { // These hold the current page preview items. It is empty if the current page is the first page. private ArrayList mCurrentPageParams = new ArrayList<>(); + // We clip the preview items during the middle of the animation, so that it does not go outside + // of the visual shape. We stop clipping at this threshold, since the preview items ultimately + // do not get cropped in their resting state. + private final float mClipThreshold; private float mCurrentPageItemsTransX = 0; private boolean mShouldSlideInFirstPage; @@ -96,6 +102,7 @@ public class PreviewItemManager { mIcon = icon; mIconSize = ActivityContext.lookupContext( mContext).getDeviceProfile().folderChildIconSizePx; + mClipThreshold = Utilities.dpToPx(1f); } /** @@ -163,41 +170,60 @@ public class PreviewItemManager { } public void drawParams(Canvas canvas, ArrayList params, - float transX) { - canvas.translate(transX, 0); + PointF offset, boolean shouldClipPath, Path clipPath) { // The first item should be drawn last (ie. on top of later items) for (int i = params.size() - 1; i >= 0; i--) { PreviewItemDrawingParams p = params.get(i); if (!p.hidden) { - drawPreviewItem(canvas, p); + // Exiting param should always be clipped. + boolean isExiting = p.index == EXIT_INDEX; + drawPreviewItem(canvas, p, offset, isExiting | shouldClipPath, clipPath); } } - canvas.translate(-transX, 0); } + /** + * Draws the preview items on {@param canvas}. + */ public void draw(Canvas canvas) { + int saveCount = canvas.getSaveCount(); // The items are drawn in coordinates relative to the preview offset PreviewBackground bg = mIcon.getFolderBackground(); - canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY); - + Path clipPath = bg.getClipPath(); float firstPageItemsTransX = 0; if (mShouldSlideInFirstPage) { - drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX); - + PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + mCurrentPageItemsTransX, + bg.basePreviewOffsetY); + boolean shouldClip = mCurrentPageItemsTransX > mClipThreshold; + drawParams(canvas, mCurrentPageParams, firstPageOffset, shouldClip, clipPath); firstPageItemsTransX = -ITEM_SLIDE_IN_OUT_DISTANCE_PX + mCurrentPageItemsTransX; } - drawParams(canvas, mFirstPageParams, firstPageItemsTransX); - canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY); + PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + firstPageItemsTransX, + bg.basePreviewOffsetY); + boolean shouldClipFirstPage = firstPageItemsTransX < -mClipThreshold; + drawParams(canvas, mFirstPageParams, firstPageOffset, shouldClipFirstPage, clipPath); + canvas.restoreToCount(saveCount); } public void onParamsChanged() { mIcon.invalidate(); } - private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) { + /** + * Draws each preview item. + * + * @param offset The offset needed to draw the preview items. + * @param shouldClipPath Iff true, clip path using {@param clipPath}. + * @param clipPath The clip path of the folder icon. + */ + private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params, PointF offset, + boolean shouldClipPath, Path clipPath) { canvas.save(); - canvas.translate(params.transX, params.transY); + if (shouldClipPath) { + canvas.clipPath(clipPath); + } + canvas.translate(offset.x + params.transX, offset.y + params.transY); canvas.scale(params.scale, params.scale); Drawable d = params.drawable;