| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3.graphics; |
| |
| import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; |
| |
| import android.app.Notification; |
| import android.content.Context; |
| import android.graphics.Color; |
| import android.util.Log; |
| |
| import com.android.launcher3.R; |
| import com.android.launcher3.util.Themes; |
| |
| import androidx.core.graphics.ColorUtils; |
| |
| /** |
| * Contains colors based on the dominant color of an icon. |
| */ |
| public class IconPalette { |
| |
| private static final boolean DEBUG = false; |
| private static final String TAG = "IconPalette"; |
| |
| private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f; |
| private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f; |
| |
| /** |
| * Returns a color suitable for the progress bar color of preload icon. |
| */ |
| public static int getPreloadProgressColor(Context context, int dominantColor) { |
| int result = dominantColor; |
| |
| // Make sure that the dominant color has enough saturation to be visible properly. |
| float[] hsv = new float[3]; |
| Color.colorToHSV(result, hsv); |
| if (hsv[1] < MIN_PRELOAD_COLOR_SATURATION) { |
| result = Themes.getColorAccent(context); |
| } else { |
| hsv[2] = Math.max(MIN_PRELOAD_COLOR_LIGHTNESS, hsv[2]); |
| result = Color.HSVToColor(hsv); |
| } |
| return result; |
| } |
| |
| /** |
| * Resolves a color such that it has enough contrast to be used as the |
| * color of an icon or text on the given background color. |
| * |
| * @return a color of the same hue with enough contrast against the background. |
| * |
| * This was copied from com.android.internal.util.NotificationColorUtil. |
| */ |
| public static int resolveContrastColor(Context context, int color, int background) { |
| final int resolvedColor = resolveColor(context, color); |
| |
| int contrastingColor = ensureTextContrast(resolvedColor, background); |
| |
| if (contrastingColor != resolvedColor) { |
| if (DEBUG){ |
| Log.w(TAG, String.format( |
| "Enhanced contrast of notification for %s " + |
| "%s (over background) by changing #%s to %s", |
| context.getPackageName(), |
| contrastChange(resolvedColor, contrastingColor, background), |
| Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor))); |
| } |
| } |
| return contrastingColor; |
| } |
| |
| /** |
| * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT} |
| * |
| * This was copied from com.android.internal.util.NotificationColorUtil. |
| */ |
| private static int resolveColor(Context context, int color) { |
| if (color == Notification.COLOR_DEFAULT) { |
| return context.getColor(R.color.notification_icon_default_color); |
| } |
| return color; |
| } |
| |
| /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */ |
| private static String contrastChange(int colorOld, int colorNew, int bg) { |
| return String.format("from %.2f:1 to %.2f:1", |
| ColorUtils.calculateContrast(colorOld, bg), |
| ColorUtils.calculateContrast(colorNew, bg)); |
| } |
| |
| /** |
| * Finds a text color with sufficient contrast over bg that has the same hue as the original |
| * color. |
| * |
| * This was copied from com.android.internal.util.NotificationColorUtil. |
| */ |
| private static int ensureTextContrast(int color, int bg) { |
| return findContrastColor(color, bg, 4.5); |
| } |
| /** |
| * Finds a suitable color such that there's enough contrast. |
| * |
| * @param fg the color to start searching from. |
| * @param bg the color to ensure contrast against. |
| * @param minRatio the minimum contrast ratio required. |
| * @return a color with the same hue as {@param color}, potentially darkened to meet the |
| * contrast ratio. |
| * |
| * This was copied from com.android.internal.util.NotificationColorUtil. |
| */ |
| private static int findContrastColor(int fg, int bg, double minRatio) { |
| if (ColorUtils.calculateContrast(fg, bg) >= minRatio) { |
| return fg; |
| } |
| |
| double[] lab = new double[3]; |
| ColorUtils.colorToLAB(bg, lab); |
| double bgL = lab[0]; |
| ColorUtils.colorToLAB(fg, lab); |
| double fgL = lab[0]; |
| boolean isBgDark = bgL < 50; |
| |
| double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL; |
| final double a = lab[1], b = lab[2]; |
| for (int i = 0; i < 15 && high - low > 0.00001; i++) { |
| final double l = (low + high) / 2; |
| fg = ColorUtils.LABToColor(l, a, b); |
| if (ColorUtils.calculateContrast(fg, bg) > minRatio) { |
| if (isBgDark) high = l; else low = l; |
| } else { |
| if (isBgDark) low = l; else high = l; |
| } |
| } |
| return ColorUtils.LABToColor(low, a, b); |
| } |
| |
| public static int getMutedColor(int color, float whiteScrimAlpha) { |
| int whiteScrim = setColorAlphaBound(Color.WHITE, (int) (255 * whiteScrimAlpha)); |
| return ColorUtils.compositeColors(whiteScrim, color); |
| } |
| } |