Merge "Only apply legacy treatment on app icons that is targeting O" into ub-launcher3-dorval
diff --git a/res/animator/overview_button_anim.xml b/res/animator/overview_button_anim.xml
index aac3d26..585f165 100644
--- a/res/animator/overview_button_anim.xml
+++ b/res/animator/overview_button_anim.xml
@@ -24,13 +24,6 @@
             android:valueType="floatType" />
     </item>
 
-    <item android:state_focused="true">
-        <objectAnimator
-            android:duration="@android:integer/config_shortAnimTime"
-            android:propertyName="alpha"
-            android:valueTo="0.5"
-            android:valueType="floatType" />
-    </item>
     <item>
         <objectAnimator
             android:duration="@android:integer/config_shortAnimTime"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 31fae4f..a6f8bf4 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -25,7 +25,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"未安装该应用。"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"应用不可用"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"安全模式下不允许使用下载的此应用"</string>
-    <string name="safemode_widget_error" msgid="4863470563535682004">"安全模式下不允许使用小部件"</string>
+    <string name="safemode_widget_error" msgid="4863470563535682004">"安全模式下不允许使用微件"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"无法使用快捷方式"</string>
     <string name="home_screen" msgid="806512411299847073">"主屏幕"</string>
     <string name="custom_actions" msgid="3747508247759093328">"自定义操作"</string>
@@ -68,7 +68,7 @@
     <string name="folder_closed" msgid="4100806530910930934">"文件夹已关闭"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"小部件"</string>
+    <string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"设置"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 83c58da..ee92cf1 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -30,7 +30,7 @@
     <string name="home_screen" msgid="806512411299847073">"主螢幕"</string>
     <string name="custom_actions" msgid="3747508247759093328">"自訂動作"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,您也可以使用自訂動作。"</string>
+    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,你也可以使用自訂動作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"按住即可手動放置"</string>
@@ -71,7 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"總覽"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
@@ -88,7 +88,7 @@
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"你尚未安裝這個圖示代表的應用程式。你可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下載「<xliff:g id="NAME">%1$s</xliff:g>」,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"「<xliff:g id="NAME">%1$s</xliff:g>」小工具"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index dd8c313..ca74f15 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -182,8 +182,6 @@
     <dimen name="system_shortcut_header_icon_padding">12dp</dimen>
 
 <!-- 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_large_padding">3dp</dimen>
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index bad7018..cb40d3d 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -23,9 +24,12 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Property;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -42,9 +46,9 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
+import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.text.NumberFormat;
 
@@ -90,6 +94,28 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     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")
     private boolean mStayPressed;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -369,6 +395,7 @@
     public void draw(Canvas canvas) {
         if (!mCustomShadowsEnabled) {
             super.draw(canvas);
+            drawBadgeIfNecessary(canvas);
             return;
         }
 
@@ -395,6 +422,7 @@
         if ((getCurrentTextColor() >> 24) == 0) {
             getPaint().clearShadowLayer();
             super.draw(canvas);
+            drawBadgeIfNecessary(canvas);
             return;
         }
 
@@ -410,6 +438,50 @@
                 density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
         super.draw(canvas);
         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;
+    }
+
+    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
@@ -500,13 +572,21 @@
 
     public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
         if (mIcon instanceof FastBitmapDrawable) {
-            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
-            BadgeRenderer badgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
-            PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(mLauncher);
-            if (popup != null) {
-                popup.updateNotificationHeader(badgeInfo, itemInfo);
+            boolean wasBadged = mBadgeInfo != null;
+            mBadgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
+            boolean isBadged = mBadgeInfo != null;
+            float newBadgeScale = isBadged ? 1f : 0;
+            mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
+            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();
+                }
             }
-            ((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer, animate);
         }
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 43f7d23..e47031a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -197,8 +197,6 @@
         hotseatBarBottomPaddingPx = 0;
         hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
 
-        mBadgeRenderer = new BadgeRenderer(context);
-
         // Determine sizes.
         widthPx = width;
         heightPx = height;
@@ -213,6 +211,9 @@
         // Calculate the remaining vars
         updateAvailableDimensions(dm, res);
         computeAllAppsButtonSize(context);
