Home | History | Annotate | Download | only in dynamicui
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3.uioverrides.dynamicui;
     18 
     19 import android.content.Context;
     20 import android.graphics.Color;
     21 import android.support.annotation.NonNull;
     22 import android.support.annotation.Nullable;
     23 import android.support.v4.graphics.ColorUtils;
     24 import android.util.Log;
     25 import android.util.Pair;
     26 import android.util.Range;
     27 
     28 import com.android.launcher3.R;
     29 import com.android.launcher3.Utilities;
     30 
     31 import java.util.Arrays;
     32 import java.util.LinkedList;
     33 import java.util.List;
     34 
     35 /**
     36  * Implementation of tonal color extraction
     37  **/
     38 public class ColorExtractionAlgorithm {
     39 
     40     public static ColorExtractionAlgorithm newInstance(Context context) {
     41         return Utilities.getOverrideObject(ColorExtractionAlgorithm.class,
     42                 context.getApplicationContext(), R.string.color_extraction_impl_class);
     43     }
     44 
     45     private static final String TAG = "Tonal";
     46 
     47     // Used for tonal palette fitting
     48     private static final float FIT_WEIGHT_H = 1.0f;
     49     private static final float FIT_WEIGHT_S = 1.0f;
     50     private static final float FIT_WEIGHT_L = 10.0f;
     51 
     52     public static final int MAIN_COLOR_LIGHT = 0xffb0b0b0;
     53     public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e;
     54     public static final int MAIN_COLOR_DARK = 0xff212121;
     55     public static final int SECONDARY_COLOR_DARK = 0xff000000;
     56 
     57     // Temporary variable to avoid allocations
     58     private float[] mTmpHSL = new float[3];
     59 
     60     public Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
     61         if (inWallpaperColors == null) {
     62             return applyFallback(inWallpaperColors);
     63         }
     64 
     65         final List<Integer> mainColors = getMainColors(inWallpaperColors);
     66         final int mainColorsSize = mainColors.size();
     67         final boolean supportsDarkText = (inWallpaperColors.getColorHints() &
     68                 WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) != 0;
     69 
     70         if (mainColorsSize == 0) {
     71             return applyFallback(inWallpaperColors);
     72         }
     73         // Tonal is not really a sort, it takes a color from the extracted
     74         // palette and finds a best fit amongst a collection of pre-defined
     75         // palettes. The best fit is tweaked to be closer to the source color
     76         // and replaces the original palette
     77 
     78         // Get the most preeminent, non-blacklisted color.
     79         Integer bestColor = 0;
     80         final float[] hsl = new float[3];
     81         for (int i = 0; i < mainColorsSize; i++) {
     82             final int colorValue = mainColors.get(i);
     83             ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
     84                     Color.blue(colorValue), hsl);
     85 
     86             // Stop when we find a color that meets our criteria
     87             if (!isBlacklisted(hsl)) {
     88                 bestColor = colorValue;
     89                 break;
     90             }
     91         }
     92 
     93         // Fail if not found
     94         if (bestColor == null) {
     95             return applyFallback(inWallpaperColors);
     96         }
     97 
     98         int colorValue = bestColor;
     99         ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
    100                 hsl);
    101 
    102         // The Android HSL definition requires the hue to go from 0 to 360 but
    103         // the Material Tonal Palette defines hues from 0 to 1.
    104         hsl[0] /= 360f;
    105 
    106         // Find the palette that contains the closest color
    107         TonalPalette palette = findTonalPalette(hsl[0], hsl[1]);
    108         if (palette == null) {
    109             Log.w(TAG, "Could not find a tonal palette!");
    110             return applyFallback(inWallpaperColors);
    111         }
    112 
    113         // Figure out what's the main color index in the optimal palette
    114         int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
    115         if (fitIndex == -1) {
    116             Log.w(TAG, "Could not find best fit!");
    117             return applyFallback(inWallpaperColors);
    118         }
    119 
    120         // Generate the 10 colors palette by offsetting each one of them
    121         float[] h = fit(palette.h, hsl[0], fitIndex,
    122                 Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
    123         float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
    124         float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
    125 
    126         int primaryIndex = fitIndex;
    127         int mainColor = getColorInt(primaryIndex, h, s, l);
    128 
    129         // We might want use the fallback in case the extracted color is brighter than our
    130         // light fallback or darker than our dark fallback.
    131         ColorUtils.colorToHSL(mainColor, mTmpHSL);
    132         final float mainLuminosity = mTmpHSL[2];
    133         ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
    134         final float lightLuminosity = mTmpHSL[2];
    135         if (mainLuminosity > lightLuminosity) {
    136             return applyFallback(inWallpaperColors);
    137         }
    138         ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
    139         final float darkLuminosity = mTmpHSL[2];
    140         if (mainLuminosity < darkLuminosity) {
    141             return applyFallback(inWallpaperColors);
    142         }
    143 
    144         // Dark colors:
    145         // Stops at 4th color, only lighter if dark text is supported
    146         if (supportsDarkText) {
    147             primaryIndex = h.length - 1;
    148         } else if (fitIndex < 2) {
    149             primaryIndex = 0;
    150         } else {
    151             primaryIndex = Math.min(fitIndex, 3);
    152         }
    153         int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
    154         int secondaryColor = getColorInt(secondaryIndex, h, s, l);
    155 
    156         return new Pair<>(mainColor, secondaryColor);
    157     }
    158 
    159     public static Pair<Integer, Integer> applyFallback(WallpaperColorsCompat inWallpaperColors) {
    160         boolean light = inWallpaperColors != null
    161                 && (inWallpaperColors.getColorHints()
    162                     & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT)!= 0;
    163         int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
    164         int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK;
    165         return new Pair<>(innerColor, outerColor);
    166     }
    167 
    168     private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
    169         mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
    170         mTmpHSL[1] = s[fitIndex];
    171         mTmpHSL[2] = l[fitIndex];
    172         return ColorUtils.HSLToColor(mTmpHSL);
    173     }
    174 
    175     /**
    176      * Checks if a given color exists in the blacklist
    177      * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
    178      * @return true if color should be avoided
    179      */
    180     private boolean isBlacklisted(float[] hsl) {
    181         for (ColorRange badRange: BLACKLISTED_COLORS) {
    182             if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
    183                 return true;
    184             }
    185         }
    186         return false;
    187     }
    188 
    189     /**
    190      * Offsets all colors by a delta, clamping values that go beyond what's
    191      * supported on the color space.
    192      * @param data what you want to fit
    193      * @param v how big should be the offset
    194      * @param index which index to calculate the delta against
    195      * @param min minimum accepted value (clamp)
    196      * @param max maximum accepted value (clamp)
    197      * @return new shifted palette
    198      */
    199     private static float[] fit(float[] data, float v, int index, float min, float max) {
    200         float[] fitData = new float[data.length];
    201         float delta = v - data[index];
    202 
    203         for (int i = 0; i < data.length; i++) {
    204             fitData[i] = Utilities.boundToRange(data[i] + delta, min, max);
    205         }
    206 
    207         return fitData;
    208     }
    209 
    210     /**
    211      * Finds the closest color in a palette, given another HSL color
    212      *
    213      * @param palette where to search
    214      * @param h hue
    215      * @param s saturation
    216      * @param l lightness
    217      * @return closest index or -1 if palette is empty.
    218      */
    219     private static int bestFit(@NonNull TonalPalette palette, float h, float s, float l) {
    220         int minErrorIndex = -1;
    221         float minError = Float.POSITIVE_INFINITY;
    222 
    223         for (int i = 0; i < palette.h.length; i++) {
    224             float error =
    225                     FIT_WEIGHT_H * Math.abs(h - palette.h[i])
    226                             + FIT_WEIGHT_S * Math.abs(s - palette.s[i])
    227                             + FIT_WEIGHT_L * Math.abs(l - palette.l[i]);
    228             if (error < minError) {
    229                 minError = error;
    230                 minErrorIndex = i;
    231             }
    232         }
    233 
    234         return minErrorIndex;
    235     }
    236 
    237     @Nullable
    238     private static TonalPalette findTonalPalette(float h, float s) {
    239         // Fallback to a grey palette if the color is too desaturated.
    240         // This avoids hue shifts.
    241         if (s < 0.05f) {
    242             return GREY_PALETTE;
    243         }
    244 
    245         TonalPalette best = null;
    246         float error = Float.POSITIVE_INFINITY;
    247 
    248         for (int i = 0; i < TONAL_PALETTES.length; i++) {
    249             final TonalPalette candidate = TONAL_PALETTES[i];
    250 
    251             if (h >= candidate.minHue && h <= candidate.maxHue) {
    252                 best = candidate;
    253                 break;
    254             }
    255 
    256             if (candidate.maxHue > 1.0f && h >= 0.0f && h <= fract(candidate.maxHue)) {
    257                 best = candidate;
    258                 break;
    259             }
    260 
    261             if (candidate.minHue < 0.0f && h >= fract(candidate.minHue) && h <= 1.0f) {
    262                 best = candidate;
    263                 break;
    264             }
    265 
    266             if (h <= candidate.minHue && candidate.minHue - h < error) {
    267                 best = candidate;
    268                 error = candidate.minHue - h;
    269             } else if (h >= candidate.maxHue && h - candidate.maxHue < error) {
    270                 best = candidate;
    271                 error = h - candidate.maxHue;
    272             } else if (candidate.maxHue > 1.0f && h >= fract(candidate.maxHue)
    273                     && h - fract(candidate.maxHue) < error) {
    274                 best = candidate;
    275                 error = h - fract(candidate.maxHue);
    276             } else if (candidate.minHue < 0.0f && h <= fract(candidate.minHue)
    277                     && fract(candidate.minHue) - h < error) {
    278                 best = candidate;
    279                 error = fract(candidate.minHue) - h;
    280             }
    281         }
    282 
    283         return best;
    284     }
    285 
    286     private static float fract(float v) {
    287         return v - (float) Math.floor(v);
    288     }
    289 
    290     static class TonalPalette {
    291         final float[] h;
    292         final float[] s;
    293         final float[] l;
    294         final float minHue;
    295         final float maxHue;
    296 
    297         TonalPalette(float[] h, float[] s, float[] l) {
    298             if (h.length != s.length || s.length != l.length) {
    299                 throw new IllegalArgumentException("All arrays should have the same size. h: "
    300                         + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
    301                         + Arrays.toString(l));
    302             }
    303 
    304             this.h = h;
    305             this.s = s;
    306             this.l = l;
    307 
    308             float minHue = Float.POSITIVE_INFINITY;
    309             float maxHue = Float.NEGATIVE_INFINITY;
    310 
    311             for (float v : h) {
    312                 minHue = Math.min(v, minHue);
    313                 maxHue = Math.max(v, maxHue);
    314             }
    315 
    316             this.minHue = minHue;
    317             this.maxHue = maxHue;
    318         }
    319     }
    320 
    321     // Data definition of Material Design tonal palettes
    322     // When the sort type is set to TONAL, these palettes are used to find
    323     // a best fit. Each palette is defined as 22 HSL colors
    324     private static final TonalPalette[] TONAL_PALETTES = {
    325             new TonalPalette(
    326                     new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
    327                             0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
    328                             0.027397260273972573f, 0.017543859649122865f},
    329                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
    330                             1f},
    331                     new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
    332                             0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
    333                             0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
    334                             0.8568627450980393f, 0.9254901960784314f}
    335             ),
    336             new TonalPalette(
    337                     new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
    338                             0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
    339                             0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
    340                             0.5748031496062993f, 0.5582010582010583f},
    341                     new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
    342                             0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
    343                             1f},
    344                     new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
    345                             0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
    346                             0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
    347                             0.7509803921568627f, 0.8764705882352941f}
    348             ),
    349             new TonalPalette(
    350                     new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
    351                             0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
    352                             0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
    353                             0.508080808080808f, 0.5f},
    354                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
    355                             1f},
    356                     new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
    357                             0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
    358                             0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
    359                             0.6764705882352942f, 0.8f}
    360             ),
    361             new TonalPalette(
    362                     new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
    363                             0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
    364                             0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
    365                             0.4671052631578947f},
    366                     new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
    367                             1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
    368                     new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
    369                             0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
    370                             0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
    371                             0.7509803921568627f, 0.8509803921568627f}
    372             ),
    373             new TonalPalette(
    374                     new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
    375                             0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
    376                             0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
    377                             0.3467741935483871f, 0.3703703703703704f},
    378                     new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
    379                             0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
    380                             0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
    381                     new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
    382                             0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
    383                             0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
    384                             0.8784313725490196f, 0.9294117647058824f}
    385             ),
    386             new TonalPalette(
    387                     new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
    388                             0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
    389                             0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
    390                             0.1946778711484594f, 0.18604651162790695f},
    391                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
    392                     new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
    393                             0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
    394                             0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
    395                             0.7666666666666666f, 0.8313725490196078f}
    396             ),
    397             new TonalPalette(
    398                     new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
    399                             0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
    400                             0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
    401                             0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
    402                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
    403                     new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
    404                             0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
    405                             0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
    406                             0.8509803921568627f, 0.9f}
    407             ),
    408             new TonalPalette(
    409                     new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
    410                             0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
    411                             0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
    412                             0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
    413                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
    414                     new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
    415                             0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
    416             ),
    417             new TonalPalette(
    418                     new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
    419                             0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
    420                             0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
    421                             0.9754098360655739f, 0.9824561403508771f},
    422                     new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
    423                             0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
    424                             1f, 1f, 1f, 1f, 1f},
    425                     new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
    426                             0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
    427                             0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
    428                             0.9254901960784314f}
    429             ),
    430             new TonalPalette(
    431                     new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
    432                             0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
    433                             0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
    434                             0.7771084337349398f, 0.7747747747747749f},
    435                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
    436                             0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
    437                     new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
    438                             0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
    439                             0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
    440                             0.7470588235294118f, 0.8450980392156863f}
    441             ),
    442             new TonalPalette(
    443                     new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
    444                             0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
    445                             0.009057971014492771f, 0.026748971193415648f,
    446                             0.041666666666666616f, 0.05303030303030304f},
    447                     new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
    448                             0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
    449                             0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
    450                     new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
    451                             0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
    452                             0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
    453                             0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
    454             ),
    455             new TonalPalette(
    456                     new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
    457                             0.7802083333333333f, 0.7844311377245509f, 0.796875f,
    458                             0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
    459                             0.8562091503267975f, 0.8666666666666667f},
    460                     new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
    461                             0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
    462                             0.9560439560439562f, 1f, 1f},
    463                     new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
    464                             0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
    465                             0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
    466                             0.9411764705882353f}
    467             ),
    468             new TonalPalette(
    469                     new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
    470                             0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
    471                             0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
    472                             0.6666666666666666f, 0.6666666666666666f},
    473                     new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
    474                             0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
    475                             0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
    476                             0.29999999999999966f, 0.5000000000000004f},
    477                     new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
    478                             0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
    479                             0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
    480                             0.8823529411764706f, 0.9372549019607843f}
    481             ),
    482             new TonalPalette(
    483                     new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
    484                             0.9944812362030905f, 0f, 0f,
    485                             0.0047348484848484815f, 0.00316455696202532f, 0f,
    486                             0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
    487                     new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
    488                             0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
    489                             0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
    490                             0.8181818181818189f},
    491                     new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
    492                             0.4215686274509804f,
    493                             0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
    494                             0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
    495                             0.892156862745098f, 0.9568627450980391f}
    496             ),
    497             new TonalPalette(
    498                     new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
    499                             0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
    500                             0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
    501                             0.9426229508196722f, 0.9444444444444444f},
    502                     new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
    503                             0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
    504                             0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
    505                             0.8000000000000006f},
    506                     new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
    507                             0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
    508                             0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
    509                             0.8529411764705882f, 0.9411764705882353f}
    510             ),
    511             new TonalPalette(
    512                     new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
    513                             0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
    514                             0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
    515                             0.7252252252252251f, 0.7333333333333333f},
    516                     new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
    517                             0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
    518                             0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
    519                             0.45679012345678977f, 0.4545454545454551f},
    520                     new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
    521                             0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
    522                             0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
    523                             0.8411764705882353f, 0.9352941176470588f}
    524             ),
    525             new TonalPalette(
    526                     new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
    527                             0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
    528                             0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
    529                             0.6428571428571429f},
    530                     new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
    531                             0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
    532                             0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
    533                     new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
    534                             0.40588235294117647f, 0.44705882352941173f,
    535                             0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
    536                             0.8431372549019608f, 0.9372549019607843f}
    537             ),
    538             new TonalPalette(
    539                     new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
    540                             0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
    541                             0.484375f, 0.4841269841269842f, 0.48444444444444457f,
    542                             0.48518518518518516f, 0.4907407407407408f},
    543                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
    544                             0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
    545                     new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
    546                             0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
    547                             0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
    548             ),
    549             new TonalPalette(
    550                     new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
    551                             0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
    552                             0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
    553                             0.5512820512820514f, 0.5666666666666667f},
    554                     new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
    555                             0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
    556                             0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
    557                             0.15662650602409653f, 0.151515151515151f},
    558                     new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
    559                             0.2627450980392157f,
    560                             0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
    561                             0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
    562                             0.9352941176470588f}
    563             ),
    564             new TonalPalette(
    565                     new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
    566                             0.03947368421052631f, 0.04166666666666668f,
    567                             0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
    568                             0.04444444444444459f, 0.05555555555555529f},
    569                     new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
    570                             0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
    571                             0.15315315315315312f, 0.15189873417721522f,
    572                             0.15789473684210534f, 0.15789473684210542f},
    573                     new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
    574                             0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
    575                             0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
    576                             0.9254901960784314f}
    577             ),
    578             new TonalPalette(
    579                     new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
    580                             0.07254901960784313f, 0.0934640522875817f,
    581                             0.10457516339869281f, 0.11699346405228758f,
    582                             0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
    583                             0.12500000000000003f, 0.12777777777777777f},
    584                     new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
    585                     new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
    586                             0.5f, 0.5784313725490196f,
    587                             0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
    588                             0.9411764705882353f}
    589             )
    590     };
    591 
    592     private static final TonalPalette GREY_PALETTE = new TonalPalette(
    593             new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
    594             new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
    595             new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
    596                     0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
    597                     0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
    598     );
    599 
    600     @SuppressWarnings("WeakerAccess")
    601     static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
    602 
    603             // Red
    604             new ColorRange(
    605                     new Range<>(0f, 20f) /* H */,
    606                     new Range<>(0.7f, 1f) /* S */,
    607                     new Range<>(0.21f, 0.79f)) /* L */,
    608             new ColorRange(
    609                     new Range<>(0f, 20f),
    610                     new Range<>(0.3f, 0.7f),
    611                     new Range<>(0.355f, 0.653f)),
    612 
    613             // Red Orange
    614             new ColorRange(
    615                     new Range<>(20f, 40f),
    616                     new Range<>(0.7f, 1f),
    617                     new Range<>(0.28f, 0.643f)),
    618             new ColorRange(
    619                     new Range<>(20f, 40f),
    620                     new Range<>(0.3f, 0.7f),
    621                     new Range<>(0.414f, 0.561f)),
    622             new ColorRange(
    623                     new Range<>(20f, 40f),
    624                     new Range<>(0f, 3f),
    625                     new Range<>(0.343f, 0.584f)),
    626 
    627             // Orange
    628             new ColorRange(
    629                     new Range<>(40f, 60f),
    630                     new Range<>(0.7f, 1f),
    631                     new Range<>(0.173f, 0.349f)),
    632             new ColorRange(
    633                     new Range<>(40f, 60f),
    634                     new Range<>(0.3f, 0.7f),
    635                     new Range<>(0.233f, 0.427f)),
    636             new ColorRange(
    637                     new Range<>(40f, 60f),
    638                     new Range<>(0f, 0.3f),
    639                     new Range<>(0.231f, 0.484f)),
    640 
    641             // Yellow 60
    642             new ColorRange(
    643                     new Range<>(60f, 80f),
    644                     new Range<>(0.7f, 1f),
    645                     new Range<>(0.488f, 0.737f)),
    646             new ColorRange(
    647                     new Range<>(60f, 80f),
    648                     new Range<>(0.3f, 0.7f),
    649                     new Range<>(0.673f, 0.837f)),
    650 
    651             // Yellow Green 80
    652             new ColorRange(
    653                     new Range<>(80f, 100f),
    654                     new Range<>(0.7f, 1f),
    655                     new Range<>(0.469f, 0.61f)),
    656 
    657             // Yellow green 100
    658             new ColorRange(
    659                     new Range<>(100f, 120f),
    660                     new Range<>(0.7f, 1f),
    661                     new Range<>(0.388f, 0.612f)),
    662             new ColorRange(
    663                     new Range<>(100f, 120f),
    664                     new Range<>(0.3f, 0.7f),
    665                     new Range<>(0.424f, 0.541f)),
    666 
    667             // Green
    668             new ColorRange(
    669                     new Range<>(120f, 140f),
    670                     new Range<>(0.7f, 1f),
    671                     new Range<>(0.375f, 0.52f)),
    672             new ColorRange(
    673                     new Range<>(120f, 140f),
    674                     new Range<>(0.3f, 0.7f),
    675                     new Range<>(0.435f, 0.524f)),
    676 
    677             // Green Blue 140
    678             new ColorRange(
    679                     new Range<>(140f, 160f),
    680                     new Range<>(0.7f, 1f),
    681                     new Range<>(0.496f, 0.641f)),
    682 
    683             // Seafoam
    684             new ColorRange(
    685                     new Range<>(160f, 180f),
    686                     new Range<>(0.7f, 1f),
    687                     new Range<>(0.496f, 0.567f)),
    688 
    689             // Cyan
    690             new ColorRange(
    691                     new Range<>(180f, 200f),
    692                     new Range<>(0.7f, 1f),
    693                     new Range<>(0.52f, 0.729f)),
    694 
    695             // Blue
    696             new ColorRange(
    697                     new Range<>(220f, 240f),
    698                     new Range<>(0.7f, 1f),
    699                     new Range<>(0.396f, 0.571f)),
    700             new ColorRange(
    701                     new Range<>(220f, 240f),
    702                     new Range<>(0.3f, 0.7f),
    703                     new Range<>(0.425f, 0.551f)),
    704 
    705             // Blue Purple 240
    706             new ColorRange(
    707                     new Range<>(240f, 260f),
    708                     new Range<>(0.7f, 1f),
    709                     new Range<>(0.418f, 0.639f)),
    710             new ColorRange(
    711                     new Range<>(220f, 240f),
    712                     new Range<>(0.3f, 0.7f),
    713                     new Range<>(0.441f, 0.576f)),
    714 
    715             // Blue Purple 260
    716             new ColorRange(
    717                     new Range<>(260f, 280f),
    718                     new Range<>(0.3f, 1f), // Bigger range
    719                     new Range<>(0.461f, 0.553f)),
    720 
    721             // Fuchsia
    722             new ColorRange(
    723                     new Range<>(300f, 320f),
    724                     new Range<>(0.7f, 1f),
    725                     new Range<>(0.484f, 0.588f)),
    726             new ColorRange(
    727                     new Range<>(300f, 320f),
    728                     new Range<>(0.3f, 0.7f),
    729                     new Range<>(0.48f, 0.592f)),
    730 
    731             // Pink
    732             new ColorRange(
    733                     new Range<>(320f, 340f),
    734                     new Range<>(0.7f, 1f),
    735                     new Range<>(0.466f, 0.629f)),
    736 
    737             // Soft red
    738             new ColorRange(
    739                     new Range<>(340f, 360f),
    740                     new Range<>(0.7f, 1f),
    741                     new Range<>(0.437f, 0.596f))
    742     };
    743 
    744     /**
    745      * Representation of an HSL color range.
    746      * <ul>
    747      * <li>hsl[0] is Hue [0 .. 360)</li>
    748      * <li>hsl[1] is Saturation [0...1]</li>
    749      * <li>hsl[2] is Lightness [0...1]</li>
    750      * </ul>
    751      */
    752     static class ColorRange {
    753         private Range<Float> mHue;
    754         private Range<Float> mSaturation;
    755         private Range<Float> mLightness;
    756 
    757         ColorRange(Range<Float> hue, Range<Float> saturation, Range<Float> lightness) {
    758             mHue = hue;
    759             mSaturation = saturation;
    760             mLightness = lightness;
    761         }
    762 
    763         boolean containsColor(float h, float s, float l) {
    764             if (!mHue.contains(h)) {
    765                 return false;
    766             } else if (!mSaturation.contains(s)) {
    767                 return false;
    768             } else if (!mLightness.contains(l)) {
    769                 return false;
    770             }
    771             return true;
    772         }
    773 
    774         float[] getCenter() {
    775             return new float[] {
    776                     mHue.getLower() + (mHue.getUpper() - mHue.getLower()) / 2f,
    777                     mSaturation.getLower() + (mSaturation.getUpper() - mSaturation.getLower()) / 2f,
    778                     mLightness.getLower() + (mLightness.getUpper() - mLightness.getLower()) / 2f
    779             };
    780         }
    781 
    782         @Override
    783         public String toString() {
    784             return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
    785         }
    786     }
    787 
    788     private static List<Integer> getMainColors(WallpaperColorsCompat wallpaperColors) {
    789         LinkedList<Integer> colors = new LinkedList<>();
    790         if (wallpaperColors.getPrimaryColor() != 0) {
    791             colors.add(wallpaperColors.getPrimaryColor());
    792         }
    793         if (wallpaperColors.getSecondaryColor() != 0) {
    794             colors.add(wallpaperColors.getSecondaryColor());
    795         }
    796         if (wallpaperColors.getTertiaryColor() != 0) {
    797             colors.add(wallpaperColors.getTertiaryColor());
    798         }
    799         return colors;
    800     }
    801 }
    802