Merge "Creating a custom drawable to customize shadow." into ub-launcher3-dorval-polish
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index 8eb7e84..b820f59 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -46,7 +46,7 @@
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Галоўная"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Выдаліць"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Выдаліць"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі пра праграму"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі пра праграмы"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 05502cf..3db1aa8 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -33,7 +33,7 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dos veces y mantén pulsado el widget que quieras seleccionar o utiliza acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén pulsado el elemento para añadirlo manualmente"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantenlo pulsado para añadirlo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Añadir automáticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 85b9712..858abfd 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -29,7 +29,7 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"קיצור הדרך אינו זמין"</string>
     <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_press_widget_to_add" msgid="7699152356777458215">"אפשר לבחור את הווידג\'ט אם נוגעים בו נגיעה רציפה."</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>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 2162292..cfdcc18 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -70,7 +70,7 @@
     <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="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
-    <string name="settings_button_text" msgid="8119458837558863227">"Тууралоолор"</string>
+    <string name="settings_button_text" msgid="8119458837558863227">"Жөндөөлөр"</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>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index eb36a18..0c655fd 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -30,7 +30,7 @@
     <string name="home_screen" msgid="806512411299847073">"Ana ekran"</string>
     <string name="custom_actions" msgid="3747508247759093328">"Özel işlemler"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez hafifçe dokunun ve basılı tutun."</string>
+    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Manuel olarak yerleştirmek için dokunun ve basılı tutun"</string>
@@ -63,8 +63,8 @@
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
     <string name="folder_opened" msgid="94695026776264709">"Klasör açıldı, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Klasörü kapatmak için hafifçe dokunun"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için hafifçe dokunun"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Klasörü kapatmak için dokunun"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için dokunun"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Klasör kapatıldı"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 689e4f4..07236d6a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -477,7 +477,7 @@
     }
 
     private boolean hasBadge() {
-        return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0);
+        return mBadgeInfo != null;
     }
 
     public void getIconBounds(Rect outBounds) {
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 736dfeb..1217030 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -37,6 +37,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Process;
 import android.os.SystemClock;
@@ -194,7 +195,7 @@
 
     protected Bitmap makeDefaultIcon(UserHandle user) {
         Drawable unbadged = getFullResDefaultActivityIcon();
-        return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext);
+        return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, Build.VERSION_CODES.O);
     }
 
     /**
@@ -380,7 +381,7 @@
         if (entry == null) {
             entry = new CacheEntry();
             entry.icon = LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
-                    mContext);
+                    mContext,  app.getApplicationInfo().targetSdkVersion);
         }
         entry.title = app.getLabel();
         entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
@@ -437,10 +438,9 @@
      * Updates {@param application} only if a valid entry is found.
      */
     public synchronized void updateTitleAndIcon(AppInfo application) {
-        boolean usePackageIcon = application instanceof PromiseAppInfo;
         CacheEntry entry = cacheLocked(application.componentName,
                 Provider.<LauncherActivityInfo>of(null),
-                application.user, usePackageIcon, application.usingLowResIcon);
+                application.user, false, application.usingLowResIcon);
         if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
             applyCacheEntry(entry, application);
         }
