OSDN Git Service

Support notifications with 0 count (show as dots)
authorTony Wickham <twickham@google.com>
Thu, 27 Apr 2017 01:13:56 +0000 (18:13 -0700)
committerTony <twickham@google.com>
Thu, 27 Apr 2017 02:39:05 +0000 (19:39 -0700)
- 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

src/com/android/launcher3/BubbleTextView.java
src/com/android/launcher3/badge/BadgeRenderer.java
src/com/android/launcher3/badge/FolderBadgeInfo.java
src/com/android/launcher3/folder/FolderIcon.java
src/com/android/launcher3/graphics/IconPalette.java
src/com/android/launcher3/notification/NotificationKeyData.java
src/com/android/launcher3/popup/PopupContainerWithArrow.java
src/com/android/launcher3/popup/PopupDataProvider.java

index 24d16d7..cb40d3d 100644 (file)
@@ -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) {
index a1ef22f..2971ceb 100644 (file)
@@ -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();
     }
index f7c64aa..3a1bf60 100644 (file)
@@ -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;
     }
 }
index c78925c..25123fb 100644 (file)
@@ -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();
     }
index c45f481..1212cba 100644 (file)
@@ -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);
     }
 
index bf7ae1a..154ea21 100644 (file)
@@ -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) {
index b00eb1f..deab3d9 100644 (file)
@@ -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);
index eaceaa9..de9f25e 100644 (file)
@@ -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;
+                    }
                 }
             }
         }