Merge "In-app gradient color based on systemui theme." into oc-dr1-dev

This commit is contained in:
Lucas Dupin
2017-07-13 17:03:47 +00:00
committed by Android (Google) Code Review
7 changed files with 164 additions and 90 deletions

View File

@@ -43,10 +43,6 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
private static final String TAG = "ColorExtractor";
public static final int FALLBACK_COLOR = 0xff83888d;
private int mMainFallbackColor = FALLBACK_COLOR;
private int mSecondaryFallbackColor = FALLBACK_COLOR;
private final SparseArray<GradientColors[]> mGradientColors;
private final ArrayList<OnColorsChangedListener> mOnColorsChangedListeners;
private final Context mContext;
@@ -73,6 +69,9 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
}
mOnColorsChangedListeners = new ArrayList<>();
GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
if (wallpaperManager == null) {
Log.w(TAG, "Can't listen to color changes!");
@@ -83,23 +82,18 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
Trace.beginSection("ColorExtractor#getWallpaperColors");
mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
GradientColors[] systemColors = mGradientColors.get(
WallpaperManager.FLAG_SYSTEM);
extractInto(mSystemColors,
systemColors[TYPE_NORMAL],
systemColors[TYPE_DARK],
systemColors[TYPE_EXTRA_DARK]);
GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
extractInto(mLockColors,
lockColors[TYPE_NORMAL],
lockColors[TYPE_DARK],
lockColors[TYPE_EXTRA_DARK]);
triggerColorsChanged(WallpaperManager.FLAG_SYSTEM
| WallpaperManager.FLAG_LOCK);
Trace.endSection();
}
// Initialize all gradients with the current colors
extractInto(mSystemColors,
systemColors[TYPE_NORMAL],
systemColors[TYPE_DARK],
systemColors[TYPE_EXTRA_DARK]);
extractInto(mLockColors,
lockColors[TYPE_NORMAL],
lockColors[TYPE_DARK],
lockColors[TYPE_EXTRA_DARK]);
}
/**
@@ -181,25 +175,8 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
private void extractInto(WallpaperColors inWallpaperColors,
GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark,
GradientColors outGradientColorsExtraDark) {
if (inWallpaperColors == null) {
applyFallback(outGradientColorsNormal);
applyFallback(outGradientColorsDark);
applyFallback(outGradientColorsExtraDark);
return;
}
boolean success = mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
outGradientColorsDark, outGradientColorsExtraDark);
if (!success) {
applyFallback(outGradientColorsNormal);
applyFallback(outGradientColorsDark);
applyFallback(outGradientColorsExtraDark);
}
}
private void applyFallback(GradientColors outGradientColors) {
outGradientColors.setMainColor(mMainFallbackColor);
outGradientColors.setSecondaryColor(mSecondaryFallbackColor);
outGradientColors.setSupportsDarkText(false);
}
public void destroy() {
@@ -218,8 +195,8 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener
}
public static class GradientColors {
private int mMainColor = FALLBACK_COLOR;
private int mSecondaryColor = FALLBACK_COLOR;
private int mMainColor;
private int mSecondaryColor;
private boolean mSupportsDarkText;
public void setMainColor(int mainColor) {

View File

@@ -38,9 +38,8 @@ public interface ExtractionType {
* @param outGradientColorsNormal object that should receive normal colors
* @param outGradientColorsDark object that should receive dark colors
* @param outGradientColorsExtraDark object that should receive extra dark colors
* @return true if successful.
*/
boolean extractInto(WallpaperColors inWallpaperColors,
void extractInto(WallpaperColors inWallpaperColors,
ColorExtractor.GradientColors outGradientColorsNormal,
ColorExtractor.GradientColors outGradientColorsDark,
ColorExtractor.GradientColors outGradientColorsExtraDark);

View File

@@ -44,24 +44,54 @@ public class Tonal implements ExtractionType {
private static final boolean DEBUG = true;
public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0;
public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e;
public static final int MAIN_COLOR_DARK = 0xff212121;
public static final int SECONDARY_COLOR_DARK = 0xff000000;
// Temporary variable to avoid allocations
private float[] mTmpHSL = new float[3];
/**
* Grab colors from WallpaperColors as set them into GradientColors
* Grab colors from WallpaperColors and set them into GradientColors.
* Also applies the default gradient in case extraction fails.
*
* @param inWallpaperColors input
* @param outColorsNormal colors for normal theme
* @param outColorsDark colors for dar theme
* @param outColorsExtraDark colors for extra dark theme
* @return true if successful
* @param inWallpaperColors Input.
* @param outColorsNormal Colors for normal theme.
* @param outColorsDark Colors for dar theme.
* @param outColorsExtraDark Colors for extra dark theme.
*/
public boolean extractInto(@NonNull WallpaperColors inWallpaperColors,
public void extractInto(@Nullable WallpaperColors inWallpaperColors,
@NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
@NonNull GradientColors outColorsExtraDark) {
boolean success = runTonalExtraction(inWallpaperColors, outColorsNormal, outColorsDark,
outColorsExtraDark);
if (!success) {
applyFallback(inWallpaperColors, outColorsNormal, outColorsDark, outColorsExtraDark);
}
}
/**
* Grab colors from WallpaperColors and set them into GradientColors.
*
* @param inWallpaperColors Input.
* @param outColorsNormal Colors for normal theme.
* @param outColorsDark Colors for dar theme.
* @param outColorsExtraDark Colors for extra dark theme.
* @return True if succeeded or false if failed.
*/
private boolean runTonalExtraction(@Nullable WallpaperColors inWallpaperColors,
@NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
@NonNull GradientColors outColorsExtraDark) {
if (inWallpaperColors == null) {
return false;
}
final List<Color> mainColors = inWallpaperColors.getMainColors();
final int mainColorsSize = mainColors.size();
final boolean supportsDarkText = (inWallpaperColors.getColorHints() &
WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
if (mainColorsSize == 0) {
return false;
@@ -120,7 +150,6 @@ public class Tonal implements ExtractionType {
float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
final int textInversionIndex = h.length - 3;
if (DEBUG) {
StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex +
". Main color: " + Integer.toHexString(getColorInt(fitIndex, h, s, l)) +
@@ -135,21 +164,38 @@ public class Tonal implements ExtractionType {
Log.d(TAG, builder.toString());
}
int primaryIndex = fitIndex;
int mainColor = getColorInt(primaryIndex, h, s, l);
// We might want use the fallback in case the extracted color is brighter than our
// light fallback or darker than our dark fallback.
ColorUtils.colorToHSL(mainColor, mTmpHSL);
final float mainLuminosity = mTmpHSL[2];
ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
final float lightLuminosity = mTmpHSL[2];
if (mainLuminosity > lightLuminosity) {
return false;
}
ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
final float darkLuminosity = mTmpHSL[2];
if (mainLuminosity < darkLuminosity) {
return false;
}
// Normal colors:
// best fit + a 2 colors offset
int primaryIndex = fitIndex;
outColorsNormal.setMainColor(mainColor);
int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
outColorsNormal.setMainColor(getColorInt(primaryIndex, h, s, l));
outColorsNormal.setSecondaryColor(getColorInt(secondaryIndex, h, s, l));
// Dark colors:
// Stops at 4th color, only lighter if dark text is supported
if (fitIndex < 2) {
primaryIndex = 0;
} else if (fitIndex < textInversionIndex) {
primaryIndex = Math.min(fitIndex, 3);
} else {
if (supportsDarkText) {
primaryIndex = h.length - 1;
} else if (fitIndex < 2) {
primaryIndex = 0;
} else {
primaryIndex = Math.min(fitIndex, 3);
}
secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
outColorsDark.setMainColor(getColorInt(primaryIndex, h, s, l));
@@ -157,18 +203,17 @@ public class Tonal implements ExtractionType {
// Extra Dark:
// Stay close to dark colors until dark text is supported
if (fitIndex < 2) {
primaryIndex = 0;
} else if (fitIndex < textInversionIndex) {
primaryIndex = 2;
} else {
if (supportsDarkText) {
primaryIndex = h.length - 1;
} else if (fitIndex < 2) {
primaryIndex = 0;
} else {
primaryIndex = 2;
}
secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
outColorsExtraDark.setMainColor(getColorInt(primaryIndex, h, s, l));
outColorsExtraDark.setSecondaryColor(getColorInt(secondaryIndex, h, s, l));
final boolean supportsDarkText = fitIndex >= textInversionIndex;
outColorsNormal.setSupportsDarkText(supportsDarkText);
outColorsDark.setSupportsDarkText(supportsDarkText);
outColorsExtraDark.setSupportsDarkText(supportsDarkText);
@@ -181,6 +226,33 @@ public class Tonal implements ExtractionType {
return true;
}
private void applyFallback(@Nullable WallpaperColors inWallpaperColors,
GradientColors outColorsNormal, GradientColors outColorsDark,
GradientColors outColorsExtraDark) {
applyFallback(inWallpaperColors, outColorsNormal);
applyFallback(inWallpaperColors, outColorsDark);
applyFallback(inWallpaperColors, outColorsExtraDark);
}
/**
* Sets the gradient to the light or dark fallbacks based on the current wallpaper colors.
*
* @param inWallpaperColors Colors to read.
* @param outGradientColors Destination.
*/
public static void applyFallback(@Nullable WallpaperColors inWallpaperColors,
@NonNull GradientColors outGradientColors) {
boolean light = inWallpaperColors != null
&& (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT)
!= 0;
int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK;
outGradientColors.setMainColor(innerColor);
outGradientColors.setSecondaryColor(outerColor);
outGradientColors.setSupportsDarkText(light);
}
private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
mTmpHSL[1] = s[fitIndex];

View File

@@ -16,6 +16,7 @@
package com.android.systemui.colorextraction;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
import android.os.Handler;
@@ -47,10 +48,10 @@ public class SysuiColorExtractor extends ColorExtractor {
@VisibleForTesting
public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) {
super(context, type);
mWpHiddenColors = new GradientColors();
mWpHiddenColors.setMainColor(FALLBACK_COLOR);
mWpHiddenColors.setSecondaryColor(FALLBACK_COLOR);
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
updateDefaultGradients(systemColors);
if (registerVisibility) {
try {
@@ -71,6 +72,24 @@ public class SysuiColorExtractor extends ColorExtractor {
}
}
private void updateDefaultGradients(WallpaperColors colors) {
Tonal.applyFallback(colors, mWpHiddenColors);
}
@Override
public void onColorsChanged(WallpaperColors colors, int which) {
super.onColorsChanged(colors, which);
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
updateDefaultGradients(colors);
}
}
@VisibleForTesting
GradientColors getFallbackColors() {
return mWpHiddenColors;
}
/**
* Get TYPE_NORMAL colors when wallpaper is visible, or fallback otherwise.
*

View File

@@ -48,14 +48,12 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
@Test
public void getColors_usesGreyIfWallpaperNotVisible() {
ColorExtractor.GradientColors fallbackColors = new ColorExtractor.GradientColors();
fallbackColors.setMainColor(ColorExtractor.FALLBACK_COLOR);
fallbackColors.setSecondaryColor(ColorExtractor.FALLBACK_COLOR);
SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(), new Tonal(), false);
simulateEvent(extractor);
extractor.setWallpaperVisible(false);
ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
for (int which : sWhich) {
for (int type : sTypes) {
assertEquals("Not using fallback!", extractor.getColors(which, type),
@@ -76,7 +74,6 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
outGradientColorsNormal.set(colors);
outGradientColorsDark.set(colors);
outGradientColorsExtraDark.set(colors);
return true;
}, false);
simulateEvent(extractor);
extractor.setWallpaperVisible(true);
@@ -91,7 +88,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
private void simulateEvent(SysuiColorExtractor extractor) {
// Let's fake a color event
extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null, 0),
extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null, 0),
WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
}
}

View File

@@ -61,21 +61,6 @@ public class ColorExtractorTest {
.extractInto(any(), any(), any(), any());
}
@Test
public void getColors_usesFallbackIfFails() {
ExtractionType alwaysFail =
(inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
outGradientColorsExtraDark) -> false;
ColorExtractor extractor = new ColorExtractor(mContext, alwaysFail);
GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM);
assertEquals("Should be using the fallback color.",
colors.getMainColor(), ColorExtractor.FALLBACK_COLOR);
assertEquals("Should be using the fallback color.",
colors.getSecondaryColor(), ColorExtractor.FALLBACK_COLOR);
assertFalse("Dark text support should be false.", colors.supportsDarkText());
}
@Test
public void getColors_usesExtractedColors() {
GradientColors colorsExpectedNormal = new GradientColors();
@@ -96,8 +81,6 @@ public class ColorExtractorTest {
outGradientColorsNormal.set(colorsExpectedNormal);
outGradientColorsDark.set(colorsExpectedDark);
outGradientColorsExtraDark.set(colorsExpectedExtraDark);
// Successful extraction
return true;
};
ColorExtractor extractor = new ColorExtractor(mContext, type);

View File

@@ -39,6 +39,31 @@ import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class TonalTest {
@Test
public void extractInto_usesFallback() {
GradientColors normal = new GradientColors();
Tonal tonal = new Tonal();
tonal.extractInto(null, normal, new GradientColors(),
new GradientColors());
assertFalse("Should use fallback color if WallpaperColors is null.",
normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT);
}
@Test
public void extractInto_usesFallbackWhenTooLightOrDark() {
GradientColors normal = new GradientColors();
Tonal tonal = new Tonal();
tonal.extractInto(new WallpaperColors(Color.valueOf(0xff000000), null, null, 0),
normal, new GradientColors(), new GradientColors());
assertTrue("Should use fallback color if WallpaperColors is too dark.",
normal.getMainColor() == Tonal.MAIN_COLOR_DARK);
tonal.extractInto(new WallpaperColors(Color.valueOf(0xffffffff), null, null, 0),
normal, new GradientColors(), new GradientColors());
assertTrue("Should use fallback color if WallpaperColors is too light.",
normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT);
}
@Test
public void colorRange_containsColor() {
Tonal.ColorRange colorRange = new Tonal.ColorRange(new Range<>(0f, 50f),
@@ -72,8 +97,10 @@ public class TonalTest {
// Make sure that palette generation will fail
Tonal tonal = new Tonal();
boolean success = tonal.extractInto(colors, new GradientColors(), new GradientColors(),
GradientColors normal = new GradientColors();
tonal.extractInto(colors, normal, new GradientColors(),
new GradientColors());
assertFalse("Cannot generate a tonal palette from blacklisted colors ", success);
assertFalse("Cannot generate a tonal palette from blacklisted colors.",
normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT);
}
}