Merge "Update icon badges to match spec" into ub-launcher3-dorval
This commit is contained in:
committed by
Android (Google) Code Review
commit
0fa96ea2f6
@@ -182,8 +182,6 @@
|
|||||||
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
|
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
|
||||||
|
|
||||||
<!-- Icon badges (with notification counts) -->
|
<!-- Icon badges (with notification counts) -->
|
||||||
<dimen name="badge_size">24dp</dimen>
|
|
||||||
<dimen name="badge_text_size">12dp</dimen>
|
|
||||||
<dimen name="badge_small_padding">0dp</dimen>
|
<dimen name="badge_small_padding">0dp</dimen>
|
||||||
<dimen name="badge_large_padding">3dp</dimen>
|
<dimen name="badge_large_padding">3dp</dimen>
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.launcher3;
|
package com.android.launcher3;
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@@ -23,9 +24,12 @@ import android.content.res.TypedArray;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Property;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
@@ -42,6 +46,7 @@ import com.android.launcher3.badge.BadgeRenderer;
|
|||||||
import com.android.launcher3.folder.FolderIcon;
|
import com.android.launcher3.folder.FolderIcon;
|
||||||
import com.android.launcher3.graphics.DrawableFactory;
|
import com.android.launcher3.graphics.DrawableFactory;
|
||||||
import com.android.launcher3.graphics.HolographicOutlineHelper;
|
import com.android.launcher3.graphics.HolographicOutlineHelper;
|
||||||
|
import com.android.launcher3.graphics.IconPalette;
|
||||||
import com.android.launcher3.graphics.PreloadIconDrawable;
|
import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||||
import com.android.launcher3.model.PackageItemInfo;
|
import com.android.launcher3.model.PackageItemInfo;
|
||||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||||
@@ -90,6 +95,28 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
|||||||
@ViewDebug.ExportedProperty(category = "launcher")
|
@ViewDebug.ExportedProperty(category = "launcher")
|
||||||
private int mTextColor;
|
private int mTextColor;
|
||||||
|
|
||||||
|
private BadgeInfo mBadgeInfo;
|
||||||
|
private BadgeRenderer mBadgeRenderer;
|
||||||
|
private IconPalette mIconPalette;
|
||||||
|
private float mBadgeScale;
|
||||||
|
private boolean mForceHideBadge;
|
||||||
|
private Point mTempSpaceForBadgeOffset = new Point();
|
||||||
|
private Rect mTempIconBounds = new Rect();
|
||||||
|
|
||||||
|
private static final Property<BubbleTextView, Float> BADGE_SCALE_PROPERTY
|
||||||
|
= new Property<BubbleTextView, Float>(Float.TYPE, "badgeScale") {
|
||||||
|
@Override
|
||||||
|
public Float get(BubbleTextView bubbleTextView) {
|
||||||
|
return bubbleTextView.mBadgeScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(BubbleTextView bubbleTextView, Float value) {
|
||||||
|
bubbleTextView.mBadgeScale = value;
|
||||||
|
bubbleTextView.invalidate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@ViewDebug.ExportedProperty(category = "launcher")
|
@ViewDebug.ExportedProperty(category = "launcher")
|
||||||
private boolean mStayPressed;
|
private boolean mStayPressed;
|
||||||
@ViewDebug.ExportedProperty(category = "launcher")
|
@ViewDebug.ExportedProperty(category = "launcher")
|
||||||
@@ -369,6 +396,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
|||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
if (!mCustomShadowsEnabled) {
|
if (!mCustomShadowsEnabled) {
|
||||||
super.draw(canvas);
|
super.draw(canvas);
|
||||||
|
drawBadgeIfNecessary(canvas);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +423,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
|||||||
if ((getCurrentTextColor() >> 24) == 0) {
|
if ((getCurrentTextColor() >> 24) == 0) {
|
||||||
getPaint().clearShadowLayer();
|
getPaint().clearShadowLayer();
|
||||||
super.draw(canvas);
|
super.draw(canvas);
|
||||||
|
drawBadgeIfNecessary(canvas);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +439,50 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
|||||||
density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
|
density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
|
||||||
super.draw(canvas);
|
super.draw(canvas);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|
||||||
|
drawBadgeIfNecessary(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the icon badge in the top right corner of the icon bounds.
|
||||||
|
* @param canvas The canvas to draw to.
|
||||||
|
*/
|
||||||
|
private void drawBadgeIfNecessary(Canvas canvas) {
|
||||||
|
if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
|
||||||
|
getIconBounds(mTempIconBounds);
|
||||||
|
mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
|
||||||
|
final int scrollX = getScrollX();
|
||||||
|
final int scrollY = getScrollY();
|
||||||
|
canvas.translate(scrollX, scrollY);
|
||||||
|
mBadgeRenderer.draw(canvas, mIconPalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
|
||||||
|
mTempSpaceForBadgeOffset);
|
||||||
|
canvas.translate(-scrollX, -scrollY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceHideBadge(boolean forceHideBadge) {
|
||||||
|
if (mForceHideBadge == forceHideBadge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mForceHideBadge = forceHideBadge;
|
||||||
|
|
||||||
|
if (forceHideBadge) {
|
||||||
|
invalidate();
|
||||||
|
} else if (hasBadge()) {
|
||||||
|
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, 0, 1).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasBadge() {
|
||||||
|
return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getIconBounds(Rect outBounds) {
|
||||||
|
int top = getPaddingTop();
|
||||||
|
int left = (getWidth() - mIconSize) / 2;
|
||||||
|
int right = left + mIconSize;
|
||||||
|
int bottom = top + mIconSize;
|
||||||
|
outBounds.set(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -506,7 +579,22 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
|||||||
if (popup != null) {
|
if (popup != null) {
|
||||||
popup.updateNotificationHeader(badgeInfo, itemInfo);
|
popup.updateNotificationHeader(badgeInfo, itemInfo);
|
||||||
}
|
}
|
||||||
((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer, animate);
|
|
||||||
|
boolean wasBadged = mBadgeInfo != null;
|
||||||
|
boolean isBadged = badgeInfo != null;
|
||||||
|
float newBadgeScale = isBadged ? 1f : 0;
|
||||||
|
mBadgeInfo = badgeInfo;
|
||||||
|
mBadgeRenderer = badgeRenderer;
|
||||||
|
if (wasBadged || isBadged) {
|
||||||
|
mIconPalette = ((FastBitmapDrawable) mIcon).getIconPalette();
|
||||||
|
// Animate when a badge is first added or when it is removed.
|
||||||
|
if (animate && (wasBadged ^ isBadged) && isShown()) {
|
||||||
|
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
|
||||||
|
} else {
|
||||||
|
mBadgeScale = newBadgeScale;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,8 +197,6 @@ public class DeviceProfile {
|
|||||||
hotseatBarBottomPaddingPx = 0;
|
hotseatBarBottomPaddingPx = 0;
|
||||||
hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
|
hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
|
||||||
|
|
||||||
mBadgeRenderer = new BadgeRenderer(context);
|
|
||||||
|
|
||||||
// Determine sizes.
|
// Determine sizes.
|
||||||
widthPx = width;
|
widthPx = width;
|
||||||
heightPx = height;
|
heightPx = height;
|
||||||
@@ -213,6 +211,9 @@ public class DeviceProfile {
|
|||||||
// Calculate the remaining vars
|
// Calculate the remaining vars
|
||||||
updateAvailableDimensions(dm, res);
|
updateAvailableDimensions(dm, res);
|
||||||
computeAllAppsButtonSize(context);
|
computeAllAppsButtonSize(context);
|
||||||
|
|
||||||
|
// This is done last, after iconSizePx is calculated above.
|
||||||
|
mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
|
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.util.Property;
|
import android.util.Property;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.launcher3.badge.BadgeInfo;
|
|
||||||
import com.android.launcher3.badge.BadgeRenderer;
|
|
||||||
import com.android.launcher3.graphics.IconPalette;
|
import com.android.launcher3.graphics.IconPalette;
|
||||||
|
|
||||||
public class FastBitmapDrawable extends Drawable {
|
public class FastBitmapDrawable extends Drawable {
|
||||||
@@ -77,24 +75,7 @@ public class FastBitmapDrawable extends Drawable {
|
|||||||
private boolean mIsPressed;
|
private boolean mIsPressed;
|
||||||
private boolean mIsDisabled;
|
private boolean mIsDisabled;
|
||||||
|
|
||||||
private BadgeInfo mBadgeInfo;
|
|
||||||
private BadgeRenderer mBadgeRenderer;
|
|
||||||
private IconPalette mIconPalette;
|
private IconPalette mIconPalette;
|
||||||
private float mBadgeScale;
|
|
||||||
|
|
||||||
private static final Property<FastBitmapDrawable, Float> BADGE_SCALE_PROPERTY
|
|
||||||
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "badgeScale") {
|
|
||||||
@Override
|
|
||||||
public Float get(FastBitmapDrawable fastBitmapDrawable) {
|
|
||||||
return fastBitmapDrawable.mBadgeScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
|
|
||||||
fastBitmapDrawable.mBadgeScale = value;
|
|
||||||
fastBitmapDrawable.invalidateSelf();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
|
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
|
||||||
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
|
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
|
||||||
@@ -124,30 +105,9 @@ public class FastBitmapDrawable extends Drawable {
|
|||||||
setFilterBitmap(true);
|
setFilterBitmap(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyIconBadge(final BadgeInfo badgeInfo, BadgeRenderer badgeRenderer,
|
|
||||||
boolean animate) {
|
|
||||||
boolean wasBadged = mBadgeInfo != null;
|
|
||||||
boolean isBadged = badgeInfo != null;
|
|
||||||
float newBadgeScale = isBadged ? 1f : 0;
|
|
||||||
mBadgeInfo = badgeInfo;
|
|
||||||
mBadgeRenderer = badgeRenderer;
|
|
||||||
if (wasBadged || isBadged) {
|
|
||||||
mIconPalette = getIconPalette();
|
|
||||||
// Animate when a badge is first added or when it is removed.
|
|
||||||
if (animate && (wasBadged ^ isBadged) && isVisible()) {
|
|
||||||
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
|
|
||||||
} else {
|
|
||||||
mBadgeScale = newBadgeScale;
|
|
||||||
invalidateSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
drawInternal(canvas);
|
drawInternal(canvas);
|
||||||
// Draw the icon badge in the top right corner.
|
|
||||||
drawBadgeIfNecessary(canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawWithBrightness(Canvas canvas, float brightness) {
|
public void drawWithBrightness(Canvas canvas, float brightness) {
|
||||||
@@ -161,12 +121,6 @@ public class FastBitmapDrawable extends Drawable {
|
|||||||
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
|
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void drawBadgeIfNecessary(Canvas canvas) {
|
|
||||||
if (hasBadge()) {
|
|
||||||
mBadgeRenderer.draw(canvas, mIconPalette, mBadgeInfo, getBounds(), mBadgeScale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IconPalette getIconPalette() {
|
public IconPalette getIconPalette() {
|
||||||
if (mIconPalette == null) {
|
if (mIconPalette == null) {
|
||||||
mIconPalette = IconPalette.fromDominantColor(Utilities
|
mIconPalette = IconPalette.fromDominantColor(Utilities
|
||||||
@@ -175,10 +129,6 @@ public class FastBitmapDrawable extends Drawable {
|
|||||||
return mIconPalette;
|
return mIconPalette;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasBadge() {
|
|
||||||
return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setColorFilter(ColorFilter cf) {
|
public void setColorFilter(ColorFilter cf) {
|
||||||
// No op
|
// No op
|
||||||
|
|||||||
@@ -2247,16 +2247,12 @@ public class Workspace extends PagedView
|
|||||||
Point dragVisualizeOffset = null;
|
Point dragVisualizeOffset = null;
|
||||||
Rect dragRect = null;
|
Rect dragRect = null;
|
||||||
if (child instanceof BubbleTextView) {
|
if (child instanceof BubbleTextView) {
|
||||||
int iconSize = grid.iconSizePx;
|
dragRect = new Rect();
|
||||||
int top = child.getPaddingTop();
|
((BubbleTextView) child).getIconBounds(dragRect);
|
||||||
int left = (b.getWidth() - iconSize) / 2;
|
dragLayerY += dragRect.top;
|
||||||
int right = left + iconSize;
|
// Note: The dragRect is used to calculate drag layer offsets, but the
|
||||||
int bottom = top + iconSize;
|
|
||||||
dragLayerY += top;
|
|
||||||
// Note: The drag region is used to calculate drag layer offsets, but the
|
|
||||||
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
|
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
|
||||||
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
|
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
|
||||||
dragRect = new Rect(left, top, right, bottom);
|
|
||||||
} else if (child instanceof FolderIcon) {
|
} else if (child instanceof FolderIcon) {
|
||||||
int previewSize = grid.folderIconSizePx;
|
int previewSize = grid.folderIconSizePx;
|
||||||
dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
|
dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.Shader;
|
import android.graphics.Shader;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.graphics.IconPalette;
|
import com.android.launcher3.graphics.IconPalette;
|
||||||
@@ -36,31 +38,41 @@ import com.android.launcher3.graphics.ShadowGenerator;
|
|||||||
*/
|
*/
|
||||||
public class BadgeRenderer {
|
public class BadgeRenderer {
|
||||||
|
|
||||||
|
// The badge sizes are defined as percentages of the app icon size.
|
||||||
|
private static final float SIZE_PERCENTAGE = 0.38f;
|
||||||
|
// Used to expand the width of the badge for each additional digit.
|
||||||
|
private static final float CHAR_SIZE_PERCENTAGE = 0.12f;
|
||||||
|
private static final float TEXT_SIZE_PERCENTAGE = 0.26f;
|
||||||
|
private static final float OFFSET_PERCENTAGE = 0.02f;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final int mSize;
|
private final int mSize;
|
||||||
|
private final int mCharSize;
|
||||||
private final int mTextHeight;
|
private final int mTextHeight;
|
||||||
|
private final int mOffset;
|
||||||
private final IconDrawer mLargeIconDrawer;
|
private final IconDrawer mLargeIconDrawer;
|
||||||
private final IconDrawer mSmallIconDrawer;
|
private final IconDrawer mSmallIconDrawer;
|
||||||
private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
|
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
|
||||||
| Paint.FILTER_BITMAP_FLAG);
|
| Paint.FILTER_BITMAP_FLAG);
|
||||||
private final Bitmap mBackgroundWithShadow;
|
private final SparseArray<Bitmap> mBackgroundsWithShadow;
|
||||||
|
|
||||||
public BadgeRenderer(final Context context) {
|
public BadgeRenderer(Context context, int iconSizePx) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
mSize = res.getDimensionPixelSize(R.dimen.badge_size);
|
mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
|
||||||
|
mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
|
||||||
|
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
|
||||||
|
mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
|
||||||
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
|
mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
|
||||||
mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
|
mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
|
||||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.badge_text_size));
|
|
||||||
mTextPaint.setFakeBoldText(true);
|
|
||||||
// Measure the text height.
|
// Measure the text height.
|
||||||
Rect tempTextHeight = new Rect();
|
Rect tempTextHeight = new Rect();
|
||||||
mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
|
mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
|
||||||
mTextHeight = tempTextHeight.height();
|
mTextHeight = tempTextHeight.height();
|
||||||
|
|
||||||
mBackgroundWithShadow = ShadowGenerator.createCircleWithShadow(Color.WHITE, mSize);
|
mBackgroundsWithShadow = new SparseArray<>(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,29 +82,43 @@ public class BadgeRenderer {
|
|||||||
* @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
|
* @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
|
||||||
* @param iconBounds The bounds of the icon being badged.
|
* @param iconBounds The bounds of the icon being badged.
|
||||||
* @param badgeScale The progress of the animation, from 0 to 1.
|
* @param badgeScale The progress of the animation, from 0 to 1.
|
||||||
|
* @param spaceForOffset How much space is available to offset the badge up and to the right.
|
||||||
*/
|
*/
|
||||||
public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
|
public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
|
||||||
Rect iconBounds, float badgeScale) {
|
Rect iconBounds, float badgeScale, Point spaceForOffset) {
|
||||||
mTextPaint.setColor(palette.textColor);
|
mTextPaint.setColor(palette.textColor);
|
||||||
canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
|
||||||
// We draw the badge relative to its center.
|
|
||||||
canvas.translate(iconBounds.right - mSize / 2, iconBounds.top + mSize / 2);
|
|
||||||
canvas.scale(badgeScale, badgeScale);
|
|
||||||
mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
|
|
||||||
int backgroundSize = mBackgroundWithShadow.getHeight(); // Same as width.
|
|
||||||
canvas.drawBitmap(mBackgroundWithShadow, -backgroundSize / 2, -backgroundSize / 2,
|
|
||||||
mBackgroundPaint);
|
|
||||||
IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
|
IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
|
||||||
? mLargeIconDrawer : mSmallIconDrawer;
|
? mLargeIconDrawer : mSmallIconDrawer;
|
||||||
Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
|
Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
|
||||||
mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
|
mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
|
||||||
|
String notificationCount = icon != null || badgeInfo == null ? "0"
|
||||||
|
: String.valueOf(badgeInfo.getNotificationCount());
|
||||||
|
int numChars = notificationCount.length();
|
||||||
|
int width = mSize + mCharSize * (numChars - 1);
|
||||||
|
// Lazily load the background with shadow.
|
||||||
|
Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
|
||||||
|
if (backgroundWithShadow == null) {
|
||||||
|
backgroundWithShadow = ShadowGenerator.createPillWithShadow(Color.WHITE, width, mSize);
|
||||||
|
mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
|
||||||
|
}
|
||||||
|
canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
||||||
|
// We draw the badge relative to its center.
|
||||||
|
int badgeCenterX = iconBounds.right - width / 2;
|
||||||
|
int badgeCenterY = iconBounds.top + mSize / 2;
|
||||||
|
int offsetX = Math.min(mOffset, spaceForOffset.x);
|
||||||
|
int offsetY = Math.min(mOffset, spaceForOffset.y);
|
||||||
|
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
|
||||||
|
canvas.scale(badgeScale, badgeScale);
|
||||||
|
// Draw the background and shadow.
|
||||||
|
mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
|
||||||
|
int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
|
||||||
|
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||||
|
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
// Draw the notification icon with padding.
|
// Draw the notification icon with padding.
|
||||||
iconDrawer.drawIcon(icon, canvas);
|
iconDrawer.drawIcon(icon, canvas);
|
||||||
} else {
|
} else {
|
||||||
// Draw the notification count.
|
// Draw the notification count.
|
||||||
String notificationCount = badgeInfo == null ? "0"
|
|
||||||
: String.valueOf(badgeInfo.getNotificationCount());
|
|
||||||
canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
|
canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
|
||||||
}
|
}
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.graphics.Color;
|
|||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffXfermode;
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.graphics.RadialGradient;
|
import android.graphics.RadialGradient;
|
||||||
@@ -121,7 +122,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||||||
private PreviewLayoutRule mPreviewLayoutRule;
|
private PreviewLayoutRule mPreviewLayoutRule;
|
||||||
|
|
||||||
boolean mAnimating = false;
|
boolean mAnimating = false;
|
||||||
private Rect mOldBounds = new Rect();
|
private Rect mTempBounds = new Rect();
|
||||||
|
|
||||||
private float mSlop;
|
private float mSlop;
|
||||||
|
|
||||||
@@ -134,6 +135,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||||||
private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
|
private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
|
||||||
private BadgeRenderer mBadgeRenderer;
|
private BadgeRenderer mBadgeRenderer;
|
||||||
private float mBadgeScale;
|
private float mBadgeScale;
|
||||||
|
private Point mTempSpaceForBadgeOffset = new Point();
|
||||||
|
|
||||||
private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
|
private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
|
||||||
= new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
|
= new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
|
||||||
@@ -496,7 +498,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||||||
Drawable d = params.drawable;
|
Drawable d = params.drawable;
|
||||||
|
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
mOldBounds.set(d.getBounds());
|
mTempBounds.set(d.getBounds());
|
||||||
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
|
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
|
||||||
if (d instanceof FastBitmapDrawable) {
|
if (d instanceof FastBitmapDrawable) {
|
||||||
FastBitmapDrawable fd = (FastBitmapDrawable) d;
|
FastBitmapDrawable fd = (FastBitmapDrawable) d;
|
||||||
@@ -507,7 +509,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||||||
d.draw(canvas);
|
d.draw(canvas);
|
||||||
d.clearColorFilter();
|
d.clearColorFilter();
|
||||||
}
|
}
|
||||||
d.setBounds(mOldBounds);
|
d.setBounds(mTempBounds);
|
||||||
}
|
}
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
@@ -878,14 +880,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
|
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 offsetX = mBackground.getOffsetX();
|
||||||
int offsetY = mBackground.getOffsetY();
|
int offsetY = mBackground.getOffsetY();
|
||||||
int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
|
int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
|
||||||
Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
|
mTempBounds.set(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
|
||||||
|
|
||||||
|
// If we are animating to the accepting state, animate the badge out.
|
||||||
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
|
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
|
||||||
mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
|
mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
|
||||||
|
mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, mTempBounds,
|
||||||
|
badgeScale, mTempSpaceForBadgeOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,12 +173,12 @@ public class IconPalette {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getMutedColor(int color) {
|
private static int getMutedColor(int color) {
|
||||||
int alpha = (int) (255 * 0.15f);
|
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.87f));
|
||||||
return ColorUtils.compositeColors(ColorUtils.setAlphaComponent(color, alpha), Color.WHITE);
|
return ColorUtils.compositeColors(whiteScrim, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getTextColorForBackground(int backgroundColor) {
|
private static int getTextColorForBackground(int backgroundColor) {
|
||||||
return getLighterOrDarkerVersionOfColor(backgroundColor, 3f);
|
return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getLowContrastColor(int color) {
|
private static int getLowContrastColor(int color) {
|
||||||
|
|||||||
@@ -83,34 +83,44 @@ public class ShadowGenerator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap createCircleWithShadow(int circleColor, int diameter) {
|
public static Bitmap createPillWithShadow(int rectColor, int width, int height) {
|
||||||
|
|
||||||
float shadowRadius = diameter * 1f / 32;
|
float shadowRadius = height * 1f / 32;
|
||||||
float shadowYOffset = diameter * 1f / 16;
|
float shadowYOffset = height * 1f / 16;
|
||||||
|
int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA / 2;
|
||||||
|
int keyShadowAlpha = KEY_SHADOW_ALPHA / 2;
|
||||||
|
|
||||||
int radius = diameter / 2;
|
int radius = height / 2;
|
||||||
|
|
||||||
Canvas canvas = new Canvas();
|
Canvas canvas = new Canvas();
|
||||||
Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||||
blurPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, Blur.NORMAL));
|
blurPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, Blur.NORMAL));
|
||||||
|
|
||||||
int center = Math.round(radius + shadowRadius + shadowYOffset);
|
int centerX = Math.round(width / 2 + shadowRadius);
|
||||||
|
int centerY = Math.round(radius + shadowRadius + shadowYOffset);
|
||||||
|
int center = Math.max(centerX, centerY);
|
||||||
int size = center * 2;
|
int size = center * 2;
|
||||||
Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
|
Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
|
||||||
canvas.setBitmap(result);
|
canvas.setBitmap(result);
|
||||||
|
|
||||||
|
int left = center - width / 2;
|
||||||
|
int top = center - height / 2;
|
||||||
|
int right = center + width / 2;
|
||||||
|
int bottom = center + height / 2;
|
||||||
|
|
||||||
// Draw ambient shadow, center aligned within size
|
// Draw ambient shadow, center aligned within size
|
||||||
blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
|
blurPaint.setAlpha(ambientShadowAlpha);
|
||||||
canvas.drawCircle(center, center, radius, blurPaint);
|
canvas.drawRoundRect(left, top, right, bottom, radius, radius, blurPaint);
|
||||||
|
|
||||||
// Draw key shadow, bottom aligned within size
|
// Draw key shadow, bottom aligned within size
|
||||||
blurPaint.setAlpha(KEY_SHADOW_ALPHA);
|
blurPaint.setAlpha(keyShadowAlpha);
|
||||||
canvas.drawCircle(center, center + shadowYOffset, radius, blurPaint);
|
canvas.drawRoundRect(left, top + shadowYOffset, right, bottom + shadowYOffset,
|
||||||
|
radius, radius, blurPaint);
|
||||||
|
|
||||||
// Draw the circle
|
// Draw the circle
|
||||||
Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||||
drawPaint.setColor(circleColor);
|
drawPaint.setColor(rectColor);
|
||||||
canvas.drawCircle(center, center, radius, drawPaint);
|
canvas.drawRoundRect(left, top, right, bottom, radius, radius, drawPaint);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
|||||||
animateOpen();
|
animateOpen();
|
||||||
|
|
||||||
mOriginalIcon = originalIcon;
|
mOriginalIcon = originalIcon;
|
||||||
|
|
||||||
mLauncher.getDragController().addDragListener(this);
|
mLauncher.getDragController().addDragListener(this);
|
||||||
|
mOriginalIcon.forceHideBadge(true);
|
||||||
|
|
||||||
// Load the shortcuts on a background thread and update the container as it animates.
|
// Load the shortcuts on a background thread and update the container as it animates.
|
||||||
final Looper workerLooper = LauncherModel.getWorkerLooper();
|
final Looper workerLooper = LauncherModel.getWorkerLooper();
|
||||||
@@ -799,6 +799,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
|||||||
});
|
});
|
||||||
mOpenCloseAnimator = shortcutAnims;
|
mOpenCloseAnimator = shortcutAnims;
|
||||||
shortcutAnims.start();
|
shortcutAnims.start();
|
||||||
|
mOriginalIcon.forceHideBadge(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -814,6 +815,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
|||||||
boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
|
boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
|
||||||
== LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
== LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||||
mOriginalIcon.setTextVisibility(!isInHotseat);
|
mOriginalIcon.setTextVisibility(!isInHotseat);
|
||||||
|
mOriginalIcon.forceHideBadge(false);
|
||||||
mLauncher.getDragController().removeDragListener(this);
|
mLauncher.getDragController().removeDragListener(this);
|
||||||
mLauncher.getDragLayer().removeView(this);
|
mLauncher.getDragLayer().removeView(this);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user