@@ -538,7 +538,8 @@
 
                 if (info != null) {
                     entry.icon = LauncherIcons.createBadgedIconBitmap(
-                            getFullResIcon(info), info.getUser(), mContext);
+                            getFullResIcon(info), info.getUser(), mContext,
+                            infoProvider.get().getApplicationInfo().targetSdkVersion);
                 } else {
                     if (usePackageIcon) {
                         CacheEntry packageEntry = getEntryForPackageLocked(
@@ -635,7 +636,7 @@
                     // Load the full res icon for the application, but if useLowResIcon is set, then
                     // only keep the low resolution icon instead of the larger full-sized icon
                     Bitmap icon = LauncherIcons.createBadgedIconBitmap(
-                            appInfo.loadIcon(mPackageManager), user, mContext);
+                            appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
                     Bitmap lowResIcon =  generateLowResIcon(icon, mPackageBgColor);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
@@ -768,7 +769,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 11;
+        private final static int DB_VERSION = 13;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 4ae3649..c525cd4 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -22,6 +22,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -435,20 +436,19 @@
         float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
         float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
         float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
-        int shadowColor = ColorUtils.setAlphaComponent(
-                res.getColor(R.color.default_shadow_color_no_alpha),
-                ShadowGenerator.AMBIENT_SHADOW_ALPHA);
 
         RectF bounds = new RectF(shadowBlur, shadowBlur,
                 width - shadowBlur, height - shadowBlur - keyShadowDistance);
         p.setColor(Color.WHITE);
 
         // Key shadow
-        p.setShadowLayer(shadowBlur, 0, keyShadowDistance, shadowColor);
+        p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
+                ShadowGenerator.KEY_SHADOW_ALPHA << 24);
         c.drawRoundRect(bounds, corner, corner, p);
 
         // Ambient shadow
-        p.setShadowLayer(shadowBlur, 0, 0, shadowColor);
+        p.setShadowLayer(shadowBlur, 0, 0,
+                ColorUtils.setAlphaComponent(Color.BLACK, ShadowGenerator.AMBIENT_SHADOW_ALPHA));
         c.drawRoundRect(bounds, corner, corner, p);
 
         p.clearShadowLayer();
@@ -482,7 +482,7 @@
         RectF boxRect = drawBoxWithShadow(c, p, size, size);
 
         Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
-                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext);
+                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, Build.VERSION_CODES.O);
         Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
 
         boxRect.set(0, 0, iconSize, iconSize);
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index a1ef22f..ba1977a 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -38,18 +38,25 @@
  */
 public class BadgeRenderer {
 
+    private static final boolean DOTS_ONLY = true;
+
     // 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);
@@ -63,6 +70,8 @@
         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,10 +100,10 @@
                 ? 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);
+        int width = DOTS_ONLY ? mSize : mSize + mCharSize * (numChars - 1);
         // Lazily load the background with shadow.
         Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
         if (backgroundWithShadow == null) {
@@ -105,21 +114,42 @@
         // We draw the badge relative to its center.
         int badgeCenterX = iconBounds.right - width / 2;
         int badgeCenterY = iconBounds.top + mSize / 2;
+        boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;
+        boolean isIcon = !DOTS_ONLY && 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();
     }
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/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 14f4e6c..7178c5e 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -449,6 +449,12 @@
     }
 
     @Override
+    public void setInsets(Rect insets) {
+        super.setInsets(insets);
+        setBackgroundResource(insets.top == 0 ? 0 : R.drawable.workspace_bg);
+    }
+
+    @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(getContext(), attrs);
     }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index a0ceb49..1601edb 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -783,6 +783,7 @@
         if (mFolderIcon != null) {
             mFolderIcon.setVisibility(View.VISIBLE);
             if (wasAnimated) {
+                mFolderIcon.mBackground.animateBackgroundStroke();
                 mFolderIcon.requestFocus();
             }
         }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6f10d84..1680f0b 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -406,7 +406,7 @@
     }
 
     public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
-        updateBadgeScale(mBadgeInfo.getNotificationCount(), badgeInfo.getNotificationCount());
+        updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
         mBadgeInfo = badgeInfo;
     }
 
