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

Commit 04d2ec59 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "WallpaperColors refactor" into oc-dr1-dev

parents 963a2077 84b89d9d
Loading
Loading
Loading
Loading
+8 −4
Original line number Original line Diff line number Diff line
@@ -6089,13 +6089,17 @@ package android.app {
  public final class WallpaperColors implements android.os.Parcelable {
  public final class WallpaperColors implements android.os.Parcelable {
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
    ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
    method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
    method public boolean supportsDarkText();
    method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
    method public int getColorHints();
    method public android.graphics.Color getPrimaryColor();
    method public android.graphics.Color getSecondaryColor();
    method public android.graphics.Color getTertiaryColor();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
  }
  }
  public final class WallpaperInfo implements android.os.Parcelable {
  public final class WallpaperInfo implements android.os.Parcelable {
+8 −4
Original line number Original line Diff line number Diff line
@@ -6299,13 +6299,17 @@ package android.app {
  public final class WallpaperColors implements android.os.Parcelable {
  public final class WallpaperColors implements android.os.Parcelable {
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
    ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
    method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
    method public boolean supportsDarkText();
    method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
    method public int getColorHints();
    method public android.graphics.Color getPrimaryColor();
    method public android.graphics.Color getSecondaryColor();
    method public android.graphics.Color getTertiaryColor();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
  }
  }
  public final class WallpaperInfo implements android.os.Parcelable {
  public final class WallpaperInfo implements android.os.Parcelable {
+8 −4
Original line number Original line Diff line number Diff line
@@ -6110,13 +6110,17 @@ package android.app {
  public final class WallpaperColors implements android.os.Parcelable {
  public final class WallpaperColors implements android.os.Parcelable {
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(android.os.Parcel);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
    ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
    ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
    method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
    method public boolean supportsDarkText();
    method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
    method public int getColorHints();
    method public android.graphics.Color getPrimaryColor();
    method public android.graphics.Color getSecondaryColor();
    method public android.graphics.Color getTertiaryColor();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
    field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
  }
  }
  public final class WallpaperInfo implements android.os.Parcelable {
  public final class WallpaperInfo implements android.os.Parcelable {
+281 −56
Original line number Original line Diff line number Diff line
@@ -16,63 +16,203 @@


package android.app;
package android.app;


import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.util.Size;


import android.util.Pair;
import com.android.internal.graphics.palette.Palette;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.List;


/**
/**
 * A class containing information about the colors of a wallpaper.
 * Provides information about the colors of a wallpaper.
 * <p>
 * This class contains two main components:
 * <ul>
 * <li>Named colors: Most visually representative colors of a wallpaper. Can be either
 * {@link WallpaperColors#getPrimaryColor()}, {@link WallpaperColors#getSecondaryColor()}
 * or {@link WallpaperColors#getTertiaryColor()}.
 * </li>
 * <li>Hints: How colors may affect other system components. Currently the only supported hint is
 * {@link WallpaperColors#HINT_SUPPORTS_DARK_TEXT}, which specifies if dark text is preferred
 * over the wallpaper.</li>
 * </ul>
 */
 */
public final class WallpaperColors implements Parcelable {
public final class WallpaperColors implements Parcelable {


    private static final float BRIGHT_LUMINANCE = 0.9f;
    /**
    private final List<Pair<Color, Integer>> mColors;
     * Specifies that dark text is preferred over the current wallpaper for best presentation.
    private final boolean mSupportsDarkText;
     * <p>
     * eg. A launcher may set its text color to black if this flag is specified.
     */
    public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;

    // Maximum size that a bitmap can have to keep our calculations sane
    private static final int MAX_BITMAP_SIZE = 112;

    // Even though we have a maximum size, we'll mainly match bitmap sizes
    // using the area instead. This way our comparisons are aspect ratio independent.
    private static final int MAX_WALLPAPER_EXTRACTION_AREA = MAX_BITMAP_SIZE * MAX_BITMAP_SIZE;

    // When extracting the main colors, only consider colors
    // present in at least MIN_COLOR_OCCURRENCE of the image
    private static final float MIN_COLOR_OCCURRENCE = 0.05f;

    // Minimum mean luminosity that an image needs to have to support dark text
    private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.9f;
    // We also check if the image has dark pixels in it,
    // to avoid bright images with some dark spots.
    private static final float DARK_PIXEL_LUMINANCE = 0.45f;
    private static final float MAX_DARK_AREA = 0.05f;

    private final ArrayList<Color> mMainColors;
    private int mColorHints;


    public WallpaperColors(Parcel parcel) {
    public WallpaperColors(Parcel parcel) {
        mColors = new ArrayList<>();
        mMainColors = new ArrayList<>();
        int count = parcel.readInt();
        final int count = parcel.readInt();
        for (int i = 0; i < count; i++) {
        for (int i = 0; i < count; i++) {
            Color color = Color.valueOf(parcel.readInt());
            final int colorInt = parcel.readInt();
            int weight = parcel.readInt();
            Color color = Color.valueOf(colorInt);
            mColors.add(new Pair<>(color, weight));
            mMainColors.add(color);
        }
        }
        mSupportsDarkText = parcel.readBoolean();
        mColorHints = parcel.readInt();
    }
    }


    /**
    /**
     * Wallpaper color details containing a list of colors and their weights,
     * Constructs {@link WallpaperColors} from a drawable.
     * as if it were an histogram.
     * <p>
     * This list can be extracted from a bitmap by the Palette API.
     * Main colors will be extracted from the drawable and hints will be calculated.
     *
     *
     * Dark text support will be calculated internally based on the histogram.
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     * @param drawable Source where to extract from.
     */
    public static WallpaperColors fromDrawable(Drawable drawable) {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();

        // Some drawables do not have intrinsic dimensions
        if (width <= 0 || height <= 0) {
            width = MAX_BITMAP_SIZE;
            height = MAX_BITMAP_SIZE;
        }

        Size optimalSize = calculateOptimalSize(width, height);
        Bitmap bitmap = Bitmap.createBitmap(optimalSize.getWidth(), optimalSize.getHeight(),
                Bitmap.Config.ARGB_8888);
        final Canvas bmpCanvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
        drawable.draw(bmpCanvas);

        final WallpaperColors colors = WallpaperColors.fromBitmap(bitmap);
        bitmap.recycle();

        return colors;
    }

    /**
     * Constructs {@link WallpaperColors} from a bitmap.
     * <p>
     * Main colors will be extracted from the bitmap and hints will be calculated.
     *
     *
     * @param colors list of pairs where each pair contains a color
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     *               and number of occurrences/influence.
     * @param bitmap Source where to extract from.
     */
     */
    public WallpaperColors(List<Pair<Color, Integer>> colors) {
    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) {
        this(colors, calculateDarkTextSupport(colors));
        if (bitmap == null) {
            throw new IllegalArgumentException("Bitmap can't be null");
        }

        final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
        if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) {
            Size optimalSize = calculateOptimalSize(bitmap.getWidth(), bitmap.getHeight());
            Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(),
                    optimalSize.getHeight(), true /* filter */);
            bitmap.recycle();
            bitmap = scaledBitmap;
        }

        final Palette palette = Palette
                .from(bitmap)
                .clearFilters()
                .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
                .generate();

        // Remove insignificant colors and sort swatches by population
        final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
        final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE;
        swatches.removeIf(s -> s.getPopulation() < minColorArea);
        swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());

        final int swatchesSize = swatches.size();
        Color primary = null, secondary = null, tertiary = null;

        swatchLoop:
        for (int i = 0; i < swatchesSize; i++) {
            Color color = Color.valueOf(swatches.get(i).getRgb());
            switch (i) {
                case 0:
                    primary = color;
                    break;
                case 1:
                    secondary = color;
                    break;
                case 2:
                    tertiary = color;
                    break;
                default:
                    // out of bounds
                    break swatchLoop;
            }
        }

        int hints = 0;
        if (calculateDarkTextSupport(bitmap)) {
            hints |= HINT_SUPPORTS_DARK_TEXT;
        }
        return new WallpaperColors(primary, secondary, tertiary, hints);
    }
    }


    /**
    /**
     * Wallpaper color details containing a list of colors and their weights,
     * Constructs a new object from three colors, where hints can be specified.
     * as if it were an histogram.
     * Explicit dark text support.
     *
     *
     * @param colors list of pairs where each pair contains a color
     * @param primaryColor Primary color.
     *               and number of occurrences/influence.
     * @param secondaryColor Secondary color.
     * @param supportsDarkText can have dark text on top or not
     * @param tertiaryColor Tertiary color.
     * @param colorHints A combination of WallpaperColor hints.
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     * @see WallpaperColors#fromBitmap(Bitmap)
     * @see WallpaperColors#fromDrawable(Drawable)
     */
     */
    public WallpaperColors(List<Pair<Color, Integer>> colors, boolean supportsDarkText) {
    public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
        if (colors == null)
            @Nullable Color tertiaryColor, int colorHints) {
            colors = new ArrayList<>();

        mColors = colors;
        if (primaryColor == null) {
        mSupportsDarkText = supportsDarkText;
            throw new IllegalArgumentException("Primary color should never be null.");
        }

        mMainColors = new ArrayList<>(3);
        mMainColors.add(primaryColor);
        if (secondaryColor != null) {
            mMainColors.add(secondaryColor);
        }
        if (tertiaryColor != null) {
            if (secondaryColor == null) {
                throw new IllegalArgumentException("tertiaryColor can't be specified when "
                        + "secondaryColor is null");
            }
            mMainColors.add(tertiaryColor);
        }

        mColorHints = colorHints;
    }
    }


    public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
    public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
@@ -94,21 +234,53 @@ public final class WallpaperColors implements Parcelable {


    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        int count = mColors.size();
        List<Color> mainColors = getMainColors();
        int count = mainColors.size();
        dest.writeInt(count);
        dest.writeInt(count);
        for (Pair<Color, Integer> color : mColors) {
        for (int i = 0; i < count; i++) {
            dest.writeInt(color.first.toArgb());
            Color color = mainColors.get(i);
            dest.writeInt(color.second);
            dest.writeInt(color.toArgb());
        }
        }
        dest.writeBoolean(mSupportsDarkText);
        dest.writeInt(mColorHints);
    }
    }


    /**
    /**
     * List of colors with their occurrences. The bigger the int, the more relevant the color.
     * Gets the most visually representative color of the wallpaper.
     * @return list of colors paired with their weights.
     * "Visually representative" means easily noticeable in the image,
     * probably happening at high frequency.
     *
     * @return A color.
     */
    public @NonNull Color getPrimaryColor() {
        return mMainColors.get(0);
    }

    /**
     * Gets the second most preeminent color of the wallpaper. Can be null.
     *
     * @return A color, may be null.
     */
    public @Nullable Color getSecondaryColor() {
        return mMainColors.size() < 2 ? null : mMainColors.get(1);
    }

    /**
     * Gets the third most preeminent color of the wallpaper. Can be null.
     *
     * @return A color, may be null.
     */
    public @Nullable Color getTertiaryColor() {
        return mMainColors.size() < 3 ? null : mMainColors.get(2);
    }

    /**
     * List of most preeminent colors, sorted by importance.
     *
     * @return List of colors.
     * @hide
     */
     */
    public List<Pair<Color, Integer>> getColors() {
    public @NonNull List<Color> getMainColors() {
        return mColors;
        return Collections.unmodifiableList(mMainColors);
    }
    }


    @Override
    @Override
@@ -118,38 +290,91 @@ public final class WallpaperColors implements Parcelable {
        }
        }


        WallpaperColors other = (WallpaperColors) o;
        WallpaperColors other = (WallpaperColors) o;
        return mColors.equals(other.mColors) && mSupportsDarkText == other.mSupportsDarkText;
        return mMainColors.equals(other.mMainColors)
                && mColorHints == other.mColorHints;
    }
    }


    @Override
    @Override
    public int hashCode() {
    public int hashCode() {
        return 31 * mColors.hashCode() + (mSupportsDarkText ? 1 : 0);
        return 31 * mMainColors.hashCode() + mColorHints;
    }
    }


    /**
    /**
     * Whether or not dark text is legible on top of this wallpaper.
     * Combination of WallpaperColor hints.
     *
     *
     * @return true if dark text is supported
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     * @return True if dark text is supported.
     */
     */
    public boolean supportsDarkText() {
    public int getColorHints() {
        return mSupportsDarkText;
        return mColorHints;
    }
    }


    private static boolean calculateDarkTextSupport(List<Pair<Color, Integer>> colors) {
    /**
        if (colors == null) {
     * @param colorHints Combination of WallpaperColors hints.
     * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
     * @hide
     */
    public void setColorHints(int colorHints) {
        mColorHints = colorHints;
    }

    /**
     * Checks if image is bright and clean enough to support light text.
     *
     * @param source What to read.
     * @return Whether image supports dark text or not.
     */
    private static boolean calculateDarkTextSupport(Bitmap source) {
        if (source == null) {
            return false;
            return false;
        }
        }


        Pair<Color, Integer> mainColor = null;
        int[] pixels = new int[source.getWidth() * source.getHeight()];
        double totalLuminance = 0;
        final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
        int darkPixels = 0;
        source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
                source.getWidth(), source.getHeight());


        for (Pair<Color, Integer> color : colors) {
        // This bitmap was already resized to fit the maximum allowed area.
            if (mainColor == null) {
        // Let's just loop through the pixels, no sweat!
                mainColor = color;
        for (int i = 0; i < pixels.length; i++) {
            } else if (color.second > mainColor.second) {
            final float luminance = Color.luminance(pixels[i]);
                mainColor = color;
            final int alpha = Color.alpha(pixels[i]);

            // Make sure we don't have a dark pixel mass that will
            // make text illegible.
            if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) {
                darkPixels++;
                if (darkPixels > maxDarkPixels) {
                    return false;
                }
                }
            }
            }
        return mainColor != null &&

                mainColor.first.luminance() > BRIGHT_LUMINANCE;
            totalLuminance += luminance;
        }
        return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE;
    }

    private static Size calculateOptimalSize(int width, int height) {
        // Calculate how big the bitmap needs to be.
        // This avoids unnecessary processing and allocation inside Palette.
        final int requestedArea = width * height;
        double scale = 1;
        if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
            scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
        }
        int newWidth = (int) (width * scale);
        int newHeight = (int) (height * scale);
        // Dealing with edge cases of the drawable being too wide or too tall.
        // Width or height would end up being 0, in this case we'll set it to 1.
        if (newWidth == 0) {
            newWidth = 1;
        }
        if (newHeight == 0) {
            newHeight = 1;
        }

        return new Size(newWidth, newHeight);
    }
    }
}
}
+17 −12
Original line number Original line Diff line number Diff line
@@ -17,25 +17,19 @@
package android.service.wallpaper;
package android.service.wallpaper;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.WallpaperColors;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.MergedConfiguration;
import android.view.WindowInsets;

import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.app.Service;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Bundle;
import android.os.Bundle;
@@ -44,6 +38,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.Message;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.Display;
import android.view.Display;
import android.view.Gravity;
import android.view.Gravity;
import android.view.IWindowSession;
import android.view.IWindowSession;
@@ -55,9 +50,14 @@ import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerGlobal;


import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;

import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -563,8 +563,13 @@ public abstract class WallpaperService extends Service {
         * Notifies the system about what colors the wallpaper is using.
         * Notifies the system about what colors the wallpaper is using.
         * You might return null if no color information is available at the moment. In that case
         * You might return null if no color information is available at the moment. In that case
         * you might want to call {@link #invalidateColors()} in a near future.
         * you might want to call {@link #invalidateColors()} in a near future.
         * <p>
         * The simplest way of creating A {@link android.app.WallpaperColors} object is by using
         * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or
         * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify
         * your main colors and dark text support explicitly using one of the constructors.
         *
         *
         * @return List of wallpaper colors and their weights.
         * @return Wallpaper colors.
         * @hide
         * @hide
         */
         */
        public @Nullable WallpaperColors onComputeWallpaperColors() {
        public @Nullable WallpaperColors onComputeWallpaperColors() {
Loading