Merge "Changing the FolderIcon shape based on AdpativeIcon" into ub-launcher3-master
This commit is contained in:
commit
3ed8626e83
|
@ -19,6 +19,7 @@ package com.android.launcher3;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
|
import com.android.launcher3.folder.FolderShape;
|
||||||
import com.android.launcher3.graphics.IconShapeOverride;
|
import com.android.launcher3.graphics.IconShapeOverride;
|
||||||
import com.android.launcher3.logging.FileLog;
|
import com.android.launcher3.logging.FileLog;
|
||||||
import com.android.launcher3.util.ResourceBasedOverride;
|
import com.android.launcher3.util.ResourceBasedOverride;
|
||||||
|
@ -39,5 +40,6 @@ public class MainProcessInitializer implements ResourceBasedOverride {
|
||||||
FeatureFlags.initialize(context);
|
FeatureFlags.initialize(context);
|
||||||
IconShapeOverride.apply(context);
|
IconShapeOverride.apply(context);
|
||||||
SessionCommitReceiver.applyDefaultUserPrefs(context);
|
SessionCommitReceiver.applyDefaultUserPrefs(context);
|
||||||
|
FolderShape.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,8 +127,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
|
|
||||||
private static final int DEFAULT_PAGE = 0;
|
private static final int DEFAULT_PAGE = 0;
|
||||||
|
|
||||||
private static final boolean MAP_NO_RECURSE = false;
|
public static final boolean MAP_NO_RECURSE = false;
|
||||||
private static final boolean MAP_RECURSE = true;
|
public static final boolean MAP_RECURSE = true;
|
||||||
|
|
||||||
// The screen id used for the empty screen always present to the right.
|
// The screen id used for the empty screen always present to the right.
|
||||||
public static final int EXTRA_EMPTY_SCREEN_ID = -201;
|
public static final int EXTRA_EMPTY_SCREEN_ID = -201;
|
||||||
|
@ -3121,7 +3121,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
* @param recurse true: iterate over folder children. false: op get the folders themselves.
|
* @param recurse true: iterate over folder children. false: op get the folders themselves.
|
||||||
* @param op the operator to map over the shortcuts
|
* @param op the operator to map over the shortcuts
|
||||||
*/
|
*/
|
||||||
void mapOverItems(boolean recurse, ItemOperator op) {
|
public void mapOverItems(boolean recurse, ItemOperator op) {
|
||||||
ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
|
ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
|
||||||
final int containerCount = containers.size();
|
final int containerCount = containers.size();
|
||||||
for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
|
for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
|
||||||
|
|
|
@ -26,6 +26,8 @@ import android.animation.AnimatorSet;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.Selection;
|
import android.text.Selection;
|
||||||
|
@ -151,6 +153,8 @@ public class Folder extends AbstractFloatingView implements DragSource,
|
||||||
// Cell ranks used for drag and drop
|
// Cell ranks used for drag and drop
|
||||||
@Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
|
@Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
|
||||||
|
|
||||||
|
private Path mClipPath;
|
||||||
|
|
||||||
@ViewDebug.ExportedProperty(category = "launcher",
|
@ViewDebug.ExportedProperty(category = "launcher",
|
||||||
mapping = {
|
mapping = {
|
||||||
@ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"),
|
@ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"),
|
||||||
|
@ -1476,4 +1480,25 @@ public class Folder extends AbstractFloatingView implements DragSource,
|
||||||
sHintText = res.getString(R.string.folder_hint_text);
|
sHintText = res.getString(R.string.folder_hint_text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
|
||||||
|
* rounded rect.
|
||||||
|
*/
|
||||||
|
public void setClipPath(Path clipPath) {
|
||||||
|
mClipPath = clipPath;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
if (mClipPath != null) {
|
||||||
|
int count = canvas.save();
|
||||||
|
canvas.clipPath(mClipPath);
|
||||||
|
super.draw(canvas);
|
||||||
|
canvas.restoreToCount(count);
|
||||||
|
} else {
|
||||||
|
super.draw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package com.android.launcher3.folder;
|
||||||
import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
|
import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
|
||||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_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.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||||
|
import static com.android.launcher3.folder.FolderShape.getShape;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
@ -40,7 +41,6 @@ import com.android.launcher3.R;
|
||||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.anim.PropertyResetListener;
|
import com.android.launcher3.anim.PropertyResetListener;
|
||||||
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
|
|
||||||
import com.android.launcher3.dragndrop.DragLayer;
|
import com.android.launcher3.dragndrop.DragLayer;
|
||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.util.Themes;
|
||||||
|
|
||||||
|
@ -166,7 +166,6 @@ public class FolderAnimationManager {
|
||||||
Math.round((totalOffsetX + initialSize) / initialScale),
|
Math.round((totalOffsetX + initialSize) / initialScale),
|
||||||
Math.round((paddingOffsetY + initialSize) / initialScale));
|
Math.round((paddingOffsetY + initialSize) / initialScale));
|
||||||
Rect endRect = new Rect(0, 0, lp.width, lp.height);
|
Rect endRect = new Rect(0, 0, lp.width, lp.height);
|
||||||
float initialRadius = initialSize / initialScale / 2f;
|
|
||||||
float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
|
float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
|
||||||
|
|
||||||
// Create the animators.
|
// Create the animators.
|
||||||
|
@ -189,14 +188,8 @@ public class FolderAnimationManager {
|
||||||
play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
|
play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
|
||||||
play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
|
play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
|
||||||
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
|
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
|
||||||
RoundedRectRevealOutlineProvider outlineProvider = new RoundedRectRevealOutlineProvider(
|
play(a, getShape().createRevealAnimator(
|
||||||
initialRadius, finalRadius, startRect, endRect) {
|
mFolder, startRect, endRect, finalRadius, !mIsOpening));
|
||||||
@Override
|
|
||||||
public boolean shouldRemoveElevationDuringAnimation() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
play(a, outlineProvider.createRevealAnimator(mFolder, !mIsOpening));
|
|
||||||
|
|
||||||
// Animate the elevation midway so that the shadow is not noticeable in the background.
|
// Animate the elevation midway so that the shadow is not noticeable in the background.
|
||||||
int midDuration = mDuration / 2;
|
int midDuration = mDuration / 2;
|
||||||
|
|
|
@ -75,6 +75,7 @@ import androidx.annotation.NonNull;
|
||||||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||||
*/
|
*/
|
||||||
public class FolderIcon extends FrameLayout implements FolderListener {
|
public class FolderIcon extends FrameLayout implements FolderListener {
|
||||||
|
|
||||||
@Thunk Launcher mLauncher;
|
@Thunk Launcher mLauncher;
|
||||||
@Thunk Folder mFolder;
|
@Thunk Folder mFolder;
|
||||||
private FolderInfo mInfo;
|
private FolderInfo mInfo;
|
||||||
|
@ -477,20 +478,9 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||||
if (mFolder == null) return;
|
if (mFolder == null) return;
|
||||||
if (mFolder.getItemCount() == 0 && !mAnimating) return;
|
if (mFolder.getItemCount() == 0 && !mAnimating) return;
|
||||||
|
|
||||||
final int saveCount;
|
final int saveCount = canvas.save();
|
||||||
|
canvas.clipPath(mBackground.getClipPath());
|
||||||
if (canvas.isHardwareAccelerated()) {
|
|
||||||
saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
|
|
||||||
} else {
|
|
||||||
saveCount = canvas.save();
|
|
||||||
canvas.clipPath(mBackground.getClipPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreviewItemManager.draw(canvas);
|
mPreviewItemManager.draw(canvas);
|
||||||
|
|
||||||
if (canvas.isHardwareAccelerated()) {
|
|
||||||
mBackground.clipCanvasHardware(canvas);
|
|
||||||
}
|
|
||||||
canvas.restoreToCount(saveCount);
|
canvas.restoreToCount(saveCount);
|
||||||
|
|
||||||
if (!mBackground.drawingDelegated()) {
|
if (!mBackground.drawingDelegated()) {
|
||||||
|
|
|
@ -0,0 +1,422 @@
|
||||||
|
/*
|
||||||
|
* 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 static com.android.launcher3.Workspace.MAP_NO_RECURSE;
|
||||||
|
|
||||||
|
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.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.view.ViewOutlineProvider;
|
||||||
|
|
||||||
|
import com.android.launcher3.Launcher;
|
||||||
|
import com.android.launcher3.LauncherAppState;
|
||||||
|
import com.android.launcher3.MainThreadExecutor;
|
||||||
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 static FolderShape[] getAllShapes() {
|
||||||
|
return new FolderShape[] {
|
||||||
|
new Circle(),
|
||||||
|
new RoundedSquare(8f / 50), // Ratios based on path defined in config_icon_mask
|
||||||
|
new RoundedSquare(30f / 50),
|
||||||
|
new Square(),
|
||||||
|
new TearDrop(),
|
||||||
|
new Squircle()};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Animator createRevealAnimator(Folder target, Rect startRect, Rect endRect,
|
||||||
|
float endRadius, boolean isReversed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract shape where the reveal animation is a derivative of a round rect animation
|
||||||
|
*/
|
||||||
|
private static abstract class SimpleRectShape extends FolderShape {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Animator createRevealAnimator(Folder 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 Animator createRevealAnimator(Folder 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 Square extends SimpleRectShape {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
|
||||||
|
float cx = radius + offsetX;
|
||||||
|
float cy = radius + offsetY;
|
||||||
|
canvas.drawRect(cx - radius, cy - radius, cx + radius, cy + radius, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addShape(Path path, float offsetX, float offsetY, float radius) {
|
||||||
|
float cx = radius + offsetX;
|
||||||
|
float cy = radius + offsetY;
|
||||||
|
path.addRect(cx - radius, cy - radius, cx + radius, cy + radius, Path.Direction.CW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getStartRadius(Rect startRect) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RoundedSquare extends SimpleRectShape {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ratio of corner radius to half size. Based on the
|
||||||
|
*/
|
||||||
|
private final float mRadiusFactor;
|
||||||
|
|
||||||
|
public RoundedSquare(float radiusFactor) {
|
||||||
|
mRadiusFactor = radiusFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 * mRadiusFactor;
|
||||||
|
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 * mRadiusFactor;
|
||||||
|
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) * mRadiusFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TearDrop extends PathShape {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Radio of short radius to large radius, based on the shape options defined in the config.
|
||||||
|
*/
|
||||||
|
private static final float RADIUS_RATIO = 15f / 50;
|
||||||
|
|
||||||
|
private final float[] mTempRadii = new float[8];
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addShape(Path p, float offsetX, float offsetY, float r1) {
|
||||||
|
float r2 = r1 * RADIUS_RATIO;
|
||||||
|
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 * RADIUS_RATIO;
|
||||||
|
|
||||||
|
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 static final float RADIUS_RATIO = 10f / 50;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addShape(Path p, float offsetX, float offsetY, float r) {
|
||||||
|
float cx = r + offsetX;
|
||||||
|
float cy = r + offsetY;
|
||||||
|
float control = r - r * RADIUS_RATIO;
|
||||||
|
|
||||||
|
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 * RADIUS_RATIO;
|
||||||
|
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 closest to the {@link AdaptiveIconDrawable}
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
if (!Utilities.ATLEAST_OREO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new MainThreadExecutor().execute(FolderShape::pickShapeInBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
|
protected static void pickShapeInBackground() {
|
||||||
|
// 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()) {
|
||||||
|
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) {
|
||||||
|
FolderShape shape = closestShape;
|
||||||
|
new MainThreadExecutor().execute(() -> updateFolderShape(shape));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateFolderShape(FolderShape shape) {
|
||||||
|
sInstance = shape;
|
||||||
|
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
||||||
|
if (app == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Launcher launcher = (Launcher) app.getModel().getCallback();
|
||||||
|
if (launcher != null) {
|
||||||
|
launcher.getWorkspace().mapOverItems(MAP_NO_RECURSE, (i, v) -> {
|
||||||
|
if (v instanceof FolderIcon) {
|
||||||
|
v.invalidate();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package com.android.launcher3.folder;
|
package com.android.launcher3.folder;
|
||||||
|
|
||||||
|
import static com.android.launcher3.folder.FolderShape.getShape;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
@ -49,16 +51,6 @@ public class PreviewBackground {
|
||||||
|
|
||||||
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
|
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
|
||||||
|
|
||||||
private final PorterDuffXfermode mClipPorterDuffXfermode
|
|
||||||
= new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
|
|
||||||
// Create a RadialGradient such that it draws a black circle and then extends with
|
|
||||||
// transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
|
|
||||||
// just at the edge quickly change it to transparent.
|
|
||||||
private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
|
|
||||||
new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
|
|
||||||
new float[] {0, 0.999f, 1},
|
|
||||||
Shader.TileMode.CLAMP);
|
|
||||||
|
|
||||||
private final PorterDuffXfermode mShadowPorterDuffXfermode
|
private final PorterDuffXfermode mShadowPorterDuffXfermode
|
||||||
= new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
|
= new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
|
||||||
private RadialGradient mShadowShader = null;
|
private RadialGradient mShadowShader = null;
|
||||||
|
@ -208,8 +200,7 @@ public class PreviewBackground {
|
||||||
mPaint.setStyle(Paint.Style.FILL);
|
mPaint.setStyle(Paint.Style.FILL);
|
||||||
mPaint.setColor(getBgColor());
|
mPaint.setColor(getBgColor());
|
||||||
|
|
||||||
drawCircle(canvas, 0 /* deltaRadius */);
|
getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
|
||||||
|
|
||||||
drawShadow(canvas);
|
drawShadow(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +235,7 @@ public class PreviewBackground {
|
||||||
mPaint.setShader(null);
|
mPaint.setShader(null);
|
||||||
if (canvas.isHardwareAccelerated()) {
|
if (canvas.isHardwareAccelerated()) {
|
||||||
mPaint.setXfermode(mShadowPorterDuffXfermode);
|
mPaint.setXfermode(mShadowPorterDuffXfermode);
|
||||||
canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
|
getShape().drawShape(canvas, offsetX, offsetY, radius, mPaint);
|
||||||
mPaint.setXfermode(null);
|
mPaint.setXfermode(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +278,10 @@ public class PreviewBackground {
|
||||||
mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
|
mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
|
||||||
mPaint.setStyle(Paint.Style.STROKE);
|
mPaint.setStyle(Paint.Style.STROKE);
|
||||||
mPaint.setStrokeWidth(mStrokeWidth);
|
mPaint.setStrokeWidth(mStrokeWidth);
|
||||||
drawCircle(canvas, 1 /* deltaRadius */);
|
|
||||||
|
float inset = 1f;
|
||||||
|
getShape().drawShape(canvas,
|
||||||
|
getOffsetX() + inset, getOffsetY() + inset, getScaledRadius() - inset, mPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawLeaveBehind(Canvas canvas) {
|
public void drawLeaveBehind(Canvas canvas) {
|
||||||
|
@ -296,40 +290,17 @@ public class PreviewBackground {
|
||||||
|
|
||||||
mPaint.setStyle(Paint.Style.FILL);
|
mPaint.setStyle(Paint.Style.FILL);
|
||||||
mPaint.setColor(Color.argb(160, 245, 245, 245));
|
mPaint.setColor(Color.argb(160, 245, 245, 245));
|
||||||
drawCircle(canvas, 0 /* deltaRadius */);
|
getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
|
||||||
|
|
||||||
mScale = originalScale;
|
mScale = originalScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawCircle(Canvas canvas,float deltaRadius) {
|
|
||||||
float radius = getScaledRadius();
|
|
||||||
canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
|
|
||||||
radius - deltaRadius, mPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path getClipPath() {
|
public Path getClipPath() {
|
||||||
mPath.reset();
|
mPath.reset();
|
||||||
float r = getScaledRadius();
|
getShape().addShape(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
|
||||||
mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
|
|
||||||
return mPath;
|
return mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is the callers responsibility to save and restore the canvas layers.
|
|
||||||
void clipCanvasHardware(Canvas canvas) {
|
|
||||||
mPaint.setColor(Color.BLACK);
|
|
||||||
mPaint.setStyle(Paint.Style.FILL);
|
|
||||||
mPaint.setXfermode(mClipPorterDuffXfermode);
|
|
||||||
|
|
||||||
float radius = getScaledRadius();
|
|
||||||
mShaderMatrix.setScale(radius, radius);
|
|
||||||
mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
|
|
||||||
mClipShader.setLocalMatrix(mShaderMatrix);
|
|
||||||
mPaint.setShader(mClipShader);
|
|
||||||
canvas.drawPaint(mPaint);
|
|
||||||
mPaint.setXfermode(null);
|
|
||||||
mPaint.setShader(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
|
private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
|
||||||
if (mDrawingDelegate != delegate) {
|
if (mDrawingDelegate != delegate) {
|
||||||
delegate.addFolderBackground(this);
|
delegate.addFolderBackground(this);
|
||||||
|
|
Loading…
Reference in New Issue