diff options
3 files changed, 113 insertions, 23 deletions
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 57c7efca5715..b9d3e75b9943 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -26,6 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Size; +import com.android.internal.graphics.ColorUtils; import com.android.internal.graphics.palette.Palette; import com.android.internal.graphics.palette.VariationalKMeansQuantizer; @@ -50,6 +51,14 @@ public final class WallpaperColors implements Parcelable { */ public static final int HINT_SUPPORTS_DARK_TEXT = 0x1; + /** + * Specifies that dark theme is preferred over the current wallpaper for best presentation. + * <p> + * eg. A launcher may set its drawer color to black if this flag is specified. + * @hide + */ + public static final int HINT_SUPPORTS_DARK_THEME = 0x2; + // Maximum size that a bitmap can have to keep our calculations sane private static final int MAX_BITMAP_SIZE = 112; @@ -61,8 +70,10 @@ public final class WallpaperColors implements Parcelable { // present in at least MIN_COLOR_OCCURRENCE of the image private static final float MIN_COLOR_OCCURRENCE = 0.05f; + // Decides when dark theme is optimal for this wallpaper + private static final float DARK_THEME_MEAN_LUMINANCE = 0.25f; // Minimum mean luminosity that an image needs to have to support dark text - private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.9f; + private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.75f; // We also check if the image has dark pixels in it, // to avoid bright images with some dark spots. private static final float DARK_PIXEL_LUMINANCE = 0.45f; @@ -169,10 +180,7 @@ public final class WallpaperColors implements Parcelable { } } - int hints = 0; - if (calculateDarkTextSupport(bitmap)) { - hints |= HINT_SUPPORTS_DARK_TEXT; - } + int hints = calculateHints(bitmap); return new WallpaperColors(primary, secondary, tertiary, hints); } @@ -335,9 +343,9 @@ public final class WallpaperColors implements Parcelable { * @param source What to read. * @return Whether image supports dark text or not. */ - private static boolean calculateDarkTextSupport(Bitmap source) { + private static int calculateHints(Bitmap source) { if (source == null) { - return false; + return 0; } int[] pixels = new int[source.getWidth() * source.getHeight()]; @@ -349,22 +357,29 @@ public final class WallpaperColors implements Parcelable { // This bitmap was already resized to fit the maximum allowed area. // Let's just loop through the pixels, no sweat! + float[] tmpHsl = new float[3]; for (int i = 0; i < pixels.length; i++) { - final float luminance = Color.luminance(pixels[i]); + ColorUtils.colorToHSL(pixels[i], tmpHsl); + final float luminance = tmpHsl[2]; final int alpha = Color.alpha(pixels[i]); - // Make sure we don't have a dark pixel mass that will // make text illegible. if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) { darkPixels++; - if (darkPixels > maxDarkPixels) { - return false; - } } - totalLuminance += luminance; } - return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE; + + int hints = 0; + double meanLuminance = totalLuminance / pixels.length; + if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) { + hints |= HINT_SUPPORTS_DARK_TEXT; + } + if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) { + hints |= HINT_SUPPORTS_DARK_THEME; + } + + return hints; } private static Size calculateOptimalSize(int width, int height) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index a882e7ad5ea4..20c2ad66ec58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4576,15 +4576,10 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean useDarkText = mColorExtractor.getColors(which, true /* ignoreVisibility */) .supportsDarkText(); // And wallpaper defines if QS should be light or dark. - boolean useDarkTheme = false; - final WallpaperColors systemColors = - mColorExtractor.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); - if (systemColors != null) { - int mainColor = systemColors.getPrimaryColor().toArgb(); - float[] hsl = new float[3]; - ColorUtils.colorToHSL(mainColor, hsl); - useDarkTheme = hsl[2] < 0.2f; - } + WallpaperColors systemColors = mColorExtractor + .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + final boolean useDarkTheme = systemColors != null + && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; // Enable/disable dark UI. if (isUsingDarkTheme() != useDarkTheme) { diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java new file mode 100644 index 000000000000..5bbd82bea3de --- /dev/null +++ b/tests/Internal/src/android/app/WallpaperColorsTest.java @@ -0,0 +1,80 @@ +/* + * 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 android.app; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WallpaperColorsTest { + + @Test + public void supportsDarkTextOverrideTest() { + final Color color = Color.valueOf(Color.WHITE); + // Default should not support dark text! + WallpaperColors colors = new WallpaperColors(color, null, null, 0); + Assert.assertTrue("Default behavior is not to support dark text", + (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0); + + // Override it + colors = new WallpaperColors(color, null, null, WallpaperColors.HINT_SUPPORTS_DARK_TEXT); + Assert.assertFalse("Forcing dark text support doesn't work", + (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0); + } + + /** + * Sanity check to guarantee that white supports dark text and black doesn't + */ + @Test + public void colorHintsTest() { + Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + + canvas.drawColor(Color.WHITE); + int hints = WallpaperColors.fromBitmap(image).getColorHints(); + boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; + boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + Assert.assertTrue("White surface should support dark text", supportsDarkText); + Assert.assertFalse("White surface shouldn't support dark theme", supportsDarkTheme); + + canvas.drawColor(Color.BLACK); + hints = WallpaperColors.fromBitmap(image).getColorHints(); + supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; + supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0; + Assert.assertFalse("Black surface shouldn't support dark text", supportsDarkText); + Assert.assertTrue("Black surface should support dark theme", supportsDarkTheme); + + Paint paint = new Paint(); + paint.setStyle(Paint.Style.FILL); + paint.setColor(Color.BLACK); + canvas.drawColor(Color.WHITE); + canvas.drawRect(0, 0, 8, 8, paint); + supportsDarkText = (WallpaperColors.fromBitmap(image) + .getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; + Assert.assertFalse("Light surface shouldn't support dark text " + + "when it contains dark pixels", supportsDarkText); + } +} |