blob: 3003ce80cbc1b41d9ab4b5401d20e1383ca12270 [file] [log] [blame]
Lucas Dupin65f56582017-04-27 10:21:41 -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
Lucas Dupine2292a92017-07-06 14:35:30 -070017package com.android.internal.colorextraction;
Lucas Dupin65f56582017-04-27 10:21:41 -070018
Lucas Dupin75ec3792017-06-29 14:07:18 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
Lucas Dupin65f56582017-04-27 10:21:41 -070021import android.app.WallpaperColors;
22import android.app.WallpaperManager;
23import android.content.Context;
wilsonshih0e490d9e2019-02-13 12:20:01 +080024import android.os.AsyncTask;
Lucas Dupin65f56582017-04-27 10:21:41 -070025import android.util.Log;
Lucas Dupin7aaa3532017-05-28 08:51:07 -070026import android.util.SparseArray;
Lucas Dupin65f56582017-04-27 10:21:41 -070027
Lucas Dupine2292a92017-07-06 14:35:30 -070028import com.android.internal.annotations.VisibleForTesting;
29import com.android.internal.colorextraction.types.ExtractionType;
30import com.android.internal.colorextraction.types.Tonal;
Lucas Dupin65f56582017-04-27 10:21:41 -070031
Lucas Dupine17ce522017-07-17 15:45:06 -070032import java.lang.ref.WeakReference;
Lucas Dupin314d41f2017-05-08 15:52:58 -070033import java.util.ArrayList;
34
Lucas Dupin65f56582017-04-27 10:21:41 -070035/**
36 * Class to process wallpaper colors and generate a tonal palette based on them.
37 */
38public class ColorExtractor implements WallpaperManager.OnColorsChangedListener {
Lucas Dupin7aaa3532017-05-28 08:51:07 -070039
40 public static final int TYPE_NORMAL = 0;
41 public static final int TYPE_DARK = 1;
42 public static final int TYPE_EXTRA_DARK = 2;
43 private static final int[] sGradientTypes = new int[]{TYPE_NORMAL, TYPE_DARK, TYPE_EXTRA_DARK};
44
Lucas Dupin65f56582017-04-27 10:21:41 -070045 private static final String TAG = "ColorExtractor";
Lucas Dupin50ba9912017-07-14 11:55:05 -070046 private static final boolean DEBUG = false;
Lucas Dupin7aaa3532017-05-28 08:51:07 -070047
Lucas Dupin8a4522d2017-07-26 21:48:06 -070048 protected final SparseArray<GradientColors[]> mGradientColors;
Lucas Dupine17ce522017-07-17 15:45:06 -070049 private final ArrayList<WeakReference<OnColorsChangedListener>> mOnColorsChangedListeners;
Lucas Dupin65f56582017-04-27 10:21:41 -070050 private final Context mContext;
51 private final ExtractionType mExtractionType;
Lucas Dupin8a4522d2017-07-26 21:48:06 -070052 protected WallpaperColors mSystemColors;
53 protected WallpaperColors mLockColors;
Lucas Dupin65f56582017-04-27 10:21:41 -070054
55 public ColorExtractor(Context context) {
Lucas Dupin5febf7e2019-06-04 17:34:37 -070056 this(context, new Tonal(context), true /* immediately */,
57 context.getSystemService(WallpaperManager.class));
Lucas Dupin7aaa3532017-05-28 08:51:07 -070058 }
59
60 @VisibleForTesting
Lucas Dupin5febf7e2019-06-04 17:34:37 -070061 public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately,
62 WallpaperManager wallpaperManager) {
Lucas Dupin65f56582017-04-27 10:21:41 -070063 mContext = context;
Lucas Dupin7aaa3532017-05-28 08:51:07 -070064 mExtractionType = extractionType;
65
66 mGradientColors = new SparseArray<>();
67 for (int which : new int[] { WallpaperManager.FLAG_LOCK, WallpaperManager.FLAG_SYSTEM}) {
68 GradientColors[] colors = new GradientColors[sGradientTypes.length];
69 mGradientColors.append(which, colors);
70 for (int type : sGradientTypes) {
71 colors[type] = new GradientColors();
72 }
73 }
74
Lucas Dupin314d41f2017-05-08 15:52:58 -070075 mOnColorsChangedListeners = new ArrayList<>();
Lucas Dupin5febf7e2019-06-04 17:34:37 -070076 wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
77 initExtractColors(wallpaperManager, immediately);
wilsonshih0e490d9e2019-02-13 12:20:01 +080078 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -070079
wilsonshih0e490d9e2019-02-13 12:20:01 +080080 private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
81 if (immediately) {
Lucas Dupin75ec3792017-06-29 14:07:18 -070082 mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
83 mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
wilsonshih0e490d9e2019-02-13 12:20:01 +080084 extractWallpaperColors();
85 } else {
86 new LoadWallpaperColors().executeOnExecutor(
87 AsyncTask.THREAD_POOL_EXECUTOR, wallpaperManager);
Lucas Dupin65f56582017-04-27 10:21:41 -070088 }
wilsonshih0e490d9e2019-02-13 12:20:01 +080089 }
Lucas Dupinc77b71d2017-07-05 17:34:41 -070090
wilsonshih0e490d9e2019-02-13 12:20:01 +080091 private class LoadWallpaperColors extends AsyncTask<WallpaperManager, Void, Void> {
92 private WallpaperColors mSystemColors;
93 private WallpaperColors mLockColors;
94 @Override
95 protected Void doInBackground(WallpaperManager... params) {
96 mSystemColors = params[0].getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
97 mLockColors = params[0].getWallpaperColors(WallpaperManager.FLAG_LOCK);
98 return null;
99 }
100 @Override
101 protected void onPostExecute(Void b) {
102 ColorExtractor.this.mSystemColors = mSystemColors;
103 ColorExtractor.this.mLockColors = mLockColors;
104 extractWallpaperColors();
105 triggerColorsChanged(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
106 }
107 }
108
Lucas Dupin89233c42019-06-10 18:00:22 -0700109 protected void extractWallpaperColors() {
wilsonshih0e490d9e2019-02-13 12:20:01 +0800110 GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
111 GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
Lucas Dupinc77b71d2017-07-05 17:34:41 -0700112 extractInto(mSystemColors,
113 systemColors[TYPE_NORMAL],
114 systemColors[TYPE_DARK],
115 systemColors[TYPE_EXTRA_DARK]);
116 extractInto(mLockColors,
117 lockColors[TYPE_NORMAL],
118 lockColors[TYPE_DARK],
119 lockColors[TYPE_EXTRA_DARK]);
Lucas Dupin65f56582017-04-27 10:21:41 -0700120 }
121
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700122 /**
Lucas Dupin78d4aff2017-07-17 10:15:30 -0700123 * Retrieve gradient colors for a specific wallpaper.
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700124 *
125 * @param which FLAG_LOCK or FLAG_SYSTEM
126 * @return colors
127 */
128 @NonNull
Lucas Dupin65f56582017-04-27 10:21:41 -0700129 public GradientColors getColors(int which) {
Lucas Dupin78d4aff2017-07-17 10:15:30 -0700130 return getColors(which, TYPE_DARK);
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700131 }
132
133 /**
134 * Get current gradient colors for one of the possible gradient types
135 *
136 * @param which FLAG_LOCK or FLAG_SYSTEM
137 * @param type TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK
138 * @return colors
139 */
Lucas Dupin75ec3792017-06-29 14:07:18 -0700140 @NonNull
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700141 public GradientColors getColors(int which, int type) {
142 if (type != TYPE_NORMAL && type != TYPE_DARK && type != TYPE_EXTRA_DARK) {
143 throw new IllegalArgumentException(
144 "type should be TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK");
Lucas Dupin65f56582017-04-27 10:21:41 -0700145 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700146 if (which != WallpaperManager.FLAG_LOCK && which != WallpaperManager.FLAG_SYSTEM) {
147 throw new IllegalArgumentException("which should be FLAG_SYSTEM or FLAG_NORMAL");
148 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700149 return mGradientColors.get(which)[type];
Lucas Dupin65f56582017-04-27 10:21:41 -0700150 }
151
Lucas Dupin75ec3792017-06-29 14:07:18 -0700152 /**
153 * Get the last available WallpaperColors without forcing new extraction.
154 *
155 * @param which FLAG_LOCK or FLAG_SYSTEM
156 * @return Last cached colors
157 */
158 @Nullable
159 public WallpaperColors getWallpaperColors(int which) {
160 if (which == WallpaperManager.FLAG_LOCK) {
161 return mLockColors;
162 } else if (which == WallpaperManager.FLAG_SYSTEM) {
163 return mSystemColors;
164 } else {
165 throw new IllegalArgumentException("Invalid value for which: " + which);
166 }
167 }
168
Lucas Dupin65f56582017-04-27 10:21:41 -0700169 @Override
170 public void onColorsChanged(WallpaperColors colors, int which) {
Lucas Dupin50ba9912017-07-14 11:55:05 -0700171 if (DEBUG) {
172 Log.d(TAG, "New wallpaper colors for " + which + ": " + colors);
173 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700174 boolean changed = false;
Lucas Dupin65f56582017-04-27 10:21:41 -0700175 if ((which & WallpaperManager.FLAG_LOCK) != 0) {
Lucas Dupin75ec3792017-06-29 14:07:18 -0700176 mLockColors = colors;
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700177 GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
178 extractInto(colors, lockColors[TYPE_NORMAL], lockColors[TYPE_DARK],
179 lockColors[TYPE_EXTRA_DARK]);
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700180 changed = true;
Lucas Dupin65f56582017-04-27 10:21:41 -0700181 }
182 if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
Lucas Dupin75ec3792017-06-29 14:07:18 -0700183 mSystemColors = colors;
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700184 GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
185 extractInto(colors, systemColors[TYPE_NORMAL], systemColors[TYPE_DARK],
186 systemColors[TYPE_EXTRA_DARK]);
187 changed = true;
188 }
189
190 if (changed) {
191 triggerColorsChanged(which);
Lucas Dupin65f56582017-04-27 10:21:41 -0700192 }
193 }
194
Lucas Dupin1ead7fc2017-05-24 14:14:44 -0700195 protected void triggerColorsChanged(int which) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700196 ArrayList<WeakReference<OnColorsChangedListener>> references =
197 new ArrayList<>(mOnColorsChangedListeners);
198 final int size = references.size();
199 for (int i = 0; i < size; i++) {
200 final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
201 final OnColorsChangedListener listener = weakReference.get();
202 if (listener == null) {
203 mOnColorsChangedListeners.remove(weakReference);
204 } else {
205 listener.onColorsChanged(this, which);
206 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700207 }
208 }
209
210 private void extractInto(WallpaperColors inWallpaperColors,
211 GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark,
212 GradientColors outGradientColorsExtraDark) {
Lucas Dupinc77b71d2017-07-05 17:34:41 -0700213 mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700214 outGradientColorsDark, outGradientColorsExtraDark);
Lucas Dupin65f56582017-04-27 10:21:41 -0700215 }
216
217 public void destroy() {
218 WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
219 if (wallpaperManager != null) {
220 wallpaperManager.removeOnColorsChangedListener(this);
221 }
222 }
223
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700224 public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700225 mOnColorsChangedListeners.add(new WeakReference<>(listener));
Lucas Dupin314d41f2017-05-08 15:52:58 -0700226 }
227
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700228 public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700229 ArrayList<WeakReference<OnColorsChangedListener>> references =
230 new ArrayList<>(mOnColorsChangedListeners);
231 final int size = references.size();
232 for (int i = 0; i < size; i++) {
233 final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
234 if (weakReference.get() == listener) {
235 mOnColorsChangedListeners.remove(weakReference);
236 break;
237 }
238 }
Lucas Dupin314d41f2017-05-08 15:52:58 -0700239 }
240
Lucas Dupin65f56582017-04-27 10:21:41 -0700241 public static class GradientColors {
Lucas Dupinc77b71d2017-07-05 17:34:41 -0700242 private int mMainColor;
243 private int mSecondaryColor;
Robert Snoeberger0397c842019-02-07 14:25:46 -0500244 private int[] mColorPalette;
Lucas Dupin65f56582017-04-27 10:21:41 -0700245 private boolean mSupportsDarkText;
246
247 public void setMainColor(int mainColor) {
248 mMainColor = mainColor;
249 }
250
251 public void setSecondaryColor(int secondaryColor) {
252 mSecondaryColor = secondaryColor;
253 }
254
Robert Snoeberger0397c842019-02-07 14:25:46 -0500255 public void setColorPalette(int[] colorPalette) {
256 mColorPalette = colorPalette;
257 }
258
Lucas Dupin65f56582017-04-27 10:21:41 -0700259 public void setSupportsDarkText(boolean supportsDarkText) {
260 mSupportsDarkText = supportsDarkText;
261 }
262
263 public void set(GradientColors other) {
264 mMainColor = other.mMainColor;
265 mSecondaryColor = other.mSecondaryColor;
Robert Snoeberger0397c842019-02-07 14:25:46 -0500266 mColorPalette = other.mColorPalette;
Lucas Dupin65f56582017-04-27 10:21:41 -0700267 mSupportsDarkText = other.mSupportsDarkText;
268 }
269
270 public int getMainColor() {
271 return mMainColor;
272 }
273
274 public int getSecondaryColor() {
275 return mSecondaryColor;
276 }
277
Robert Snoeberger0397c842019-02-07 14:25:46 -0500278 public int[] getColorPalette() {
279 return mColorPalette;
280 }
281
Lucas Dupin65f56582017-04-27 10:21:41 -0700282 public boolean supportsDarkText() {
283 return mSupportsDarkText;
284 }
285
286 @Override
287 public boolean equals(Object o) {
288 if (o == null || o.getClass() != getClass()) {
289 return false;
290 }
291 GradientColors other = (GradientColors) o;
292 return other.mMainColor == mMainColor &&
293 other.mSecondaryColor == mSecondaryColor &&
294 other.mSupportsDarkText == mSupportsDarkText;
295 }
296
297 @Override
298 public int hashCode() {
299 int code = mMainColor;
300 code = 31 * code + mSecondaryColor;
301 code = 31 * code + (mSupportsDarkText ? 0 : 1);
302 return code;
303 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700304
305 @Override
306 public String toString() {
307 return "GradientColors(" + Integer.toHexString(mMainColor) + ", "
308 + Integer.toHexString(mSecondaryColor) + ")";
309 }
Lucas Dupin65f56582017-04-27 10:21:41 -0700310 }
311
312 public interface OnColorsChangedListener {
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700313 void onColorsChanged(ColorExtractor colorExtractor, int which);
Lucas Dupin65f56582017-04-27 10:21:41 -0700314 }
Robert Snoeberger0397c842019-02-07 14:25:46 -0500315}