@@ -415,12 +415,10 @@
     }
 
     /**
-     * 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()) {
@@ -549,6 +547,7 @@
         private float mScale = 1f;
         private float mColorMultiplier = 1f;
         private float mStrokeWidth;
+        private int mStrokeAlpha = MAX_BG_OPACITY;
         private View mInvalidateDelegate;
 
         public int previewSize;
@@ -574,6 +573,21 @@
         private static final int SHADOW_OPACITY = 40;
 
         ValueAnimator mScaleAnimator;
+        ObjectAnimator mStrokeAlphaAnimator;
+
+        private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
+                new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
+                    @Override
+                    public Integer get(PreviewBackground previewBackground) {
+                        return previewBackground.mStrokeAlpha;
+                    }
+
+                    @Override
+                    public void set(PreviewBackground previewBackground, Integer alpha) {
+                        previewBackground.mStrokeAlpha = alpha;
+                        previewBackground.invalidate();
+                    }
+                };
 
         public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDelegate,
                    int availableSpace, int topPadding) {
@@ -683,8 +697,24 @@
             canvas.restoreToCount(saveCount);
         }
 
+        public void animateBackgroundStroke() {
+            if (mStrokeAlphaAnimator != null) {
+                mStrokeAlphaAnimator.cancel();
+            }
+            mStrokeAlphaAnimator = ObjectAnimator
+                    .ofArgb(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
+                    .setDuration(100);
+            mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mStrokeAlphaAnimator = null;
+                }
+            });
+            mStrokeAlphaAnimator.start();
+        }
+
         public void drawBackgroundStroke(Canvas canvas) {
-            mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+            mPaint.setColor(Color.argb(mStrokeAlpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
             mPaint.setStyle(Paint.Style.STROKE);
             mPaint.setStrokeWidth(mStrokeWidth);
             drawCircle(canvas, 1 /* deltaRadius */);
@@ -894,7 +924,7 @@
             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);
@@ -1079,20 +1109,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/FixedScaleDrawable.java b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
index 7ee3d80..262a95e 100644
--- a/src/com/android/launcher3/graphics/FixedScaleDrawable.java
+++ b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
@@ -19,17 +19,18 @@
 
     // TODO b/33553066 use the constant defined in MaskableIconDrawable
     private static final float LEGACY_ICON_SCALE = .7f * .6667f;
-    private float mScale;
+    private float mScaleX, mScaleY;
 
     public FixedScaleDrawable() {
         super(new ColorDrawable());
-        mScale = LEGACY_ICON_SCALE;
+        mScaleX = LEGACY_ICON_SCALE;
+        mScaleY = LEGACY_ICON_SCALE;
     }
 
     @Override
     public void draw(Canvas canvas) {
         int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        canvas.scale(mScale, mScale,
+        canvas.scale(mScaleX, mScaleY,
                 getBounds().exactCenterX(), getBounds().exactCenterY());
         super.draw(canvas);
         canvas.restoreToCount(saveCount);
@@ -42,6 +43,14 @@
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
 
     public void setScale(float scale) {
-        mScale = scale * LEGACY_ICON_SCALE;
+        float h = getIntrinsicHeight();
+        float w = getIntrinsicWidth();
+        mScaleX = scale * LEGACY_ICON_SCALE;
+        mScaleY = scale * LEGACY_ICON_SCALE;
+        if (h > w && w > 0) {
+            mScaleX *= w / h;
+        } else if (w > h && h > 0) {
+            mScaleY *= h / w;
+        }
     }
 }
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index c45f481..0182e53 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -35,7 +35,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "IconPalette";
 
-    public static final IconPalette FOLDER_ICON_PALETTE = new IconPalette(Color.WHITE);
+    public static final IconPalette FOLDER_ICON_PALETTE = new IconPalette(Color.parseColor("#BDC1C6"));
 
     private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
     private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
@@ -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,7 +177,11 @@
     }
 
     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);
     }
 
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index f652a5c..746a639 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
+import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 
@@ -94,13 +95,13 @@
      * The bitmap is also visually normalized with other icons.
      */
     public static Bitmap createBadgedIconBitmap(
-            Drawable icon, UserHandle user, Context context) {
+            Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
 
         IconNormalizer normalizer;
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO()) {
+            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
@@ -148,13 +149,13 @@
      * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
      * normalized with other icons and has enough spacing to add shadow.
      */
-    public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) {
+    public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context, int iconAppTargetSdk) {
         RectF iconBounds = new RectF();
         IconNormalizer normalizer;
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO()) {
+            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
@@ -179,7 +180,7 @@
 
     /**
      * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using
-     * {@link #createScaledBitmapWithoutShadow(Drawable, Context)}
+     * {@link #createScaledBitmapWithoutShadow(Drawable, Context, int)}
      */
     public static Bitmap addShadowToIcon(Bitmap icon, Context context) {
         return ShadowGenerator.getInstance(context).recreateIcon(icon);
@@ -251,7 +252,6 @@
                     width = (int) (height * ratio);
                 }
             }
