• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics;
18 
19 import android.app.Notification;
20 import android.content.Context;
21 import android.graphics.Color;
22 import android.support.v4.graphics.ColorUtils;
23 import android.util.Log;
24 
25 import com.android.launcher3.R;
26 import com.android.launcher3.util.Themes;
27 
28 /**
29  * Contains colors based on the dominant color of an icon.
30  */
31 public class IconPalette {
32 
33     private static final boolean DEBUG = false;
34     private static final String TAG = "IconPalette";
35 
36     private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
37     private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
38 
39     /**
40      * Returns a color suitable for the progress bar color of preload icon.
41      */
getPreloadProgressColor(Context context, int dominantColor)42     public static int getPreloadProgressColor(Context context, int dominantColor) {
43         int result = dominantColor;
44 
45         // Make sure that the dominant color has enough saturation to be visible properly.
46         float[] hsv = new float[3];
47         Color.colorToHSV(result, hsv);
48         if (hsv[1] < MIN_PRELOAD_COLOR_SATURATION) {
49             result = Themes.getColorAccent(context);
50         } else {
51             hsv[2] = Math.max(MIN_PRELOAD_COLOR_LIGHTNESS, hsv[2]);
52             result = Color.HSVToColor(hsv);
53         }
54         return result;
55     }
56 
57     /**
58      * Resolves a color such that it has enough contrast to be used as the
59      * color of an icon or text on the given background color.
60      *
61      * @return a color of the same hue with enough contrast against the background.
62      *
63      * This was copied from com.android.internal.util.NotificationColorUtil.
64      */
resolveContrastColor(Context context, int color, int background)65     public static int resolveContrastColor(Context context, int color, int background) {
66         final int resolvedColor = resolveColor(context, color);
67 
68         int contrastingColor = ensureTextContrast(resolvedColor, background);
69 
70         if (contrastingColor != resolvedColor) {
71             if (DEBUG){
72                 Log.w(TAG, String.format(
73                         "Enhanced contrast of notification for %s " +
74                                 "%s (over background) by changing #%s to %s",
75                         context.getPackageName(),
76                         contrastChange(resolvedColor, contrastingColor, background),
77                         Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor)));
78             }
79         }
80         return contrastingColor;
81     }
82 
83     /**
84      * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
85      *
86      * This was copied from com.android.internal.util.NotificationColorUtil.
87      */
resolveColor(Context context, int color)88     private static int resolveColor(Context context, int color) {
89         if (color == Notification.COLOR_DEFAULT) {
90             return context.getColor(R.color.notification_icon_default_color);
91         }
92         return color;
93     }
94 
95     /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */
contrastChange(int colorOld, int colorNew, int bg)96     private static String contrastChange(int colorOld, int colorNew, int bg) {
97         return String.format("from %.2f:1 to %.2f:1",
98                 ColorUtils.calculateContrast(colorOld, bg),
99                 ColorUtils.calculateContrast(colorNew, bg));
100     }
101 
102     /**
103      * Finds a text color with sufficient contrast over bg that has the same hue as the original
104      * color.
105      *
106      * This was copied from com.android.internal.util.NotificationColorUtil.
107      */
ensureTextContrast(int color, int bg)108     private static int ensureTextContrast(int color, int bg) {
109         return findContrastColor(color, bg, 4.5);
110     }
111     /**
112      * Finds a suitable color such that there's enough contrast.
113      *
114      * @param fg the color to start searching from.
115      * @param bg the color to ensure contrast against.
116      * @param minRatio the minimum contrast ratio required.
117      * @return a color with the same hue as {@param color}, potentially darkened to meet the
118      *          contrast ratio.
119      *
120      * This was copied from com.android.internal.util.NotificationColorUtil.
121      */
findContrastColor(int fg, int bg, double minRatio)122     private static int findContrastColor(int fg, int bg, double minRatio) {
123         if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
124             return fg;
125         }
126 
127         double[] lab = new double[3];
128         ColorUtils.colorToLAB(bg, lab);
129         double bgL = lab[0];
130         ColorUtils.colorToLAB(fg, lab);
131         double fgL = lab[0];
132         boolean isBgDark = bgL < 50;
133 
134         double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
135         final double a = lab[1], b = lab[2];
136         for (int i = 0; i < 15 && high - low > 0.00001; i++) {
137             final double l = (low + high) / 2;
138             fg = ColorUtils.LABToColor(l, a, b);
139             if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
140                 if (isBgDark) high = l; else low = l;
141             } else {
142                 if (isBgDark) low = l; else high = l;
143             }
144         }
145         return ColorUtils.LABToColor(low, a, b);
146     }
147 
getMutedColor(int color, float whiteScrimAlpha)148     public static int getMutedColor(int color, float whiteScrimAlpha) {
149         int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
150         return ColorUtils.compositeColors(whiteScrim, color);
151     }
152 }
153