blob: 38a98d3c88071e2e9aff6b53fcebc985fcf42180 [file] [log] [blame]
Lucas Dupinc40608c2017-04-14 18:33:08 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package android.app;
18
Lucas Dupin84b89d92017-05-09 12:16:19 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
vincentpengbe09f282018-12-11 11:05:02 +080021import android.annotation.SystemApi;
Lucas Dupin84b89d92017-05-09 12:16:19 -070022import android.graphics.Bitmap;
23import android.graphics.Canvas;
Lucas Dupinc40608c2017-04-14 18:33:08 -070024import android.graphics.Color;
Lucas Dupinffbf37e2018-03-02 12:18:50 -080025import android.graphics.Rect;
Lucas Dupin84b89d92017-05-09 12:16:19 -070026import android.graphics.drawable.Drawable;
Lucas Dupinc40608c2017-04-14 18:33:08 -070027import android.os.Parcel;
28import android.os.Parcelable;
Lucas Dupina291d192018-06-07 13:59:42 -070029import android.util.Log;
Lucas Dupin84b89d92017-05-09 12:16:19 -070030import android.util.Size;
Lucas Dupinc40608c2017-04-14 18:33:08 -070031
Lucas Dupin4bd24f32017-06-29 12:20:29 -070032import com.android.internal.graphics.ColorUtils;
Lucas Dupin84b89d92017-05-09 12:16:19 -070033import com.android.internal.graphics.palette.Palette;
Lucas Dupin1d3c00d52017-06-05 08:40:39 -070034import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
Lucas Dupina291d192018-06-07 13:59:42 -070035import com.android.internal.util.ContrastColorUtil;
Lucas Dupinc40608c2017-04-14 18:33:08 -070036
Lucas Dupina291d192018-06-07 13:59:42 -070037import java.io.FileOutputStream;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -070038import java.util.ArrayList;
Lucas Dupin84b89d92017-05-09 12:16:19 -070039import java.util.Collections;
Lucas Dupinc40608c2017-04-14 18:33:08 -070040import java.util.List;
41
42/**
Lucas Dupin84b89d92017-05-09 12:16:19 -070043 * Provides information about the colors of a wallpaper.
44 * <p>
Lucas Dupinbdffdd52017-06-28 09:49:47 -070045 * Exposes the 3 most visually representative colors of a wallpaper. Can be either
Lucas Dupin84b89d92017-05-09 12:16:19 -070046 * {@link WallpaperColors#getPrimaryColor()}, {@link WallpaperColors#getSecondaryColor()}
47 * or {@link WallpaperColors#getTertiaryColor()}.
Lucas Dupinc40608c2017-04-14 18:33:08 -070048 */
49public final class WallpaperColors implements Parcelable {
50
Lucas Dupina291d192018-06-07 13:59:42 -070051 private static final boolean DEBUG_DARK_PIXELS = false;
52
Lucas Dupin84b89d92017-05-09 12:16:19 -070053 /**
54 * Specifies that dark text is preferred over the current wallpaper for best presentation.
55 * <p>
56 * eg. A launcher may set its text color to black if this flag is specified.
Lucas Dupinbdffdd52017-06-28 09:49:47 -070057 * @hide
Lucas Dupin84b89d92017-05-09 12:16:19 -070058 */
vincentpengbe09f282018-12-11 11:05:02 +080059 @SystemApi
Lucas Dupine2efebc2017-08-11 10:30:58 -070060 public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
Lucas Dupin84b89d92017-05-09 12:16:19 -070061
Lucas Dupin4bd24f32017-06-29 12:20:29 -070062 /**
63 * Specifies that dark theme is preferred over the current wallpaper for best presentation.
64 * <p>
65 * eg. A launcher may set its drawer color to black if this flag is specified.
66 * @hide
67 */
vincentpengbe09f282018-12-11 11:05:02 +080068 @SystemApi
Lucas Dupine2efebc2017-08-11 10:30:58 -070069 public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
70
71 /**
72 * Specifies that this object was generated by extracting colors from a bitmap.
73 * @hide
74 */
75 public static final int HINT_FROM_BITMAP = 1 << 2;
Lucas Dupin4bd24f32017-06-29 12:20:29 -070076
Lucas Dupin84b89d92017-05-09 12:16:19 -070077 // Maximum size that a bitmap can have to keep our calculations sane
78 private static final int MAX_BITMAP_SIZE = 112;
79
80 // Even though we have a maximum size, we'll mainly match bitmap sizes
81 // using the area instead. This way our comparisons are aspect ratio independent.
82 private static final int MAX_WALLPAPER_EXTRACTION_AREA = MAX_BITMAP_SIZE * MAX_BITMAP_SIZE;
83
84 // When extracting the main colors, only consider colors
85 // present in at least MIN_COLOR_OCCURRENCE of the image
86 private static final float MIN_COLOR_OCCURRENCE = 0.05f;
87
Lucas Dupin4bd24f32017-06-29 12:20:29 -070088 // Decides when dark theme is optimal for this wallpaper
89 private static final float DARK_THEME_MEAN_LUMINANCE = 0.25f;
Lucas Dupin84b89d92017-05-09 12:16:19 -070090 // Minimum mean luminosity that an image needs to have to support dark text
Lucas Dupin4bd24f32017-06-29 12:20:29 -070091 private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.75f;
Lucas Dupin84b89d92017-05-09 12:16:19 -070092 // We also check if the image has dark pixels in it,
93 // to avoid bright images with some dark spots.
Lucas Dupina291d192018-06-07 13:59:42 -070094 private static final float DARK_PIXEL_CONTRAST = 6f;
95 private static final float MAX_DARK_AREA = 0.025f;
Lucas Dupin84b89d92017-05-09 12:16:19 -070096
97 private final ArrayList<Color> mMainColors;
98 private int mColorHints;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -070099
Lucas Dupinc40608c2017-04-14 18:33:08 -0700100 public WallpaperColors(Parcel parcel) {
Lucas Dupin84b89d92017-05-09 12:16:19 -0700101 mMainColors = new ArrayList<>();
102 final int count = parcel.readInt();
103 for (int i = 0; i < count; i++) {
104 final int colorInt = parcel.readInt();
105 Color color = Color.valueOf(colorInt);
106 mMainColors.add(color);
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700107 }
Lucas Dupin84b89d92017-05-09 12:16:19 -0700108 mColorHints = parcel.readInt();
Lucas Dupinc40608c2017-04-14 18:33:08 -0700109 }
110
111 /**
Lucas Dupin84b89d92017-05-09 12:16:19 -0700112 * Constructs {@link WallpaperColors} from a drawable.
113 * <p>
Lucas Dupinbdffdd52017-06-28 09:49:47 -0700114 * Main colors will be extracted from the drawable.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700115 *
Lucas Dupin84b89d92017-05-09 12:16:19 -0700116 * @param drawable Source where to extract from.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700117 */
Lucas Dupin84b89d92017-05-09 12:16:19 -0700118 public static WallpaperColors fromDrawable(Drawable drawable) {
Lucas Dupinffbf37e2018-03-02 12:18:50 -0800119 if (drawable == null) {
120 throw new IllegalArgumentException("Drawable cannot be null");
121 }
122
123 Rect initialBounds = drawable.copyBounds();
Lucas Dupin84b89d92017-05-09 12:16:19 -0700124 int width = drawable.getIntrinsicWidth();
125 int height = drawable.getIntrinsicHeight();
126
127 // Some drawables do not have intrinsic dimensions
128 if (width <= 0 || height <= 0) {
129 width = MAX_BITMAP_SIZE;
130 height = MAX_BITMAP_SIZE;
131 }
132
133 Size optimalSize = calculateOptimalSize(width, height);
134 Bitmap bitmap = Bitmap.createBitmap(optimalSize.getWidth(), optimalSize.getHeight(),
135 Bitmap.Config.ARGB_8888);
136 final Canvas bmpCanvas = new Canvas(bitmap);
137 drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
138 drawable.draw(bmpCanvas);
139
140 final WallpaperColors colors = WallpaperColors.fromBitmap(bitmap);
141 bitmap.recycle();
142
Lucas Dupinffbf37e2018-03-02 12:18:50 -0800143 drawable.setBounds(initialBounds);
Lucas Dupin84b89d92017-05-09 12:16:19 -0700144 return colors;
Lucas Dupinc40608c2017-04-14 18:33:08 -0700145 }
146
147 /**
Lucas Dupin84b89d92017-05-09 12:16:19 -0700148 * Constructs {@link WallpaperColors} from a bitmap.
149 * <p>
Lucas Dupinbdffdd52017-06-28 09:49:47 -0700150 * Main colors will be extracted from the bitmap.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700151 *
Lucas Dupin84b89d92017-05-09 12:16:19 -0700152 * @param bitmap Source where to extract from.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700153 */
Lucas Dupin84b89d92017-05-09 12:16:19 -0700154 public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) {
155 if (bitmap == null) {
156 throw new IllegalArgumentException("Bitmap can't be null");
157 }
158
159 final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
Lucas Dupin42acf602017-07-13 16:32:44 -0700160 boolean shouldRecycle = false;
Lucas Dupin84b89d92017-05-09 12:16:19 -0700161 if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) {
Lucas Dupin42acf602017-07-13 16:32:44 -0700162 shouldRecycle = true;
Lucas Dupin84b89d92017-05-09 12:16:19 -0700163 Size optimalSize = calculateOptimalSize(bitmap.getWidth(), bitmap.getHeight());
Lucas Dupin42acf602017-07-13 16:32:44 -0700164 bitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(),
Lucas Dupin84b89d92017-05-09 12:16:19 -0700165 optimalSize.getHeight(), true /* filter */);
Lucas Dupin84b89d92017-05-09 12:16:19 -0700166 }
167
168 final Palette palette = Palette
169 .from(bitmap)
Lucas Dupin1d3c00d52017-06-05 08:40:39 -0700170 .setQuantizer(new VariationalKMeansQuantizer())
171 .maximumColorCount(5)
Lucas Dupin84b89d92017-05-09 12:16:19 -0700172 .clearFilters()
173 .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
174 .generate();
175
176 // Remove insignificant colors and sort swatches by population
177 final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
178 final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE;
179 swatches.removeIf(s -> s.getPopulation() < minColorArea);
180 swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());
181
182 final int swatchesSize = swatches.size();
183 Color primary = null, secondary = null, tertiary = null;
184
185 swatchLoop:
186 for (int i = 0; i < swatchesSize; i++) {
187 Color color = Color.valueOf(swatches.get(i).getRgb());
188 switch (i) {
189 case 0:
190 primary = color;
191 break;
192 case 1:
193 secondary = color;
194 break;
195 case 2:
196 tertiary = color;
197 break;
198 default:
199 // out of bounds
200 break swatchLoop;
201 }
202 }
203
Lucas Dupinb5e50532018-05-24 16:33:14 +0000204 int hints = calculateDarkHints(bitmap);
Lucas Dupin42acf602017-07-13 16:32:44 -0700205
206 if (shouldRecycle) {
207 bitmap.recycle();
208 }
209
Lucas Dupine2efebc2017-08-11 10:30:58 -0700210 return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints);
Lucas Dupin84b89d92017-05-09 12:16:19 -0700211 }
212
213 /**
Lucas Dupinbdffdd52017-06-28 09:49:47 -0700214 * Constructs a new object from three colors.
215 *
216 * @param primaryColor Primary color.
217 * @param secondaryColor Secondary color.
218 * @param tertiaryColor Tertiary color.
219 * @see WallpaperColors#fromBitmap(Bitmap)
220 * @see WallpaperColors#fromDrawable(Drawable)
221 */
222 public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
223 @Nullable Color tertiaryColor) {
224 this(primaryColor, secondaryColor, tertiaryColor, 0);
225 }
226
227 /**
Lucas Dupin84b89d92017-05-09 12:16:19 -0700228 * Constructs a new object from three colors, where hints can be specified.
229 *
230 * @param primaryColor Primary color.
231 * @param secondaryColor Secondary color.
232 * @param tertiaryColor Tertiary color.
233 * @param colorHints A combination of WallpaperColor hints.
234 * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
235 * @see WallpaperColors#fromBitmap(Bitmap)
236 * @see WallpaperColors#fromDrawable(Drawable)
Lucas Dupinbdffdd52017-06-28 09:49:47 -0700237 * @hide
Lucas Dupin84b89d92017-05-09 12:16:19 -0700238 */
vincentpengbe09f282018-12-11 11:05:02 +0800239 @SystemApi
Lucas Dupin84b89d92017-05-09 12:16:19 -0700240 public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
241 @Nullable Color tertiaryColor, int colorHints) {
242
243 if (primaryColor == null) {
244 throw new IllegalArgumentException("Primary color should never be null.");
245 }
246
247 mMainColors = new ArrayList<>(3);
248 mMainColors.add(primaryColor);
249 if (secondaryColor != null) {
250 mMainColors.add(secondaryColor);
251 }
252 if (tertiaryColor != null) {
253 if (secondaryColor == null) {
254 throw new IllegalArgumentException("tertiaryColor can't be specified when "
255 + "secondaryColor is null");
256 }
257 mMainColors.add(tertiaryColor);
258 }
259
260 mColorHints = colorHints;
Lucas Dupinc40608c2017-04-14 18:33:08 -0700261 }
262
263 public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
264 @Override
265 public WallpaperColors createFromParcel(Parcel in) {
266 return new WallpaperColors(in);
267 }
268
269 @Override
270 public WallpaperColors[] newArray(int size) {
271 return new WallpaperColors[size];
272 }
273 };
274
275 @Override
276 public int describeContents() {
277 return 0;
278 }
279
280 @Override
281 public void writeToParcel(Parcel dest, int flags) {
Lucas Dupin84b89d92017-05-09 12:16:19 -0700282 List<Color> mainColors = getMainColors();
283 int count = mainColors.size();
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700284 dest.writeInt(count);
Lucas Dupin84b89d92017-05-09 12:16:19 -0700285 for (int i = 0; i < count; i++) {
286 Color color = mainColors.get(i);
287 dest.writeInt(color.toArgb());
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700288 }
Lucas Dupin84b89d92017-05-09 12:16:19 -0700289 dest.writeInt(mColorHints);
Lucas Dupinc40608c2017-04-14 18:33:08 -0700290 }
291
292 /**
Lucas Dupin84b89d92017-05-09 12:16:19 -0700293 * Gets the most visually representative color of the wallpaper.
294 * "Visually representative" means easily noticeable in the image,
295 * probably happening at high frequency.
296 *
297 * @return A color.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700298 */
Lucas Dupin84b89d92017-05-09 12:16:19 -0700299 public @NonNull Color getPrimaryColor() {
300 return mMainColors.get(0);
301 }
302
303 /**
304 * Gets the second most preeminent color of the wallpaper. Can be null.
305 *
306 * @return A color, may be null.
307 */
308 public @Nullable Color getSecondaryColor() {
309 return mMainColors.size() < 2 ? null : mMainColors.get(1);
310 }
311
312 /**
313 * Gets the third most preeminent color of the wallpaper. Can be null.
314 *
315 * @return A color, may be null.
316 */
317 public @Nullable Color getTertiaryColor() {
318 return mMainColors.size() < 3 ? null : mMainColors.get(2);
319 }
320
321 /**
322 * List of most preeminent colors, sorted by importance.
323 *
324 * @return List of colors.
325 * @hide
326 */
327 public @NonNull List<Color> getMainColors() {
328 return Collections.unmodifiableList(mMainColors);
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700329 }
330
331 @Override
332 public boolean equals(Object o) {
333 if (o == null || getClass() != o.getClass()) {
334 return false;
335 }
336
337 WallpaperColors other = (WallpaperColors) o;
Lucas Dupin84b89d92017-05-09 12:16:19 -0700338 return mMainColors.equals(other.mMainColors)
339 && mColorHints == other.mColorHints;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700340 }
341
342 @Override
343 public int hashCode() {
Lucas Dupin84b89d92017-05-09 12:16:19 -0700344 return 31 * mMainColors.hashCode() + mColorHints;
Lucas Dupinc40608c2017-04-14 18:33:08 -0700345 }
346
347 /**
Lucas Dupin84b89d92017-05-09 12:16:19 -0700348 * Combination of WallpaperColor hints.
Lucas Dupinc40608c2017-04-14 18:33:08 -0700349 *
Lucas Dupin84b89d92017-05-09 12:16:19 -0700350 * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
351 * @return True if dark text is supported.
Lucas Dupinbdffdd52017-06-28 09:49:47 -0700352 * @hide
Lucas Dupinc40608c2017-04-14 18:33:08 -0700353 */
vincentpengbe09f282018-12-11 11:05:02 +0800354 @SystemApi
Lucas Dupin84b89d92017-05-09 12:16:19 -0700355 public int getColorHints() {
356 return mColorHints;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700357 }
358
Lucas Dupin84b89d92017-05-09 12:16:19 -0700359 /**
360 * @param colorHints Combination of WallpaperColors hints.
361 * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
362 * @hide
363 */
364 public void setColorHints(int colorHints) {
365 mColorHints = colorHints;
366 }
367
368 /**
369 * Checks if image is bright and clean enough to support light text.
370 *
371 * @param source What to read.
372 * @return Whether image supports dark text or not.
373 */
Lucas Dupine2efebc2017-08-11 10:30:58 -0700374 private static int calculateDarkHints(Bitmap source) {
Lucas Dupin84b89d92017-05-09 12:16:19 -0700375 if (source == null) {
Lucas Dupin4bd24f32017-06-29 12:20:29 -0700376 return 0;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700377 }
378
Lucas Dupin84b89d92017-05-09 12:16:19 -0700379 int[] pixels = new int[source.getWidth() * source.getHeight()];
380 double totalLuminance = 0;
381 final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
382 int darkPixels = 0;
383 source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
384 source.getWidth(), source.getHeight());
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700385
Lucas Dupin84b89d92017-05-09 12:16:19 -0700386 // This bitmap was already resized to fit the maximum allowed area.
387 // Let's just loop through the pixels, no sweat!
Lucas Dupin4bd24f32017-06-29 12:20:29 -0700388 float[] tmpHsl = new float[3];
Lucas Dupin84b89d92017-05-09 12:16:19 -0700389 for (int i = 0; i < pixels.length; i++) {
Lucas Dupin4bd24f32017-06-29 12:20:29 -0700390 ColorUtils.colorToHSL(pixels[i], tmpHsl);
391 final float luminance = tmpHsl[2];
Lucas Dupin84b89d92017-05-09 12:16:19 -0700392 final int alpha = Color.alpha(pixels[i]);
Lucas Dupin84b89d92017-05-09 12:16:19 -0700393 // Make sure we don't have a dark pixel mass that will
394 // make text illegible.
Lucas Dupina291d192018-06-07 13:59:42 -0700395 final boolean satisfiesTextContrast = ContrastColorUtil
396 .calculateContrast(pixels[i], Color.BLACK) > DARK_PIXEL_CONTRAST;
397 if (!satisfiesTextContrast && alpha != 0) {
Lucas Dupin84b89d92017-05-09 12:16:19 -0700398 darkPixels++;
Lucas Dupina291d192018-06-07 13:59:42 -0700399 if (DEBUG_DARK_PIXELS) {
400 pixels[i] = Color.RED;
401 }
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700402 }
Lucas Dupin84b89d92017-05-09 12:16:19 -0700403 totalLuminance += luminance;
Lucas Dupinea1fb1e2017-04-05 17:39:44 -0700404 }
Lucas Dupin4bd24f32017-06-29 12:20:29 -0700405
406 int hints = 0;
407 double meanLuminance = totalLuminance / pixels.length;
408 if (meanLuminance > BRIGHT_IMAGE_MEAN_LUMINANCE && darkPixels < maxDarkPixels) {
409 hints |= HINT_SUPPORTS_DARK_TEXT;
410 }
411 if (meanLuminance < DARK_THEME_MEAN_LUMINANCE) {
412 hints |= HINT_SUPPORTS_DARK_THEME;
413 }
414
Lucas Dupina291d192018-06-07 13:59:42 -0700415 if (DEBUG_DARK_PIXELS) {
416 try (FileOutputStream out = new FileOutputStream("/data/pixels.png")) {
417 source.setPixels(pixels, 0, source.getWidth(), 0, 0, source.getWidth(),
418 source.getHeight());
419 source.compress(Bitmap.CompressFormat.PNG, 100, out);
420 } catch (Exception e) {
421 e.printStackTrace();
422 }
423 Log.d("WallpaperColors", "l: " + meanLuminance + ", d: " + darkPixels +
424 " maxD: " + maxDarkPixels + " numPixels: " + pixels.length);
425 }
426
Lucas Dupin4bd24f32017-06-29 12:20:29 -0700427 return hints;
Lucas Dupin84b89d92017-05-09 12:16:19 -0700428 }
429
430 private static Size calculateOptimalSize(int width, int height) {
431 // Calculate how big the bitmap needs to be.
432 // This avoids unnecessary processing and allocation inside Palette.
433 final int requestedArea = width * height;
434 double scale = 1;
435 if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
436 scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
437 }
438 int newWidth = (int) (width * scale);
439 int newHeight = (int) (height * scale);
440 // Dealing with edge cases of the drawable being too wide or too tall.
441 // Width or height would end up being 0, in this case we'll set it to 1.
442 if (newWidth == 0) {
443 newWidth = 1;
444 }
445 if (newHeight == 0) {
446 newHeight = 1;
447 }
448
449 return new Size(newWidth, newHeight);
Lucas Dupinc40608c2017-04-14 18:33:08 -0700450 }
Lucas Dupin50ba9912017-07-14 11:55:05 -0700451
452 @Override
453 public String toString() {
454 final StringBuilder colors = new StringBuilder();
455 for (int i = 0; i < mMainColors.size(); i++) {
456 colors.append(Integer.toHexString(mMainColors.get(i).toArgb())).append(" ");
457 }
458 return "[WallpaperColors: " + colors.toString() + "h: " + mColorHints + "]";
459 }
Lucas Dupinc40608c2017-04-14 18:33:08 -0700460}