Merge "Portrait grid changes for Workspace/All Apps." into ub-launcher3-dorval-polish
diff --git a/src/com/android/launcher3/compat/WallpaperColorsCompat.java b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
index fd08f94..58d2a80 100644
--- a/src/com/android/launcher3/compat/WallpaperColorsCompat.java
+++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
@@ -15,29 +15,40 @@
  */
 package com.android.launcher3.compat;
 
-import android.util.SparseIntArray;
-
 /**
  * A compatibility layer around platform implementation of WallpaperColors
  */
 public class WallpaperColorsCompat {
 
-    private final SparseIntArray mColors;
-    private final boolean mSupportsDarkText;
+    public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
 
-    public WallpaperColorsCompat(SparseIntArray colors, boolean supportsDarkText) {
-        mColors = colors;
-        mSupportsDarkText = supportsDarkText;
+    private final int mPrimaryColor;
+    private final int mSecondaryColor;
+    private final int mTertiaryColor;
+    private final int mColorHints;
+
+    public WallpaperColorsCompat(int primaryColor, int secondaryColor, int tertiaryColor,
+            int colorHints) {
+        mPrimaryColor = primaryColor;
+        mSecondaryColor = secondaryColor;
+        mTertiaryColor = tertiaryColor;
+        mColorHints = colorHints;
     }
 
-    /**
-     * A map of color code to their occurrences. The bigger the int, the more relevant the color.
-     */
-    public SparseIntArray getColors() {
-        return mColors;
+    public int getPrimaryColor() {
+        return mPrimaryColor;
     }
 
-    public boolean supportsDarkText() {
-        return mSupportsDarkText;
+    public int getSecondaryColor() {
+        return mSecondaryColor;
     }
+
+    public int getTertiaryColor() {
+        return mTertiaryColor;
+    }
+
+    public int getColorHints() {
+        return mColorHints;
+    }
+
 }
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 8bdcedb..8e572ee 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -15,10 +15,6 @@
  */
 package com.android.launcher3.compat;
 
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-
-import static com.android.launcher3.Utilities.getDevicePrefs;
-
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.job.JobInfo;
@@ -45,12 +41,17 @@
 import android.support.v7.graphics.Palette;
 import android.util.Log;
 import android.util.Pair;
-import android.util.SparseIntArray;
 
 import com.android.launcher3.Utilities;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static com.android.launcher3.Utilities.getDevicePrefs;
 
 public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
 
@@ -154,11 +155,12 @@
             return Pair.create(wallpaperId, null);
         }
 
-        SparseIntArray colorsToOccurrences = new SparseIntArray((parts.length - 2) / 2);
-        for (int i = 2; i < parts.length; i += 2) {
-            colorsToOccurrences.put(Integer.parseInt(parts[i]), Integer.parseInt(parts[i + 1]));
-        }
-        return Pair.create(wallpaperId, new WallpaperColorsCompat(colorsToOccurrences, false));
+        int primary = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
+        int secondary = parts.length > 3 ? Integer.parseInt(parts[3]) : 0;
+        int tertiary = parts.length > 4 ? Integer.parseInt(parts[4]) : 0;
+
+        return Pair.create(wallpaperId, new WallpaperColorsCompat(primary, secondary, tertiary,
+                0 /* hints */));
     }
 
     /**
@@ -262,12 +264,22 @@
                 bitmap.recycle();
 
                 StringBuilder builder = new StringBuilder(value);
+                List<Pair<Integer,Integer>> colorsToOccurrences = new ArrayList<>();
                 for (Palette.Swatch swatch : palette.getSwatches()) {
-                    builder.append(',')
-                            .append(swatch.getRgb())
-                            .append(',')
-                            .append(swatch.getPopulation());
+                    colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation()));
                 }
+
+                Collections.sort(colorsToOccurrences, new Comparator<Pair<Integer, Integer>>() {
+                    @Override
+                    public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
+                        return b.second - a.second;
+                    }
+                });
+
+                for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) {
+                    builder.append(',').append(colorsToOccurrences.get(i).first);
+                }
+
                 value = builder.toString();
             }
 
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
index c74ccc0..6233fab 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -22,13 +22,10 @@
 import android.os.Build;
 import android.support.annotation.Nullable;
 import android.util.Log;
-import android.util.Pair;
-import android.util.SparseIntArray;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
@@ -41,8 +38,10 @@
     private final Method mAddOCLMethod;
 
     private final Method mWCGetMethod;
-    private final Method mWCGetColorsMethod;
-    private final Method mWCSupportsDarkTextMethod;
+    private final Method mWCGetPrimaryColorMethod;
+    private final Method mWCGetSecondaryColorMethod;
+    private final Method mWCGetTertiaryColorMethod;
+    private final Method mWCColorHintsMethod;
 
     WallpaperManagerCompatVOMR1(Context context) throws Exception {
         mWm = context.getSystemService(WallpaperManager.class);
@@ -53,8 +52,10 @@
 
         mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
         Class wallpaperColorsClass = mWCGetMethod.getReturnType();
-        mWCGetColorsMethod = wallpaperColorsClass.getDeclaredMethod("getColors");
-        mWCSupportsDarkTextMethod = wallpaperColorsClass.getDeclaredMethod("supportsDarkText");
+        mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor");
+        mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor");
+        mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor");
+        mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints");
     }
 
     @Nullable
@@ -98,12 +99,13 @@
         if (colors == null) {
             return null;
         }
-        List<Pair<Color, Integer>> list = (List) mWCGetColorsMethod.invoke(colors);
-        boolean supportsDarkText = (Boolean) mWCSupportsDarkTextMethod.invoke(colors);
-        SparseIntArray colorMap = new SparseIntArray(list.size());
-        for (Pair<Color, Integer> color : list) {
-            colorMap.put(color.first.toArgb(), color.second);
-        }
-        return new WallpaperColorsCompat(colorMap, supportsDarkText);
+        Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors);
+        Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors);
+        Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors);
+        int primaryVal = primary != null ? primary.toArgb() : 0;
+        int secondaryVal = secondary != null ? secondary.toArgb() : 0;
+        int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
+        int colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+        return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 7410ae6..50ad0ff 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -165,7 +165,7 @@
                 ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, initialDragViewScale, scaleDps);
-
+        dragView.setItemInfo(dragInfo);
         mDragObject.dragComplete = false;
         if (mOptions.isAccessibleDrag) {
             // For an accessible drag, we assume the view is being dragged from the center.
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7806c98..9c6b956 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -22,25 +22,44 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.SuppressLint;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.Arrays;
 
-public class DragView extends View {
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+public class DragView extends FrameLayout {
     public static final int COLOR_CHANGE_DURATION = 120;
     public static final int VIEW_ZOOM_DURATION = 150;
 
@@ -57,6 +76,7 @@
 
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
+    private final Launcher mLauncher;
     private final DragLayer mDragLayer;
     @Thunk final DragController mDragController;
     private boolean mHasDrawn = false;
@@ -76,6 +96,20 @@
     private int mAnimatedShiftX;
     private int mAnimatedShiftY;
 
+    // Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true}
+    private SpringAnimation mSpringX, mSpringY;
+    private ImageView mFgImageView, mBgImageView;
+    private Path mScaledMaskPath;
+    // TODO: figure out if there is smarter way to retrieve these two constants below
+    private final static float ADAPTIVE_ICON_SCALE = .731121626f;
+    private final static float ADAPTIVE_ICON_MASK_SCALE = 1.165f; //1.185f;
+
+    // Following three values are fine tuned with motion ux designer
+    private final static int STIFFNESS = 4000;
+    private final static float DAMPENING_RATIO = 1f;
+    private final static int PARALLAX_MAX_IN_DP = 8;
+    private final int mDelta;
+
     /**
      * Construct the drag view.
      * <p>
@@ -89,6 +123,7 @@
     public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
                     final float initialScale, final float finalScaleDps) {
         super(launcher);
+        mLauncher = launcher;
         mDragLayer = launcher.getDragLayer();
         mDragController = launcher.getDragController();
 
@@ -142,8 +177,119 @@
         mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
 
         mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
         setElevation(getResources().getDimension(R.dimen.drag_elevation));
+        setWillNotDraw(false);
+        mDelta = (int)(getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP);
+    }
+
+    /**
+     * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
+     */
+    public void setItemInfo(final ItemInfo info) {
+        if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
+            return;
+        }
+        if (!(info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT)) {
+            return;
+        }
+        // Load the adaptive icon on a background thread and add the view in ui thread.
+        final Looper workerLooper = LauncherModel.getWorkerLooper();
+        new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
+            @Override
+            public void run() {
+                PackageManager pm = (mLauncher).getPackageManager();
+                try {
+                    Drawable dr = pm.getActivityIcon(info.getTargetComponent());
+                    if (dr instanceof AdaptiveIconDrawable) {
+                        int w = mBitmap.getWidth();
+                        int h = mBitmap.getHeight();
+                        AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
+                        adaptiveIcon.setBounds(0, 0, w, h);
+                        setupMaskPath(adaptiveIcon);
+                        mFgImageView = setupImageView(adaptiveIcon.getForeground());
+                        mBgImageView = setupImageView(adaptiveIcon.getBackground());
+                        mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X);
+                        mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y);
+
+                        new Handler(Looper.getMainLooper()).post(new Runnable() {
+                            @Override
+                            public void run() {
+                                addView(mBgImageView);
+                                addView(mFgImageView);
+                                setWillNotDraw(true);
+                            }
+                        });
+                    }
+                } catch (PackageManager.NameNotFoundException e) { }
+            }});
+    }
+
+    private ImageView setupImageView(Drawable drawable) {
+        FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
+        ImageView imageViewOut = new ImageView(getContext());
+        imageViewOut.setLayoutParams(params);
+        imageViewOut.setScaleType(ImageView.ScaleType.CENTER);
+        imageViewOut.setScaleX(ADAPTIVE_ICON_SCALE);
+        imageViewOut.setScaleY(ADAPTIVE_ICON_SCALE);
+        imageViewOut.setImageDrawable(drawable);
+        return imageViewOut;
+    }
+
+    private SpringAnimation setupSpringAnimation(int minValue, int maxValue,
+            DynamicAnimation.ViewProperty property) {
+        SpringAnimation s = new SpringAnimation(mFgImageView, property, 0);
+        s.setMinValue(minValue).setMaxValue(maxValue);
+        s.setSpring(new SpringForce(0)
+                        .setDampingRatio(DAMPENING_RATIO)
+                        .setStiffness(STIFFNESS));
+        return s;
+    }
+
+    private void setupMaskPath(AdaptiveIconDrawable dr) {
+        Matrix m = new Matrix();
+        m.setScale(ADAPTIVE_ICON_SCALE * ADAPTIVE_ICON_MASK_SCALE,
+                ADAPTIVE_ICON_SCALE * ADAPTIVE_ICON_MASK_SCALE,
+                dr.getBounds().centerX(),
+                dr.getBounds().centerY());
+        mScaledMaskPath = new Path();
+        dr.getIconMask().transform(m, mScaledMaskPath);
+    }
+
+    private void applySpring(int x, int y) {
+        if (mSpringX == null || mSpringY == null) {
+            return;
+        }
+        mSpringX.animateToFinalPosition(Utilities.boundToRange(x, -mDelta, mDelta));
+        mSpringY.animateToFinalPosition(Utilities.boundToRange(y, -mDelta, mDelta));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int w = right - left;
+        int h = bottom - top;
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).layout(-w / 4, -h / 4, w + w / 4, h + h / 4);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int w = mBitmap.getWidth();
+        int h = mBitmap.getHeight();
+        setMeasuredDimension(w, h);
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).measure(w, h);
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mScaledMaskPath != null) {
+            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+            canvas.clipPath(mScaledMaskPath);
+        }
+        super.dispatchDraw(canvas);
     }
 
     /** Sets the scale of the view over the normal workspace icon size. */
