Merge changes I858017b8,Ife32b600 into oc-dev
* changes: Improved the media color extraction algorithm Fixed broken coretests
This commit is contained in:
@@ -169,4 +169,8 @@ public class ImageFloatingTextView extends TextView {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getLayoutHeight() {
|
||||
return getLayout().getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class MessagingLinearLayout extends ViewGroup {
|
||||
first = false;
|
||||
boolean measuredTooSmall = false;
|
||||
if (textChild != null) {
|
||||
measuredTooSmall = childHeight < textChild.getLayout().getHeight()
|
||||
measuredTooSmall = childHeight < textChild.getLayoutHeight()
|
||||
+ textChild.getPaddingTop() + textChild.getPaddingBottom();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:maxHeight="300px"
|
||||
android:spacing="5px">
|
||||
|
||||
</com.android.internal.widget.MessagingLinearLayout>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
@@ -111,6 +111,9 @@ public class ImageFloatingTextViewTest {
|
||||
mTextView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
mView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
assertEquals(mTextView.getMeasuredHeight(), mView.getMeasuredHeight());
|
||||
// We're at most allowed to be the same height as the regular textview and maybe a bit
|
||||
// smaller since our layout snaps to full textlines.
|
||||
assertTrue("The measured view should never be taller then the normal textview!",
|
||||
mView.getMeasuredHeight() <= mTextView.getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Debug;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.espresso.core.deps.guava.base.Function;
|
||||
import android.support.test.filters.SmallTest;
|
||||
@@ -46,8 +45,7 @@ public class MessagingLinearLayoutTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
// maxHeight: 300px
|
||||
// spacing: 50px
|
||||
// spacing: 5px
|
||||
mView = (MessagingLinearLayout) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.messaging_linear_layout_test, null);
|
||||
}
|
||||
@@ -81,8 +79,8 @@ public class MessagingLinearLayoutTest {
|
||||
|
||||
assertEquals(3, child1.getNumIndentLines());
|
||||
assertEquals(0, child2.getNumIndentLines());
|
||||
assertFalse(child1.isHidden());
|
||||
assertFalse(child2.isHidden());
|
||||
assertFalse("child1 should not be hidden", child1.isHidden());
|
||||
assertFalse("child2 should not be hidden", child2.isHidden());
|
||||
assertEquals(205, mView.getMeasuredHeight());
|
||||
}
|
||||
|
||||
@@ -100,8 +98,8 @@ public class MessagingLinearLayoutTest {
|
||||
|
||||
assertEquals(2, child1.getNumIndentLines());
|
||||
assertEquals(1, child2.getNumIndentLines());
|
||||
assertFalse(child1.isHidden());
|
||||
assertFalse(child2.isHidden());
|
||||
assertFalse("child1 should not be hidden", child1.isHidden());
|
||||
assertFalse("child2 should not be hidden", child2.isHidden());
|
||||
assertEquals(105, mView.getMeasuredHeight());
|
||||
}
|
||||
|
||||
@@ -118,14 +116,14 @@ public class MessagingLinearLayoutTest {
|
||||
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
|
||||
|
||||
assertEquals(3, child2.getNumIndentLines());
|
||||
assertTrue(child1.isHidden());
|
||||
assertFalse(child2.isHidden());
|
||||
assertEquals(300, mView.getMeasuredHeight());
|
||||
assertTrue("child1 should be hidden", child1.isHidden());
|
||||
assertFalse("child2 should not be hidden", child2.isHidden());
|
||||
assertEquals(350, mView.getMeasuredHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeSmall_largeWrapsWith3indentbutnot3_andHitsMax() {
|
||||
FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 5 : 4);
|
||||
public void testLargeSmall_largeWrapsWith3indentbutNotFullHeight_andHitsMax() {
|
||||
FakeImageFloatingTextView child1 = fakeChild((i) -> i > 2 ? 7 : 6);
|
||||
FakeImageFloatingTextView child2 = fakeChild((i) -> 1);
|
||||
|
||||
mView.setNumIndentLines(2);
|
||||
@@ -135,10 +133,11 @@ public class MessagingLinearLayoutTest {
|
||||
mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
|
||||
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
|
||||
|
||||
assertTrue(child1.isHidden());
|
||||
assertFalse(child2.isHidden());
|
||||
assertEquals(50, mView.getMeasuredHeight());
|
||||
assertEquals(2, child2.getNumIndentLines());
|
||||
assertFalse("child1 should not be hidden", child1.isHidden());
|
||||
assertFalse("child2 should not be hidden", child2.isHidden());
|
||||
assertEquals(355, mView.getMeasuredHeight());
|
||||
assertEquals(3, child1.getNumIndentLines());
|
||||
assertEquals(0, child2.getNumIndentLines());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,8 +152,8 @@ public class MessagingLinearLayoutTest {
|
||||
mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
|
||||
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
|
||||
|
||||
assertFalse(child1.isHidden());
|
||||
assertFalse(child2.isHidden());
|
||||
assertFalse("child1 should not be hidden", child1.isHidden());
|
||||
assertFalse("child2 should not be hidden", child2.isHidden());
|
||||
assertEquals(255, mView.getMeasuredHeight());
|
||||
assertEquals(3, child1.getNumIndentLines());
|
||||
assertEquals(0, child2.getNumIndentLines());
|
||||
@@ -183,11 +182,24 @@ public class MessagingLinearLayoutTest {
|
||||
return mNumIndentLines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutHeight() {
|
||||
return Math.max(LINE_HEIGHT, getMeasuredHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(
|
||||
getDefaultSize(500, widthMeasureSpec),
|
||||
resolveSize(getDesiredHeight(), heightMeasureSpec));
|
||||
clampToMultiplesOfLineHeight(resolveSize(getDesiredHeight(),
|
||||
heightMeasureSpec)));
|
||||
}
|
||||
|
||||
private int clampToMultiplesOfLineHeight(int size) {
|
||||
if (size <= LINE_HEIGHT) {
|
||||
return size;
|
||||
}
|
||||
return (size / LINE_HEIGHT) * LINE_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.util.LayoutDirection;
|
||||
|
||||
@@ -41,10 +40,31 @@ public class MediaNotificationProcessor {
|
||||
/**
|
||||
* The fraction below which we select the vibrant instead of the light/dark vibrant color
|
||||
*/
|
||||
private static final float POPULATION_FRACTION_FOR_MORE_VIBRANT = 0.75f;
|
||||
private static final float POPULATION_FRACTION_FOR_MORE_VIBRANT = 1.0f;
|
||||
|
||||
/**
|
||||
* Minimum saturation that a muted color must have if there exists if deciding between two
|
||||
* colors
|
||||
*/
|
||||
private static final float MIN_SATURATION_WHEN_DECIDING = 0.19f;
|
||||
|
||||
/**
|
||||
* Minimum fraction that any color must have to be picked up as a text color
|
||||
*/
|
||||
private static final double MINIMUM_IMAGE_FRACTION = 0.002;
|
||||
|
||||
/**
|
||||
* The population fraction to select the dominant color as the text color over a the colored
|
||||
* ones.
|
||||
*/
|
||||
private static final float POPULATION_FRACTION_FOR_DOMINANT = 0.01f;
|
||||
|
||||
/**
|
||||
* The population fraction to select a white or black color as the background over a color.
|
||||
*/
|
||||
private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
|
||||
private static final float BLACK_MAX_LIGHTNESS = 0.08f;
|
||||
private static final float WHITE_MIN_LIGHTNESS = 0.92f;
|
||||
private static final float WHITE_MIN_LIGHTNESS = 0.90f;
|
||||
private static final int RESIZE_BITMAP_AREA = 150 * 150;
|
||||
private final ImageGradientColorizer mColorizer;
|
||||
private final Context mContext;
|
||||
@@ -109,8 +129,11 @@ public class MediaNotificationProcessor {
|
||||
.resizeBitmapArea(RESIZE_BITMAP_AREA);
|
||||
Palette palette = paletteBuilder.generate();
|
||||
backgroundColor = findBackgroundColorAndFilter(palette);
|
||||
// we want the full region again
|
||||
paletteBuilder.setRegion(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
// we want most of the full region again, slightly shifted to the right
|
||||
float textColorStartWidthFraction = 0.4f;
|
||||
paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0,
|
||||
bitmap.getWidth(),
|
||||
bitmap.getHeight());
|
||||
if (mFilteredBackgroundHsl != null) {
|
||||
paletteBuilder.addFilter((rgb, hsl) -> {
|
||||
// at least 10 degrees hue difference
|
||||
@@ -120,78 +143,7 @@ public class MediaNotificationProcessor {
|
||||
}
|
||||
paletteBuilder.addFilter(mBlackWhiteFilter);
|
||||
palette = paletteBuilder.generate();
|
||||
int foregroundColor;
|
||||
if (NotificationColorUtil.isColorLight(backgroundColor)) {
|
||||
Palette.Swatch first = palette.getDarkVibrantSwatch();
|
||||
Palette.Swatch second = palette.getVibrantSwatch();
|
||||
if (first != null && second != null) {
|
||||
int firstPopulation = first.getPopulation();
|
||||
int secondPopulation = second.getPopulation();
|
||||
if (firstPopulation / secondPopulation
|
||||
< POPULATION_FRACTION_FOR_MORE_VIBRANT) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
foregroundColor = first.getRgb();
|
||||
}
|
||||
} else if (first != null) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else if (second != null) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
first = palette.getMutedSwatch();
|
||||
second = palette.getDarkMutedSwatch();
|
||||
if (first != null && second != null) {
|
||||
float firstSaturation = first.getHsl()[1];
|
||||
float secondSaturation = second.getHsl()[1];
|
||||
if (firstSaturation > secondSaturation) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else {
|
||||
foregroundColor = second.getRgb();
|
||||
}
|
||||
} else if (first != null) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else if (second != null) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
foregroundColor = Color.BLACK;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Palette.Swatch first = palette.getLightVibrantSwatch();
|
||||
Palette.Swatch second = palette.getVibrantSwatch();
|
||||
if (first != null && second != null) {
|
||||
int firstPopulation = first.getPopulation();
|
||||
int secondPopulation = second.getPopulation();
|
||||
if (firstPopulation / secondPopulation
|
||||
< POPULATION_FRACTION_FOR_MORE_VIBRANT) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
foregroundColor = first.getRgb();
|
||||
}
|
||||
} else if (first != null) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else if (second != null) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
first = palette.getMutedSwatch();
|
||||
second = palette.getLightMutedSwatch();
|
||||
if (first != null && second != null) {
|
||||
float firstSaturation = first.getHsl()[1];
|
||||
float secondSaturation = second.getHsl()[1];
|
||||
if (firstSaturation > secondSaturation) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else {
|
||||
foregroundColor = second.getRgb();
|
||||
}
|
||||
} else if (first != null) {
|
||||
foregroundColor = first.getRgb();
|
||||
} else if (second != null) {
|
||||
foregroundColor = second.getRgb();
|
||||
} else {
|
||||
foregroundColor = Color.WHITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
int foregroundColor = selectForegroundColor(backgroundColor, palette);
|
||||
builder.setColorPalette(backgroundColor, foregroundColor);
|
||||
} else {
|
||||
int id = mIsLowPriority
|
||||
@@ -206,6 +158,95 @@ public class MediaNotificationProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private int selectForegroundColor(int backgroundColor, Palette palette) {
|
||||
if (NotificationColorUtil.isColorLight(backgroundColor)) {
|
||||
return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getDarkMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.BLACK);
|
||||
} else {
|
||||
return selectForegroundColorForSwatches(palette.getLightVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getLightMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
private int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
|
||||
Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
|
||||
Palette.Swatch dominantSwatch, int fallbackColor) {
|
||||
Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
|
||||
if (coloredCandidate == null) {
|
||||
coloredCandidate = selectMutedCandidate(mutedSwatch, moreMutedSwatch);
|
||||
}
|
||||
if (coloredCandidate != null) {
|
||||
if (dominantSwatch == coloredCandidate) {
|
||||
return coloredCandidate.getRgb();
|
||||
} else if ((float) coloredCandidate.getPopulation() / dominantSwatch.getPopulation()
|
||||
< POPULATION_FRACTION_FOR_DOMINANT
|
||||
&& dominantSwatch.getHsl()[1] > MIN_SATURATION_WHEN_DECIDING) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return coloredCandidate.getRgb();
|
||||
}
|
||||
} else if (hasEnoughPopulation(dominantSwatch)) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return fallbackColor;
|
||||
}
|
||||
}
|
||||
|
||||
private Palette.Swatch selectMutedCandidate(Palette.Swatch first,
|
||||
Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
float firstSaturation = first.getHsl()[1];
|
||||
float secondSaturation = second.getHsl()[1];
|
||||
float populationFraction = first.getPopulation() / (float) second.getPopulation();
|
||||
if (firstSaturation * populationFraction > secondSaturation) {
|
||||
return first;
|
||||
} else {
|
||||
return second;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Palette.Swatch selectVibrantCandidate(Palette.Swatch first, Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
int firstPopulation = first.getPopulation();
|
||||
int secondPopulation = second.getPopulation();
|
||||
if (firstPopulation / (float) secondPopulation
|
||||
< POPULATION_FRACTION_FOR_MORE_VIBRANT) {
|
||||
return second;
|
||||
} else {
|
||||
return first;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasEnoughPopulation(Palette.Swatch swatch) {
|
||||
// We want a fraction that is at least 1% of the image
|
||||
return swatch != null
|
||||
&& (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
|
||||
}
|
||||
|
||||
private int findBackgroundColorAndFilter(Palette palette) {
|
||||
// by default we use the dominant palette
|
||||
Palette.Swatch dominantSwatch = palette.getDominantSwatch();
|
||||
|
||||
Reference in New Issue
Block a user