OSDN Git Service

Merge "Uninstalling app from recent apps shelf should not leave a hole." into ub...
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / graphics / IconPalette.java
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.graphics.ColorMatrix;
23 import android.graphics.ColorMatrixColorFilter;
24 import android.support.v4.graphics.ColorUtils;
25 import android.util.Log;
26
27 import com.android.launcher3.R;
28 import com.android.launcher3.util.Themes;
29
30 /**
31  * Contains colors based on the dominant color of an icon.
32  */
33 public class IconPalette {
34
35     private static final boolean DEBUG = false;
36     private static final String TAG = "IconPalette";
37
38     public static final IconPalette FOLDER_ICON_PALETTE = new IconPalette(Color.parseColor("#BDC1C6"));
39
40     private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
41     private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
42
43     public final int dominantColor;
44     public final int backgroundColor;
45     public final ColorMatrixColorFilter backgroundColorMatrixFilter;
46     public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
47     public final int textColor;
48     public final int secondaryColor;
49
50     private IconPalette(int color) {
51         dominantColor = color;
52         backgroundColor = getMutedColor(dominantColor);
53         ColorMatrix backgroundColorMatrix = new ColorMatrix();
54         Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
55         backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
56         // Get slightly more saturated background color.
57         Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
58         saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
59         textColor = getTextColorForBackground(backgroundColor);
60         secondaryColor = getLowContrastColor(backgroundColor);
61     }
62
63     /**
64      * Returns a color suitable for the progress bar color of preload icon.
65      */
66     public int getPreloadProgressColor(Context context) {
67         int result = dominantColor;
68
69         // Make sure that the dominant color has enough saturation to be visible properly.
70         float[] hsv = new float[3];
71         Color.colorToHSV(result, hsv);
72         if (hsv[1] < MIN_PRELOAD_COLOR_SATURATION) {
73             result = Themes.getColorAccent(context);
74         } else {
75             hsv[2] = Math.max(MIN_PRELOAD_COLOR_LIGHTNESS, hsv[2]);
76             result = Color.HSVToColor(hsv);
77         }
78         return result;
79     }
80
81     public static IconPalette fromDominantColor(int dominantColor) {
82         return new IconPalette(dominantColor);
83     }
84
85     /**
86      * Resolves a color such that it has enough contrast to be used as the
87      * color of an icon or text on the given background color.
88      *
89      * @return a color of the same hue with enough contrast against the background.
90      *
91      * This was copied from com.android.internal.util.NotificationColorUtil.
92      */
93     public static int resolveContrastColor(Context context, int color, int background) {
94         final int resolvedColor = resolveColor(context, color);
95
96         int contrastingColor = ensureTextContrast(resolvedColor, background);
97
98         if (contrastingColor != resolvedColor) {
99             if (DEBUG){
100                 Log.w(TAG, String.format(
101                         "Enhanced contrast of notification for %s " +
102                                 "%s (over background) by changing #%s to %s",
103                         context.getPackageName(),
104                         contrastChange(resolvedColor, contrastingColor, background),
105                         Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor)));
106             }
107         }
108         return contrastingColor;
109     }
110
111     /**
112      * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
113      *
114      * This was copied from com.android.internal.util.NotificationColorUtil.
115      */
116     private static int resolveColor(Context context, int color) {
117         if (color == Notification.COLOR_DEFAULT) {
118             return context.getColor(R.color.notification_icon_default_color);
119         }
120         return color;
121     }
122
123     /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */
124     private static String contrastChange(int colorOld, int colorNew, int bg) {
125         return String.format("from %.2f:1 to %.2f:1",
126                 ColorUtils.calculateContrast(colorOld, bg),
127                 ColorUtils.calculateContrast(colorNew, bg));
128     }
129
130     /**
131      * Finds a text color with sufficient contrast over bg that has the same hue as the original
132      * color.
133      *
134      * This was copied from com.android.internal.util.NotificationColorUtil.
135      */
136     private static int ensureTextContrast(int color, int bg) {
137         return findContrastColor(color, bg, true, 4.5);
138     }
139     /**
140      * Finds a suitable color such that there's enough contrast.
141      *
142      * @param color the color to start searching from.
143      * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
144      * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
145      * @param minRatio the minimum contrast ratio required.
146      * @return a color with the same hue as {@param color}, potentially darkened to meet the
147      *          contrast ratio.
148      *
149      * This was copied from com.android.internal.util.NotificationColorUtil.
150      */
151     private static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
152         int fg = findFg ? color : other;
153         int bg = findFg ? other : color;
154         if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
155             return color;
156         }
157
158         double[] lab = new double[3];
159         ColorUtils.colorToLAB(findFg ? fg : bg, lab);
160
161         double low = 0, high = lab[0];
162         final double a = lab[1], b = lab[2];
163         for (int i = 0; i < 15 && high - low > 0.00001; i++) {
164             final double l = (low + high) / 2;
165             if (findFg) {
166                 fg = ColorUtils.LABToColor(l, a, b);
167             } else {
168                 bg = ColorUtils.LABToColor(l, a, b);
169             }
170             if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
171                 low = l;
172             } else {
173                 high = l;
174             }
175         }
176         return ColorUtils.LABToColor(low, a, b);
177     }
178
179     private static int getMutedColor(int color) {
180         return getMutedColor(color, 0.87f);
181     }
182
183     private static int getMutedColor(int color, float whiteScrimAlpha) {
184         int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
185         return ColorUtils.compositeColors(whiteScrim, color);
186     }
187
188     private static int getTextColorForBackground(int backgroundColor) {
189         return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
190     }
191
192     private static int getLowContrastColor(int color) {
193         return getLighterOrDarkerVersionOfColor(color, 1.5f);
194     }
195
196     private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
197         int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
198         int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
199         int translucentWhiteOrBlack;
200         if (whiteMinAlpha >= 0) {
201             translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
202         } else if (blackMinAlpha >= 0) {
203             translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
204         } else {
205             translucentWhiteOrBlack = Color.WHITE;
206         }
207         return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
208     }
209 }