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

Commit f62c474b authored by Jeff DeCew's avatar Jeff DeCew Committed by Automerger Merge Worker
Browse files

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

Merge "Resolve Notification.Colors once, instead of lazily." into sc-dev am: f294d0ba am: 8d6af97d am: 9dc3fc72

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14140988

Change-Id: Ife1e9a14027a89c2f2c11865d897adda5c809abb
parents 40e282d1 9dc3fc72
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;
    }

    /**