• 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 package com.android.wallpaper.module;
17 
18 import static com.android.wallpaper.compat.WallpaperManagerCompat.FLAG_LOCK;
19 import static com.android.wallpaper.compat.WallpaperManagerCompat.FLAG_SYSTEM;
20 
21 import android.annotation.SuppressLint;
22 import android.app.WallpaperManager;
23 import android.content.Context;
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.graphics.drawable.BitmapDrawable;
27 import android.os.AsyncTask;
28 import android.os.Build;
29 import android.os.ParcelFileDescriptor;
30 import android.util.Log;
31 
32 import com.android.wallpaper.R;
33 import com.android.wallpaper.asset.BitmapUtils;
34 import com.android.wallpaper.compat.WallpaperManagerCompat;
35 import com.android.wallpaper.model.LiveWallpaperMetadata;
36 import com.android.wallpaper.model.WallpaperMetadata;
37 
38 import java.io.FileInputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.List;
44 
45 /**
46  * Default implementation of {@link WallpaperRefresher} which refreshes wallpaper metadata
47  * asynchronously.
48  */
49 @SuppressLint("ServiceCast")
50 public class DefaultWallpaperRefresher implements WallpaperRefresher {
51 
52     private static final String TAG = "DefaultWPRefresher";
53 
54     private final Context mAppContext;
55     private final WallpaperPreferences mWallpaperPreferences;
56     private final WallpaperManager mWallpaperManager;
57     private final WallpaperStatusChecker mWallpaperStatusChecker;
58 
59     /**
60      * @param context The application's context.
61      */
DefaultWallpaperRefresher(Context context)62     public DefaultWallpaperRefresher(Context context) {
63         mAppContext = context.getApplicationContext();
64 
65         Injector injector = InjectorProvider.getInjector();
66         mWallpaperPreferences = injector.getPreferences(mAppContext);
67         mWallpaperStatusChecker = injector.getWallpaperStatusChecker();
68 
69         // Retrieve WallpaperManager using Context#getSystemService instead of
70         // WallpaperManager#getInstance so it can be mocked out in test.
71         mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
72     }
73 
74     @Override
refresh(RefreshListener listener)75     public void refresh(RefreshListener listener) {
76         GetWallpaperMetadataAsyncTask task = new GetWallpaperMetadataAsyncTask(listener);
77         task.execute();
78     }
79 
80     /**
81      * Retrieves the current wallpaper's thumbnail and metadata off the UI thread.
82      */
83     private class GetWallpaperMetadataAsyncTask extends
84             AsyncTask<Void, Void, List<WallpaperMetadata>> {
85 
86         private final RefreshListener mListener;
87         private final WallpaperManagerCompat mWallpaperManagerCompat;
88 
89         private long mCurrentHomeWallpaperHashCode;
90         private long mCurrentLockWallpaperHashCode;
91         private String mSystemWallpaperServiceName;
92 
93         @SuppressLint("ServiceCast")
GetWallpaperMetadataAsyncTask(RefreshListener listener)94         public GetWallpaperMetadataAsyncTask(RefreshListener listener) {
95             mListener = listener;
96             mWallpaperManagerCompat =
97                     InjectorProvider.getInjector().getWallpaperManagerCompat(mAppContext);
98         }
99 
100         @Override
doInBackground(Void... unused)101         protected List<WallpaperMetadata> doInBackground(Void... unused) {
102             List<WallpaperMetadata> wallpaperMetadatas = new ArrayList<>();
103 
104             boolean isHomeScreenStatic = mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM) == null;
105             if (!isHomeScreenMetadataCurrent() || (isHomeScreenStatic
106                     && isHomeScreenAttributionsEmpty())) {
107                 mWallpaperPreferences.clearHomeWallpaperMetadata();
108                 setFallbackHomeScreenWallpaperMetadata();
109             }
110 
111             boolean isLockScreenWallpaperCurrentlySet =
112                     mWallpaperStatusChecker.isLockWallpaperSet(mAppContext);
113 
114             if (mWallpaperManager.getWallpaperInfo() == null) {
115                 wallpaperMetadatas.add(new WallpaperMetadata(
116                         mWallpaperPreferences.getHomeWallpaperAttributions(),
117                         mWallpaperPreferences.getHomeWallpaperActionUrl(),
118                         mWallpaperPreferences.getHomeWallpaperActionLabelRes(),
119                         mWallpaperPreferences.getHomeWallpaperActionIconRes(),
120                         mWallpaperPreferences.getHomeWallpaperCollectionId(),
121                         mWallpaperPreferences.getHomeWallpaperBackingFileName(),
122                         null));
123             } else {
124                 wallpaperMetadatas.add(
125                         new LiveWallpaperMetadata(mWallpaperManager.getWallpaperInfo()));
126             }
127 
128             // Return only home metadata if pre-N device or lock screen wallpaper is not explicitly
129             // set.
130             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N
131                     || !isLockScreenWallpaperCurrentlySet) {
132                 return wallpaperMetadatas;
133             }
134 
135             boolean isLockScreenStatic = mWallpaperManager.getWallpaperInfo(FLAG_LOCK) == null;
136             if (!isLockScreenMetadataCurrent() || (isLockScreenStatic
137                     && isLockScreenAttributionsEmpty())) {
138                 mWallpaperPreferences.clearLockWallpaperMetadata();
139                 setFallbackLockScreenWallpaperMetadata();
140             }
141 
142             if (mWallpaperManager.getWallpaperInfo(FLAG_LOCK) == null
143                     || !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
144                 wallpaperMetadatas.add(new WallpaperMetadata(
145                         mWallpaperPreferences.getLockWallpaperAttributions(),
146                         mWallpaperPreferences.getLockWallpaperActionUrl(),
147                         mWallpaperPreferences.getLockWallpaperActionLabelRes(),
148                         mWallpaperPreferences.getLockWallpaperActionIconRes(),
149                         mWallpaperPreferences.getLockWallpaperCollectionId(),
150                         mWallpaperPreferences.getLockWallpaperBackingFileName(),
151                         null));
152             } else {
153                 wallpaperMetadatas.add(new LiveWallpaperMetadata(
154                         mWallpaperManager.getWallpaperInfo(FLAG_LOCK)));
155             }
156 
157             return wallpaperMetadatas;
158         }
159 
160         @Override
onPostExecute(List<WallpaperMetadata> metadatas)161         protected void onPostExecute(List<WallpaperMetadata> metadatas) {
162             if (metadatas.size() > 2) {
163                 Log.e(TAG,
164                         "Got more than 2 WallpaperMetadata objects - only home and (optionally) "
165                         + "lock are permitted.");
166                 return;
167             }
168 
169             mListener.onRefreshed(metadatas.get(0), metadatas.size() > 1 ? metadatas.get(1) : null,
170                     mWallpaperPreferences.getWallpaperPresentationMode());
171         }
172 
173         /**
174          * Sets fallback wallpaper attributions to WallpaperPreferences when the saved metadata did
175          * not match the system wallpaper. For live wallpapers, loads the label (title) but for
176          * image wallpapers loads a generic title string.
177          */
setFallbackHomeScreenWallpaperMetadata()178         private void setFallbackHomeScreenWallpaperMetadata() {
179             android.app.WallpaperInfo wallpaperComponent = mWallpaperManager.getWallpaperInfo();
180             if (wallpaperComponent == null) { // Image wallpaper
181                 mWallpaperPreferences.setHomeWallpaperAttributions(
182                         Arrays.asList(mAppContext.getResources()
183                                 .getString(R.string.fallback_wallpaper_title)));
184 
185                 // Set wallpaper ID if at least N or set a hash code if an earlier version of
186                 // Android.
187                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
188                     mWallpaperPreferences.setHomeWallpaperManagerId(
189                             mWallpaperManagerCompat.getWallpaperId(FLAG_SYSTEM));
190                 } else {
191                     mWallpaperPreferences.setHomeWallpaperHashCode(
192                             getCurrentHomeWallpaperHashCode());
193                 }
194             } else { // Live wallpaper
195                 mWallpaperPreferences.setHomeWallpaperAttributions(Arrays.asList(
196                         wallpaperComponent.loadLabel(mAppContext.getPackageManager()).toString()));
197                 mWallpaperPreferences.setHomeWallpaperServiceName(mSystemWallpaperServiceName);
198             }
199             mWallpaperPreferences.setWallpaperPresentationMode(
200                     WallpaperPreferences.PRESENTATION_MODE_STATIC);
201         }
202 
203         /**
204          * Sets fallback lock screen wallpaper attributions to WallpaperPreferences. This should be
205          * called when the saved lock screen wallpaper metadata does not match the currently set
206          * lock screen wallpaper.
207          */
setFallbackLockScreenWallpaperMetadata()208         private void setFallbackLockScreenWallpaperMetadata() {
209             mWallpaperPreferences.setLockWallpaperAttributions(
210                     Arrays.asList(mAppContext.getResources()
211                             .getString(R.string.fallback_wallpaper_title)));
212             mWallpaperPreferences.setLockWallpaperId(mWallpaperManagerCompat.getWallpaperId(
213                     FLAG_LOCK));
214         }
215 
216         /**
217          * Returns whether the home screen metadata saved in WallpaperPreferences corresponds to the
218          * current system wallpaper.
219          */
isHomeScreenMetadataCurrent()220         private boolean isHomeScreenMetadataCurrent() {
221             return (mWallpaperManager.getWallpaperInfo() == null)
222                     ? isHomeScreenImageWallpaperCurrent()
223                     : isHomeScreenLiveWallpaperCurrent();
224         }
225 
226         /**
227          * Returns whether the home screen attributions saved in WallpaperPreferences is empty.
228          */
isHomeScreenAttributionsEmpty()229         private boolean isHomeScreenAttributionsEmpty() {
230             List<String> homeScreenAttributions =
231                     mWallpaperPreferences.getHomeWallpaperAttributions();
232             return homeScreenAttributions.get(0) == null
233                     && homeScreenAttributions.get(1) == null
234                     && homeScreenAttributions.get(2) == null;
235         }
236 
getCurrentHomeWallpaperHashCode()237         private long getCurrentHomeWallpaperHashCode() {
238             if (mCurrentHomeWallpaperHashCode == 0) {
239                 BitmapDrawable wallpaperDrawable = (BitmapDrawable)
240                         mWallpaperManagerCompat.getDrawable();
241                 Bitmap wallpaperBitmap = wallpaperDrawable.getBitmap();
242                 mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap);
243 
244                 // Manually request that WallpaperManager loses its reference to the current
245                 // wallpaper bitmap, which can occupy a large memory allocation for the lifetime of
246                 // the app.
247                 mWallpaperManager.forgetLoadedWallpaper();
248             }
249             return mCurrentHomeWallpaperHashCode;
250         }
251 
getCurrentLockWallpaperHashCode()252         private long getCurrentLockWallpaperHashCode() {
253             if (mCurrentLockWallpaperHashCode == 0
254                     && mWallpaperStatusChecker.isLockWallpaperSet(mAppContext)) {
255                 Bitmap wallpaperBitmap = getLockWallpaperBitmap();
256                 mCurrentLockWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap);
257             }
258             return mCurrentLockWallpaperHashCode;
259         }
260 
261         /**
262          * Returns the lock screen wallpaper currently set on the device as a Bitmap, or null if no
263          * lock screen wallpaper is set.
264          */
getLockWallpaperBitmap()265         private Bitmap getLockWallpaperBitmap() {
266             Bitmap lockBitmap = null;
267 
268             ParcelFileDescriptor pfd = mWallpaperManagerCompat.getWallpaperFile(
269                     FLAG_LOCK);
270             // getWallpaperFile returns null if the lock screen isn't explicitly set, so need this
271             // check.
272             if (pfd != null) {
273                 InputStream fileStream = null;
274                 try {
275                     fileStream = new FileInputStream(pfd.getFileDescriptor());
276                     lockBitmap = BitmapFactory.decodeStream(fileStream);
277                     pfd.close();
278                     return lockBitmap;
279                 } catch (IOException e) {
280                     Log.e(TAG, "IO exception when closing the file descriptor.");
281                 } finally {
282                     if (fileStream != null) {
283                         try {
284                             fileStream.close();
285                         } catch (IOException e) {
286                             Log.e(TAG,
287                                     "IO exception when closing input stream for lock screen WP.");
288                         }
289                     }
290                 }
291             }
292 
293             return lockBitmap;
294         }
295 
296         /**
297          * Returns whether the image wallpaper set to the system matches the metadata in
298          * WallpaperPreferences.
299          */
isHomeScreenImageWallpaperCurrent()300         private boolean isHomeScreenImageWallpaperCurrent() {
301             long savedBitmapHash = mWallpaperPreferences.getHomeWallpaperHashCode();
302 
303             // Use WallpaperManager IDs to check same-ness of image wallpaper on N+ versions of
304             // Android only when there is no saved bitmap hash code (which could be leftover from a
305             // previous build of the app that did not use wallpaper IDs).
306             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && savedBitmapHash == 0) {
307                 return mWallpaperPreferences.getHomeWallpaperManagerId()
308                         == mWallpaperManagerCompat.getWallpaperId(FLAG_SYSTEM);
309             }
310 
311             return savedBitmapHash == getCurrentHomeWallpaperHashCode();
312         }
313 
314         /**
315          * Returns whether the live wallpaper set to the system's home screen matches the metadata
316          * in WallpaperPreferences.
317          */
isHomeScreenLiveWallpaperCurrent()318         private boolean isHomeScreenLiveWallpaperCurrent() {
319             mSystemWallpaperServiceName = mWallpaperManager.getWallpaperInfo().getServiceName();
320             String homeWallpaperServiceName = mWallpaperPreferences.getHomeWallpaperServiceName();
321             return mSystemWallpaperServiceName.equals(homeWallpaperServiceName);
322         }
323 
324         /**
325          * Returns whether the lock screen metadata saved in WallpaperPreferences corresponds to the
326          * current lock screen wallpaper.
327          */
isLockScreenMetadataCurrent()328         private boolean isLockScreenMetadataCurrent() {
329             return (mWallpaperManager.getWallpaperInfo(FLAG_LOCK) == null)
330                     ? isLockScreenImageWallpaperCurrent()
331                     : isLockScreenLiveWallpaperCurrent();
332         }
333 
334         /**
335          * Returns whether the image wallpaper set for the lock screen matches the metadata in
336          * WallpaperPreferences.
337          */
isLockScreenImageWallpaperCurrent()338         private boolean isLockScreenImageWallpaperCurrent() {
339             // Check for lock wallpaper image same-ness only when there is no stored lock wallpaper
340             // hash code. Otherwise if there is a lock wallpaper hash code stored in
341             // {@link WallpaperPreferences}, then check hash codes.
342             long savedLockWallpaperHash = mWallpaperPreferences.getLockWallpaperHashCode();
343 
344             if (savedLockWallpaperHash == 0) {
345                 return mWallpaperPreferences.getLockWallpaperId()
346                         == mWallpaperManagerCompat.getWallpaperId(FLAG_LOCK);
347             } else {
348                 return savedLockWallpaperHash == getCurrentLockWallpaperHashCode();
349             }
350         }
351 
352         /**
353          * Returns whether the live wallpaper for the home screen matches the metadata in
354          * WallpaperPreferences.
355          */
isLockScreenLiveWallpaperCurrent()356         private boolean isLockScreenLiveWallpaperCurrent() {
357             String currentServiceName = mWallpaperManager.getWallpaperInfo(FLAG_LOCK)
358                     .getServiceName();
359             String storedServiceName = mWallpaperPreferences.getLockWallpaperServiceName();
360             return currentServiceName.equals(storedServiceName);
361         }
362 
363 
364         /**
365          * Returns whether the lock screen attributions saved in WallpaperPreferences are empty.
366          */
isLockScreenAttributionsEmpty()367         private boolean isLockScreenAttributionsEmpty() {
368             List<String> attributions = mWallpaperPreferences.getLockWallpaperAttributions();
369             return attributions.get(0) == null
370                     && attributions.get(1) == null
371                     && attributions.get(2) == null;
372         }
373     }
374 }
375