• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.internal.colorextraction;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.WallpaperColors;
22 import android.app.WallpaperManager;
23 import android.content.Context;
24 import android.os.AsyncTask;
25 import android.util.Log;
26 import android.util.SparseArray;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.colorextraction.types.ExtractionType;
30 import com.android.internal.colorextraction.types.Tonal;
31 
32 import java.lang.ref.WeakReference;
33 import java.util.ArrayList;
34 
35 /**
36  * Class to process wallpaper colors and generate a tonal palette based on them.
37  */
38 public class ColorExtractor implements WallpaperManager.OnColorsChangedListener {
39 
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 
45     private static final String TAG = "ColorExtractor";
46     private static final boolean DEBUG = false;
47 
48     protected final SparseArray<GradientColors[]> mGradientColors;
49     private final ArrayList<WeakReference<OnColorsChangedListener>> mOnColorsChangedListeners;
50     private final Context mContext;
51     private final ExtractionType mExtractionType;
52     protected WallpaperColors mSystemColors;
53     protected WallpaperColors mLockColors;
54 
ColorExtractor(Context context)55     public ColorExtractor(Context context) {
56         this(context, new Tonal(context), true /* immediately */,
57                 context.getSystemService(WallpaperManager.class));
58     }
59 
60     @VisibleForTesting
ColorExtractor(Context context, ExtractionType extractionType, boolean immediately, WallpaperManager wallpaperManager)61     public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately,
62             WallpaperManager wallpaperManager) {
63         mContext = context;
64         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 
75         mOnColorsChangedListeners = new ArrayList<>();
76 
77         if (wallpaperManager == null) {
78             Log.w(TAG, "Can't listen to color changes!");
79         } else {
80             wallpaperManager.addOnColorsChangedListener(this, null /* handler */);
81             initExtractColors(wallpaperManager, immediately);
82         }
83     }
84 
initExtractColors(WallpaperManager wallpaperManager, boolean immediately)85     private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) {
86         if (immediately) {
87             mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
88             mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
89             extractWallpaperColors();
90         } else {
91             new LoadWallpaperColors().executeOnExecutor(
92                     AsyncTask.THREAD_POOL_EXECUTOR, wallpaperManager);
93         }
94     }
95 
96     private class LoadWallpaperColors extends AsyncTask<WallpaperManager, Void, Void> {
97         private WallpaperColors mSystemColors;
98         private WallpaperColors mLockColors;
99         @Override
doInBackground(WallpaperManager... params)100         protected Void doInBackground(WallpaperManager... params) {
101             mSystemColors = params[0].getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
102             mLockColors = params[0].getWallpaperColors(WallpaperManager.FLAG_LOCK);
103             return null;
104         }
105         @Override
onPostExecute(Void b)106         protected void onPostExecute(Void b) {
107             ColorExtractor.this.mSystemColors = mSystemColors;
108             ColorExtractor.this.mLockColors = mLockColors;
109             extractWallpaperColors();
110             triggerColorsChanged(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
111         }
112     }
113 
extractWallpaperColors()114     protected void extractWallpaperColors() {
115         GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
116         GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
117         extractInto(mSystemColors,
118                 systemColors[TYPE_NORMAL],
119                 systemColors[TYPE_DARK],
120                 systemColors[TYPE_EXTRA_DARK]);
121         extractInto(mLockColors,
122                 lockColors[TYPE_NORMAL],
123                 lockColors[TYPE_DARK],
124                 lockColors[TYPE_EXTRA_DARK]);
125     }
126 
127     /**
128      * Retrieve gradient colors for a specific wallpaper.
129      *
130      * @param which FLAG_LOCK or FLAG_SYSTEM
131      * @return colors
132      */
133     @NonNull
getColors(int which)134     public GradientColors getColors(int which) {
135         return getColors(which, TYPE_DARK);
136     }
137 
138     /**
139      * Get current gradient colors for one of the possible gradient types
140      *
141      * @param which FLAG_LOCK or FLAG_SYSTEM
142      * @param type TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK
143      * @return colors
144      */
145     @NonNull
getColors(int which, int type)146     public GradientColors getColors(int which, int type) {
147         if (type != TYPE_NORMAL && type != TYPE_DARK && type != TYPE_EXTRA_DARK) {
148             throw new IllegalArgumentException(
149                     "type should be TYPE_NORMAL, TYPE_DARK or TYPE_EXTRA_DARK");
150         }
151         if (which != WallpaperManager.FLAG_LOCK && which != WallpaperManager.FLAG_SYSTEM) {
152             throw new IllegalArgumentException("which should be FLAG_SYSTEM or FLAG_NORMAL");
153         }
154         return mGradientColors.get(which)[type];
155     }
156 
157     /**
158      * Get the last available WallpaperColors without forcing new extraction.
159      *
160      * @param which FLAG_LOCK or FLAG_SYSTEM
161      * @return Last cached colors
162      */
163     @Nullable
getWallpaperColors(int which)164     public WallpaperColors getWallpaperColors(int which) {
165         if (which == WallpaperManager.FLAG_LOCK) {
166             return mLockColors;
167         } else if (which == WallpaperManager.FLAG_SYSTEM) {
168             return mSystemColors;
169         } else {
170             throw new IllegalArgumentException("Invalid value for which: " + which);
171         }
172     }
173 
174     @Override
onColorsChanged(WallpaperColors colors, int which)175     public void onColorsChanged(WallpaperColors colors, int which) {
176         if (DEBUG) {
177             Log.d(TAG, "New wallpaper colors for " + which + ": " + colors);
178         }
179         boolean changed = false;
180         if ((which & WallpaperManager.FLAG_LOCK) != 0) {
181             mLockColors = colors;
182             GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
183             extractInto(colors, lockColors[TYPE_NORMAL], lockColors[TYPE_DARK],
184                     lockColors[TYPE_EXTRA_DARK]);
185             changed = true;
186         }
187         if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
188             mSystemColors = colors;
189             GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
190             extractInto(colors, systemColors[TYPE_NORMAL], systemColors[TYPE_DARK],
191                     systemColors[TYPE_EXTRA_DARK]);
192             changed = true;
193         }
194 
195         if (changed) {
196             triggerColorsChanged(which);
197         }
198     }
199 
triggerColorsChanged(int which)200     protected void triggerColorsChanged(int which) {
201         ArrayList<WeakReference<OnColorsChangedListener>> references =
202                 new ArrayList<>(mOnColorsChangedListeners);
203         final int size = references.size();
204         for (int i = 0; i < size; i++) {
205             final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
206             final OnColorsChangedListener listener = weakReference.get();
207             if (listener == null) {
208                 mOnColorsChangedListeners.remove(weakReference);
209             } else {
210                 listener.onColorsChanged(this, which);
211             }
212         }
213     }
214 
extractInto(WallpaperColors inWallpaperColors, GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark, GradientColors outGradientColorsExtraDark)215     private void extractInto(WallpaperColors inWallpaperColors,
216             GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark,
217             GradientColors outGradientColorsExtraDark) {
218         mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
219                 outGradientColorsDark, outGradientColorsExtraDark);
220     }
221 
destroy()222     public void destroy() {
223         WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
224         if (wallpaperManager != null) {
225             wallpaperManager.removeOnColorsChangedListener(this);
226         }
227     }
228 
addOnColorsChangedListener(@onNull OnColorsChangedListener listener)229     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
230         mOnColorsChangedListeners.add(new WeakReference<>(listener));
231     }
232 
removeOnColorsChangedListener(@onNull OnColorsChangedListener listener)233     public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
234         ArrayList<WeakReference<OnColorsChangedListener>> references =
235                 new ArrayList<>(mOnColorsChangedListeners);
236         final int size = references.size();
237         for (int i = 0; i < size; i++) {
238             final WeakReference<OnColorsChangedListener> weakReference = references.get(i);
239             if (weakReference.get() == listener) {
240                 mOnColorsChangedListeners.remove(weakReference);
241                 break;
242             }
243         }
244     }
245 
246     public static class GradientColors {
247         private int mMainColor;
248         private int mSecondaryColor;
249         private int[] mColorPalette;
250         private boolean mSupportsDarkText;
251 
setMainColor(int mainColor)252         public void setMainColor(int mainColor) {
253             mMainColor = mainColor;
254         }
255 
setSecondaryColor(int secondaryColor)256         public void setSecondaryColor(int secondaryColor) {
257             mSecondaryColor = secondaryColor;
258         }
259 
setColorPalette(int[] colorPalette)260         public void setColorPalette(int[] colorPalette) {
261             mColorPalette = colorPalette;
262         }
263 
setSupportsDarkText(boolean supportsDarkText)264         public void setSupportsDarkText(boolean supportsDarkText) {
265             mSupportsDarkText = supportsDarkText;
266         }
267 
set(GradientColors other)268         public void set(GradientColors other) {
269             mMainColor = other.mMainColor;
270             mSecondaryColor = other.mSecondaryColor;
271             mColorPalette = other.mColorPalette;
272             mSupportsDarkText = other.mSupportsDarkText;
273         }
274 
getMainColor()275         public int getMainColor() {
276             return mMainColor;
277         }
278 
getSecondaryColor()279         public int getSecondaryColor() {
280             return mSecondaryColor;
281         }
282 
getColorPalette()283         public int[] getColorPalette() {
284             return mColorPalette;
285         }
286 
supportsDarkText()287         public boolean supportsDarkText() {
288             return mSupportsDarkText;
289         }
290 
291         @Override
equals(Object o)292         public boolean equals(Object o) {
293             if (o == null || o.getClass() != getClass()) {
294                 return false;
295             }
296             GradientColors other = (GradientColors) o;
297             return other.mMainColor == mMainColor &&
298                     other.mSecondaryColor == mSecondaryColor &&
299                     other.mSupportsDarkText == mSupportsDarkText;
300         }
301 
302         @Override
hashCode()303         public int hashCode() {
304             int code = mMainColor;
305             code = 31 * code + mSecondaryColor;
306             code = 31 * code + (mSupportsDarkText ? 0 : 1);
307             return code;
308         }
309 
310         @Override
toString()311         public String toString() {
312             return "GradientColors(" + Integer.toHexString(mMainColor) + ", "
313                     + Integer.toHexString(mSecondaryColor) + ")";
314         }
315     }
316 
317     public interface OnColorsChangedListener {
onColorsChanged(ColorExtractor colorExtractor, int which)318         void onColorsChanged(ColorExtractor colorExtractor, int which);
319     }
320 }
321