Merge "Updating FolderIcon drawing to avoid dependency on software layer." into ub-launcher3-dorval
am: 3ba48fa019
Change-Id: Id18f5a34264411582115cde8cddd0dc8ecded8df
This commit is contained in:
commit
dec75d5add
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue