Adding utility method to get adaptive icon scale
Change-Id: I5ff190c3b794bb13309375782ccd420e85b59091
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 2461e28..2450039 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -19,7 +19,7 @@
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;
-import static com.android.launcher3.folder.FolderShape.getShape;
+import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.Animator;
diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/folder/FolderShape.java
deleted file mode 100644
index ec6078e..0000000
--- a/src/com/android/launcher3/folder/FolderShape.java
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Copyright (C) 2018 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.folder;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.FloatArrayEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
-import android.graphics.RegionIterator;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.util.Xml;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ClipPathView;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.Nullable;
-
-/**
- * Abstract representation of the shape of a folder icon
- */
-public abstract class FolderShape {
-
- private static FolderShape sInstance = new Circle();
-
- public static FolderShape getShape() {
- return sInstance;
- }
-
- private SparseArray<TypedValue> mAttrs;
-
- public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
- Paint paint);
-
- public abstract void addShape(Path path, float offsetX, float offsetY, float radius);
-
- public abstract <T extends View & ClipPathView> Animator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed);
-
- @Nullable
- public TypedValue getAttrValue(int attr) {
- return mAttrs == null ? null : mAttrs.get(attr);
- }
-
- /**
- * Abstract shape where the reveal animation is a derivative of a round rect animation
- */
- private static abstract class SimpleRectShape extends FolderShape {
-
- @Override
- public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
- return new RoundedRectRevealOutlineProvider(
- getStartRadius(startRect), endRadius, startRect, endRect) {
- @Override
- public boolean shouldRemoveElevationDuringAnimation() {
- return true;
- }
- }.createRevealAnimator(target, isReversed);
- }
-
- protected abstract float getStartRadius(Rect startRect);
- }
-
- /**
- * Abstract shape which draws using {@link Path}
- */
- private static abstract class PathShape extends FolderShape {
-
- private final Path mTmpPath = new Path();
-
- @Override
- public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
- Paint paint) {
- mTmpPath.reset();
- addShape(mTmpPath, offsetX, offsetY, radius);
- canvas.drawPath(mTmpPath, paint);
- }
-
- protected abstract AnimatorUpdateListener newUpdateListener(
- Rect startRect, Rect endRect, float endRadius, Path outPath);
-
- @Override
- public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
- Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
- Path path = new Path();
- AnimatorUpdateListener listener =
- newUpdateListener(startRect, endRect, endRadius, path);
-
- ValueAnimator va =
- isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
- va.addListener(new AnimatorListenerAdapter() {
- private ViewOutlineProvider mOldOutlineProvider;
-
- public void onAnimationStart(Animator animation) {
- mOldOutlineProvider = target.getOutlineProvider();
- target.setOutlineProvider(null);
-
- target.setTranslationZ(-target.getElevation());
- }
-
- public void onAnimationEnd(Animator animation) {
- target.setTranslationZ(0);
- target.setClipPath(null);
- target.setOutlineProvider(mOldOutlineProvider);
- }
- });
-
- va.addUpdateListener((anim) -> {
- path.reset();
- listener.onAnimationUpdate(anim);
- target.setClipPath(path);
- });
-
- return va;
- }
- }
-
- public static final class Circle extends SimpleRectShape {
-
- @Override
- public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
- canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
- }
-
- @Override
- public void addShape(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;
- }
- }
-
- public static class RoundedSquare extends SimpleRectShape {
-
- /**
- * Ratio of corner radius to half size.
- */
- private final float mRadiusRatio;
-
- public RoundedSquare(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- float cr = radius * mRadiusRatio;
- canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, p);
- }
-
- @Override
- public void addShape(Path path, float offsetX, float offsetY, float radius) {
- float cx = radius + offsetX;
- float cy = radius + offsetY;
- float cr = radius * mRadiusRatio;
- path.addRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr,
- Path.Direction.CW);
- }
-
- @Override
- protected float getStartRadius(Rect startRect) {
- return (startRect.width() / 2f) * mRadiusRatio;
- }
- }
-
- public static class TearDrop extends PathShape {
-
- /**
- * Radio of short radius to large radius, based on the shape options defined in the config.
- */
- private final float mRadiusRatio;
- private final float[] mTempRadii = new float[8];
-
- public TearDrop(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void addShape(Path p, float offsetX, float offsetY, float r1) {
- float r2 = r1 * mRadiusRatio;
- float cx = r1 + offsetX;
- float cy = r1 + offsetY;
-
- p.addRoundRect(cx - r1, cy - r1, cx + r1, cy + r1, getRadiiArray(r1, r2),
- 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
- protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
- float endRadius, Path outPath) {
- float r1 = startRect.width() / 2f;
- float r2 = r1 * mRadiusRatio;
-
- float[] startValues = new float[] {
- startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r2};
- 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);
- };
- }
- }
-
- public static class Squircle extends PathShape {
-
- /**
- * Radio of radius to circle radius, based on the shape options defined in the config.
- */
- private final float mRadiusRatio;
-
- public Squircle(float radiusRatio) {
- mRadiusRatio = radiusRatio;
- }
-
- @Override
- public void addShape(Path p, float offsetX, float offsetY, float r) {
- float cx = r + offsetX;
- float cy = r + offsetY;
- float control = r - r * mRadiusRatio;
-
- p.moveTo(cx, cy - r);
- addLeftCurve(cx, cy, r, control, p);
- addRightCurve(cx, cy, r, control, p);
- addLeftCurve(cx, cy, -r, -control, p);
- addRightCurve(cx, cy, -r, -control, p);
- p.close();
- }
-
- private void addLeftCurve(float cx, float cy, float r, float control, Path path) {
- path.cubicTo(
- cx - control, cy - r,
- cx - r, cy - control,
- cx - r, cy);
- }
-
- private void addRightCurve(float cx, float cy, float r, float control, Path path) {
- path.cubicTo(
- cx - r, cy + control,
- cx - control, cy + r,
- cx, cy + r);
- }
-
- @Override
- protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
- float endR, Path outPath) {
-
- float startCX = startRect.exactCenterX();
- float startCY = startRect.exactCenterY();
- float startR = startRect.width() / 2f;
- float startControl = startR - startR * mRadiusRatio;
- float startHShift = 0;
- float startVShift = 0;
-
- float endCX = endRect.exactCenterX();
- float endCY = endRect.exactCenterY();
- // Approximate corner circle using bezier curves
- // http://spencermortensen.com/articles/bezier-circle/
- float endControl = endR * 0.551915024494f;
- float endHShift = endRect.width() / 2f - endR;
- float endVShift = endRect.height() / 2f - endR;
-
- return (anim) -> {
- float progress = (Float) anim.getAnimatedValue();
-
- float cx = (1 - progress) * startCX + progress * endCX;
- float cy = (1 - progress) * startCY + progress * endCY;
- float r = (1 - progress) * startR + progress * endR;
- float control = (1 - progress) * startControl + progress * endControl;
- float hShift = (1 - progress) * startHShift + progress * endHShift;
- float vShift = (1 - progress) * startVShift + progress * endVShift;
-
- outPath.moveTo(cx, cy - vShift - r);
- outPath.rLineTo(-hShift, 0);
-
- addLeftCurve(cx - hShift, cy - vShift, r, control, outPath);
- outPath.rLineTo(0, vShift + vShift);
-
- addRightCurve(cx - hShift, cy + vShift, r, control, outPath);
- outPath.rLineTo(hShift + hShift, 0);
-
- addLeftCurve(cx + hShift, cy + vShift, -r, -control, outPath);
- outPath.rLineTo(0, -vShift - vShift);
-
- addRightCurve(cx + hShift, cy - vShift, -r, -control, outPath);
- outPath.close();
- };
- }
- }
-
- /**
- * Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
- */
- public static void init(Context context) {
- if (!Utilities.ATLEAST_OREO) {
- return;
- }
- pickBestShape(context);
- }
-
- private static FolderShape getShapeDefinition(String type, float radius) {
- switch (type) {
- case "Circle":
- return new Circle();
- case "RoundedSquare":
- return new RoundedSquare(radius);
- case "TearDrop":
- return new TearDrop(radius);
- case "Squircle":
- return new Squircle(radius);
- default:
- throw new IllegalArgumentException("Invalid shape type: " + type);
- }
- }
-
- private static List<FolderShape> getAllShapes(Context context) {
- ArrayList<FolderShape> result = new ArrayList<>();
- try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) {
-
- // Find the root tag
- int type;
- while ((type = parser.next()) != XmlPullParser.END_TAG
- && type != XmlPullParser.END_DOCUMENT
- && !"shapes".equals(parser.getName()));
-
- final int depth = parser.getDepth();
- int[] radiusAttr = new int[] {R.attr.folderIconRadius};
- IntArray keysToIgnore = new IntArray(0);
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
- if (type == XmlPullParser.START_TAG) {
- AttributeSet attrs = Xml.asAttributeSet(parser);
- TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr);
- FolderShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
- a.recycle();
-
- shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore);
- result.add(shape);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- throw new RuntimeException(e);
- }
- return result;
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- protected static void pickBestShape(Context context) {
- // Pick any large size
- int size = 200;
-
- Region full = new Region(0, 0, size, size);
- Region iconR = new Region();
- AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
- new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
- drawable.setBounds(0, 0, size, size);
- iconR.setPath(drawable.getIconMask(), full);
-
- Path shapePath = new Path();
- Region shapeR = new Region();
- Rect tempRect = new Rect();
-
- // Find the shape with minimum area of divergent region.
- int minArea = Integer.MAX_VALUE;
- FolderShape closestShape = null;
- for (FolderShape shape : getAllShapes(context)) {
- shapePath.reset();
- shape.addShape(shapePath, 0, 0, size / 2f);
- shapeR.setPath(shapePath, full);
- shapeR.op(iconR, Op.XOR);
-
- RegionIterator itr = new RegionIterator(shapeR);
- int area = 0;
-
- while (itr.next(tempRect)) {
- area += tempRect.width() * tempRect.height();
- }
- if (area < minArea) {
- minArea = area;
- closestShape = shape;
- }
- }
-
- if (closestShape != null) {
- sInstance = closestShape;
- }
- }
-}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 46df77a..09e8276 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,7 +16,7 @@
package com.android.launcher3.folder;
-import static com.android.launcher3.folder.FolderShape.getShape;
+import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.Animator;
@@ -311,7 +311,7 @@
public Path getClipPath() {
mPath.reset();
- getShape().addShape(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
+ getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
return mPath;
}