+
+        // This is done last, after iconSizePx is calculated above.
+        mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
     }
 
     DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index be3ba90..a096a1d 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -32,8 +32,6 @@
 import android.util.Property;
 import android.util.SparseArray;
 
-import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.graphics.IconPalette;
 
 public class FastBitmapDrawable extends Drawable {
@@ -77,24 +75,7 @@
     private boolean mIsPressed;
     private boolean mIsDisabled;
 
-    private BadgeInfo mBadgeInfo;
-    private BadgeRenderer mBadgeRenderer;
     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
             = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
@@ -124,30 +105,9 @@
         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
     public void draw(Canvas canvas) {
         drawInternal(canvas);
-        // Draw the icon badge in the top right corner.
-        drawBadgeIfNecessary(canvas);
     }
 
     public void drawWithBrightness(Canvas canvas, float brightness) {
@@ -161,12 +121,6 @@
         canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
     }
 
-    protected void drawBadgeIfNecessary(Canvas canvas) {
-        if (hasBadge()) {
-            mBadgeRenderer.draw(canvas, mIconPalette, mBadgeInfo, getBounds(), mBadgeScale);
-        }
-    }
-
     public IconPalette getIconPalette() {
         if (mIconPalette == null) {
             mIconPalette = IconPalette.fromDominantColor(Utilities
@@ -175,10 +129,6 @@
         return mIconPalette;
     }
 
-    private boolean hasBadge() {
-        return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0;
-    }
-
     @Override
     public void setColorFilter(ColorFilter cf) {
         // No op
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index dbf535a..3c29f5e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1576,6 +1576,11 @@
             public void run() {
                 mWorkspace.updateIconBadges(updatedBadges);
                 mAppsView.updateIconBadges(updatedBadges);
+
+                PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
+                if (popup != null) {
+                    popup.updateNotificationHeader(updatedBadges);
+                }
             }
         };
         if (!waitUntilResume(r)) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7562dd8..48a62d9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2247,16 +2247,12 @@
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
-            int iconSize = grid.iconSizePx;
-            int top = child.getPaddingTop();
-            int left = (b.getWidth() - iconSize) / 2;
-            int right = left + iconSize;
-            int bottom = top + iconSize;
-            dragLayerY += top;
-            // Note: The drag region is used to calculate drag layer offsets, but the
+            dragRect = new Rect();
+            ((BubbleTextView) child).getIconBounds(dragRect);
+            dragLayerY += dragRect.top;
+            // Note: The dragRect is used to calculate drag layer offsets, but the
             // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
             dragVisualizeOffset = new Point(- halfPadding, halfPadding);
-            dragRect = new Rect(left, top, right, bottom);
         } else if (child instanceof FolderIcon) {
             int previewSize = grid.folderIconSizePx;
             dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index cc5fa8c..2c7d156 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
@@ -485,11 +486,15 @@
 
     public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
         final PackageUserKey packageUserKey = new PackageUserKey(null, null);
-        for (AlphabeticalAppsList.AdapterItem app : mApps.getAdapterItems()) {
-            if (app.appInfo != null && packageUserKey.updateFromItemInfo(app.appInfo)) {
-                if (updatedBadges.contains(packageUserKey)) {
-                    mAdapter.notifyItemChanged(app.position);
-                }
+        final int n = mAppsRecyclerView.getChildCount();
+        for (int i = 0; i < n; i++) {
+            View child = mAppsRecyclerView.getChildAt(i);
+            if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
+                continue;
+            }
+            ItemInfo info = (ItemInfo) child.getTag();
+            if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
+                ((BubbleTextView) child).applyBadgeState(info, true /* animate */);
             }
         }
     }
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 5896928..2971ceb 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -22,9 +22,11 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.support.annotation.Nullable;
+import android.util.SparseArray;
 
 import com.android.launcher3.R;
 import com.android.launcher3.graphics.IconPalette;
@@ -36,31 +38,48 @@
  */
 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 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);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_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;
         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);
+        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));
         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.
         Rect tempTextHeight = new Rect();
         mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
         mTextHeight = tempTextHeight.height();
 
