diff options
author | Jeff DeCew <jeffdq@google.com> | 2021-09-10 15:28:37 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-09-10 15:28:37 +0000 |
commit | 2f0616bd79b56d44436870b8e734b44770f5705a (patch) | |
tree | 40d1a93a1208ccd978b87821d3285623a5dca9ae /core/tests | |
parent | 8173263d78e19e5b1091d0d91be00dc7f8e7d21a (diff) | |
parent | 050a0d0d64a33d16584319929456552ecc151a42 (diff) |
Merge changes I0fc3b7ae,I712e6d6b into sc-qpr1-dev
* changes:
Reduce the color contrast requirements for the emphasized action button fill color.
Refactor the color span code inside Notification.java and add tests.
Diffstat (limited to 'core/tests')
-rw-r--r-- | core/tests/coretests/src/android/app/NotificationTest.java | 181 | ||||
-rw-r--r-- | core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java | 6 |
2 files changed, 173 insertions, 14 deletions
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 685671b083c4..34c1763b3286 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -16,9 +16,11 @@ package android.app; -import static androidx.core.graphics.ColorUtils.calculateContrast; +import static android.app.Notification.Builder.ensureColorSpanContrast; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; +import static com.android.internal.util.ContrastColorUtilTest.assertContrastIsAtLeast; +import static com.android.internal.util.ContrastColorUtilTest.assertContrastIsWithinRange; import static com.google.common.truth.Truth.assertThat; @@ -35,6 +37,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.Intent; import android.content.LocusId; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.graphics.BitmapFactory; import android.graphics.Color; @@ -42,12 +45,21 @@ import android.graphics.drawable.Icon; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.text.style.TextAppearanceSpan; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; +import com.android.internal.util.ContrastColorUtil; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -334,6 +346,163 @@ public class NotificationTest { } @Test + public void testBuilder_getFullLengthSpanColor_returnsNullForString() { + assertThat(Notification.Builder.getFullLengthSpanColor("String")).isNull(); + } + + @Test + public void testBuilder_getFullLengthSpanColor_returnsNullWithPartialSpan() { + CharSequence text = new SpannableStringBuilder() + .append("text with ") + .append("some red", new ForegroundColorSpan(Color.RED), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(Notification.Builder.getFullLengthSpanColor(text)).isNull(); + } + + @Test + public void testBuilder_getFullLengthSpanColor_worksWithSingleSpan() { + CharSequence text = new SpannableStringBuilder() + .append("text that is all red", new ForegroundColorSpan(Color.RED), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(Notification.Builder.getFullLengthSpanColor(text)).isEqualTo(Color.RED); + } + + @Test + public void testBuilder_getFullLengthSpanColor_worksWithFullAndPartialSpans() { + Spannable text = new SpannableString("blue text with yellow and green"); + text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.BLUE), 0, text.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.GREEN), 26, 31, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(Notification.Builder.getFullLengthSpanColor(text)).isEqualTo(Color.BLUE); + } + + @Test + public void testBuilder_getFullLengthSpanColor_worksWithTextAppearance() { + Spannable text = new SpannableString("title text with yellow and green"); + text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(mContext, + R.style.TextAppearance_DeviceDefault_Notification_Title); + int expectedTextColor = textAppearanceSpan.getTextColor().getDefaultColor(); + text.setSpan(textAppearanceSpan, 0, text.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.GREEN), 26, 31, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(Notification.Builder.getFullLengthSpanColor(text)).isEqualTo(expectedTextColor); + } + + @Test + public void testBuilder_ensureColorSpanContrast_removesAllFullLengthColorSpans() { + Spannable text = new SpannableString("blue text with yellow and green"); + text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.BLUE), 0, text.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + TextAppearanceSpan taSpan = new TextAppearanceSpan(mContext, + R.style.TextAppearance_DeviceDefault_Notification_Title); + assertThat(taSpan.getTextColor()).isNotNull(); // it must be set to prove it is cleared. + text.setSpan(taSpan, 0, text.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.GREEN), 26, 31, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + Spannable result = (Spannable) ensureColorSpanContrast(text, Color.BLACK); + Object[] spans = result.getSpans(0, result.length(), Object.class); + assertThat(spans).hasLength(3); + + assertThat(result.getSpanStart(spans[0])).isEqualTo(15); + assertThat(result.getSpanEnd(spans[0])).isEqualTo(21); + assertThat(((ForegroundColorSpan) spans[0]).getForegroundColor()).isEqualTo(Color.YELLOW); + + assertThat(result.getSpanStart(spans[1])).isEqualTo(0); + assertThat(result.getSpanEnd(spans[1])).isEqualTo(31); + assertThat(spans[1]).isNotSameInstanceAs(taSpan); // don't mutate the existing span + assertThat(((TextAppearanceSpan) spans[1]).getFamily()).isEqualTo(taSpan.getFamily()); + assertThat(((TextAppearanceSpan) spans[1]).getTextColor()).isNull(); + + assertThat(result.getSpanStart(spans[2])).isEqualTo(26); + assertThat(result.getSpanEnd(spans[2])).isEqualTo(31); + assertThat(((ForegroundColorSpan) spans[2]).getForegroundColor()).isEqualTo(Color.GREEN); + } + + @Test + public void testBuilder_ensureColorSpanContrast_partialLength_adjusted() { + int background = 0xFFFF0101; // Slightly lighter red + CharSequence text = new SpannableStringBuilder() + .append("text with ") + .append("some red", new ForegroundColorSpan(Color.RED), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + CharSequence result = ensureColorSpanContrast(text, background); + + // ensure the span has been updated to have > 1.3:1 contrast ratio with fill color + Object[] spans = ((Spannable) result).getSpans(0, result.length(), Object.class); + assertThat(spans).hasLength(1); + int foregroundColor = ((ForegroundColorSpan) spans[0]).getForegroundColor(); + assertContrastIsWithinRange(foregroundColor, background, 3, 3.2); + } + + @Test + public void testBuilder_ensureColorSpanContrast_worksWithComplexInput() { + Spannable text = new SpannableString("blue text with yellow and green and cyan"); + text.setSpan(new ForegroundColorSpan(Color.YELLOW), 15, 21, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.BLUE), 0, text.length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + // cyan TextAppearanceSpan + TextAppearanceSpan taSpan = new TextAppearanceSpan(mContext, + R.style.TextAppearance_DeviceDefault_Notification_Title); + taSpan = new TextAppearanceSpan(taSpan.getFamily(), taSpan.getTextStyle(), + taSpan.getTextSize(), ColorStateList.valueOf(Color.CYAN), null); + text.setSpan(taSpan, 36, 40, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(Color.GREEN), 26, 31, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + Spannable result = (Spannable) ensureColorSpanContrast(text, Color.GRAY); + Object[] spans = result.getSpans(0, result.length(), Object.class); + assertThat(spans).hasLength(3); + + assertThat(result.getSpanStart(spans[0])).isEqualTo(15); + assertThat(result.getSpanEnd(spans[0])).isEqualTo(21); + assertThat(((ForegroundColorSpan) spans[0]).getForegroundColor()).isEqualTo(Color.YELLOW); + + assertThat(result.getSpanStart(spans[1])).isEqualTo(36); + assertThat(result.getSpanEnd(spans[1])).isEqualTo(40); + assertThat(spans[1]).isNotSameInstanceAs(taSpan); // don't mutate the existing span + assertThat(((TextAppearanceSpan) spans[1]).getFamily()).isEqualTo(taSpan.getFamily()); + ColorStateList newCyanList = ((TextAppearanceSpan) spans[1]).getTextColor(); + assertThat(newCyanList).isNotNull(); + assertContrastIsWithinRange(newCyanList.getDefaultColor(), Color.GRAY, 3, 3.2); + + assertThat(result.getSpanStart(spans[2])).isEqualTo(26); + assertThat(result.getSpanEnd(spans[2])).isEqualTo(31); + int newGreen = ((ForegroundColorSpan) spans[2]).getForegroundColor(); + assertThat(newGreen).isNotEqualTo(Color.GREEN); + assertContrastIsWithinRange(newGreen, Color.GRAY, 3, 3.2); + } + + @Test + public void testBuilder_ensureButtonFillContrast_adjustsDarker() { + int background = Color.LTGRAY; + int foreground = Color.LTGRAY; + int result = Notification.Builder.ensureButtonFillContrast(foreground, background); + assertContrastIsWithinRange(result, background, 1.3, 1.5); + assertThat(ContrastColorUtil.calculateLuminance(result)) + .isLessThan(ContrastColorUtil.calculateLuminance(background)); + } + + @Test + public void testBuilder_ensureButtonFillContrast_adjustsLighter() { + int background = Color.DKGRAY; + int foreground = Color.DKGRAY; + int result = Notification.Builder.ensureButtonFillContrast(foreground, background); + assertContrastIsWithinRange(result, background, 1.3, 1.5); + assertThat(ContrastColorUtil.calculateLuminance(result)) + .isGreaterThan(ContrastColorUtil.calculateLuminance(background)); + } + + @Test public void testColors_ensureColors_dayMode_producesValidPalette() { Notification.Colors c = new Notification.Colors(); boolean colorized = false; @@ -437,16 +606,6 @@ public class NotificationTest { assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5); } - private void assertContrastIsAtLeast(int foreground, int background, double minContrast) { - try { - assertThat(calculateContrast(foreground, background)).isAtLeast(minContrast); - } catch (AssertionError e) { - throw new AssertionError( - String.format("Insufficient contrast: foreground=#%08x background=#%08x", - foreground, background), e); - } - } - private void resolveColorsInNightMode(boolean nightMode, Notification.Colors c, int rawColor, boolean colorized) { runInNightMode(nightMode, diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java index 9da720cbfa87..cfe660c77817 100644 --- a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java @@ -70,13 +70,13 @@ public class ContrastColorUtilTest extends TestCase { assertContrastIsWithinRange(selfContrastColor, lightBg, 4.5, 4.75); } - private void assertContrastIsWithinRange(int foreground, int background, + public static void assertContrastIsWithinRange(int foreground, int background, double minContrast, double maxContrast) { assertContrastIsAtLeast(foreground, background, minContrast); assertContrastIsAtMost(foreground, background, maxContrast); } - private void assertContrastIsAtLeast(int foreground, int background, double minContrast) { + public static void assertContrastIsAtLeast(int foreground, int background, double minContrast) { try { assertThat(calculateContrast(foreground, background)).isAtLeast(minContrast); } catch (AssertionError e) { @@ -86,7 +86,7 @@ public class ContrastColorUtilTest extends TestCase { } } - private void assertContrastIsAtMost(int foreground, int background, double maxContrast) { + public static void assertContrastIsAtMost(int foreground, int background, double maxContrast) { try { assertThat(calculateContrast(foreground, background)).isAtMost(maxContrast); } catch (AssertionError e) { |