Merge "Updating FolderIcon drawing to avoid dependency on software layer." into ub-launcher3-dorval

am: 3ba48fa019

Change-Id: Id18f5a34264411582115cde8cddd0dc8ecded8df
This commit is contained in:
Sunny Goyal 2017-03-06 18:59:41 +00:00 committed by android-build-merger
commit dec75d5add
2 changed files with 134 additions and 79 deletions

View File

@ -105,7 +105,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
Paint mFolderBgPaint = new Paint();
private float mBackgroundAlpha;
@ -501,9 +500,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
bg.drawBackground(canvas, mFolderBgPaint);
bg.drawBackground(canvas);
if (!bg.isClipping) {
bg.drawBackgroundStroke(canvas, mFolderBgPaint);
bg.drawBackgroundStroke(canvas);
}
canvas.restore();
}
@ -513,7 +512,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mFolderLeaveBehind.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
mFolderLeaveBehind.drawLeaveBehind(canvas);
canvas.restore();
}
}
@ -528,7 +527,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
bg.drawBackgroundStroke(canvas, mFolderBgPaint);
bg.drawBackgroundStroke(canvas);
canvas.restore();
}
}

View File

@ -24,11 +24,15 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
@ -126,8 +130,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
private Drawable mReferenceDrawable = null;
Paint mBgPaint = new Paint();
private Alarm mOpenAlarm = new Alarm();
private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
@ -180,11 +182,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
DeviceProfile grid = launcher.getDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
// For performance and compatibility reasons we render the preview using a software layer.
// In particular, hardware path clipping has spotty ecosystem support and bad performance.
// Software rendering also allows us to use shadow layers.
icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));
icon.setClipToPadding(false);
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
@ -495,7 +492,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
canvas.save();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(params.transX, params.transY);
canvas.scale(params.scale, params.scale);
Drawable d = params.drawable;
@ -522,10 +519,29 @@ public class FolderIcon extends FrameLayout implements FolderListener {
* information, handles drawing, and animation (accept state <--> rest state).
*/
public static class PreviewBackground {
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
= new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private RadialGradient mShadowShader = null;
private final Matrix mShaderMatrix = new Matrix();
private final Path mPath = new Path();
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float mScale = 1f;
private float mColorMultiplier = 1f;
private Path mClipPath = new Path();
private int mStrokeWidth;
private float mStrokeWidth;
private View mInvalidateDelegate;
public int previewSize;
@ -548,7 +564,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private static final int BG_OPACITY = 160;
private static final int MAX_BG_OPACITY = 225;
private static final int BG_INTENSITY = 245;
private static final int SHADOW_OPACITY = 80;
private static final int SHADOW_OPACITY = 40;
ValueAnimator mScaleAnimator;
@ -564,7 +580,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
mStrokeWidth = Utilities.pxFromDp(1, dm);
// Stroke width is 1dp
mStrokeWidth = dm.density;
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
mShadowShader = new RadialGradient(0, 0, 1,
new int[] {shadowColor, Color.TRANSPARENT},
new float[] {radius / shadowRadius, 1},
Shader.TileMode.CLAMP);
invalidate();
}
@ -594,10 +619,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
void invalidate() {
int radius = getScaledRadius();
mClipPath.reset();
mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);
if (mInvalidateDelegate != null) {
mInvalidateDelegate.invalidate();
}
@ -612,70 +633,94 @@ public class FolderIcon extends FrameLayout implements FolderListener {
invalidate();
}
public void drawBackground(Canvas canvas, Paint paint) {
canvas.save();
canvas.translate(getOffsetX(), getOffsetY());
paint.reset();
paint.setStyle(Paint.Style.FILL);
paint.setXfermode(null);
paint.setAntiAlias(true);
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
drawCircle(canvas, 0 /* deltaRadius */);
// Draw shadow.
if (mShadowShader == null) {
return;
}
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
mPaint.setColor(Color.BLACK);
int offsetX = getOffsetX();
int offsetY = getOffsetY();
final int saveCount;
if (canvas.isHardwareAccelerated()) {
saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
canvas.drawCircle(radius, radius, radius, paint);
canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
}
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.TRANSPARENT);
paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
canvas.drawCircle(radius, radius, radius, paint);
mShaderMatrix.setScale(shadowRadius, shadowRadius);
mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
mShadowShader.setLocalMatrix(mShaderMatrix);
mPaint.setShader(mShadowShader);
canvas.drawPaint(mPaint);
mPaint.setShader(null);
canvas.restore();
if (canvas.isHardwareAccelerated()) {
mPaint.setXfermode(mShadowPorterDuffXfermode);
canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
mPaint.setXfermode(null);
}
canvas.restoreToCount(saveCount);
}
public void drawBackgroundStroke(Canvas canvas, Paint paint) {
canvas.save();
canvas.translate(getOffsetX(), getOffsetY());
paint.reset();
paint.setAntiAlias(true);
paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mStrokeWidth);
float radius = getScaledRadius();
canvas.drawCircle(radius, radius, radius - 1, paint);
canvas.restore();
public void drawBackgroundStroke(Canvas canvas) {
mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
drawCircle(canvas, 1 /* deltaRadius */);
}
public void drawLeaveBehind(Canvas canvas, Paint paint) {
public void drawLeaveBehind(Canvas canvas) {
float originalScale = mScale;
mScale = 0.5f;
canvas.save();
canvas.translate(getOffsetX(), getOffsetY());
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.argb(160, 245, 245, 245));
drawCircle(canvas, 0 /* deltaRadius */);
paint.reset();
paint.setAntiAlias(true);
paint.setColor(Color.argb(160, 245, 245, 245));
float radius = getScaledRadius();
canvas.drawCircle(radius, radius, radius, paint);
canvas.restore();
mScale = originalScale;
}
// It is the callers responsibility to save and restore the canvas.
private void clipCanvas(Canvas canvas) {
canvas.translate(getOffsetX(), getOffsetY());
canvas.clipPath(mClipPath);
canvas.translate(-getOffsetX(), -getOffsetY());
private void drawCircle(Canvas canvas,float deltaRadius) {
float radius = getScaledRadius();
canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
radius - deltaRadius, mPaint);
}
// It is the callers responsibility to save and restore the canvas layers.
private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
mPath.reset();
float r = getScaledRadius();
mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
canvas.clipPath(mPath, op);
}
// It is the callers responsibility to save and restore the canvas layers.
private void clipCanvasHardware(Canvas canvas) {
mPaint.setColor(Color.BLACK);
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) {
@ -795,17 +840,22 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
if (!mBackground.drawingDelegated()) {
mBackground.drawBackground(canvas, mBgPaint);
mBackground.drawBackground(canvas);
}
if (mFolder == null) return;
if (mFolder.getItemCount() == 0 && !mAnimating) return;
canvas.save();
final int saveCount;
if (mPreviewLayoutRule.clipToBackground()) {
mBackground.clipCanvas(canvas);
if (canvas.isHardwareAccelerated()) {
saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
if (mPreviewLayoutRule.clipToBackground()) {
mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
}
}
// The items are drawn in coordinates relative to the preview offset
@ -818,18 +868,24 @@ public class FolderIcon extends FrameLayout implements FolderListener {
drawPreviewItem(canvas, p);
}
}
canvas.restore();
canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
mBackground.clipCanvasHardware(canvas);
}
canvas.restoreToCount(saveCount);
if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
mBackground.drawBackgroundStroke(canvas, mBgPaint);
mBackground.drawBackgroundStroke(canvas);
}
int offsetX = mBackground.getOffsetX();
int offsetY = mBackground.getOffsetY();
int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
// If we are animating to the accepting state, animate the badge out.
int offsetX = mBackground.getOffsetX();
int offsetY = mBackground.getOffsetY();
int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
}