-        mBackgroundWithShadow = ShadowGenerator.createCircleWithShadow(Color.WHITE, mSize);
+        mBackgroundsWithShadow = new SparseArray<>(3);
     }
 
     /**
@@ -70,30 +89,65 @@
      * @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 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,
-            Rect iconBounds, float badgeScale) {
+            Rect iconBounds, float badgeScale, Point spaceForOffset) {
         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()
                 ? mLargeIconDrawer : mSmallIconDrawer;
         Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
                 mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
-        if (icon != null) {
-            // Draw the notification icon with padding.
-            iconDrawer.drawIcon(icon, canvas);
-        } else {
-            // Draw the notification count.
-            String notificationCount = badgeInfo == null ? "0"
-                    : String.valueOf(badgeInfo.getNotificationCount());
+        String notificationCount = 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;
+        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);
+        // Prepare the background and shadow and possible stacking effect.
+        mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
+        int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
+        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();
     }
diff --git a/src/com/android/launcher3/badge/FolderBadgeInfo.java b/src/com/android/launcher3/badge/FolderBadgeInfo.java
index f7c64aa..3a1bf60 100644
--- a/src/com/android/launcher3/badge/FolderBadgeInfo.java
+++ b/src/com/android/launcher3/badge/FolderBadgeInfo.java
@@ -19,14 +19,14 @@
 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 @@
         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;
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f216010..25123fb 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -27,6 +27,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.RadialGradient;
@@ -121,7 +122,7 @@
     private PreviewLayoutRule mPreviewLayoutRule;
 
     boolean mAnimating = false;
-    private Rect mOldBounds = new Rect();
+    private Rect mTempBounds = new Rect();
 
     private float mSlop;
 
@@ -134,6 +135,7 @@
     private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
     private BadgeRenderer mBadgeRenderer;
     private float mBadgeScale;
+    private Point mTempSpaceForBadgeOffset = new Point();
 
     private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
             = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
@@ -401,17 +403,15 @@
     }
 
     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()) {
@@ -496,7 +496,7 @@
         Drawable d = params.drawable;
 
         if (d != null) {
-            mOldBounds.set(d.getBounds());
+            mTempBounds.set(d.getBounds());
             d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
             if (d instanceof FastBitmapDrawable) {
                 FastBitmapDrawable fd = (FastBitmapDrawable) d;
@@ -507,7 +507,7 @@
                 d.draw(canvas);
                 d.clearColorFilter();
             }
-            d.setBounds(mOldBounds);
+            d.setBounds(mTempBounds);
         }
         canvas.restore();
     }
@@ -877,15 +877,17 @@
             mBackground.drawBackgroundStroke(canvas);
         }
 
-        if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
-            // If we are animating to the accepting state, animate the badge out.
+        if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
             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);
+            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());
-            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);
         }
     }
 
@@ -1042,20 +1044,20 @@
 
     @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();
     }
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index cd7cf70..1212cba 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -43,6 +43,7 @@
     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 @@
         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,12 +177,16 @@
     }
 
     private static int getMutedColor(int color) {
-        int alpha = (int) (255 * 0.15f);
-        return ColorUtils.compositeColors(ColorUtils.setAlphaComponent(color, alpha), Color.WHITE);
+        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);
     }
 
     private static int getTextColorForBackground(int backgroundColor) {
-        return getLighterOrDarkerVersionOfColor(backgroundColor, 3f);
+        return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
     }
 
     private static int getLowContrastColor(int color) {
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 6c603c9..5dd8d20 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -83,34 +83,44 @@
         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 shadowYOffset = diameter * 1f / 16;
+        float shadowRadius = height * 1f / 32;
+        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();
         Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         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;
         Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
         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
-        blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
-        canvas.drawCircle(center, center, radius, blurPaint);
+        blurPaint.setAlpha(ambientShadowAlpha);
+        canvas.drawRoundRect(left, top, right, bottom, radius, radius, blurPaint);
 
         // Draw key shadow, bottom aligned within size
-        blurPaint.setAlpha(KEY_SHADOW_ALPHA);
-        canvas.drawCircle(center, center + shadowYOffset, radius, blurPaint);
+        blurPaint.setAlpha(keyShadowAlpha);
+        canvas.drawRoundRect(left, top + shadowYOffset, right, bottom + shadowYOffset,
+                radius, radius, blurPaint);
 
         // Draw the circle
         Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        drawPaint.setColor(circleColor);
-        canvas.drawCircle(center, center, radius, drawPaint);
+        drawPaint.setColor(rectColor);
+        canvas.drawRoundRect(left, top, right, bottom, radius, radius, drawPaint);
 
         return result;
     }
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index bf7ae1a..154ea21 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -37,7 +37,7 @@
     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) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index a4823ae..deab3d9 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
@@ -172,6 +173,8 @@
         final int arrowVerticalOffset = resources.getDimensionPixelSize(
                 R.dimen.popup_arrow_vertical_offset);
 
+        mOriginalIcon = originalIcon;
+
         // Add dummy views first, and populate with real info when ready.
         PopupPopulator.Item[] itemsToPopulate = PopupPopulator
                 .getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
@@ -200,9 +203,7 @@
                 ? Collections.EMPTY_LIST
                 : mShortcutsItemView.getSystemShortcutViews(reverseOrder);
         if (mNotificationItemView != null) {
-            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider()
-                    .getBadgeInfoForItem(originalItemInfo);
-            updateNotificationHeader(badgeInfo, originalIcon);
+            updateNotificationHeader();
         }
 
         // Add the arrow.
@@ -212,9 +213,8 @@
 
         animateOpen();
 
-        mOriginalIcon = originalIcon;
-
         mLauncher.getDragController().addDragListener(this);
+        mOriginalIcon.forceHideBadge(true);
 
         // Load the shortcuts on a background thread and update the container as it animates.
         final Looper workerLooper = LauncherModel.getWorkerLooper();
@@ -551,21 +551,27 @@
     }
 
     /**
-     * Updates the notification header to reflect the badge info. Since this can be called
-     * for any badge info (not necessarily the one associated with this app), we first
-     * check that the ItemInfo matches the one of this popup.
+     * Updates the notification header if the original icon's badge updated.
      */
