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.systemui.statusbar.notification; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Color; 21 22 import androidx.palette.graphics.Palette; 23 24 import java.util.List; 25 26 /** 27 * A gutted class that now contains only a color extraction utility used by the 28 * MediaArtworkProcessor, which has otherwise supplanted this. 29 * 30 * TODO(b/182926117): move this into MediaArtworkProcessor.kt 31 */ 32 public class MediaNotificationProcessor { 33 34 /** 35 * The population fraction to select a white or black color as the background over a color. 36 */ 37 private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f; 38 private static final float BLACK_MAX_LIGHTNESS = 0.08f; 39 private static final float WHITE_MIN_LIGHTNESS = 0.90f; 40 private static final int RESIZE_BITMAP_AREA = 150 * 150; 41 MediaNotificationProcessor()42 private MediaNotificationProcessor() { 43 } 44 45 /** 46 * Finds an appropriate background swatch from media artwork. 47 * 48 * @param artwork Media artwork 49 * @return Swatch that should be used as the background of the media notification. 50 */ findBackgroundSwatch(Bitmap artwork)51 public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) { 52 return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate()); 53 } 54 55 /** 56 * Finds an appropriate background swatch from the palette of media artwork. 57 * 58 * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder} 59 * @return Swatch that should be used as the background of the media notification. 60 */ findBackgroundSwatch(Palette palette)61 public static Palette.Swatch findBackgroundSwatch(Palette palette) { 62 // by default we use the dominant palette 63 Palette.Swatch dominantSwatch = palette.getDominantSwatch(); 64 if (dominantSwatch == null) { 65 return new Palette.Swatch(Color.WHITE, 100); 66 } 67 68 if (!isWhiteOrBlack(dominantSwatch.getHsl())) { 69 return dominantSwatch; 70 } 71 // Oh well, we selected black or white. Lets look at the second color! 72 List<Palette.Swatch> swatches = palette.getSwatches(); 73 float highestNonWhitePopulation = -1; 74 Palette.Swatch second = null; 75 for (Palette.Swatch swatch : swatches) { 76 if (swatch != dominantSwatch 77 && swatch.getPopulation() > highestNonWhitePopulation 78 && !isWhiteOrBlack(swatch.getHsl())) { 79 second = swatch; 80 highestNonWhitePopulation = swatch.getPopulation(); 81 } 82 } 83 if (second == null) { 84 return dominantSwatch; 85 } 86 if (dominantSwatch.getPopulation() / highestNonWhitePopulation 87 > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) { 88 // The dominant swatch is very dominant, lets take it! 89 // We're not filtering on white or black 90 return dominantSwatch; 91 } else { 92 return second; 93 } 94 } 95 96 /** 97 * Generate a palette builder for media artwork. 98 * 99 * For producing a smooth background transition, the palette is extracted from only the left 100 * side of the artwork. 101 * 102 * @param artwork Media artwork 103 * @return Builder that generates the {@link Palette} for the media artwork. 104 */ generateArtworkPaletteBuilder(Bitmap artwork)105 public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) { 106 // for the background we only take the left side of the image to ensure 107 // a smooth transition 108 return Palette.from(artwork) 109 .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight()) 110 .clearFilters() // we want all colors, red / white / black ones too! 111 .resizeBitmapArea(RESIZE_BITMAP_AREA); 112 } 113 isWhiteOrBlack(float[] hsl)114 private static boolean isWhiteOrBlack(float[] hsl) { 115 return isBlack(hsl) || isWhite(hsl); 116 } 117 118 /** 119 * @return true if the color represents a color which is close to black. 120 */ isBlack(float[] hslColor)121 private static boolean isBlack(float[] hslColor) { 122 return hslColor[2] <= BLACK_MAX_LIGHTNESS; 123 } 124 125 /** 126 * @return true if the color represents a color which is close to white. 127 */ isWhite(float[] hslColor)128 private static boolean isWhite(float[] hslColor) { 129 return hslColor[2] >= WHITE_MIN_LIGHTNESS; 130 } 131 } 132