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