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

Commit c77b71d7 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

In-app gradient color based on systemui theme.

When viewing your scrim on top of an app (wallpaper not visible),
the gradient color should be dark if your wallpaper is dark and
light if your wallpaper is light.

Change-Id: I5f3aea5bf9ec066b7caecd7cadfd2814e3758bd1
Fixes: 63121129
Bug: 63365056
Test: runtest -x tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
Test: runtest -x packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
parent 2b6d358d
Loading
Loading
Loading
Loading
+16 −39
Original line number Diff line number Diff line
@@ -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);
            Trace.endSection();
        }

            GradientColors[] systemColors = mGradientColors.get(
                    WallpaperManager.FLAG_SYSTEM);
        // Initialize all gradients with the current colors
        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();
        }
    }

    /**
@@ -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) {
+1 −2
Original line number Diff line number Diff line
@@ -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);
+91 −19
Original line number Diff line number Diff line
@@ -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.
     */
    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 successful
     * @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.
     */
    public boolean extractInto(@NonNull WallpaperColors inWallpaperColors,
    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) {
        if (supportsDarkText) {
            primaryIndex = h.length - 1;
        } else if (fitIndex < 2) {
            primaryIndex = 0;
        } else if (fitIndex < textInversionIndex) {
            primaryIndex = Math.min(fitIndex, 3);
        } else {
            primaryIndex = h.length - 1;
            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) {
        if (supportsDarkText) {
            primaryIndex = h.length - 1;
        } else if (fitIndex < 2) {
            primaryIndex = 0;
        } else if (fitIndex < textInversionIndex) {
            primaryIndex = 2;
        } else {
            primaryIndex = h.length - 1;
            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];
+22 −3
Original line number Diff line number Diff line
@@ -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.
     *
+3 −6
Original line number Diff line number Diff line
@@ -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);
    }
}
 No newline at end of file
Loading