Loading core/java/android/app/WallpaperColors.java +29 −14 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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()]; Loading @@ -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; } totalLuminance += luminance; int hints = 0; double meanLuminance = totalLuminance / pixels.length; if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) { hints |= HINT_SUPPORTS_DARK_TEXT; } return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE; if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) { hints |= HINT_SUPPORTS_DARK_THEME; } return hints; } private static Size calculateOptimalSize(int width, int height) { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +4 −9 Original line number Diff line number Diff line Loading @@ -4587,15 +4587,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) { Loading tests/Internal/src/android/app/WallpaperColorsTest.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * 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); } } Loading
core/java/android/app/WallpaperColors.java +29 −14 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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()]; Loading @@ -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; } totalLuminance += luminance; int hints = 0; double meanLuminance = totalLuminance / pixels.length; if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) { hints |= HINT_SUPPORTS_DARK_TEXT; } return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE; if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) { hints |= HINT_SUPPORTS_DARK_THEME; } return hints; } private static Size calculateOptimalSize(int width, int height) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +4 −9 Original line number Diff line number Diff line Loading @@ -4587,15 +4587,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) { Loading
tests/Internal/src/android/app/WallpaperColorsTest.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * 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); } }