blob: ef98a5e9f787e580fdef2689f495047b5c83d17c [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;
Lucas Dupin75ec3792017-06-29 14:07:18 -070024import android.os.Trace;
Lucas Dupin26fb43c2017-07-14 11:55:05 -070025import android.os.UserHandle;
Lucas Dupin65f56582017-04-27 10:21:41 -070026import android.util.Log;
Lucas Dupin7aaa3532017-05-28 08:51:07 -070027import android.util.SparseArray;
Lucas Dupin65f56582017-04-27 10:21:41 -070028
Lucas Dupine2292a92017-07-06 14:35:30 -070029import com.android.internal.annotations.VisibleForTesting;
30import com.android.internal.colorextraction.types.ExtractionType;
31import com.android.internal.colorextraction.types.Tonal;
Lucas Dupin65f56582017-04-27 10:21:41 -070032
Lucas Dupine17ce522017-07-17 15:45:06 -070033import java.lang.ref.WeakReference;
Lucas Dupin314d41f2017-05-08 15:52:58 -070034import java.util.ArrayList;
Lucas Dupine17ce522017-07-17 15:45:06 -070035import java.util.Iterator;
Lucas Dupin314d41f2017-05-08 15:52:58 -070036
Lucas Dupin65f56582017-04-27 10:21:41 -070037/**
38 * Class to process wallpaper colors and generate a tonal palette based on them.
39 */
40public class ColorExtractor implements WallpaperManager.OnColorsChangedListener {
Lucas Dupin7aaa3532017-05-28 08:51:07 -070041
42 public static final int TYPE_NORMAL = 0;
43 public static final int TYPE_DARK = 1;
44 public static final int TYPE_EXTRA_DARK = 2;
45 private static final int[] sGradientTypes = new int[]{TYPE_NORMAL, TYPE_DARK, TYPE_EXTRA_DARK};
46
Lucas Dupin65f56582017-04-27 10:21:41 -070047 private static final String TAG = "ColorExtractor";
Lucas Dupin26fb43c2017-07-14 11:55:05 -070048 private static final boolean DEBUG = false;
Lucas Dupin7aaa3532017-05-28 08:51:07 -070049
Lucas Dupin8a4522d2017-07-26 21:48:06 -070050 protected final SparseArray<GradientColors[]> mGradientColors;
Lucas Dupine17ce522017-07-17 15:45:06 -070051 private final ArrayList<WeakReference<OnColorsChangedListener>> mOnColorsChangedListeners;
Lucas Dupin65f56582017-04-27 10:21:41 -070052 private final Context mContext;
53 private final ExtractionType mExtractionType;
Lucas Dupin8a4522d2017-07-26 21:48:06 -070054 protected WallpaperColors mSystemColors;
55 protected WallpaperColors mLockColors;
Lucas Dupin65f56582017-04-27 10:21:41 -070056
57 public ColorExtractor(Context context) {
Lucas Dupin6e69c852017-07-06 16:09:24 -070058 this(context, new Tonal(context));
Lucas Dupin7aaa3532017-05-28 08:51:07 -070059 }
60
61 @VisibleForTesting
62 public ColorExtractor(Context context, ExtractionType extractionType) {
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 Dupinc77b71d2017-07-05 17:34:41 -070076 GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
77 GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
78
Lucas Dupin65f56582017-04-27 10:21:41 -070079 WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
Lucas Dupin65f56582017-04-27 10:21:41 -070080 if (wallpaperManager == null) {
81 Log.w(TAG, "Can't listen to color changes!");
82 } else {
83 wallpaperManager.addOnColorsChangedListener(this);
Lucas Dupin7aaa3532017-05-28 08:51:07 -070084
85 // Initialize all gradients with the current colors
Lucas Dupin75ec3792017-06-29 14:07:18 -070086 Trace.beginSection("ColorExtractor#getWallpaperColors");
87 mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
88 mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
Lucas Dupin75ec3792017-06-29 14:07:18 -070089 Trace.endSection();
Lucas Dupin65f56582017-04-27 10:21:41 -070090 }
Lucas Dupinc77b71d2017-07-05 17:34:41 -070091
92 // Initialize all gradients with the current colors
93 extractInto(mSystemColors,
94 systemColors[TYPE_NORMAL],
95 systemColors[TYPE_DARK],
96 systemColors[TYPE_EXTRA_DARK]);
97 extractInto(mLockColors,
98 lockColors[TYPE_NORMAL],
99 lockColors[TYPE_DARK],
100 lockColors[TYPE_EXTRA_DARK]);
Lucas Dupin65f56582017-04-27 10:21:41 -0700101 }
102
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700103 /**
Lucas Dupin78d4aff2017-07-17 10:15:30 -0700104 * Retrieve gradient colors for a specific wallpaper.
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700105 *
106 * @param which FLAG_LOCK or FLAG_SYSTEM
107 * @return colors
108 */
109 @NonNull
Lucas Dupin65f56582017-04-27 10:21:41 -0700110 public GradientColors getColors(int which) {
Lucas Dupin78d4aff2017-07-17 10:15:30 -0700111 return getColors(which, TYPE_DARK);
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700112 }
113
114 /**
115 * Get current gradient colors for one of the possible gradient types
116 *
117 * @param which FLAG_LOCK or FLAG_SYSTEM
118 * @param type TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK
119 * @return colors
120 */
Lucas Dupin75ec3792017-06-29 14:07:18 -0700121 @NonNull
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700122 public GradientColors getColors(int which, int type) {
123 if (type != TYPE_NORMAL && type != TYPE_DARK && type != TYPE_EXTRA_DARK) {
124 throw new IllegalArgumentException(
125 "type should be TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK");
Lucas Dupin65f56582017-04-27 10:21:41 -0700126 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700127 if (which != WallpaperManager.FLAG_LOCK && which != WallpaperManager.FLAG_SYSTEM) {
128 throw new IllegalArgumentException("which should be FLAG_SYSTEM or FLAG_NORMAL");
129 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700130 return mGradientColors.get(which)[type];
Lucas Dupin65f56582017-04-27 10:21:41 -0700131 }
132
Lucas Dupin75ec3792017-06-29 14:07:18 -0700133 /**
134 * Get the last available WallpaperColors without forcing new extraction.
135 *
136 * @param which FLAG_LOCK or FLAG_SYSTEM
137 * @return Last cached colors
138 */
139 @Nullable
140 public WallpaperColors getWallpaperColors(int which) {
141 if (which == WallpaperManager.FLAG_LOCK) {
142 return mLockColors;
143 } else if (which == WallpaperManager.FLAG_SYSTEM) {
144 return mSystemColors;
145 } else {
146 throw new IllegalArgumentException("Invalid value for which: " + which);
147 }
148 }
149
Lucas Dupin65f56582017-04-27 10:21:41 -0700150 @Override
151 public void onColorsChanged(WallpaperColors colors, int which) {
Lucas Dupin26fb43c2017-07-14 11:55:05 -0700152 if (DEBUG) {
153 Log.d(TAG, "New wallpaper colors for " + which + ": " + colors);
154 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700155 boolean changed = false;
Lucas Dupin65f56582017-04-27 10:21:41 -0700156 if ((which & WallpaperManager.FLAG_LOCK) != 0) {
Lucas Dupin75ec3792017-06-29 14:07:18 -0700157 mLockColors = colors;
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700158 GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
159 extractInto(colors, lockColors[TYPE_NORMAL], lockColors[TYPE_DARK],
160 lockColors[TYPE_EXTRA_DARK]);
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700161 changed = true;
Lucas Dupin65f56582017-04-27 10:21:41 -0700162 }
163 if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
Lucas Dupin75ec3792017-06-29 14:07:18 -0700164 mSystemColors = colors;
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700165 GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
166 extractInto(colors, systemColors[TYPE_NORMAL], systemColors[TYPE_DARK],
167 systemColors[TYPE_EXTRA_DARK]);
168 changed = true;
169 }
170
171 if (changed) {
172 triggerColorsChanged(which);
Lucas Dupin65f56582017-04-27 10:21:41 -0700173 }
174 }
175
Lucas Dupin1ead7fc2017-05-24 14:14:44 -0700176 protected void triggerColorsChanged(int which) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700177 ArrayList<WeakReference<OnColorsChangedListener>> references =
178 new ArrayList<>(mOnColorsChangedListeners);
179 final int size = references.size();
180 for (int i = 0; i < size; i++) {
181 final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
182 final OnColorsChangedListener listener = weakReference.get();
183 if (listener == null) {
184 mOnColorsChangedListeners.remove(weakReference);
185 } else {
186 listener.onColorsChanged(this, which);
187 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700188 }
189 }
190
191 private void extractInto(WallpaperColors inWallpaperColors,
192 GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark,
193 GradientColors outGradientColorsExtraDark) {
Lucas Dupinc77b71d2017-07-05 17:34:41 -0700194 mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700195 outGradientColorsDark, outGradientColorsExtraDark);
Lucas Dupin65f56582017-04-27 10:21:41 -0700196 }
197
198 public void destroy() {
199 WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
200 if (wallpaperManager != null) {
201 wallpaperManager.removeOnColorsChangedListener(this);
202 }
203 }
204
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700205 public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700206 mOnColorsChangedListeners.add(new WeakReference<>(listener));
Lucas Dupin314d41f2017-05-08 15:52:58 -0700207 }
208
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700209 public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700210 ArrayList<WeakReference<OnColorsChangedListener>> references =
211 new ArrayList<>(mOnColorsChangedListeners);
212 final int size = references.size();
213 for (int i = 0; i < size; i++) {
214 final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
215 if (weakReference.get() == listener) {
216 mOnColorsChangedListeners.remove(weakReference);
217 break;
218 }
219 }
Lucas Dupin314d41f2017-05-08 15:52:58 -0700220 }
221
Lucas Dupin65f56582017-04-27 10:21:41 -0700222 public static class GradientColors {
Lucas Dupinc77b71d2017-07-05 17:34:41 -0700223 private int mMainColor;
224 private int mSecondaryColor;
Lucas Dupin65f56582017-04-27 10:21:41 -0700225 private boolean mSupportsDarkText;
226
227 public void setMainColor(int mainColor) {
228 mMainColor = mainColor;
229 }
230
231 public void setSecondaryColor(int secondaryColor) {
232 mSecondaryColor = secondaryColor;
233 }
234
235 public void setSupportsDarkText(boolean supportsDarkText) {
236 mSupportsDarkText = supportsDarkText;
237 }
238
239 public void set(GradientColors other) {
240 mMainColor = other.mMainColor;
241 mSecondaryColor = other.mSecondaryColor;
242 mSupportsDarkText = other.mSupportsDarkText;
243 }
244
245 public int getMainColor() {
246 return mMainColor;
247 }
248
249 public int getSecondaryColor() {
250 return mSecondaryColor;
251 }
252
253 public boolean supportsDarkText() {
254 return mSupportsDarkText;
255 }
256
257 @Override
258 public boolean equals(Object o) {
259 if (o == null || o.getClass() != getClass()) {
260 return false;
261 }
262 GradientColors other = (GradientColors) o;
263 return other.mMainColor == mMainColor &&
264 other.mSecondaryColor == mSecondaryColor &&
265 other.mSupportsDarkText == mSupportsDarkText;
266 }
267
268 @Override
269 public int hashCode() {
270 int code = mMainColor;
271 code = 31 * code + mSecondaryColor;
272 code = 31 * code + (mSupportsDarkText ? 0 : 1);
273 return code;
274 }
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700275
276 @Override
277 public String toString() {
278 return "GradientColors(" + Integer.toHexString(mMainColor) + ", "
279 + Integer.toHexString(mSecondaryColor) + ")";
280 }
Lucas Dupin65f56582017-04-27 10:21:41 -0700281 }
282
283 public interface OnColorsChangedListener {
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700284 void onColorsChanged(ColorExtractor colorExtractor, int which);
Lucas Dupin65f56582017-04-27 10:21:41 -0700285 }
Lucas Dupin1ead7fc2017-05-24 14:14:44 -0700286}