-
             // no intrinsic size --> use default size
             int textureWidth = iconBitmapSize;
             int textureHeight = iconBitmapSize;
@@ -265,7 +265,13 @@
             final int top = (textureHeight-height) / 2;
 
             sOldBounds.set(icon.getBounds());
-            icon.setBounds(left, top, left+width, top+height);
+            if (icon instanceof AdaptiveIconDrawable) {
+                int offset = Math.min(left, top);
+                int size = Math.max(width, height);
+                icon.setBounds(offset, offset, offset + size, offset + size);
+            } else {
+                icon.setBounds(left, top, left+width, top+height);
+            }
             canvas.save(Canvas.MATRIX_SAVE_FLAG);
             canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
             icon.draw(canvas);
@@ -288,16 +294,13 @@
         }
 
         try {
-            Class clazz = Class.forName("android.graphics.drawable.AdaptiveIconDrawable");
-            if (!clazz.isAssignableFrom(drawable.getClass())) {
-                Drawable iconWrapper =
+            if (!(drawable instanceof AdaptiveIconDrawable)) {
+                AdaptiveIconDrawable iconWrapper = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
-                FixedScaleDrawable fsd = ((FixedScaleDrawable) clazz.getMethod("getForeground")
-                        .invoke(iconWrapper));
+                FixedScaleDrawable fsd = ((FixedScaleDrawable) iconWrapper.getForeground());
                 fsd.setDrawable(drawable);
                 fsd.setScale(scale);
-
-                return iconWrapper;
+                return (Drawable) iconWrapper;
             }
         } catch (Exception e) {
             return drawable;
@@ -318,7 +321,8 @@
         IconCache cache = app.getIconCache();
         Bitmap unbadgedBitmap = unbadgedDrawable == null
                 ? cache.getDefaultIcon(Process.myUserHandle())
-                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context);
+                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context,
+                Build.VERSION_CODES.O);
 
         if (!badged) {
             return unbadgedBitmap;
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 5dd8d20..469fe34 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -87,8 +87,6 @@
 
         float shadowRadius = height * 1f / 32;
         float shadowYOffset = height * 1f / 16;
-        int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA / 2;
-        int keyShadowAlpha = KEY_SHADOW_ALPHA / 2;
 
         int radius = height / 2;
 
@@ -109,11 +107,11 @@
         int bottom = center + height / 2;
 
         // Draw ambient shadow, center aligned within size
-        blurPaint.setAlpha(ambientShadowAlpha);
+        blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
         canvas.drawRoundRect(left, top, right, bottom, radius, radius, blurPaint);
 
         // Draw key shadow, bottom aligned within size
-        blurPaint.setAlpha(keyShadowAlpha);
+        blurPaint.setAlpha(KEY_SHADOW_ALPHA);
         canvas.drawRoundRect(left, top + shadowYOffset, right, bottom + shadowYOffset,
                 radius, radius, blurPaint);
 
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b00eb1f..dc7fa05 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -578,7 +578,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);
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;
+                    }
                 }
             }
         }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index b411879..6628971 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -22,6 +22,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.view.View;
 import android.widget.RemoteViews;
 
@@ -115,7 +116,7 @@
         } else {
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
             Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
-            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher);
+            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, Build.VERSION_CODES.O);
             mAddInfo.spanX = mAddInfo.spanY = 1;
             scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();