New folder icon style (uncropped icons).
- Switch Circle to PathShape as it allows for more flexibility on
choosing what gets cropped.
- Remove shadows, and stroke.
Bug: 188095988
Test: manual
Change-Id: I602b3efcd35c2c11dc3461f49099c2f2a56eae59
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 8b18857..15131f1 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,7 +18,6 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/round_rect_folder"
android:orientation="vertical" >
<com.android.launcher3.folder.FolderPagedView
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index c67efef..68bed44 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -5,10 +5,10 @@
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;
+ 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;
public static final int EXIT_INDEX = -2;
public static final int ENTER_INDEX = -3;
@@ -130,10 +130,8 @@
public float scaleForItem(int numItems) {
// Scale is determined by the number of items in the preview.
final float scale;
- if (numItems <= 2) {
+ if (numItems <= 3) {
scale = MAX_SCALE;
- } else if (numItems == 3) {
- scale = (MAX_SCALE + MIN_SCALE) / 2;
} else {
scale = MIN_SCALE;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e387627..17c1329 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -41,6 +41,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.text.InputType;
@@ -67,6 +68,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.AbstractFloatingView;
@@ -250,6 +252,8 @@
// so that we can cancel it when starting mColorChangeAnimator.
private ObjectAnimator mOpenAnimationColorChangeAnimator;
+ private GradientDrawable mBackground;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -268,6 +272,12 @@
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
+
+ }
+
+ @Override
+ public Drawable getBackground() {
+ return mBackground;
}
@Override
@@ -276,6 +286,9 @@
final DeviceProfile dp = mActivityContext.getDeviceProfile();
final int paddingLeftRight = dp.folderContentPaddingLeftRight;
+ mBackground = (GradientDrawable) ResourcesCompat.getDrawable(getResources(),
+ R.drawable.round_rect_folder, getContext().getTheme());
+
mContent = findViewById(R.id.folder_content);
mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
mContent.setFolder(this);
@@ -1213,6 +1226,8 @@
lp.x = left;
lp.y = top;
+ mBackground.setBounds(0, 0, width, height);
+
if (mColorExtractor != null) {
mColorExtractor.removeLocations();
mColorExtractor.setListener(mColorListener);
@@ -1714,14 +1729,16 @@
}
@Override
- public void draw(Canvas canvas) {
+ protected void dispatchDraw(Canvas canvas) {
if (mClipPath != null) {
int count = canvas.save();
canvas.clipPath(mClipPath);
- super.draw(canvas);
+ mBackground.draw(canvas);
canvas.restoreToCount(count);
+ super.dispatchDraw(canvas);
} else {
- super.draw(canvas);
+ mBackground.draw(canvas);
+ super.dispatchDraw(canvas);
}
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 7fbfb89..bd0dbfd 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -37,6 +37,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
@@ -80,6 +81,8 @@
private ObjectAnimator mBgColorAnimator;
+ private DeviceProfile mDeviceProfile;
+
public FolderAnimationManager(Folder folder, boolean isOpening) {
mFolder = folder;
mContent = folder.mContent;
@@ -89,7 +92,8 @@
mPreviewBackground = mFolderIcon.mBackground;
mContext = folder.getContext();
- mPreviewVerifier = new FolderGridOrganizer(folder.mActivityContext.getDeviceProfile().inv);
+ mDeviceProfile = folder.mActivityContext.getDeviceProfile();
+ mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile.inv);
mIsOpening = isOpening;
@@ -211,8 +215,21 @@
play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
+
+ // Create reveal animator for the folder background
play(a, getShape().createRevealAnimator(
mFolder, startRect, endRect, finalRadius, !mIsOpening));
+
+ // Create reveal animator for the folder content (capture the top 4 icons 2x2)
+ int width = mContent.getPaddingLeft() + mDeviceProfile.folderCellLayoutBorderSpacingPx
+ + mDeviceProfile.folderCellWidthPx * 2;
+ int height = mContent.getPaddingTop() + mDeviceProfile.folderCellLayoutBorderSpacingPx
+ + mDeviceProfile.folderCellHeightPx * 2;
+ Rect startRect2 = new Rect(0, 0, width, height);
+ play(a, getShape().createRevealAnimator(
+ mFolder.getContent(), startRect2, endRect, finalRadius, !mIsOpening));
+
+
// Fade in the folder name, as the text can overlap the icons when grid size is small.
mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 279c445..6b12d86 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -614,10 +614,7 @@
if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
- final int saveCount = canvas.save();
- canvas.clipPath(mBackground.getClipPath());
mPreviewItemManager.draw(canvas);
- canvas.restoreToCount(saveCount);
if (!mBackground.drawingDelegated()) {
mBackground.drawBackgroundStroke(canvas);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 7fc3740..3d2884a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -22,6 +22,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import android.util.AttributeSet;
@@ -49,6 +50,7 @@
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ClipPathView;
import java.util.ArrayList;
import java.util.Iterator;
@@ -57,7 +59,7 @@
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
-public class FolderPagedView extends PagedView<PageIndicatorDots> {
+public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
private static final String TAG = "FolderPagedView";
@@ -89,6 +91,8 @@
private Folder mFolder;
+ private Path mClipPath;
+
// If the views are attached to the folder or not. A folder should be bound when its
// animating or is open.
private boolean mViewsBound = false;
@@ -128,8 +132,16 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- mFocusIndicatorHelper.draw(canvas);
- super.dispatchDraw(canvas);
+ if (mClipPath != null) {
+ int count = canvas.save();
+ canvas.clipPath(mClipPath);
+ mFocusIndicatorHelper.draw(canvas);
+ super.dispatchDraw(canvas);
+ canvas.restoreToCount(count);
+ } else {
+ mFocusIndicatorHelper.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
}
/**
@@ -628,4 +640,10 @@
public int itemsPerPage() {
return mOrganizer.getMaxItemsPerPage();
}
+
+ @Override
+ public void setClipPath(Path clipPath) {
+ mClipPath = clipPath;
+ invalidate();
+ }
}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 4eab63e..204decb 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -51,6 +51,7 @@
public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
private static final boolean DRAW_SHADOW = false;
+ private static final boolean DRAW_STROKE = false;
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
@@ -303,6 +304,10 @@
}
public void animateBackgroundStroke() {
+ if (!DRAW_STROKE) {
+ return;
+ }
+
if (mStrokeAlphaAnimator != null) {
mStrokeAlphaAnimator.cancel();
}
@@ -319,6 +324,9 @@
}
public void drawBackgroundStroke(Canvas canvas) {
+ if (!DRAW_STROKE) {
+ return;
+ }
mPaint.setColor(setColorAlphaBound(mStrokeColor, mStrokeAlpha));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
@@ -363,7 +371,7 @@
}
mDrawingDelegate = null;
- isClipping = true;
+ isClipping = false;
invalidate();
}
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
index 2da679c..f82b07e 100644
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -156,19 +156,43 @@
}
}
- public static final class Circle extends SimpleRectShape {
+ public static final class Circle extends PathShape {
- @Override
- public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
- canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
+ private final float[] mTempRadii = new float[8];
+
+ protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+ float endRadius, Path outPath) {
+ float r1 = getStartRadius(startRect);
+
+ float[] startValues = new float[] {
+ startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r1};
+ float[] endValues = new float[] {
+ endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
+
+ FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
+
+ return (anim) -> {
+ float progress = (Float) anim.getAnimatedValue();
+ float[] values = evaluator.evaluate(progress, startValues, endValues);
+ outPath.addRoundRect(
+ values[0], values[1], values[2], values[3],
+ getRadiiArray(values[4], values[5]), Path.Direction.CW);
+ };
}
+ private float[] getRadiiArray(float r1, float r2) {
+ mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
+ mTempRadii[6] = mTempRadii[7] = r1;
+ mTempRadii[4] = mTempRadii[5] = r2;
+ return mTempRadii;
+ }
+
+
@Override
public void addToPath(Path path, float offsetX, float offsetY, float radius) {
path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
}
- @Override
protected float getStartRadius(Rect startRect) {
return startRect.width() / 2f;
}