blob: 52b86aae8e6b3cf1873579aa03656a19c9556f05 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
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 */
16package com.android.wallpaper.module;
17
18import android.annotation.SuppressLint;
19import android.app.Activity;
20import android.app.WallpaperManager;
21import android.content.Context;
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -070022import android.content.res.Resources;
Jon Miranda16ea1b12017-12-12 14:52:48 -080023import android.graphics.Bitmap;
24import android.graphics.Bitmap.CompressFormat;
25import android.graphics.BitmapFactory;
26import android.graphics.Point;
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -070027import android.graphics.PointF;
Jon Miranda16ea1b12017-12-12 14:52:48 -080028import android.graphics.Rect;
29import android.graphics.drawable.BitmapDrawable;
30import android.os.AsyncTask;
Colin Cross1b7f2082021-05-21 11:15:20 -070031import android.os.Build;
Jon Miranda16ea1b12017-12-12 14:52:48 -080032import android.os.ParcelFileDescriptor;
Jon Miranda16ea1b12017-12-12 14:52:48 -080033import android.util.Log;
34import android.view.Display;
35import android.view.WindowManager;
36
Samuel Fufa03bc6ac2019-09-19 12:01:50 -070037import androidx.annotation.Nullable;
38
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import com.android.wallpaper.asset.Asset;
40import com.android.wallpaper.asset.Asset.BitmapReceiver;
41import com.android.wallpaper.asset.Asset.DimensionsReceiver;
42import com.android.wallpaper.asset.BitmapUtils;
43import com.android.wallpaper.asset.StreamableAsset;
44import com.android.wallpaper.asset.StreamableAsset.StreamReceiver;
Jon Miranda16ea1b12017-12-12 14:52:48 -080045import com.android.wallpaper.compat.WallpaperManagerCompat;
46import com.android.wallpaper.model.WallpaperInfo;
47import com.android.wallpaper.module.BitmapCropper.Callback;
Jon Miranda16ea1b12017-12-12 14:52:48 -080048import com.android.wallpaper.util.BitmapTransformer;
Jon Miranda16ea1b12017-12-12 14:52:48 -080049import com.android.wallpaper.util.ScreenSizeCalculator;
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -070050import com.android.wallpaper.util.WallpaperCropUtils;
Jon Miranda16ea1b12017-12-12 14:52:48 -080051
52import java.io.ByteArrayInputStream;
53import java.io.ByteArrayOutputStream;
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import java.io.FileInputStream;
Jon Miranda16ea1b12017-12-12 14:52:48 -080055import java.io.IOException;
56import java.io.InputStream;
57import java.util.List;
58
59/**
60 * Concrete implementation of WallpaperPersister which actually sets wallpapers to the system via
61 * the WallpaperManager.
62 */
63public class DefaultWallpaperPersister implements WallpaperPersister {
64
65 private static final int DEFAULT_COMPRESS_QUALITY = 100;
66 private static final String TAG = "WallpaperPersister";
67
68 private final Context mAppContext; // The application's context.
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -070069 // Context that accesses files in device protected storage
Jon Miranda16ea1b12017-12-12 14:52:48 -080070 private final WallpaperManager mWallpaperManager;
71 private final WallpaperManagerCompat mWallpaperManagerCompat;
72 private final WallpaperPreferences mWallpaperPreferences;
Jon Miranda16ea1b12017-12-12 14:52:48 -080073 private final WallpaperChangedNotifier mWallpaperChangedNotifier;
74
75 private WallpaperInfo mWallpaperInfoInPreview;
76
77 @SuppressLint("ServiceCast")
78 public DefaultWallpaperPersister(Context context) {
79 mAppContext = context.getApplicationContext();
80 // Retrieve WallpaperManager using Context#getSystemService instead of
81 // WallpaperManager#getInstance so it can be mocked out in test.
82 Injector injector = InjectorProvider.getInjector();
83 mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
84 mWallpaperManagerCompat = injector.getWallpaperManagerCompat(context);
85 mWallpaperPreferences = injector.getPreferences(context);
Jon Miranda16ea1b12017-12-12 14:52:48 -080086 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
87 }
88
89 @Override
90 public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -070091 @Nullable Rect cropRect, float scale, @Destination final int destination,
92 final SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -080093 // Set wallpaper without downscaling directly from an input stream if there's no crop rect
94 // specified by the caller and the asset is streamable.
95 if (cropRect == null && asset instanceof StreamableAsset) {
96 ((StreamableAsset) asset).fetchInputStream(new StreamReceiver() {
97 @Override
98 public void onInputStreamOpened(@Nullable InputStream inputStream) {
99 if (inputStream == null) {
100 callback.onError(null /* throwable */);
101 return;
102 }
103 setIndividualWallpaper(wallpaper, inputStream, destination, callback);
104 }
105 });
106 return;
107 }
108
109 // If no crop rect is specified but the wallpaper asset is not streamable, then fall back to
110 // using the device's display size.
111 if (cropRect == null) {
112 Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
113 .getDefaultDisplay();
114 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
115 asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
116 @Override
117 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
118 if (bitmap == null) {
119 callback.onError(null /* throwable */);
120 return;
121 }
122 setIndividualWallpaper(wallpaper, bitmap, destination, callback);
123 }
124 });
125 return;
126 }
127
128 BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800129 bitmapCropper.cropAndScaleBitmap(asset, scale, cropRect, false, new Callback() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800130 @Override
131 public void onBitmapCropped(Bitmap croppedBitmap) {
132 setIndividualWallpaper(wallpaper, croppedBitmap, destination, callback);
133 }
134
135 @Override
136 public void onError(@Nullable Throwable e) {
137 callback.onError(e);
138 }
139 });
140 }
141
142 @Override
143 public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700144 @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800145 Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
146 .getDefaultDisplay();
147 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
148
149 Asset asset = wallpaper.getAsset(activity);
150 asset.decodeRawDimensions(activity, new DimensionsReceiver() {
151 @Override
152 public void onDimensionsDecoded(@Nullable Point dimensions) {
153 if (dimensions == null) {
154 callback.onError(null);
155 return;
156 }
157
158 switch (wallpaperPosition) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700159 // Crop out screen-sized center portion of the source image if it's larger
160 // than the screen
161 // in both dimensions. Otherwise, decode the entire bitmap and fill the space
162 // around it to fill a new screen-sized bitmap with plain black pixels.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800163 case WALLPAPER_POSITION_CENTER:
164 setIndividualWallpaperWithCenterPosition(
165 wallpaper, asset, dimensions, screenSize, callback);
166 break;
167
168 // Crop out a screen-size portion of the source image and set the bitmap region.
169 case WALLPAPER_POSITION_CENTER_CROP:
170 setIndividualWallpaperWithCenterCropPosition(
171 wallpaper, asset, dimensions, screenSize, callback);
172 break;
173
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700174 // Decode full bitmap sized for screen and stretch it to fill the screen
175 // dimensions.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800176 case WALLPAPER_POSITION_STRETCH:
177 asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
178 @Override
179 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700180 setIndividualWallpaperStretch(wallpaper, bitmap,
181 screenSize /* stretchSize */,
Jon Miranda16ea1b12017-12-12 14:52:48 -0800182 WallpaperPersister.DEST_BOTH, callback);
183 }
184 });
185 break;
186
187 default:
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700188 Log.e(TAG, "Unsupported wallpaper position option specified: "
189 + wallpaperPosition);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800190 callback.onError(null);
191 }
192 }
193 });
194 }
195
196 /**
197 * Sets an individual wallpaper to both home + lock static wallpaper destinations with a center
198 * wallpaper position.
199 *
200 * @param wallpaper The wallpaper model object representing the wallpaper to be set.
201 * @param asset The wallpaper asset that should be used to set a wallpaper.
202 * @param dimensions Raw dimensions of the wallpaper asset.
203 * @param screenSize Dimensions of the device screen.
204 * @param callback Callback used to notify original caller of wallpaper set operation result.
205 */
206 private void setIndividualWallpaperWithCenterPosition(WallpaperInfo wallpaper, Asset asset,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700207 Point dimensions, Point screenSize, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800208 if (dimensions.x >= screenSize.x && dimensions.y >= screenSize.y) {
209 Rect cropRect = new Rect(
210 (dimensions.x - screenSize.x) / 2,
211 (dimensions.y - screenSize.y) / 2,
212 dimensions.x - ((dimensions.x - screenSize.x) / 2),
213 dimensions.y - ((dimensions.y - screenSize.y) / 2));
Santiago Etchebehere16cf0602020-12-11 17:04:18 -0800214 asset.decodeBitmapRegion(cropRect, screenSize.x, screenSize.y, false,
215 bitmap -> setIndividualWallpaper(wallpaper, bitmap,
216 WallpaperPersister.DEST_BOTH, callback));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800217 } else {
218 // Decode the full bitmap and pass with the screen size as a fill rect.
219 asset.decodeBitmap(dimensions.x, dimensions.y, new BitmapReceiver() {
220 @Override
221 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
222 if (bitmap == null) {
223 callback.onError(null);
224 return;
225 }
226
227 setIndividualWallpaperFill(wallpaper, bitmap, screenSize /* fillSize */,
228 WallpaperPersister.DEST_BOTH, callback);
229 }
230 });
231 }
232 }
233
234 /**
235 * Sets an individual wallpaper to both home + lock static wallpaper destinations with a center
236 * cropped wallpaper position.
237 *
238 * @param wallpaper The wallpaper model object representing the wallpaper to be set.
239 * @param asset The wallpaper asset that should be used to set a wallpaper.
240 * @param dimensions Raw dimensions of the wallpaper asset.
241 * @param screenSize Dimensions of the device screen.
242 * @param callback Callback used to notify original caller of wallpaper set operation result.
243 */
244 private void setIndividualWallpaperWithCenterCropPosition(WallpaperInfo wallpaper, Asset asset,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700245 Point dimensions, Point screenSize, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800246 float scale = Math.max((float) screenSize.x / dimensions.x,
247 (float) screenSize.y / dimensions.y);
248
249 int scaledImageWidth = (int) (dimensions.x * scale);
250 int scaledImageHeight = (int) (dimensions.y * scale);
251
252 // Crop rect is in post-scale units.
253 Rect cropRect = new Rect(
254 (scaledImageWidth - screenSize.x) / 2,
255 (scaledImageHeight - screenSize.y) / 2,
256 scaledImageWidth - ((scaledImageWidth - screenSize.x) / 2),
257 scaledImageHeight - (((scaledImageHeight - screenSize.y) / 2)));
258
259 setIndividualWallpaper(
260 wallpaper, asset, cropRect, scale, WallpaperPersister.DEST_BOTH, callback);
261 }
262
263 /**
264 * Sets a static individual wallpaper to the system via the WallpaperManager.
265 *
266 * @param wallpaper Wallpaper model object.
267 * @param croppedBitmap Bitmap representing the individual wallpaper image.
268 * @param destination The destination - where to set the wallpaper to.
269 * @param callback Called once the wallpaper was set or if an error occurred.
270 */
271 private void setIndividualWallpaper(WallpaperInfo wallpaper, Bitmap croppedBitmap,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700272 @Destination int destination, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800273 SetWallpaperTask setWallpaperTask =
274 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
275 setWallpaperTask.execute();
276 }
277
278 /**
279 * Sets a static individual wallpaper to the system via the WallpaperManager with a fill option.
280 *
281 * @param wallpaper Wallpaper model object.
282 * @param croppedBitmap Bitmap representing the individual wallpaper image.
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700283 * @param fillSize Specifies the final bitmap size that should be set to WallpaperManager.
284 * This final bitmap will show the visible area of the provided bitmap
285 * after applying a mask with black background the source bitmap and
286 * centering. There may be black borders around the original bitmap if
287 * it's smaller than the fillSize in one or both dimensions.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800288 * @param destination The destination - where to set the wallpaper to.
289 * @param callback Called once the wallpaper was set or if an error occurred.
290 */
291 private void setIndividualWallpaperFill(WallpaperInfo wallpaper, Bitmap croppedBitmap,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700292 Point fillSize, @Destination int destination, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800293 SetWallpaperTask setWallpaperTask =
294 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
295 setWallpaperTask.setFillSize(fillSize);
296 setWallpaperTask.execute();
297 }
298
299 /**
300 * Sets a static individual wallpaper to the system via the WallpaperManager with a stretch
301 * option.
302 *
303 * @param wallpaper Wallpaper model object.
304 * @param croppedBitmap Bitmap representing the individual wallpaper image.
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700305 * @param stretchSize Specifies the final size to which the bitmap should be stretched
306 * prior
Jon Miranda16ea1b12017-12-12 14:52:48 -0800307 * to being set to the device.
308 * @param destination The destination - where to set the wallpaper to.
309 * @param callback Called once the wallpaper was set or if an error occurred.
310 */
311 private void setIndividualWallpaperStretch(WallpaperInfo wallpaper, Bitmap croppedBitmap,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700312 Point stretchSize, @Destination int destination, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800313 SetWallpaperTask setWallpaperTask =
314 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
315 setWallpaperTask.setStretchSize(stretchSize);
316 setWallpaperTask.execute();
317 }
318
319 /**
320 * Sets a static individual wallpaper stream to the system via the WallpaperManager.
321 *
322 * @param wallpaper Wallpaper model object.
323 * @param inputStream JPEG or PNG stream of wallpaper image's bytes.
324 * @param destination The destination - where to set the wallpaper to.
325 * @param callback Called once the wallpaper was set or if an error occurred.
326 */
327 private void setIndividualWallpaper(WallpaperInfo wallpaper, InputStream inputStream,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700328 @Destination int destination, SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800329 SetWallpaperTask setWallpaperTask =
330 new SetWallpaperTask(wallpaper, inputStream, destination, callback);
331 setWallpaperTask.execute();
332 }
333
334 @Override
335 public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700336 int actionLabelRes, int actionIconRes, String actionUrl, String collectionId) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800337
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700338 return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl,
339 actionLabelRes, actionIconRes, collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800340 }
341
342 @Override
343 public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) {
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700344 return cropAndSetWallpaperBitmapInRotationStatic(wallpaperBitmap);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800345 }
346
347 @Override
348 public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700349 int actionLabelRes, int actionIconRes, String collectionId, int wallpaperId) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700350 return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700351 actionIconRes, collectionId, wallpaperId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800352 }
353
354 /**
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700355 * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting
356 * the
Jon Miranda16ea1b12017-12-12 14:52:48 -0800357 * current "daily wallpaper".
358 */
359 private boolean setWallpaperInRotationStatic(Bitmap wallpaperBitmap, List<String> attributions,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700360 String actionUrl, int actionLabelRes, int actionIconRes, String collectionId) {
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700361 final int wallpaperId = cropAndSetWallpaperBitmapInRotationStatic(wallpaperBitmap);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800362
363 if (wallpaperId == 0) {
364 return false;
365 }
366
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700367 return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700368 actionIconRes, collectionId, wallpaperId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800369 }
370
371 /**
372 * Finalizes wallpaper metadata by persisting them to SharedPreferences and finalizes the
373 * wallpaper image for live rotating components by copying the "preview" image to the "final"
374 * image file location.
375 *
376 * @return Whether the operation was successful.
377 */
378 private boolean finalizeWallpaperForRotatingComponent(List<String> attributions,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700379 String actionUrl,
380 int actionLabelRes,
381 int actionIconRes,
382 String collectionId,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700383 int wallpaperId) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800384 mWallpaperPreferences.clearHomeWallpaperMetadata();
385
386 boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet();
387
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700388 // Persist wallpaper IDs if the rotating wallpaper component
389 mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800390
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700391 // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set
392 // (so metadata isn't lost if a user explicitly sets a home-only wallpaper).
393 if (!isLockWallpaperSet) {
394 mWallpaperPreferences.setLockWallpaperId(wallpaperId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800395 }
396
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700397
Jon Miranda16ea1b12017-12-12 14:52:48 -0800398 mWallpaperPreferences.setHomeWallpaperAttributions(attributions);
399 mWallpaperPreferences.setHomeWallpaperActionUrl(actionUrl);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700400 mWallpaperPreferences.setHomeWallpaperActionLabelRes(actionLabelRes);
401 mWallpaperPreferences.setHomeWallpaperActionIconRes(actionIconRes);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800402 // Only set base image URL for static Backdrop images, not for rotation.
403 mWallpaperPreferences.setHomeWallpaperBaseImageUrl(null);
404 mWallpaperPreferences.setHomeWallpaperCollectionId(collectionId);
405
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700406 // Set metadata to lock screen also when the rotating wallpaper so if user sets a home
407 // screen-only wallpaper later, these attributions will still be available.
408 if (!isLockWallpaperSet) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800409 mWallpaperPreferences.setLockWallpaperAttributions(attributions);
410 mWallpaperPreferences.setLockWallpaperActionUrl(actionUrl);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700411 mWallpaperPreferences.setLockWallpaperActionLabelRes(actionLabelRes);
412 mWallpaperPreferences.setLockWallpaperActionIconRes(actionIconRes);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800413 mWallpaperPreferences.setLockWallpaperCollectionId(collectionId);
414 }
415
416 return true;
417 }
418
419 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800420 * Sets a wallpaper in rotation as a static wallpaper to the {@link WallpaperManager} with the
421 * option allowBackup=false to save user data.
422 *
423 * @return wallpaper ID for the wallpaper bitmap.
424 */
Santiago Etchebeheree95b6d92020-06-08 14:19:46 -0700425 private int cropAndSetWallpaperBitmapInRotationStatic(Bitmap wallpaperBitmap) {
426 // Calculate crop and scale of the wallpaper to match the default one used in preview
427 Point wallpaperSize = new Point(wallpaperBitmap.getWidth(), wallpaperBitmap.getHeight());
428 WindowManager windowManager =
429 (WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE);
430 Resources resources = mAppContext.getResources();
431 Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
432 resources, windowManager.getDefaultDisplay());
433 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
434 windowManager.getDefaultDisplay());
435
436 // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
437 float minWallpaperZoom =
438 WallpaperCropUtils.calculateMinZoom(wallpaperSize, screenSize);
439
440 PointF centerPosition = WallpaperCropUtils.calculateDefaultCenter(mAppContext,
441 wallpaperSize, WallpaperCropUtils.calculateVisibleRect(wallpaperSize, screenSize));
442
443 Point scaledCenter = new Point((int) (minWallpaperZoom * centerPosition.x),
444 (int) (minWallpaperZoom * centerPosition.y));
445
446 int offsetX = Math.max(0, -(screenSize.x / 2 - scaledCenter.x));
447 int offsetY = Math.max(0, -(screenSize.y / 2 - scaledCenter.y));
448
449 Rect cropRect = WallpaperCropUtils.calculateCropRect(mAppContext, minWallpaperZoom,
450 wallpaperSize, defaultCropSurfaceSize, screenSize, offsetX, offsetY);
451
452 Rect scaledCropRect = new Rect(
453 Math.round((float) cropRect.left / minWallpaperZoom),
454 Math.round((float) cropRect.top / minWallpaperZoom),
455 Math.round((float) cropRect.right / minWallpaperZoom),
456 Math.round((float) cropRect.bottom / minWallpaperZoom));
457
458 // Scale and crop the bitmap
459 wallpaperBitmap = Bitmap.createBitmap(wallpaperBitmap,
460 scaledCropRect.left,
461 scaledCropRect.top,
462 scaledCropRect.width(),
463 scaledCropRect.height());
464
Jon Miranda16ea1b12017-12-12 14:52:48 -0800465 // Set wallpaper to home-only instead of both home and lock if there's a distinct lock-only
466 // static wallpaper set so we don't override the lock wallpaper.
467 boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet();
468
469 int whichWallpaper = (isLockWallpaperSet)
470 ? WallpaperManagerCompat.FLAG_SYSTEM
471 : WallpaperManagerCompat.FLAG_SYSTEM | WallpaperManagerCompat.FLAG_LOCK;
472
473 return setBitmapToWallpaperManagerCompat(wallpaperBitmap, false /* allowBackup */,
474 whichWallpaper);
475 }
476
477 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800478 * Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}.
479 *
480 * @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of
481 * Android, otherwise on pre-N versions of Android will return a positive integer when the
482 * operation was successful and zero if the operation encountered an error.
483 */
484 private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700485 int whichWallpaper) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800486 ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
James O'Leary03375d72021-06-02 10:05:57 -0400487 if (wallpaperBitmap.compress(CompressFormat.PNG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800488 try {
489 byte[] outByteArray = tmpOut.toByteArray();
490 return mWallpaperManagerCompat.setStream(
491 new ByteArrayInputStream(outByteArray),
492 null /* visibleCropHint */,
493 allowBackup,
494 whichWallpaper);
495 } catch (IOException e) {
496 Log.e(TAG, "unable to write stream to wallpaper manager");
497 return 0;
498 }
499 } else {
500 Log.e(TAG, "unable to compress wallpaper");
501 try {
502 return mWallpaperManagerCompat.setBitmap(
503 wallpaperBitmap,
504 null /* visibleCropHint */,
505 allowBackup,
506 whichWallpaper);
507 } catch (IOException e) {
508 Log.e(TAG, "unable to set wallpaper");
509 return 0;
510 }
511 }
512 }
513
514 private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700515 int whichWallpaper) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800516 try {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700517 return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup,
518 whichWallpaper);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800519 } catch (IOException e) {
520 return 0;
521 }
522 }
523
524 @Override
525 public void setWallpaperInfoInPreview(WallpaperInfo wallpaper) {
526 mWallpaperInfoInPreview = wallpaper;
527 }
528
529 @Override
530 public void onLiveWallpaperSet() {
531 android.app.WallpaperInfo currentWallpaperComponent = mWallpaperManager.getWallpaperInfo();
chihhangchuang210c9602020-06-02 12:38:14 +0800532 android.app.WallpaperInfo previewedWallpaperComponent = mWallpaperInfoInPreview != null
533 ? mWallpaperInfoInPreview.getWallpaperComponent() : null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800534
535 // If there is no live wallpaper set on the WallpaperManager or it doesn't match the
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700536 // WallpaperInfo which was last previewed, then do nothing and nullify last previewed
537 // wallpaper.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800538 if (currentWallpaperComponent == null || previewedWallpaperComponent == null
539 || !currentWallpaperComponent.getPackageName()
540 .equals(previewedWallpaperComponent.getPackageName())) {
541 mWallpaperInfoInPreview = null;
542 return;
543 }
544
545 setLiveWallpaperMetadata();
546 }
547
548 /**
549 * Returns whether a separate lock-screen (static) wallpaper is set to the WallpaperManager.
550 */
551 private boolean isSeparateLockScreenWallpaperSet() {
552 ParcelFileDescriptor lockWallpaperFile =
553 mWallpaperManagerCompat.getWallpaperFile(WallpaperManagerCompat.FLAG_LOCK);
554
555 boolean isLockWallpaperSet = false;
556
557 if (lockWallpaperFile != null) {
558 isLockWallpaperSet = true;
559
560 try {
561 lockWallpaperFile.close();
562 } catch (IOException e) {
563 Log.e(TAG, "Unable to close PFD for lock wallpaper", e);
564 }
565 }
566
567 return isLockWallpaperSet;
568 }
569
570 /**
571 * Sets the live wallpaper's metadata on SharedPreferences.
572 */
573 private void setLiveWallpaperMetadata() {
574 android.app.WallpaperInfo previewedWallpaperComponent =
575 mWallpaperInfoInPreview.getWallpaperComponent();
576
577 mWallpaperPreferences.clearHomeWallpaperMetadata();
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700578 // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may
579 // have set the live wallpaper on the home screen only, we leave the lock wallpaper metadata
580 // intact. If the user has set the live wallpaper for both home and lock screens, then the
Jon Miranda16ea1b12017-12-12 14:52:48 -0800581 // WallpaperRefresher will pick up on that and update the preferences later.
582 mWallpaperPreferences
583 .setHomeWallpaperAttributions(mWallpaperInfoInPreview.getAttributions(mAppContext));
584 mWallpaperPreferences.setHomeWallpaperPackageName(
585 previewedWallpaperComponent.getPackageName());
Kunhung Li3e50fd22020-05-12 20:46:18 +0800586 mWallpaperPreferences.setHomeWallpaperServiceName(
587 previewedWallpaperComponent.getServiceName());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800588 mWallpaperPreferences.setHomeWallpaperCollectionId(
589 mWallpaperInfoInPreview.getCollectionId(mAppContext));
590 mWallpaperPreferences.setWallpaperPresentationMode(
591 WallpaperPreferences.PRESENTATION_MODE_STATIC);
592 mWallpaperPreferences.clearDailyRotations();
593 }
594
Jon Miranda16ea1b12017-12-12 14:52:48 -0800595 private class SetWallpaperTask extends AsyncTask<Void, Void, Boolean> {
596
597 private final WallpaperInfo mWallpaper;
598 @Destination
599 private final int mDestination;
600 private final WallpaperPersister.SetWallpaperCallback mCallback;
601
602 private Bitmap mBitmap;
603 private InputStream mInputStream;
604
605 /**
606 * Optional parameters for applying a post-decoding fill or stretch transformation.
607 */
608 @Nullable
609 private Point mFillSize;
610 @Nullable
611 private Point mStretchSize;
612
613 SetWallpaperTask(WallpaperInfo wallpaper, Bitmap bitmap, @Destination int destination,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700614 WallpaperPersister.SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800615 super();
616 mWallpaper = wallpaper;
617 mBitmap = bitmap;
618 mDestination = destination;
619 mCallback = callback;
620 }
621
622 /**
623 * Constructor for SetWallpaperTask which takes an InputStream instead of a bitmap. The task
624 * will close the InputStream once it is done with it.
625 */
626 SetWallpaperTask(WallpaperInfo wallpaper, InputStream stream,
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700627 @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800628 mWallpaper = wallpaper;
629 mInputStream = stream;
630 mDestination = destination;
631 mCallback = callback;
632 }
633
634 void setFillSize(Point fillSize) {
635 if (mStretchSize != null) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700636 throw new IllegalArgumentException(
637 "Can't pass a fill size option if a stretch size is "
638 + "already set.");
Jon Miranda16ea1b12017-12-12 14:52:48 -0800639 }
640 mFillSize = fillSize;
641 }
642
643 void setStretchSize(Point stretchSize) {
644 if (mFillSize != null) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700645 throw new IllegalArgumentException(
646 "Can't pass a stretch size option if a fill size is "
647 + "already set.");
Jon Miranda16ea1b12017-12-12 14:52:48 -0800648 }
649 mStretchSize = stretchSize;
650 }
651
652 @Override
653 protected Boolean doInBackground(Void... unused) {
654 int whichWallpaper;
655 if (mDestination == DEST_HOME_SCREEN) {
656 whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM;
657 } else if (mDestination == DEST_LOCK_SCREEN) {
658 whichWallpaper = WallpaperManagerCompat.FLAG_LOCK;
659 } else { // DEST_BOTH
660 whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM
661 | WallpaperManagerCompat.FLAG_LOCK;
662 }
663
Jon Miranda16ea1b12017-12-12 14:52:48 -0800664
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700665 boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(
666 mAppContext);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800667
668 boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED;
669 final int wallpaperId;
670 if (mBitmap != null) {
671 // Apply fill or stretch transformations on mBitmap if necessary.
672 if (mFillSize != null) {
673 mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize);
674 }
675 if (mStretchSize != null) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700676 mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y,
677 true);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800678 }
679
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700680 wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup,
681 whichWallpaper);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800682 } else if (mInputStream != null) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700683 wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup,
684 whichWallpaper);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800685 } else {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700686 Log.e(TAG,
687 "Both the wallpaper bitmap and input stream are null so we're unable to "
688 + "set any "
689 + "kind of wallpaper here.");
Jon Miranda16ea1b12017-12-12 14:52:48 -0800690 wallpaperId = 0;
691 }
692
693 if (wallpaperId > 0) {
694 if (mDestination == DEST_HOME_SCREEN
695 && mWallpaperPreferences.getWallpaperPresentationMode()
696 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
697 && !wasLockWallpaperSet
Colin Cross1b7f2082021-05-21 11:15:20 -0700698 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700699 copyRotatingWallpaperToLock();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800700 }
701 setImageWallpaperMetadata(mDestination, wallpaperId);
702 return true;
703 } else {
704 return false;
705 }
706 }
707
708 @Override
709 protected void onPostExecute(Boolean isSuccess) {
710 if (mInputStream != null) {
711 try {
712 mInputStream.close();
713 } catch (IOException e) {
714 Log.e(TAG, "Failed to close input stream " + e);
715 mCallback.onError(e /* throwable */);
716 return;
717 }
718 }
719
720 if (isSuccess) {
“Chuckffd832c2020-03-22 02:15:58 +0800721 mCallback.onSuccess(mWallpaper);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800722 mWallpaperChangedNotifier.notifyWallpaperChanged();
723 } else {
724 mCallback.onError(null /* throwable */);
725 }
726 }
727
728 /**
729 * Copies home wallpaper metadata to lock, and if rotation was enabled with a live wallpaper
730 * previously, then copies over the rotating wallpaper image to the WallpaperManager also.
731 * <p>
732 * Used to accommodate the case where a user had gone from a home+lock daily rotation to
733 * selecting a static wallpaper on home-only. The image and metadata that was previously
734 * rotating is now copied to the lock screen.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800735 */
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700736 private void copyRotatingWallpaperToLock() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800737
738 mWallpaperPreferences.setLockWallpaperAttributions(
739 mWallpaperPreferences.getHomeWallpaperAttributions());
740 mWallpaperPreferences.setLockWallpaperActionUrl(
741 mWallpaperPreferences.getHomeWallpaperActionUrl());
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700742 mWallpaperPreferences.setLockWallpaperActionLabelRes(
743 mWallpaperPreferences.getHomeWallpaperActionLabelRes());
744 mWallpaperPreferences.setLockWallpaperActionIconRes(
745 mWallpaperPreferences.getHomeWallpaperActionIconRes());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800746 mWallpaperPreferences.setLockWallpaperCollectionId(
747 mWallpaperPreferences.getHomeWallpaperCollectionId());
748
749 // Set the lock wallpaper ID to what Android set it to, following its having
750 // copied the system wallpaper over to the lock screen when we changed from
751 // "both" to distinct system and lock screen wallpapers.
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700752 mWallpaperPreferences.setLockWallpaperId(
753 mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK));
754
Jon Miranda16ea1b12017-12-12 14:52:48 -0800755 }
756
757 /**
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700758 * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the
759 * set wallpaper operation is successful.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800760 *
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700761 * @param destination Which destination of wallpaper the metadata corresponds to (home
762 * screen, lock screen, or both).
763 * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which
764 * on N and later versions of Android uniquely identifies a wallpaper
765 * image.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800766 */
767 private void setImageWallpaperMetadata(@Destination int destination, int wallpaperId) {
768 if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) {
769 mWallpaperPreferences.clearHomeWallpaperMetadata();
770 setImageWallpaperHomeMetadata(wallpaperId);
771
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700772 // Reset presentation mode to STATIC if an individual wallpaper is set to the
773 // home screen
Jon Miranda16ea1b12017-12-12 14:52:48 -0800774 // because rotation always affects at least the home screen.
775 mWallpaperPreferences.setWallpaperPresentationMode(
776 WallpaperPreferences.PRESENTATION_MODE_STATIC);
777 }
778
779 if (destination == DEST_LOCK_SCREEN || destination == DEST_BOTH) {
780 mWallpaperPreferences.clearLockWallpaperMetadata();
781 setImageWallpaperLockMetadata(wallpaperId);
782 }
783
784 mWallpaperPreferences.clearDailyRotations();
785 }
786
787 private void setImageWallpaperHomeMetadata(int homeWallpaperId) {
Colin Cross1b7f2082021-05-21 11:15:20 -0700788 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800789 mWallpaperPreferences.setHomeWallpaperManagerId(homeWallpaperId);
790 }
791
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700792 // Compute bitmap hash code after setting the wallpaper because JPEG compression has
793 // likely changed many pixels' color values. Forget the previously loaded wallpaper
794 // bitmap so that WallpaperManager doesn't return the old wallpaper drawable. Do this
795 // on N+ devices in addition to saving the wallpaper ID for the purpose of backup &
796 // restore.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800797 mWallpaperManager.forgetLoadedWallpaper();
798 mBitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap();
799 long bitmapHash = BitmapUtils.generateHashCode(mBitmap);
800
801 mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
802
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700803 mWallpaperPreferences.setHomeWallpaperAttributions(
804 mWallpaper.getAttributions(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800805 mWallpaperPreferences.setHomeWallpaperBaseImageUrl(mWallpaper.getBaseImageUrl());
806 mWallpaperPreferences.setHomeWallpaperActionUrl(mWallpaper.getActionUrl(mAppContext));
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700807 mWallpaperPreferences.setHomeWallpaperActionLabelRes(
808 mWallpaper.getActionLabelRes(mAppContext));
809 mWallpaperPreferences.setHomeWallpaperActionIconRes(
810 mWallpaper.getActionIconRes(mAppContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700811 mWallpaperPreferences.setHomeWallpaperCollectionId(
812 mWallpaper.getCollectionId(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800813 mWallpaperPreferences.setHomeWallpaperRemoteId(mWallpaper.getWallpaperId());
814 }
815
816 private void setImageWallpaperLockMetadata(int lockWallpaperId) {
817 mWallpaperPreferences.setLockWallpaperId(lockWallpaperId);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700818 mWallpaperPreferences.setLockWallpaperAttributions(
819 mWallpaper.getAttributions(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800820 mWallpaperPreferences.setLockWallpaperActionUrl(mWallpaper.getActionUrl(mAppContext));
Santiago Etchebehere1ab75482018-06-15 15:05:25 -0700821 mWallpaperPreferences.setLockWallpaperActionLabelRes(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700822 mWallpaper.getActionLabelRes(mAppContext));
Santiago Etchebehere1ab75482018-06-15 15:05:25 -0700823 mWallpaperPreferences.setLockWallpaperActionIconRes(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700824 mWallpaper.getActionIconRes(mAppContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700825 mWallpaperPreferences.setLockWallpaperCollectionId(
826 mWallpaper.getCollectionId(mAppContext));
“Chuck2c8626f2020-03-22 03:47:59 +0800827 mWallpaperPreferences.setLockWallpaperRemoteId(mWallpaper.getWallpaperId());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800828
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700829 // Save the lock wallpaper image's hash code as well for the sake of backup & restore
830 // because WallpaperManager-generated IDs are specific to a physical device and
831 // cannot be used to identify a wallpaper image on another device after restore is
832 // complete.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800833 saveLockWallpaperHashCode();
834 }
835
836 private void saveLockWallpaperHashCode() {
837 Bitmap lockBitmap = null;
838
839 ParcelFileDescriptor parcelFd = mWallpaperManagerCompat.getWallpaperFile(
840 WallpaperManagerCompat.FLAG_LOCK);
841
842 if (parcelFd == null) {
843 return;
844 }
845
846 InputStream fileStream = null;
847 try {
848 fileStream = new FileInputStream(parcelFd.getFileDescriptor());
849 lockBitmap = BitmapFactory.decodeStream(fileStream);
850 parcelFd.close();
851 } catch (IOException e) {
852 Log.e(TAG, "IO exception when closing the file descriptor.");
853 } finally {
854 if (fileStream != null) {
855 try {
856 fileStream.close();
857 } catch (IOException e) {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700858 Log.e(TAG,
859 "IO exception when closing the input stream for the lock screen "
860 + "WP.");
Jon Miranda16ea1b12017-12-12 14:52:48 -0800861 }
862 }
863 }
864
865 if (lockBitmap != null) {
866 long bitmapHash = BitmapUtils.generateHashCode(lockBitmap);
867 mWallpaperPreferences.setLockWallpaperHashCode(bitmapHash);
868 }
869 }
870 }
871}