@@ -187,11 +333,6 @@
         return mDragRegion;
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
-    }
-
     // Draws drag shadow for system DND.
     @SuppressLint("WrongCall")
     public void drawDragShadow(Canvas canvas) {
@@ -343,6 +484,9 @@
      * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
     public void move(int touchX, int touchY) {
+        if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0) {
+            applySpring(mLastTouchX - touchX, mLastTouchY - touchY);
+        }
         mLastTouchX = touchX;
         mLastTouchY = touchY;
         applyTranslation();
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
index 21d5b27..d2dd0d3 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -24,15 +24,12 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
-import android.util.SparseIntArray;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.WallpaperColorsCompat;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -52,20 +49,18 @@
     private static final float FIT_WEIGHT_S = 1.0f;
     private static final float FIT_WEIGHT_L = 10.0f;
 
-    // When extracting the main color, only consider colors
-    // present in at least MIN_COLOR_OCCURRENCE of the image
-    private static final float MIN_COLOR_OCCURRENCE = 0.1f;
-
     // Temporary variable to avoid allocations
-    private final float[] mTmpHSL = new float[3];
+    private float[] mTmpHSL = new float[3];
 
-    public @Nullable Pair<Integer, Integer> extractInto(WallpaperColorsCompat wallpaperColors) {
-        if (wallpaperColors == null) {
+    public @Nullable Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
+        if (inWallpaperColors == null) {
             return null;
         }
 
-        SparseIntArray colorsArray = wallpaperColors.getColors();
-        if (colorsArray.size() == 0) {
+        final List<Integer> mainColors = getMainColors(inWallpaperColors);
+        final int mainColorsSize = mainColors.size();
+
+        if (mainColorsSize == 0) {
             return null;
         }
         // Tonal is not really a sort, it takes a color from the extracted
@@ -73,35 +68,17 @@
         // palettes. The best fit is tweaked to be closer to the source color
         // and replaces the original palette
 
-        List<Pair<Integer, Integer>> colors = new ArrayList<>(colorsArray.size());
-        for (int i = colorsArray.size() - 1; i >= 0; i--) {
-            colors.add(Pair.create(colorsArray.keyAt(i), colorsArray.valueAt(i)));
-        }
-
-        // First find the most representative color in the image
-        populationSort(colors);
-        // Calculate total
-        int total = 0;
-        for (Pair<Integer, Integer> weightedColor : colors) {
-            total += weightedColor.second;
-        }
-
-        // Get bright colors that occur often enough in this image
-        Pair<Integer, Integer> bestColor = null;
-        float[] hsl = new float[3];
-        for (Pair<Integer, Integer> weightedColor : colors) {
-            float colorOccurrence = weightedColor.second / (float) total;
-            if (colorOccurrence < MIN_COLOR_OCCURRENCE) {
-                break;
-            }
-
-            int colorValue = weightedColor.first;
+        // Get the most preeminent, non-blacklisted color.
+        Integer bestColor = 0;
+        final float[] hsl = new float[3];
+        for (int i = 0; i < mainColorsSize; i++) {
+            final int colorValue = mainColors.get(i);
             ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
                     Color.blue(colorValue), hsl);
 
             // Stop when we find a color that meets our criteria
             if (!isBlacklisted(hsl)) {
-                bestColor = weightedColor;
+                bestColor = colorValue;
                 break;
             }
         }
@@ -111,7 +88,7 @@
             return null;
         }
 
-        int colorValue = bestColor.first;
+        int colorValue = bestColor;
         ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
                 hsl);
 
@@ -121,7 +98,6 @@
 
         // Find the palette that contains the closest color
         TonalPalette palette = findTonalPalette(hsl[0]);
-
         if (palette == null) {
             Log.w(TAG, "Could not find a tonal palette!");
             return null;
@@ -140,8 +116,7 @@
         float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
         float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
 
-        final int textInversionIndex = h.length - 3;
-
+        // Normal colors:
         // best fit + a 2 colors offset
         int primaryIndex = fitIndex;
         int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
@@ -172,15 +147,6 @@
         return false;
     }
 
-    private static void populationSort(@NonNull List<Pair<Integer, Integer>> wallpaperColors) {
-        Collections.sort(wallpaperColors, new Comparator<Pair<Integer, Integer>>() {
-            @Override
-            public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
-                return b.second - a.second;
-            }
-        });
-    }
-
     /**
      * Offsets all colors by a delta, clamping values that go beyond what's
      * supported on the color space.
@@ -234,7 +200,9 @@
         TonalPalette best = null;
         float error = Float.POSITIVE_INFINITY;
 
-        for (TonalPalette candidate : TONAL_PALETTES) {
+        for (int i = 0; i < TONAL_PALETTES.length; i++) {
+            final TonalPalette candidate = TONAL_PALETTES[i];
+
             if (h >= candidate.minHue && h <= candidate.maxHue) {
                 best = candidate;
                 break;
@@ -757,4 +725,18 @@
         }
     }
 
+    private static List<Integer> getMainColors(WallpaperColorsCompat wallpaperColors) {
+        LinkedList<Integer> colors = new LinkedList<>();
+        if (wallpaperColors.getPrimaryColor() != 0) {
+            colors.add(wallpaperColors.getPrimaryColor());
+        }
+        if (wallpaperColors.getSecondaryColor() != 0) {
+            colors.add(wallpaperColors.getSecondaryColor());
+        }
+        if (wallpaperColors.getTertiaryColor() != 0) {
+            colors.add(wallpaperColors.getTertiaryColor());
+        }
+        return colors;
+    }
+
 }
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
index e23f42f..856a26c 100644
--- a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -78,7 +78,9 @@
             mMainColor = FALLBACK_COLOR;
             mSecondaryColor = FALLBACK_COLOR;
         }
-        mSupportsDarkText = wallpaperColors != null ? wallpaperColors.supportsDarkText() : false;
+        mSupportsDarkText = wallpaperColors != null
+                ? (wallpaperColors.getColorHints()
+                    & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) > 0 : false;
         float[] hsl = new float[3];
         ColorUtils.colorToHSL(mMainColor, hsl);
         mIsDark = hsl[2] < 0.2f;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 5794004..fc25c9a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -60,7 +60,6 @@
 import com.android.launcher3.LogDecelerateInterpolator;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.PagedView;
-import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
@@ -637,7 +636,9 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                    mFolderIcon.setBackgroundVisible(false);
                     mFolderIcon.drawLeaveBehindIfExists();
+                } else {
                     mFolderIcon.setVisibility(INVISIBLE);
                 }
 
@@ -790,6 +791,7 @@
         mDragController.removeDropTarget(this);
         clearFocus();
         if (mFolderIcon != null) {
+            mFolderIcon.setBackgroundVisible(true);
             mFolderIcon.setVisibility(View.VISIBLE);
             if (wasAnimated) {
                 mFolderIcon.mBackground.animateBackgroundStroke();
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index bee0bd4..bb2d1a2 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -208,6 +208,7 @@
         play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(items, ITEMS_TEXT_COLOR_PROPERTY, Color.TRANSPARENT, finalTextColor));
         play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
+        play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
         play(a, new RoundedRectRevealOutlineProvider(initialRadius, finalRadius, startRect,
                 endRect).createRevealAnimator(mFolder, !mIsOpening));
 
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 82a0733..aaa19af 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -120,6 +120,7 @@
     private int mPrevTopPadding = -1;
 
     PreviewBackground mBackground = new PreviewBackground();
+    private boolean mBackgroundIsVisible = true;
 
     private PreviewLayoutRule mPreviewLayoutRule;
 
@@ -883,10 +884,17 @@
         mBackground.setInvalidateDelegate(this);
     }
 
+    public void setBackgroundVisible(boolean visible) {
+        mBackgroundIsVisible = visible;
+        invalidate();
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
+        if (!mBackgroundIsVisible) return;
+
         if (mReferenceDrawable != null) {
             computePreviewDrawingParams(mReferenceDrawable);
         }