Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f294d0ba authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Resolve Notification.Colors once, instead of lazily." into sc-dev

parents 211f7f9e cab7d8e0
Loading
Loading
Loading
Loading
+238 −216

File changed.

Preview size limit exceeded, changes collapsed.

+4 −1
Original line number Diff line number Diff line
@@ -595,7 +595,9 @@ public class ContrastColorUtil {
        if (backgroundColor == Notification.COLOR_DEFAULT) {
            return !defaultBackgroundIsDark;
        }
        return ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
        // Color contrast ratio luminance midpoint, X: 1.05 / (X + 0.05) = (X + 0.05) / 0.05
        // Solved as X = sqrt(.05 * 1.05) - 0.05 = 0.17912878474
        return ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.17912878474;
    }

    public static double calculateLuminance(int backgroundColor) {
@@ -619,6 +621,7 @@ public class ContrastColorUtil {
    }

    public static boolean isColorLight(int backgroundColor) {
        // TODO(b/188947832): Use 0.17912878474 instead of 0.5 to ensure better contrast
        return calculateLuminance(backgroundColor) > 0.5f;
    }

+0 −8
Original line number Diff line number Diff line
@@ -17,14 +17,6 @@
  NOTE: You might also want to edit: packages/SystemUI/res/values-night/colors.xml
  -->
<resources>
    <!-- The primary text color if the text is on top of a dark background.
    This is also affects colorized notifications with dark backgrounds. -->
    <color name="notification_primary_text_color_dark">#ddffffff</color>

    <!-- The secondary text color if the text is on top of a dark background. -->
    <color name="notification_secondary_text_color_dark">#b2ffffff</color>

    <color name="notification_default_color_dark">#ddffffff</color>

    <color name="notification_primary_text_color_current">@color/notification_primary_text_color_dark</color>
    <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_dark</color>
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ android_test {
        "frameworks-core-util-lib",
        "mockwebserver",
        "guava",
        "androidx.core_core",
        "androidx.test.espresso.core",
        "androidx.test.ext.junit",
        "androidx.test.runner",
+145 −8
Original line number Diff line number Diff line
@@ -16,6 +16,14 @@

package android.app;

import static androidx.core.graphics.ColorUtils.calculateContrast;

import static com.android.compatibility.common.util.SystemUtil.runShellCommand;

import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.fail;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
@@ -27,10 +35,10 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
import android.content.res.Configuration;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -325,13 +333,142 @@ public class NotificationTest {
        assertNull(clone.getLocusId());
    }

    private Notification.Builder getMediaNotification() {
        MediaSession session = new MediaSession(mContext, "test");
        return new Notification.Builder(mContext, "color")
                .setSmallIcon(com.android.internal.R.drawable.emergency_icon)
                .setContentTitle("Title")
                .setContentText("Text")
                .setStyle(new Notification.MediaStyle().setMediaSession(session.getSessionToken()));
    @Test
    public void testColors_ensureColors_dayMode_producesValidPalette() {
        Notification.Colors c = new Notification.Colors();
        boolean colorized = false;
        boolean nightMode = false;
        resolveColorsInNightMode(nightMode, c, Color.BLUE, colorized);
        assertValid(c);
    }

    @Test
    public void testColors_ensureColors_nightMode_producesValidPalette() {
        Notification.Colors c = new Notification.Colors();
        boolean colorized = false;
        boolean nightMode = true;
        resolveColorsInNightMode(nightMode, c, Color.BLUE, colorized);
        assertValid(c);
    }

    @Test
    public void testColors_ensureColors_colorized_producesValidPalette_default() {
        validateColorizedPaletteForColor(Notification.COLOR_DEFAULT);
    }

    @Test
    public void testColors_ensureColors_colorized_producesValidPalette_blue() {
        validateColorizedPaletteForColor(Color.BLUE);
    }

    @Test
    public void testColors_ensureColors_colorized_producesValidPalette_red() {
        validateColorizedPaletteForColor(Color.RED);
    }

    @Test
    public void testColors_ensureColors_colorized_producesValidPalette_white() {
        validateColorizedPaletteForColor(Color.WHITE);
    }

    @Test
    public void testColors_ensureColors_colorized_producesValidPalette_black() {
        validateColorizedPaletteForColor(Color.BLACK);
    }

    public void validateColorizedPaletteForColor(int rawColor) {
        Notification.Colors cDay = new Notification.Colors();
        Notification.Colors cNight = new Notification.Colors();
        boolean colorized = true;

        resolveColorsInNightMode(false, cDay, rawColor, colorized);
        resolveColorsInNightMode(true, cNight, rawColor, colorized);

        if (rawColor != Notification.COLOR_DEFAULT) {
            assertEquals(rawColor, cDay.getBackgroundColor());
            assertEquals(rawColor, cNight.getBackgroundColor());
        }

        assertValid(cDay);
        assertValid(cNight);

        if (rawColor != Notification.COLOR_DEFAULT) {
            // When a color is provided, night mode should have no effect on the notification
            assertEquals(cDay.getBackgroundColor(), cNight.getBackgroundColor());
            assertEquals(cDay.getPrimaryTextColor(), cNight.getPrimaryTextColor());
            assertEquals(cDay.getSecondaryTextColor(), cNight.getSecondaryTextColor());
            assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor());
            assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor());
            assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor());
            assertEquals(cDay.getContrastColor(), cNight.getContrastColor());
            assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha());
        }
    }

    private void assertValid(Notification.Colors c) {
        // Assert that all colors are populated
        assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getProtectionColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getPrimaryTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getSecondaryTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID);
        assertThat(c.getRippleAlpha()).isAtLeast(0x00);
        assertThat(c.getRippleAlpha()).isAtMost(0xff);

        // Assert that various colors have sufficient contrast
        assertContrastIsAtLeast(c.getPrimaryTextColor(), c.getBackgroundColor(), 4.5);
        assertContrastIsAtLeast(c.getSecondaryTextColor(), c.getBackgroundColor(), 4.5);
        assertContrastIsAtLeast(c.getPrimaryAccentColor(), c.getBackgroundColor(), 4.5);
        assertContrastIsAtLeast(c.getErrorColor(), c.getBackgroundColor(), 4.5);
        assertContrastIsAtLeast(c.getContrastColor(), c.getBackgroundColor(), 4.5);

        // This accent color is only used for emphasized buttons
        assertContrastIsAtLeast(c.getSecondaryAccentColor(), c.getBackgroundColor(), 1);
    }

    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,
                () -> c.resolvePalette(mContext, rawColor, colorized, nightMode));
    }

    private void runInNightMode(boolean nightMode, Runnable task) {
        final String initialNightMode = changeNightMode(nightMode);
        try {
            Configuration currentConfig = mContext.getResources().getConfiguration();
            boolean isNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                    == Configuration.UI_MODE_NIGHT_YES;
            assertEquals(nightMode, isNightMode);
            task.run();
        } finally {
            runShellCommand("cmd uimode night " + initialNightMode);
        }
    }


    // Change the night mode and return the previous mode
    private String changeNightMode(boolean nightMode) {
        final String nightModeText = runShellCommand("cmd uimode night");
        final String[] nightModeSplit = nightModeText.split(":");
        if (nightModeSplit.length != 2) {
            fail("Failed to get initial night mode value from " + nightModeText);
        }
        String previousMode = nightModeSplit[1].trim();
        runShellCommand("cmd uimode night " + (nightMode ? "yes" : "no"));
        return previousMode;
    }

    /**