-    public void updateNotificationHeader(BadgeInfo badgeInfo, ItemInfo originalItemInfo) {
-        if (originalItemInfo != mOriginalIcon.getTag()) {
-            return;
+    public void updateNotificationHeader(Set<PackageUserKey> updatedBadges) {
+        ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
+        PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
+        if (updatedBadges.contains(packageUser)) {
+            updateNotificationHeader();
         }
-        updateNotificationHeader(badgeInfo, mOriginalIcon);
     }
 
-    private void updateNotificationHeader(BadgeInfo badgeInfo, BubbleTextView originalIcon) {
+    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) {
-            IconPalette palette = originalIcon.getIcon() instanceof FastBitmapDrawable
-                    ? ((FastBitmapDrawable) originalIcon.getIcon()).getIconPalette()
+            IconPalette palette = mOriginalIcon.getIcon() instanceof FastBitmapDrawable
+                    ? ((FastBitmapDrawable) mOriginalIcon.getIcon()).getIconPalette()
                     : null;
             mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
         }
@@ -577,7 +583,7 @@
         }
         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);
@@ -799,6 +805,7 @@
         });
         mOpenCloseAnimator = shortcutAnims;
         shortcutAnims.start();
+        mOriginalIcon.forceHideBadge(false);
     }
 
     /**
@@ -814,6 +821,7 @@
         boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
                 == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
         mOriginalIcon.setTextVisibility(!isInHotseat);
+        mOriginalIcon.forceHideBadge(false);
         mLauncher.getDragController().removeDragListener(this);
         mLauncher.getDragLayer().removeView(this);
     }
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index eaceaa9..de9f25e 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -84,7 +84,7 @@
             badgeShouldBeRefreshed = shouldBeFilteredOut
                     ? badgeInfo.removeNotificationKey(notificationKey)
                     : badgeInfo.addOrUpdateNotificationKey(notificationKey);
-            if (badgeInfo.getNotificationCount() == 0) {
+            if (badgeInfo.getNotificationKeys().size() == 0) {
                 mPackageUserToBadgeInfos.remove(postedPackageUserKey);
             }
         }
@@ -97,7 +97,7 @@
             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 @@
         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;
+                    }
                 }
             }
         }