From 3e93ec558a3a8d80c3375a994bb2f63301558144 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 23 May 2017 14:00:32 -0700 Subject: [PATCH] Adding compat implementation for the new WallpaperManager APIs for color extraction Change-Id: Ie06c9ac3a77cd33d22ce298a55e234078895c3a0 --- AndroidManifest-common.xml | 5 + .../compat/WallpaperColorsCompat.java | 43 ++++ .../compat/WallpaperManagerCompat.java | 61 +++++ .../compat/WallpaperManagerCompatVL.java | 236 ++++++++++++++++++ .../compat/WallpaperManagerCompatVOMR1.java | 109 ++++++++ .../launcher3/dynamicui/ExtractedColors.java | 2 +- .../colorextraction/ColorExtractor.java | 50 +--- .../WallpaperColorsCompat.java | 69 ----- .../colorextraction/types/ExtractionType.java | 2 +- .../colorextraction/types/Tonal.java | 39 +-- 10 files changed, 492 insertions(+), 124 deletions(-) create mode 100644 src/com/android/launcher3/compat/WallpaperColorsCompat.java create mode 100644 src/com/android/launcher3/compat/WallpaperManagerCompat.java create mode 100644 src/com/android/launcher3/compat/WallpaperManagerCompatVL.java create mode 100644 src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java delete mode 100644 src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index 3a60a9878f..dd1446635d 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -85,6 +85,11 @@ android:process=":wallpaper_chooser"> + + diff --git a/src/com/android/launcher3/compat/WallpaperColorsCompat.java b/src/com/android/launcher3/compat/WallpaperColorsCompat.java new file mode 100644 index 0000000000..fd08f947ed --- /dev/null +++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.compat; + +import android.util.SparseIntArray; + +/** + * A compatibility layer around platform implementation of WallpaperColors + */ +public class WallpaperColorsCompat { + + private final SparseIntArray mColors; + private final boolean mSupportsDarkText; + + public WallpaperColorsCompat(SparseIntArray colors, boolean supportsDarkText) { + mColors = colors; + mSupportsDarkText = supportsDarkText; + } + + /** + * A map of color code to their occurrences. The bigger the int, the more relevant the color. + */ + public SparseIntArray getColors() { + return mColors; + } + + public boolean supportsDarkText() { + return mSupportsDarkText; + } +} diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java new file mode 100644 index 0000000000..cbcabdf9b1 --- /dev/null +++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.compat; + +import android.content.Context; +import android.support.annotation.Nullable; + +import com.android.launcher3.Utilities; + +public abstract class WallpaperManagerCompat { + + private static final Object sInstanceLock = new Object(); + private static WallpaperManagerCompat sInstance; + + public static WallpaperManagerCompat getInstance(Context context) { + synchronized (sInstanceLock) { + if (sInstance == null) { + context = context.getApplicationContext(); + + if (Utilities.isAtLeastO()) { + try { + sInstance = new WallpaperManagerCompatVOMR1(context); + } catch (Exception e) { + // The wallpaper APIs do not yet exist + } + } + if (sInstance == null) { + sInstance = new WallpaperManagerCompatVL(context); + } + } + return sInstance; + } + } + + + public abstract @Nullable WallpaperColorsCompat getWallpaperColors(int which); + + public abstract void addOnColorsChangedListener(OnColorsChangedListenerCompat listener); + + /** + * Interface definition for a callback to be invoked when colors change on a wallpaper. + */ + public interface OnColorsChangedListenerCompat { + + void onColorsChanged(WallpaperColorsCompat colors, int which); + } +} diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java new file mode 100644 index 0000000000..b175f21136 --- /dev/null +++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.compat; + +import static android.app.WallpaperManager.FLAG_SYSTEM; + +import android.app.IntentService; +import android.app.WallpaperInfo; +import android.app.WallpaperManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.os.ResultReceiver; +import android.support.annotation.Nullable; +import android.support.v7.graphics.Palette; +import android.util.Log; +import android.util.Pair; +import android.util.SparseIntArray; + +import com.android.launcher3.LauncherFiles; +import com.android.launcher3.Utilities; + +import java.io.IOException; +import java.util.ArrayList; + +public class WallpaperManagerCompatVL extends WallpaperManagerCompat { + + private static final String TAG = "WMCompatVL"; + + private static final String VERSION_PREFIX = "1,"; + private static final String KEY_COLORS = "wallpaper_parsed_colors"; + private static final String EXTRA_RECEIVER = "receiver"; + + private final ArrayList mListeners = new ArrayList<>(); + + private final Context mContext; + private WallpaperColorsCompat mColorsCompat; + + WallpaperManagerCompatVL(Context context) { + mContext = context; + + String colors = prefs(mContext).getString(KEY_COLORS, ""); + int wallpaperId = -1; + if (colors.startsWith(VERSION_PREFIX)) { + Pair storedValue = parseValue(colors); + wallpaperId = storedValue.first; + mColorsCompat = storedValue.second; + } + + if (wallpaperId == -1 || wallpaperId != getWallpaperId(context)) { + reloadColors(); + } + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + reloadColors(); + } + }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)); + } + + @Nullable + @Override + public WallpaperColorsCompat getWallpaperColors(int which) { + return which == FLAG_SYSTEM ? mColorsCompat : null; + } + + @Override + public void addOnColorsChangedListener(OnColorsChangedListenerCompat listener) { + mListeners.add(listener); + } + + private void reloadColors() { + ResultReceiver receiver = new ResultReceiver(new Handler()) { + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + handleResult(resultData.getString(KEY_COLORS)); + } + }; + mContext.startService(new Intent(mContext, ColorExtractionService.class) + .putExtra(EXTRA_RECEIVER, receiver)); + } + + private void handleResult(String result) { + prefs(mContext).edit().putString(KEY_COLORS, result).apply(); + mColorsCompat = parseValue(result).second; + for (OnColorsChangedListenerCompat listener : mListeners) { + listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM); + } + } + + private static SharedPreferences prefs(Context context) { + return context.getSharedPreferences( + LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE); + } + + private static final int getWallpaperId(Context context) { + if (!Utilities.ATLEAST_NOUGAT) { + return -1; + } + return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM); + } + + /** + * Parses the stored value and returns the wallpaper id and wallpaper colors. + */ + private static Pair parseValue(String value) { + String[] parts = value.split(","); + Integer wallpaperId = Integer.parseInt(parts[1]); + if (parts.length == 2) { + return Pair.create(wallpaperId, null); + } + + SparseIntArray colors = new SparseIntArray((parts.length - 2) / 2); + for (int i = 2; i < parts.length; i += 2) { + colors.put(Integer.parseInt(parts[i]), Integer.parseInt(parts[i + 1])); + } + return Pair.create(wallpaperId, new WallpaperColorsCompat(colors, false)); + } + + /** + * Intent service to handle color extraction + */ + public static class ColorExtractionService extends IntentService { + private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112; + + public ColorExtractionService() { + super("ColorExtractionService"); + } + + /** + * Extracts the wallpaper colors and sends the result back through the receiver. + */ + @Override + protected void onHandleIntent(@Nullable Intent intent) { + int wallpaperId = getWallpaperId(this); + + Bitmap bitmap = null; + Drawable drawable = null; + + WallpaperManager wm = WallpaperManager.getInstance(this); + WallpaperInfo info = wm.getWallpaperInfo(); + if (info != null) { + // For live wallpaper, extract colors from thumbnail + drawable = info.loadThumbnail(getPackageManager()); + } else { + if (Utilities.ATLEAST_NOUGAT) { + try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) { + BitmapRegionDecoder decoder = BitmapRegionDecoder + .newInstance(fd.getFileDescriptor(), false); + + int requestedArea = decoder.getWidth() * decoder.getHeight(); + BitmapFactory.Options options = new BitmapFactory.Options(); + + if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) { + double areaRatio = + MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea; + double nearestPowOf2 = + Math.floor(Math.log(areaRatio) / (2 * Math.log(2))); + options.inSampleSize = (int) Math.pow(2, nearestPowOf2); + } + Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight()); + bitmap = decoder.decodeRegion(region, options); + decoder.recycle(); + } catch (IOException | NullPointerException e) { + Log.e(TAG, "Fetching partial bitmap failed, trying old method", e); + } + } + if (bitmap == null) { + drawable = wm.getDrawable(); + } + } + + if (drawable != null) { + // Calculate how big the bitmap needs to be. + // This avoids unnecessary processing and allocation inside Palette. + final int requestedArea = drawable.getIntrinsicWidth() * + drawable.getIntrinsicHeight(); + double scale = 1; + if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) { + scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea); + } + bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * scale), + (int) (drawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888); + final Canvas bmpCanvas = new Canvas(bitmap); + drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); + drawable.draw(bmpCanvas); + } + + String value = VERSION_PREFIX + wallpaperId; + + if (bitmap != null) { + Palette palette = Palette.from(bitmap).generate(); + bitmap.recycle(); + + StringBuilder builder = new StringBuilder(value); + for (Palette.Swatch swatch : palette.getSwatches()) { + builder.append(',') + .append(swatch.getRgb()) + .append(',') + .append(swatch.getPopulation()); + } + value = builder.toString(); + } + + ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RECEIVER); + Bundle result = new Bundle(); + result.putString(KEY_COLORS, value); + receiver.send(0, result); + } + } +} diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java new file mode 100644 index 0000000000..c74ccc0ccf --- /dev/null +++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.compat; + +import android.annotation.TargetApi; +import android.app.WallpaperManager; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.support.annotation.Nullable; +import android.util.Log; +import android.util.Pair; +import android.util.SparseIntArray; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.List; + +@TargetApi(Build.VERSION_CODES.O) +public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat { + + private static final String TAG = "WMCompatVOMR1"; + + private final WallpaperManager mWm; + + private final Class mOCLClass; + private final Method mAddOCLMethod; + + private final Method mWCGetMethod; + private final Method mWCGetColorsMethod; + private final Method mWCSupportsDarkTextMethod; + + WallpaperManagerCompatVOMR1(Context context) throws Exception { + mWm = context.getSystemService(WallpaperManager.class); + + mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener"); + mAddOCLMethod = WallpaperManager.class.getDeclaredMethod( + "addOnColorsChangedListener", mOCLClass); + + mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class); + Class wallpaperColorsClass = mWCGetMethod.getReturnType(); + mWCGetColorsMethod = wallpaperColorsClass.getDeclaredMethod("getColors"); + mWCSupportsDarkTextMethod = wallpaperColorsClass.getDeclaredMethod("supportsDarkText"); + } + + @Nullable + @Override + public WallpaperColorsCompat getWallpaperColors(int which) { + try { + return convertColorsObject(mWCGetMethod.invoke(mWm, which)); + } catch (Exception e) { + Log.e(TAG, "Error calling wallpaper API", e); + return null; + } + } + + @Override + public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) { + Object onChangeListener = Proxy.newProxyInstance( + WallpaperManager.class.getClassLoader(), + new Class[]{mOCLClass}, + new InvocationHandler() { + @Override + public Object invoke(Object o, Method method, Object[] objects) + throws Throwable { + String methodName = method.getName(); + if ("onColorsChanged".equals(methodName)) { + listener.onColorsChanged( + convertColorsObject(objects[0]), (Integer) objects[1]); + } else if ("toString".equals(methodName)) { + return listener.toString(); + } + return null; + } + }); + try { + mAddOCLMethod.invoke(mWm, onChangeListener); + } catch (Exception e) { + Log.e(TAG, "Error calling wallpaper API", e); + } + } + + private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception { + if (colors == null) { + return null; + } + List> list = (List) mWCGetColorsMethod.invoke(colors); + boolean supportsDarkText = (Boolean) mWCSupportsDarkTextMethod.invoke(colors); + SparseIntArray colorMap = new SparseIntArray(list.size()); + for (Pair color : list) { + colorMap.put(color.first.toArgb(), color.second); + } + return new WallpaperColorsCompat(colorMap, supportsDarkText); + } +} diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java index e60a1bd7d0..96fe31a734 100644 --- a/src/com/android/launcher3/dynamicui/ExtractedColors.java +++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java @@ -170,7 +170,7 @@ public class ExtractedColors { try { WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class); ColorExtractor extractor = new ColorExtractor(context); - ColorExtractor.GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM); + ColorExtractor.GradientColors colors = extractor.getColors(); setColorAtIndex(ALLAPPS_GRADIENT_MAIN_INDEX, colors.getMainColor()); setColorAtIndex(ALLAPPS_GRADIENT_SECONDARY_INDEX, colors.getSecondaryColor()); } catch (NoSuchMethodException e) { diff --git a/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java b/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java index 153b529140..98558677ae 100644 --- a/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java +++ b/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java @@ -1,16 +1,15 @@ package com.android.launcher3.dynamicui.colorextraction; -import android.app.WallpaperManager; +import static android.app.WallpaperManager.FLAG_SYSTEM; + import android.content.Context; import android.graphics.Color; -import android.os.Parcelable; -import android.util.Log; +import com.android.launcher3.compat.WallpaperColorsCompat; +import com.android.launcher3.compat.WallpaperManagerCompat; import com.android.launcher3.dynamicui.colorextraction.types.ExtractionType; import com.android.launcher3.dynamicui.colorextraction.types.Tonal; -import java.lang.reflect.Method; - /** * Class to process wallpaper colors and generate a tonal palette based on them. @@ -18,59 +17,32 @@ import java.lang.reflect.Method; * TODO remove this class if available by platform */ public class ColorExtractor { - private static final String TAG = "ColorExtractor"; private static final int FALLBACK_COLOR = Color.WHITE; + private final Context mContext; private int mMainFallbackColor = FALLBACK_COLOR; private int mSecondaryFallbackColor = FALLBACK_COLOR; private final GradientColors mSystemColors; - private final GradientColors mLockColors; - private final Context mContext; private final ExtractionType mExtractionType; public ColorExtractor(Context context) { mContext = context; mSystemColors = new GradientColors(); - mLockColors = new GradientColors(); mExtractionType = new Tonal(); - WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class); - if (wallpaperManager == null) { - Log.w(TAG, "Can't listen to color changes!"); - } else { - Parcelable wallpaperColorsObj; - try { - Method method = WallpaperManager.class - .getDeclaredMethod("getWallpaperColors", int.class); - - wallpaperColorsObj = (Parcelable) method.invoke(wallpaperManager, - WallpaperManager.FLAG_SYSTEM); - extractInto(new WallpaperColorsCompat(wallpaperColorsObj), mSystemColors); - wallpaperColorsObj = (Parcelable) method.invoke(wallpaperManager, - WallpaperManager.FLAG_LOCK); - extractInto(new WallpaperColorsCompat(wallpaperColorsObj), mLockColors); - } catch (Exception e) { - Log.e(TAG, "reflection failed", e); - } - } + extractFrom(WallpaperManagerCompat.getInstance(context).getWallpaperColors(FLAG_SYSTEM)); } - public GradientColors getColors(int which) { - if (which == WallpaperManager.FLAG_LOCK) { - return mLockColors; - } else if (which == WallpaperManager.FLAG_SYSTEM) { - return mSystemColors; - } else { - throw new IllegalArgumentException("which should be either FLAG_SYSTEM or FLAG_LOCK"); - } + public GradientColors getColors() { + return mSystemColors; } - private void extractInto(WallpaperColorsCompat inWallpaperColors, GradientColors outGradientColors) { - applyFallback(outGradientColors); + private void extractFrom(WallpaperColorsCompat inWallpaperColors) { + applyFallback(mSystemColors); if (inWallpaperColors == null) { return; } - mExtractionType.extractInto(inWallpaperColors, outGradientColors); + mExtractionType.extractInto(inWallpaperColors, mSystemColors); } private void applyFallback(GradientColors outGradientColors) { diff --git a/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java b/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java deleted file mode 100644 index f80a675cbd..0000000000 --- a/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.android.launcher3.dynamicui.colorextraction; - -import android.graphics.Color; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Pair; - -import java.util.List; - -/** - * A wrapper around platform implementation of WallpaperColors until the - * updated SDK is available. - * - * TODO remove this class if available by platform - */ -public class WallpaperColorsCompat implements Parcelable { - - private final Parcelable mObject; - - public WallpaperColorsCompat(Parcelable object) { - mObject = object; - } - - private Object invokeMethod(String methodName) { - try { - return mObject.getClass().getDeclaredMethod(methodName).invoke(mObject); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int i) { - parcel.writeParcelable(mObject, i); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public WallpaperColorsCompat createFromParcel(Parcel source) { - Parcelable object = source.readParcelable(null); - return new WallpaperColorsCompat(object); - } - - public WallpaperColorsCompat[] newArray(int size) { - return new WallpaperColorsCompat[size]; - } - }; - - public List> getColors() { - try { - return (List>) invokeMethod("getColors"); - } catch (Exception e) { - return null; - } - } - - public boolean supportsDarkText() { - try { - return (Boolean) invokeMethod("supportsDarkText"); - } catch (Exception e) { - return false; - } - } -} diff --git a/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java b/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java index 166c7c6f40..88958a480b 100644 --- a/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java +++ b/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java @@ -1,7 +1,7 @@ package com.android.launcher3.dynamicui.colorextraction.types; +import com.android.launcher3.compat.WallpaperColorsCompat; import com.android.launcher3.dynamicui.colorextraction.ColorExtractor; -import com.android.launcher3.dynamicui.colorextraction.WallpaperColorsCompat; /** diff --git a/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java b/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java index 1e165a382a..800dcd2039 100644 --- a/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java +++ b/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java @@ -6,11 +6,15 @@ import android.support.annotation.Nullable; import android.support.v4.graphics.ColorUtils; import android.util.Log; import android.util.Pair; +import android.util.SparseIntArray; +import com.android.launcher3.compat.WallpaperColorsCompat; import com.android.launcher3.dynamicui.colorextraction.ColorExtractor; -import com.android.launcher3.dynamicui.colorextraction.WallpaperColorsCompat; +import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; +import java.util.List; /** @@ -29,9 +33,10 @@ public class Tonal implements ExtractionType { private static final float MIN_COLOR_OCCURRENCE = 0.1f; private static final float MIN_LUMINOSITY = 0.5f; - public void extractInto(WallpaperColorsCompat wallpaperColors, - ColorExtractor.GradientColors gradientColors) { - if (wallpaperColors.getColors().size() == 0) { + public void extractInto( + WallpaperColorsCompat wallpaperColors, ColorExtractor.GradientColors gradientColors) { + SparseIntArray colorsArray = wallpaperColors.getColors(); + if (colorsArray.size() == 0) { return; } // Tonal is not really a sort, it takes a color from the extracted @@ -39,24 +44,30 @@ public class Tonal implements ExtractionType { // palettes. The best fit is tweaked to be closer to the source color // and replaces the original palette + List> colors = new ArrayList<>(colorsArray.size()); + for (int i = colorsArray.size() - 1; i >= 0; i --) { + colors.add(Pair.create(colorsArray.keyAt(i), colorsArray.valueAt(i))); + } + // First find the most representative color in the image - populationSort(wallpaperColors); + populationSort(colors); + // Calculate total int total = 0; - for (Pair weightedColor : wallpaperColors.getColors()) { + for (Pair weightedColor : colors) { total += weightedColor.second; } // Get bright colors that occur often enough in this image - Pair bestColor = null; + Pair bestColor = null; float[] hsl = new float[3]; - for (Pair weightedColor : wallpaperColors.getColors()) { + for (Pair weightedColor : colors) { float colorOccurrence = weightedColor.second / (float) total; if (colorOccurrence < MIN_COLOR_OCCURRENCE) { break; } - int colorValue = weightedColor.first.toArgb(); + int colorValue = weightedColor.first; ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue), hsl); if (hsl[2] > MIN_LUMINOSITY) { @@ -66,10 +77,10 @@ public class Tonal implements ExtractionType { // Fallback to first color if (bestColor == null) { - bestColor = wallpaperColors.getColors().get(0); + bestColor = colors.get(0); } - int colorValue = bestColor.first.toArgb(); + int colorValue = bestColor.first; ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue), hsl); hsl[0] /= 360.0f; // normalize @@ -105,10 +116,10 @@ public class Tonal implements ExtractionType { gradientColors.setSecondaryColor(ColorUtils.HSLToColor(hsl)); } - private static void populationSort(@NonNull WallpaperColorsCompat wallpaperColors) { - wallpaperColors.getColors().sort(new Comparator>() { + private static void populationSort(@NonNull List> wallpaperColors) { + Collections.sort(wallpaperColors, new Comparator>() { @Override - public int compare(Pair a, Pair b) { + public int compare(Pair a, Pair b) { return b.second - a.second; } });