mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-01 08:16:49 +00:00
Support notifications with 0 count (show as dots)
- Show number if number > 0 - Show icon if number == 0 and a notification specified an icon to show - Show a dot otherwise - In cases of multiple notifications, stack a second badge behind the first (visuals will be updated in future CL, as well as support stacked dots) - Folders always show dot if any app within has a badge. Change-Id: I0a89059b0e0a0d174fe739c9da4f75fa18c0edfa
This commit is contained in:
@@ -473,7 +473,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
|
||||
}
|
||||
|
||||
private boolean hasBadge() {
|
||||
return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0);
|
||||
return mBadgeInfo != null;
|
||||
}
|
||||
|
||||
public void getIconBounds(Rect outBounds) {
|
||||
|
||||
@@ -44,12 +44,17 @@ public class BadgeRenderer {
|
||||
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 static final float STACK_OFFSET_PERCENTAGE_X = 0.05f;
|
||||
private static final float STACK_OFFSET_PERCENTAGE_Y = 0.06f;
|
||||
private static final float DOT_SCALE = 0.6f;
|
||||
|
||||
private final Context mContext;
|
||||
private final int mSize;
|
||||
private final int mCharSize;
|
||||
private final int mTextHeight;
|
||||
private final int mOffset;
|
||||
private final int mStackOffsetX;
|
||||
private final int mStackOffsetY;
|
||||
private final IconDrawer mLargeIconDrawer;
|
||||
private final IconDrawer mSmallIconDrawer;
|
||||
private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
@@ -63,6 +68,8 @@ public class BadgeRenderer {
|
||||
mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
|
||||
mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
|
||||
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
|
||||
mStackOffsetX = (int) (STACK_OFFSET_PERCENTAGE_X * iconSizePx);
|
||||
mStackOffsetY = (int) (STACK_OFFSET_PERCENTAGE_Y * iconSizePx);
|
||||
mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
|
||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||
mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
|
||||
@@ -91,7 +98,7 @@ public class BadgeRenderer {
|
||||
? mLargeIconDrawer : mSmallIconDrawer;
|
||||
Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
|
||||
mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
|
||||
String notificationCount = icon != null || badgeInfo == null ? "0"
|
||||
String notificationCount = badgeInfo == null ? "0"
|
||||
: String.valueOf(badgeInfo.getNotificationCount());
|
||||
int numChars = notificationCount.length();
|
||||
int width = mSize + mCharSize * (numChars - 1);
|
||||
@@ -105,21 +112,42 @@ public class BadgeRenderer {
|
||||
// We draw the badge relative to its center.
|
||||
int badgeCenterX = iconBounds.right - width / 2;
|
||||
int badgeCenterY = iconBounds.top + mSize / 2;
|
||||
boolean isText = badgeInfo != null && badgeInfo.getNotificationCount() != 0;
|
||||
boolean isIcon = icon != null;
|
||||
boolean isDot = !(isText || isIcon);
|
||||
if (isDot) {
|
||||
badgeScale *= DOT_SCALE;
|
||||
}
|
||||
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.
|
||||
// Prepare the background and shadow and possible stacking effect.
|
||||
mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
|
||||
int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
|
||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||
if (icon != null) {
|
||||
// Draw the notification icon with padding.
|
||||
iconDrawer.drawIcon(icon, canvas);
|
||||
} else {
|
||||
// Draw the notification count.
|
||||
boolean shouldStack = !isDot && badgeInfo != null
|
||||
&& badgeInfo.getNotificationKeys().size() > 1;
|
||||
if (shouldStack) {
|
||||
int offsetDiffX = mStackOffsetX - mOffset;
|
||||
int offsetDiffY = mStackOffsetY - mOffset;
|
||||
canvas.translate(offsetDiffX, offsetDiffY);
|
||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||
canvas.translate(-offsetDiffX, -offsetDiffY);
|
||||
}
|
||||
|
||||
if (isText) {
|
||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||
canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
|
||||
} else if (isIcon) {
|
||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||
iconDrawer.drawIcon(icon, canvas);
|
||||
} else if (isDot) {
|
||||
mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
|
||||
canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
|
||||
-backgroundWithShadowSize / 2, mBackgroundPaint);
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ package com.android.launcher3.badge;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* Subclass of BadgeInfo that only contains the badge count,
|
||||
* which is the sum of all the Folder's items' counts.
|
||||
* Subclass of BadgeInfo that only contains the badge count, which is
|
||||
* the sum of all the Folder's items' notifications (each counts as 1).
|
||||
*/
|
||||
public class FolderBadgeInfo extends BadgeInfo {
|
||||
|
||||
private static final int MIN_COUNT = 0;
|
||||
|
||||
private int mTotalNotificationCount;
|
||||
private int mNumNotifications;
|
||||
|
||||
public FolderBadgeInfo() {
|
||||
super(null);
|
||||
@@ -36,22 +36,27 @@ public class FolderBadgeInfo extends BadgeInfo {
|
||||
if (badgeToAdd == null) {
|
||||
return;
|
||||
}
|
||||
mTotalNotificationCount += badgeToAdd.getNotificationCount();
|
||||
mTotalNotificationCount = Utilities.boundToRange(
|
||||
mTotalNotificationCount, MIN_COUNT, BadgeInfo.MAX_COUNT);
|
||||
mNumNotifications += badgeToAdd.getNotificationKeys().size();
|
||||
mNumNotifications = Utilities.boundToRange(
|
||||
mNumNotifications, MIN_COUNT, BadgeInfo.MAX_COUNT);
|
||||
}
|
||||
|
||||
public void subtractBadgeInfo(BadgeInfo badgeToSubtract) {
|
||||
if (badgeToSubtract == null) {
|
||||
return;
|
||||
}
|
||||
mTotalNotificationCount -= badgeToSubtract.getNotificationCount();
|
||||
mTotalNotificationCount = Utilities.boundToRange(
|
||||
mTotalNotificationCount, MIN_COUNT, BadgeInfo.MAX_COUNT);
|
||||
mNumNotifications -= badgeToSubtract.getNotificationKeys().size();
|
||||
mNumNotifications = Utilities.boundToRange(
|
||||
mNumNotifications, MIN_COUNT, BadgeInfo.MAX_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNotificationCount() {
|
||||
return mTotalNotificationCount;
|
||||
// This forces the folder badge to always show up as a dot.
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean hasBadge() {
|
||||
return mNumNotifications > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,17 +403,15 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
}
|
||||
|
||||
public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
|
||||
updateBadgeScale(mBadgeInfo.getNotificationCount(), badgeInfo.getNotificationCount());
|
||||
updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
|
||||
mBadgeInfo = badgeInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mBadgeScale to 1 or 0, animating if oldCount or newCount is 0
|
||||
* Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
|
||||
* (the badge is being added or removed).
|
||||
*/
|
||||
private void updateBadgeScale(int oldCount, int newCount) {
|
||||
boolean wasBadged = oldCount > 0;
|
||||
boolean isBadged = newCount > 0;
|
||||
private void updateBadgeScale(boolean wasBadged, boolean isBadged) {
|
||||
float newBadgeScale = isBadged ? 1f : 0f;
|
||||
// Animate when a badge is first added or when it is removed.
|
||||
if ((wasBadged ^ isBadged) && isShown()) {
|
||||
@@ -879,7 +877,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
mBackground.drawBackgroundStroke(canvas);
|
||||
}
|
||||
|
||||
if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
|
||||
if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
|
||||
int offsetX = mBackground.getOffsetX();
|
||||
int offsetY = mBackground.getOffsetY();
|
||||
int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
|
||||
@@ -1046,20 +1044,20 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
|
||||
@Override
|
||||
public void onAdd(ShortcutInfo item) {
|
||||
int oldCount = mBadgeInfo.getNotificationCount();
|
||||
boolean wasBadged = mBadgeInfo.hasBadge();
|
||||
mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
|
||||
int newCount = mBadgeInfo.getNotificationCount();
|
||||
updateBadgeScale(oldCount, newCount);
|
||||
boolean isBadged = mBadgeInfo.hasBadge();
|
||||
updateBadgeScale(wasBadged, isBadged);
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(ShortcutInfo item) {
|
||||
int oldCount = mBadgeInfo.getNotificationCount();
|
||||
boolean wasBadged = mBadgeInfo.hasBadge();
|
||||
mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
|
||||
int newCount = mBadgeInfo.getNotificationCount();
|
||||
updateBadgeScale(oldCount, newCount);
|
||||
boolean isBadged = mBadgeInfo.hasBadge();
|
||||
updateBadgeScale(wasBadged, isBadged);
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public class IconPalette {
|
||||
public final int dominantColor;
|
||||
public final int backgroundColor;
|
||||
public final ColorMatrixColorFilter backgroundColorMatrixFilter;
|
||||
public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
|
||||
public final int textColor;
|
||||
public final int secondaryColor;
|
||||
|
||||
@@ -52,6 +53,9 @@ public class IconPalette {
|
||||
ColorMatrix backgroundColorMatrix = new ColorMatrix();
|
||||
Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
|
||||
backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
|
||||
// Get slightly more saturated background color.
|
||||
Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
|
||||
saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
|
||||
textColor = getTextColorForBackground(backgroundColor);
|
||||
secondaryColor = getLowContrastColor(backgroundColor);
|
||||
}
|
||||
@@ -173,7 +177,11 @@ public class IconPalette {
|
||||
}
|
||||
|
||||
private static int getMutedColor(int color) {
|
||||
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.87f));
|
||||
return getMutedColor(color, 0.87f);
|
||||
}
|
||||
|
||||
private static int getMutedColor(int color, float whiteScrimAlpha) {
|
||||
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
|
||||
return ColorUtils.compositeColors(whiteScrim, color);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class NotificationKeyData {
|
||||
private NotificationKeyData(String notificationKey, String shortcutId, int count) {
|
||||
this.notificationKey = notificationKey;
|
||||
this.shortcutId = shortcutId;
|
||||
this.count = Math.max(1, count);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public static NotificationKeyData fromNotification(StatusBarNotification sbn) {
|
||||
|
||||
@@ -562,6 +562,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
}
|
||||
|
||||
private void updateNotificationHeader() {
|
||||
if (true) {
|
||||
// For now, don't show any number in the popup.
|
||||
// TODO: determine whether a number makes sense, and if not, remove associated code.
|
||||
return;
|
||||
}
|
||||
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
|
||||
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
|
||||
if (mNotificationItemView != null && badgeInfo != null) {
|
||||
@@ -578,7 +583,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
|
||||
}
|
||||
ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
|
||||
BadgeInfo badgeInfo = updatedBadges.get(PackageUserKey.fromItemInfo(originalInfo));
|
||||
if (badgeInfo == null || badgeInfo.getNotificationCount() == 0) {
|
||||
if (badgeInfo == null || badgeInfo.getNotificationKeys().size() == 0) {
|
||||
AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
|
||||
final int duration = getResources().getInteger(
|
||||
R.integer.config_removeNotificationViewDuration);
|
||||
|
||||
@@ -84,7 +84,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
||||
badgeShouldBeRefreshed = shouldBeFilteredOut
|
||||
? badgeInfo.removeNotificationKey(notificationKey)
|
||||
: badgeInfo.addOrUpdateNotificationKey(notificationKey);
|
||||
if (badgeInfo.getNotificationCount() == 0) {
|
||||
if (badgeInfo.getNotificationKeys().size() == 0) {
|
||||
mPackageUserToBadgeInfos.remove(postedPackageUserKey);
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
||||
NotificationKeyData notificationKey) {
|
||||
BadgeInfo oldBadgeInfo = mPackageUserToBadgeInfos.get(removedPackageUserKey);
|
||||
if (oldBadgeInfo != null && oldBadgeInfo.removeNotificationKey(notificationKey)) {
|
||||
if (oldBadgeInfo.getNotificationCount() == 0) {
|
||||
if (oldBadgeInfo.getNotificationKeys().size() == 0) {
|
||||
mPackageUserToBadgeInfos.remove(removedPackageUserKey);
|
||||
}
|
||||
updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
|
||||
@@ -187,14 +187,21 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
|
||||
boolean hadNotificationToShow = badgeInfo.hasNotificationToShow();
|
||||
NotificationInfo notificationInfo = null;
|
||||
NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
|
||||
if (notificationListener != null && badgeInfo.getNotificationKeys().size() == 1) {
|
||||
String onlyNotificationKey = badgeInfo.getNotificationKeys().get(0).notificationKey;
|
||||
StatusBarNotification[] activeNotifications = notificationListener
|
||||
.getActiveNotifications(new String[] {onlyNotificationKey});
|
||||
if (activeNotifications.length == 1) {
|
||||
notificationInfo = new NotificationInfo(mLauncher, activeNotifications[0]);
|
||||
if (!notificationInfo.shouldShowIconInBadge()) {
|
||||
notificationInfo = null;
|
||||
if (notificationListener != null && badgeInfo.getNotificationKeys().size() >= 1) {
|
||||
// Look for the most recent notification that has an icon that should be shown in badge.
|
||||
for (NotificationKeyData notificationKeyData : badgeInfo.getNotificationKeys()) {
|
||||
String notificationKey = notificationKeyData.notificationKey;
|
||||
StatusBarNotification[] activeNotifications = notificationListener
|
||||
.getActiveNotifications(new String[]{notificationKey});
|
||||
if (activeNotifications.length == 1) {
|
||||
notificationInfo = new NotificationInfo(mLauncher, activeNotifications[0]);
|
||||
if (notificationInfo.shouldShowIconInBadge()) {
|
||||
// Found an appropriate icon.
|
||||
break;
|
||||
} else {
|
||||
// Keep looking.
|
||||
notificationInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user