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