blob: 1c9c0baf4cf56f952134038f74fae9bdd7d4d82b [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;
22import android.content.Intent;
23import android.graphics.Bitmap;
24import android.graphics.Bitmap.CompressFormat;
25import android.graphics.BitmapFactory;
26import android.graphics.Point;
27import android.graphics.Rect;
28import android.graphics.drawable.BitmapDrawable;
29import android.os.AsyncTask;
30import android.os.ParcelFileDescriptor;
31import android.support.annotation.Nullable;
32import android.util.Log;
33import android.view.Display;
34import android.view.WindowManager;
35
36import com.android.wallpaper.asset.Asset;
37import com.android.wallpaper.asset.Asset.BitmapReceiver;
38import com.android.wallpaper.asset.Asset.DimensionsReceiver;
39import com.android.wallpaper.asset.BitmapUtils;
40import com.android.wallpaper.asset.StreamableAsset;
41import com.android.wallpaper.asset.StreamableAsset.StreamReceiver;
42import com.android.wallpaper.compat.BuildCompat;
43import com.android.wallpaper.compat.WallpaperManagerCompat;
44import com.android.wallpaper.model.WallpaperInfo;
45import com.android.wallpaper.module.BitmapCropper.Callback;
46import com.android.wallpaper.module.RotatingWallpaperComponentChecker.RotatingWallpaperComponent;
47import com.android.wallpaper.util.BitmapTransformer;
48import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -070049import com.android.wallpaper.util.FileMover;
Jon Miranda16ea1b12017-12-12 14:52:48 -080050import com.android.wallpaper.util.ScreenSizeCalculator;
51
52import java.io.ByteArrayInputStream;
53import java.io.ByteArrayOutputStream;
54import java.io.File;
55import java.io.FileInputStream;
56import java.io.FileNotFoundException;
57import java.io.FileOutputStream;
58import java.io.IOException;
59import java.io.InputStream;
60import java.util.List;
61
62/**
63 * Concrete implementation of WallpaperPersister which actually sets wallpapers to the system via
64 * the WallpaperManager.
65 */
66public class DefaultWallpaperPersister implements WallpaperPersister {
67
68 private static final int DEFAULT_COMPRESS_QUALITY = 100;
69 private static final String TAG = "WallpaperPersister";
70
71 private final Context mAppContext; // The application's context.
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -070072 // Context that accesses files in device protected storage
73 private final Context mDeviceProtectedContext;
Jon Miranda16ea1b12017-12-12 14:52:48 -080074 private final WallpaperManager mWallpaperManager;
75 private final WallpaperManagerCompat mWallpaperManagerCompat;
76 private final WallpaperPreferences mWallpaperPreferences;
77 private final RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker;
78 private final WallpaperChangedNotifier mWallpaperChangedNotifier;
79
80 private WallpaperInfo mWallpaperInfoInPreview;
81
82 @SuppressLint("ServiceCast")
83 public DefaultWallpaperPersister(Context context) {
84 mAppContext = context.getApplicationContext();
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -070085 mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext();
Jon Miranda16ea1b12017-12-12 14:52:48 -080086 // Retrieve WallpaperManager using Context#getSystemService instead of
87 // WallpaperManager#getInstance so it can be mocked out in test.
88 Injector injector = InjectorProvider.getInjector();
89 mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
90 mWallpaperManagerCompat = injector.getWallpaperManagerCompat(context);
91 mWallpaperPreferences = injector.getPreferences(context);
92 mRotatingWallpaperComponentChecker = injector.getRotatingWallpaperComponentChecker();
93 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
94 }
95
96 @Override
97 public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset,
98 @Nullable Rect cropRect, float scale, @Destination final int destination,
99 final SetWallpaperCallback callback) {
100 // Set wallpaper without downscaling directly from an input stream if there's no crop rect
101 // specified by the caller and the asset is streamable.
102 if (cropRect == null && asset instanceof StreamableAsset) {
103 ((StreamableAsset) asset).fetchInputStream(new StreamReceiver() {
104 @Override
105 public void onInputStreamOpened(@Nullable InputStream inputStream) {
106 if (inputStream == null) {
107 callback.onError(null /* throwable */);
108 return;
109 }
110 setIndividualWallpaper(wallpaper, inputStream, destination, callback);
111 }
112 });
113 return;
114 }
115
116 // If no crop rect is specified but the wallpaper asset is not streamable, then fall back to
117 // using the device's display size.
118 if (cropRect == null) {
119 Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
120 .getDefaultDisplay();
121 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
122 asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
123 @Override
124 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
125 if (bitmap == null) {
126 callback.onError(null /* throwable */);
127 return;
128 }
129 setIndividualWallpaper(wallpaper, bitmap, destination, callback);
130 }
131 });
132 return;
133 }
134
135 BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
136 bitmapCropper.cropAndScaleBitmap(asset, scale, cropRect, new Callback() {
137 @Override
138 public void onBitmapCropped(Bitmap croppedBitmap) {
139 setIndividualWallpaper(wallpaper, croppedBitmap, destination, callback);
140 }
141
142 @Override
143 public void onError(@Nullable Throwable e) {
144 callback.onError(e);
145 }
146 });
147 }
148
149 @Override
150 public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper,
151 @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) {
152 Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
153 .getDefaultDisplay();
154 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
155
156 Asset asset = wallpaper.getAsset(activity);
157 asset.decodeRawDimensions(activity, new DimensionsReceiver() {
158 @Override
159 public void onDimensionsDecoded(@Nullable Point dimensions) {
160 if (dimensions == null) {
161 callback.onError(null);
162 return;
163 }
164
165 switch (wallpaperPosition) {
166 // Crop out screen-sized center portion of the source image if it's larger than the screen
167 // in both dimensions. Otherwise, decode the entire bitmap and fill the space around it to
168 // fill a new screen-sized bitmap with plain black pixels.
169 case WALLPAPER_POSITION_CENTER:
170 setIndividualWallpaperWithCenterPosition(
171 wallpaper, asset, dimensions, screenSize, callback);
172 break;
173
174 // Crop out a screen-size portion of the source image and set the bitmap region.
175 case WALLPAPER_POSITION_CENTER_CROP:
176 setIndividualWallpaperWithCenterCropPosition(
177 wallpaper, asset, dimensions, screenSize, callback);
178 break;
179
180 // Decode full bitmap sized for screen and stretch it to fill the screen dimensions.
181 case WALLPAPER_POSITION_STRETCH:
182 asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
183 @Override
184 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
185 setIndividualWallpaperStretch(wallpaper, bitmap, screenSize /* stretchSize */,
186 WallpaperPersister.DEST_BOTH, callback);
187 }
188 });
189 break;
190
191 default:
192 Log.e(TAG, "Unsupported wallpaper position option specified: " + wallpaperPosition);
193 callback.onError(null);
194 }
195 }
196 });
197 }
198
199 /**
200 * Sets an individual wallpaper to both home + lock static wallpaper destinations with a center
201 * wallpaper position.
202 *
203 * @param wallpaper The wallpaper model object representing the wallpaper to be set.
204 * @param asset The wallpaper asset that should be used to set a wallpaper.
205 * @param dimensions Raw dimensions of the wallpaper asset.
206 * @param screenSize Dimensions of the device screen.
207 * @param callback Callback used to notify original caller of wallpaper set operation result.
208 */
209 private void setIndividualWallpaperWithCenterPosition(WallpaperInfo wallpaper, Asset asset,
210 Point dimensions, Point screenSize, SetWallpaperCallback callback) {
211 if (dimensions.x >= screenSize.x && dimensions.y >= screenSize.y) {
212 Rect cropRect = new Rect(
213 (dimensions.x - screenSize.x) / 2,
214 (dimensions.y - screenSize.y) / 2,
215 dimensions.x - ((dimensions.x - screenSize.x) / 2),
216 dimensions.y - ((dimensions.y - screenSize.y) / 2));
217 asset.decodeBitmapRegion(cropRect, screenSize.x, screenSize.y, new BitmapReceiver() {
218 @Override
219 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
220 setIndividualWallpaper(wallpaper, bitmap, WallpaperPersister.DEST_BOTH, callback);
221 }
222 });
223 } else {
224 // Decode the full bitmap and pass with the screen size as a fill rect.
225 asset.decodeBitmap(dimensions.x, dimensions.y, new BitmapReceiver() {
226 @Override
227 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
228 if (bitmap == null) {
229 callback.onError(null);
230 return;
231 }
232
233 setIndividualWallpaperFill(wallpaper, bitmap, screenSize /* fillSize */,
234 WallpaperPersister.DEST_BOTH, callback);
235 }
236 });
237 }
238 }
239
240 /**
241 * Sets an individual wallpaper to both home + lock static wallpaper destinations with a center
242 * cropped wallpaper position.
243 *
244 * @param wallpaper The wallpaper model object representing the wallpaper to be set.
245 * @param asset The wallpaper asset that should be used to set a wallpaper.
246 * @param dimensions Raw dimensions of the wallpaper asset.
247 * @param screenSize Dimensions of the device screen.
248 * @param callback Callback used to notify original caller of wallpaper set operation result.
249 */
250 private void setIndividualWallpaperWithCenterCropPosition(WallpaperInfo wallpaper, Asset asset,
251 Point dimensions, Point screenSize, SetWallpaperCallback callback) {
252 float scale = Math.max((float) screenSize.x / dimensions.x,
253 (float) screenSize.y / dimensions.y);
254
255 int scaledImageWidth = (int) (dimensions.x * scale);
256 int scaledImageHeight = (int) (dimensions.y * scale);
257
258 // Crop rect is in post-scale units.
259 Rect cropRect = new Rect(
260 (scaledImageWidth - screenSize.x) / 2,
261 (scaledImageHeight - screenSize.y) / 2,
262 scaledImageWidth - ((scaledImageWidth - screenSize.x) / 2),
263 scaledImageHeight - (((scaledImageHeight - screenSize.y) / 2)));
264
265 setIndividualWallpaper(
266 wallpaper, asset, cropRect, scale, WallpaperPersister.DEST_BOTH, callback);
267 }
268
269 /**
270 * Sets a static individual wallpaper to the system via the WallpaperManager.
271 *
272 * @param wallpaper Wallpaper model object.
273 * @param croppedBitmap Bitmap representing the individual wallpaper image.
274 * @param destination The destination - where to set the wallpaper to.
275 * @param callback Called once the wallpaper was set or if an error occurred.
276 */
277 private void setIndividualWallpaper(WallpaperInfo wallpaper, Bitmap croppedBitmap,
278 @Destination int destination, SetWallpaperCallback callback) {
279 SetWallpaperTask setWallpaperTask =
280 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
281 setWallpaperTask.execute();
282 }
283
284 /**
285 * Sets a static individual wallpaper to the system via the WallpaperManager with a fill option.
286 *
287 * @param wallpaper Wallpaper model object.
288 * @param croppedBitmap Bitmap representing the individual wallpaper image.
289 * @param fillSize Specifies the final bitmap size that should be set to WallpaperManager. This
290 * final bitmap will show the visible area of the provided bitmap after applying a mask with
291 * black background the source bitmap and centering. There may be black borders around the
292 * original bitmap if it's smaller than the fillSize in one or both dimensions.
293 * @param destination The destination - where to set the wallpaper to.
294 * @param callback Called once the wallpaper was set or if an error occurred.
295 */
296 private void setIndividualWallpaperFill(WallpaperInfo wallpaper, Bitmap croppedBitmap,
297 Point fillSize, @Destination int destination, SetWallpaperCallback callback) {
298 SetWallpaperTask setWallpaperTask =
299 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
300 setWallpaperTask.setFillSize(fillSize);
301 setWallpaperTask.execute();
302 }
303
304 /**
305 * Sets a static individual wallpaper to the system via the WallpaperManager with a stretch
306 * option.
307 *
308 * @param wallpaper Wallpaper model object.
309 * @param croppedBitmap Bitmap representing the individual wallpaper image.
310 * @param stretchSize Specifies the final size to which the the bitmap should be stretched prior
311 * to being set to the device.
312 * @param destination The destination - where to set the wallpaper to.
313 * @param callback Called once the wallpaper was set or if an error occurred.
314 */
315 private void setIndividualWallpaperStretch(WallpaperInfo wallpaper, Bitmap croppedBitmap,
316 Point stretchSize, @Destination int destination, SetWallpaperCallback callback) {
317 SetWallpaperTask setWallpaperTask =
318 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
319 setWallpaperTask.setStretchSize(stretchSize);
320 setWallpaperTask.execute();
321 }
322
323 /**
324 * Sets a static individual wallpaper stream to the system via the WallpaperManager.
325 *
326 * @param wallpaper Wallpaper model object.
327 * @param inputStream JPEG or PNG stream of wallpaper image's bytes.
328 * @param destination The destination - where to set the wallpaper to.
329 * @param callback Called once the wallpaper was set or if an error occurred.
330 */
331 private void setIndividualWallpaper(WallpaperInfo wallpaper, InputStream inputStream,
332 @Destination int destination, SetWallpaperCallback callback) {
333 SetWallpaperTask setWallpaperTask =
334 new SetWallpaperTask(wallpaper, inputStream, destination, callback);
335 setWallpaperTask.execute();
336 }
337
338 @Override
339 public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700340 int actionLabelRes, int actionIconRes,
Jon Miranda16ea1b12017-12-12 14:52:48 -0800341 String actionUrl, String collectionId) {
342 @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker
343 .getCurrentRotatingWallpaperComponent(mAppContext);
344
345 switch (rotatingWallpaperComponent) {
346 case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC:
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700347 return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl,
348 actionLabelRes, actionIconRes, collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800349 case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE:
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700350 return setWallpaperInRotationLive(wallpaperBitmap, attributions, actionUrl,
351 actionLabelRes, actionIconRes, collectionId);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800352 default:
353 Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent);
354 return false;
355 }
356 }
357
358 @Override
359 public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) {
360 @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker
361 .getNextRotatingWallpaperComponent(mAppContext);
362
363 switch (rotatingWallpaperComponent) {
364 case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC:
365 return setWallpaperBitmapInRotationStatic(wallpaperBitmap);
366 case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE:
367 boolean isSuccess = setWallpaperBitmapInRotationLive(wallpaperBitmap, true /* isPreview */);
368 return isSuccess ? 1 : 0;
369 default:
370 Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent);
371 return 0;
372 }
373 }
374
375 @Override
376 public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700377 int actionLabelRes, int actionIconRes,
Jon Miranda16ea1b12017-12-12 14:52:48 -0800378 String collectionId, int wallpaperId) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700379 @RotatingWallpaperComponent int rotatingWallpaperComponent =
380 mRotatingWallpaperComponentChecker.getNextRotatingWallpaperComponent(mAppContext);
381 return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
382 actionIconRes, collectionId, wallpaperId, rotatingWallpaperComponent);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800383 }
384
385 /**
386 * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting the
387 * current "daily wallpaper".
388 */
389 private boolean setWallpaperInRotationStatic(Bitmap wallpaperBitmap, List<String> attributions,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700390 String actionUrl, int actionLabelRes,
391 int actionIconRes, String collectionId) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800392 final int wallpaperId = setWallpaperBitmapInRotationStatic(wallpaperBitmap);
393
394 if (wallpaperId == 0) {
395 return false;
396 }
397
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700398 return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
399 actionIconRes, collectionId, wallpaperId,
400 RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800401 }
402
403 /**
404 * Finalizes wallpaper metadata by persisting them to SharedPreferences and finalizes the
405 * wallpaper image for live rotating components by copying the "preview" image to the "final"
406 * image file location.
407 *
408 * @return Whether the operation was successful.
409 */
410 private boolean finalizeWallpaperForRotatingComponent(List<String> attributions,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700411 String actionUrl,
412 int actionLabelRes,
413 int actionIconRes,
414 String collectionId,
415 int wallpaperId,
416 @RotatingWallpaperComponent int rotatingWallpaperComponent) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800417 mWallpaperPreferences.clearHomeWallpaperMetadata();
418
419 boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet();
420
421 // Persist wallpaper IDs if the rotating wallpaper component is static and this device is
422 // running Android N or later.
423 if (rotatingWallpaperComponent
424 == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) {
425 if (BuildCompat.isAtLeastN()) {
426 mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId);
427
428 // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set (so
429 // metadata isn't lost if a user explicitly sets a home-only wallpaper).
430 if (!isLockWallpaperSet) {
431 mWallpaperPreferences.setLockWallpaperId(wallpaperId);
432 }
433 } else { // Pre-N but using static component
434 // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely
435 // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that
436 // WallpaperManager doesn't return the old wallpaper drawable.
437 mWallpaperManager.forgetLoadedWallpaper();
438 Bitmap bitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap();
439 long bitmapHash = BitmapUtils.generateHashCode(bitmap);
440
441 mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
442 }
443 } else { // Live wallpaper rotating component.
444
445 // Copy "preview" JPEG to "rotating" JPEG if the preview file exists.
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -0700446 File rotatingWallpaper;
447 try {
448 rotatingWallpaper = moveToDeviceProtectedStorage(
449 NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH,
450 NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
451 } catch (Exception e) {
452 DiskBasedLogger.e(
453 TAG,
454 "Unable to move preview to final file for rotating wallpaper " +
455 "file (exception)" + e.toString(),
456 mAppContext);
457 return false;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800458 }
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -0700459 if (rotatingWallpaper == null) {
460 rotatingWallpaper = mDeviceProtectedContext.getFileStreamPath(
461 NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
462 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800463 try {
464 FileInputStream fis = new FileInputStream(rotatingWallpaper.getAbsolutePath());
465 Bitmap bitmap = BitmapFactory.decodeStream(fis);
466 fis.close();
467
468 if (bitmap != null) {
469 long bitmapHash = BitmapUtils.generateHashCode(bitmap);
470 mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
471 } else {
472 Log.e(TAG, "Unable to decode rotating wallpaper file");
473 return false;
474 }
475 } catch (FileNotFoundException e) {
476 Log.e(TAG, "Rotating wallpaper file not found at path: "
477 + rotatingWallpaper.getAbsolutePath());
478 e.printStackTrace();
479 return false;
480 } catch (IOException e) {
481 Log.e(TAG, "IOException when closing FileInputStream " + e);
482 return false;
483 }
484
485 mWallpaperChangedNotifier.notifyWallpaperChanged();
486
487 // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper
488 // process so the currently displayed wallpaper updates.
489 notifyLiveWallpaperBitmapChanged();
490 }
491
492 mWallpaperPreferences.setHomeWallpaperAttributions(attributions);
493 mWallpaperPreferences.setHomeWallpaperActionUrl(actionUrl);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700494 mWallpaperPreferences.setHomeWallpaperActionLabelRes(actionLabelRes);
495 mWallpaperPreferences.setHomeWallpaperActionIconRes(actionIconRes);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800496 // Only set base image URL for static Backdrop images, not for rotation.
497 mWallpaperPreferences.setHomeWallpaperBaseImageUrl(null);
498 mWallpaperPreferences.setHomeWallpaperCollectionId(collectionId);
499
500 // Set metadata to lock screen also when the rotating wallpaper is a static one so if user sets
501 // a home screen-only wallpaper later, these attributions will still be available.
502 if (rotatingWallpaperComponent
503 == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC
504 && !isLockWallpaperSet) {
505 mWallpaperPreferences.setLockWallpaperAttributions(attributions);
506 mWallpaperPreferences.setLockWallpaperActionUrl(actionUrl);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700507 mWallpaperPreferences.setLockWallpaperActionLabelRes(actionLabelRes);
508 mWallpaperPreferences.setLockWallpaperActionIconRes(actionIconRes);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800509 mWallpaperPreferences.setLockWallpaperCollectionId(collectionId);
510 }
511
512 return true;
513 }
514
515 /**
516 * Sets wallpaper image and attributions when a live wallpaper is responsible for presenting the
517 * current "daily wallpaper".
518 */
519 private boolean setWallpaperInRotationLive(Bitmap wallpaperBitmap, List<String> attributions,
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700520 String actionUrl, int actionLabelRes,
521 int actionIconRes, String collectionId) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800522
523 synchronized (RotatingWallpaperLockProvider.getInstance()) {
524 if (!setWallpaperBitmapInRotationLive(wallpaperBitmap, false /* isPreview */)) {
525 return false;
526 }
527
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700528 return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
529 actionIconRes, collectionId,
530 0 /* wallpaperId */,
531 RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800532 }
533 }
534
535 /**
536 * Sets a wallpaper in rotation as a static wallpaper to the {@link WallpaperManager} with the
537 * option allowBackup=false to save user data.
538 *
539 * @return wallpaper ID for the wallpaper bitmap.
540 */
541 private int setWallpaperBitmapInRotationStatic(Bitmap wallpaperBitmap) {
542 // Set wallpaper to home-only instead of both home and lock if there's a distinct lock-only
543 // static wallpaper set so we don't override the lock wallpaper.
544 boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet();
545
546 int whichWallpaper = (isLockWallpaperSet)
547 ? WallpaperManagerCompat.FLAG_SYSTEM
548 : WallpaperManagerCompat.FLAG_SYSTEM | WallpaperManagerCompat.FLAG_LOCK;
549
550 return setBitmapToWallpaperManagerCompat(wallpaperBitmap, false /* allowBackup */,
551 whichWallpaper);
552 }
553
554 /**
555 * Sets a wallpaper in rotation as a live wallpaper. Writes wallpaper bitmap to a file in internal
556 * storage and sends a broadcast to the live wallpaper notifying it that rotating wallpaper image
557 * data changed.
558 *
559 * @return whether the set wallpaper operation was successful.
560 */
561 private boolean setWallpaperBitmapInRotationLive(Bitmap wallpaperBitmap, boolean isPreview) {
562 File pendingFile;
563 try {
564 pendingFile = File.createTempFile("rotating_pending", ".jpg", mAppContext.getFilesDir());
565 } catch (IOException e) {
566 Log.e(TAG, "Unable to create temp file for rotating wallpaper");
567 return false;
568 }
569
Jon Miranda16ea1b12017-12-12 14:52:48 -0800570 FileOutputStream fos;
571 try {
572 fos = mAppContext.openFileOutput(pendingFile.getName(), Context.MODE_PRIVATE);
573 } catch (FileNotFoundException e) {
574 Log.e(TAG, "Unable to open file output stream for pending rotating wallpaper file");
575 return false;
576 }
577
578 boolean compressedSuccessfully =
579 wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, fos);
580
581 // Close the file stream.
582 try {
583 fos.flush();
584 fos.close();
585 } catch (IOException e) {
586 Log.e(TAG, "Unable to close FileOutputStream for pending rotating wallpaper file"
587 + " (compress succeeded");
588 return false;
589 }
590
591 if (compressedSuccessfully) {
592 // Compressing/writing to disk succeeded, so move the pending file to the final location.
593 try {
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -0700594 if (isPreview) {
595 if (!pendingFile.renameTo(mAppContext.getFileStreamPath(
596 NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH))) {
597 return false;
598 }
599 } else {
600 moveToDeviceProtectedStorage(pendingFile.getName(),
601 NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800602 }
603 } catch (Exception e) {
604 Log.e(TAG, "Unable to rename pending to final file for rotating wallpaper file"
605 + " (exception)" + e.toString());
606 return false;
607 }
608 } else {
609 Log.e(TAG, "Unable to compress the wallpaper bitmap");
610
611 // Delete the pending file since compressing/writing the image to disk failed.
612 try {
613 pendingFile.delete();
614 } catch (SecurityException e) {
615 Log.e(TAG, "Unable to delete pending rotating wallpaper file");
616 return false;
617 }
618
619 return false;
620 }
621
622 mWallpaperChangedNotifier.notifyWallpaperChanged();
623
624 // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper
625 // process so the currently displayed wallpaper updates if the live wallpaper is set to the
626 // device.
627 notifyLiveWallpaperBitmapChanged();
628
629 return true;
630 }
631
632 /**
633 * Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}.
634 *
635 * @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of
636 * Android, otherwise on pre-N versions of Android will return a positive integer when the
637 * operation was successful and zero if the operation encountered an error.
638 */
639 private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
640 int whichWallpaper) {
641 ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
642 if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
643 try {
644 byte[] outByteArray = tmpOut.toByteArray();
645 return mWallpaperManagerCompat.setStream(
646 new ByteArrayInputStream(outByteArray),
647 null /* visibleCropHint */,
648 allowBackup,
649 whichWallpaper);
650 } catch (IOException e) {
651 Log.e(TAG, "unable to write stream to wallpaper manager");
652 return 0;
653 }
654 } else {
655 Log.e(TAG, "unable to compress wallpaper");
656 try {
657 return mWallpaperManagerCompat.setBitmap(
658 wallpaperBitmap,
659 null /* visibleCropHint */,
660 allowBackup,
661 whichWallpaper);
662 } catch (IOException e) {
663 Log.e(TAG, "unable to set wallpaper");
664 return 0;
665 }
666 }
667 }
668
669 private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,
670 int whichWallpaper) {
671 try {
672 return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup, whichWallpaper);
673 } catch (IOException e) {
674 return 0;
675 }
676 }
677
678 @Override
679 public void setWallpaperInfoInPreview(WallpaperInfo wallpaper) {
680 mWallpaperInfoInPreview = wallpaper;
681 }
682
683 @Override
684 public void onLiveWallpaperSet() {
685 android.app.WallpaperInfo currentWallpaperComponent = mWallpaperManager.getWallpaperInfo();
686 android.app.WallpaperInfo previewedWallpaperComponent =
687 mWallpaperInfoInPreview.getWallpaperComponent();
688
689 // If there is no live wallpaper set on the WallpaperManager or it doesn't match the
690 // WallpaperInfo which was last previewed, then do nothing and nullify last previewed wallpaper.
691 if (currentWallpaperComponent == null || previewedWallpaperComponent == null
692 || !currentWallpaperComponent.getPackageName()
693 .equals(previewedWallpaperComponent.getPackageName())) {
694 mWallpaperInfoInPreview = null;
695 return;
696 }
697
698 setLiveWallpaperMetadata();
699 }
700
701 /**
702 * Returns whether a separate lock-screen (static) wallpaper is set to the WallpaperManager.
703 */
704 private boolean isSeparateLockScreenWallpaperSet() {
705 ParcelFileDescriptor lockWallpaperFile =
706 mWallpaperManagerCompat.getWallpaperFile(WallpaperManagerCompat.FLAG_LOCK);
707
708 boolean isLockWallpaperSet = false;
709
710 if (lockWallpaperFile != null) {
711 isLockWallpaperSet = true;
712
713 try {
714 lockWallpaperFile.close();
715 } catch (IOException e) {
716 Log.e(TAG, "Unable to close PFD for lock wallpaper", e);
717 }
718 }
719
720 return isLockWallpaperSet;
721 }
722
723 /**
724 * Sets the live wallpaper's metadata on SharedPreferences.
725 */
726 private void setLiveWallpaperMetadata() {
727 android.app.WallpaperInfo previewedWallpaperComponent =
728 mWallpaperInfoInPreview.getWallpaperComponent();
729
730 mWallpaperPreferences.clearHomeWallpaperMetadata();
731 // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may have
732 // set the live wallpaper on the home screen only, we leave the lock wallpaper metadata intact.
733 // If the user has set the live wallpaper for both home and lock screens, then the
734 // WallpaperRefresher will pick up on that and update the preferences later.
735 mWallpaperPreferences
736 .setHomeWallpaperAttributions(mWallpaperInfoInPreview.getAttributions(mAppContext));
737 mWallpaperPreferences.setHomeWallpaperPackageName(
738 previewedWallpaperComponent.getPackageName());
739 mWallpaperPreferences.setHomeWallpaperCollectionId(
740 mWallpaperInfoInPreview.getCollectionId(mAppContext));
741 mWallpaperPreferences.setWallpaperPresentationMode(
742 WallpaperPreferences.PRESENTATION_MODE_STATIC);
743 mWallpaperPreferences.clearDailyRotations();
744 }
745
746 /**
747 * Notifies the :live_wallpaper process that the contents of the rotating live wallpaper bitmap
748 * changed.
749 */
750 private void notifyLiveWallpaperBitmapChanged() {
Jon Mirandade0b69e2018-03-20 16:03:18 -0700751 Intent intent = new Intent(mAppContext.getPackageName()
752 + NoBackupImageWallpaper.ACTION_ROTATING_WALLPAPER_CHANGED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800753 // Handled by a runtime-registered receiver in NoBackupImageWallpaper.
754 intent.setPackage(mAppContext.getPackageName());
755 mAppContext.sendBroadcast(intent);
756 }
757
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -0700758 /**
759 * Moves a file from the app's files directory to the device content protected storage
760 * directory.
761 * @param srcFileName Name of the source file (just the name, no path). It's expected to be
762 * located in {@link Context#getFilesDir()} for {@link #mAppContext}
763 * @param dstFileName Name of the destination file (just the name, no path), which will be
764 * located in {@link Context#getFilesDir()}
765 * for {@link #mDeviceProtectedContext}
766 * @return a {@link File} corresponding to the moved file in its new location, or null if
767 * nothing was moved (because srcFileName didn't exist).
768 */
769 @Nullable
770 private File moveToDeviceProtectedStorage(String srcFileName, String dstFileName)
771 throws IOException {
772 return FileMover.moveFileBetweenContexts(mAppContext, srcFileName, mDeviceProtectedContext,
773 dstFileName);
774 }
775
Jon Miranda16ea1b12017-12-12 14:52:48 -0800776 private class SetWallpaperTask extends AsyncTask<Void, Void, Boolean> {
777
778 private final WallpaperInfo mWallpaper;
779 @Destination
780 private final int mDestination;
781 private final WallpaperPersister.SetWallpaperCallback mCallback;
782
783 private Bitmap mBitmap;
784 private InputStream mInputStream;
785
786 /**
787 * Optional parameters for applying a post-decoding fill or stretch transformation.
788 */
789 @Nullable
790 private Point mFillSize;
791 @Nullable
792 private Point mStretchSize;
793
794 SetWallpaperTask(WallpaperInfo wallpaper, Bitmap bitmap, @Destination int destination,
795 WallpaperPersister.SetWallpaperCallback callback) {
796 super();
797 mWallpaper = wallpaper;
798 mBitmap = bitmap;
799 mDestination = destination;
800 mCallback = callback;
801 }
802
803 /**
804 * Constructor for SetWallpaperTask which takes an InputStream instead of a bitmap. The task
805 * will close the InputStream once it is done with it.
806 */
807 SetWallpaperTask(WallpaperInfo wallpaper, InputStream stream,
808 @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) {
809 mWallpaper = wallpaper;
810 mInputStream = stream;
811 mDestination = destination;
812 mCallback = callback;
813 }
814
815 void setFillSize(Point fillSize) {
816 if (mStretchSize != null) {
817 throw new IllegalArgumentException("Can't pass a fill size option if a stretch size is "
818 + "already set.");
819 }
820 mFillSize = fillSize;
821 }
822
823 void setStretchSize(Point stretchSize) {
824 if (mFillSize != null) {
825 throw new IllegalArgumentException("Can't pass a stretch size option if a fill size is "
826 + "already set.");
827 }
828 mStretchSize = stretchSize;
829 }
830
831 @Override
832 protected Boolean doInBackground(Void... unused) {
833 int whichWallpaper;
834 if (mDestination == DEST_HOME_SCREEN) {
835 whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM;
836 } else if (mDestination == DEST_LOCK_SCREEN) {
837 whichWallpaper = WallpaperManagerCompat.FLAG_LOCK;
838 } else { // DEST_BOTH
839 whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM
840 | WallpaperManagerCompat.FLAG_LOCK;
841 }
842
843 // NOTE: The rotating wallpaper component must be determined here, _before_ actually setting
844 // the bitmap/stream on WallpaperManagerCompat, to ensure that the
845 // RotatingWallpaperComponentChecker is doing its check while rotation is still enabled.
846 // E.g., if "live wallpaper" is the component, then it needs to check while live wallpaper is
847 // still set as the active wallpaper on the device. Otherwise, the checker would see a static
848 // wallpaper is currently set and it would return the wrong value.
849 @RotatingWallpaperComponent int currentRotatingWallpaperComponent =
850 mRotatingWallpaperComponentChecker.getCurrentRotatingWallpaperComponent(mAppContext);
851
852 boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(mAppContext);
853
854 boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED;
855 final int wallpaperId;
856 if (mBitmap != null) {
857 // Apply fill or stretch transformations on mBitmap if necessary.
858 if (mFillSize != null) {
859 mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize);
860 }
861 if (mStretchSize != null) {
862 mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y, true);
863 }
864
865 wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup, whichWallpaper);
866 } else if (mInputStream != null) {
867 wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup, whichWallpaper);
868 } else {
869 Log.e(TAG, "Both the wallpaper bitmap and input stream are null so we're unable to set any "
870 + "kind of wallpaper here.");
871 wallpaperId = 0;
872 }
873
874 if (wallpaperId > 0) {
875 if (mDestination == DEST_HOME_SCREEN
876 && mWallpaperPreferences.getWallpaperPresentationMode()
877 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
878 && !wasLockWallpaperSet
879 && BuildCompat.isAtLeastN()) {
880 copyRotatingWallpaperToLock(currentRotatingWallpaperComponent);
881 }
882 setImageWallpaperMetadata(mDestination, wallpaperId);
883 return true;
884 } else {
885 return false;
886 }
887 }
888
889 @Override
890 protected void onPostExecute(Boolean isSuccess) {
891 if (mInputStream != null) {
892 try {
893 mInputStream.close();
894 } catch (IOException e) {
895 Log.e(TAG, "Failed to close input stream " + e);
896 mCallback.onError(e /* throwable */);
897 return;
898 }
899 }
900
901 if (isSuccess) {
902 mCallback.onSuccess();
903 mWallpaperChangedNotifier.notifyWallpaperChanged();
904 } else {
905 mCallback.onError(null /* throwable */);
906 }
907 }
908
909 /**
910 * Copies home wallpaper metadata to lock, and if rotation was enabled with a live wallpaper
911 * previously, then copies over the rotating wallpaper image to the WallpaperManager also.
912 * <p>
913 * Used to accommodate the case where a user had gone from a home+lock daily rotation to
914 * selecting a static wallpaper on home-only. The image and metadata that was previously
915 * rotating is now copied to the lock screen.
916 *
917 * @param currentRotatingWallpaperComponent The component in which rotating wallpapers were
918 * presented.
919 */
920 private void copyRotatingWallpaperToLock(
921 @RotatingWallpaperComponent int currentRotatingWallpaperComponent) {
922
923 mWallpaperPreferences.setLockWallpaperAttributions(
924 mWallpaperPreferences.getHomeWallpaperAttributions());
925 mWallpaperPreferences.setLockWallpaperActionUrl(
926 mWallpaperPreferences.getHomeWallpaperActionUrl());
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700927 mWallpaperPreferences.setLockWallpaperActionLabelRes(
928 mWallpaperPreferences.getHomeWallpaperActionLabelRes());
929 mWallpaperPreferences.setLockWallpaperActionIconRes(
930 mWallpaperPreferences.getHomeWallpaperActionIconRes());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800931 mWallpaperPreferences.setLockWallpaperCollectionId(
932 mWallpaperPreferences.getHomeWallpaperCollectionId());
933
934 // Set the lock wallpaper ID to what Android set it to, following its having
935 // copied the system wallpaper over to the lock screen when we changed from
936 // "both" to distinct system and lock screen wallpapers.
937 if (currentRotatingWallpaperComponent
938 == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) {
939 mWallpaperPreferences.setLockWallpaperId(
940 mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK));
941 } else {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800942 try {
Santiago Etchebehere5a7f1dd2018-04-03 15:01:29 -0700943 FileInputStream fileInputStream = mDeviceProtectedContext.openFileInput(
944 NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800945 int lockWallpaperId = setStreamToWallpaperManagerCompat(
946 fileInputStream, false /* allowBackup */, WallpaperManagerCompat.FLAG_LOCK);
947 fileInputStream.close();
948 mWallpaperPreferences.setLockWallpaperId(lockWallpaperId);
949 } catch (FileNotFoundException e) {
950 Log.e(TAG, "Couldn't copy over previously rotating wallpaper to lock screen.");
951 } catch (IOException e) {
952 Log.e(TAG, "IOException when closing the file input stream " + e);
953 }
954 }
955 }
956
957 /**
958 * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the set
959 * wallpaper operation is successful.
960 *
961 * @param destination Which destination of wallpaper the metadata corresponds to (home screen,
962 * lock screen, or both).
963 * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which on N
964 * and later versions of Android uniquely identifies a wallpaper image.
965 */
966 private void setImageWallpaperMetadata(@Destination int destination, int wallpaperId) {
967 if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) {
968 mWallpaperPreferences.clearHomeWallpaperMetadata();
969 setImageWallpaperHomeMetadata(wallpaperId);
970
971 // Reset presentation mode to STATIC if an individual wallpaper is set to the home screen
972 // because rotation always affects at least the home screen.
973 mWallpaperPreferences.setWallpaperPresentationMode(
974 WallpaperPreferences.PRESENTATION_MODE_STATIC);
975 }
976
977 if (destination == DEST_LOCK_SCREEN || destination == DEST_BOTH) {
978 mWallpaperPreferences.clearLockWallpaperMetadata();
979 setImageWallpaperLockMetadata(wallpaperId);
980 }
981
982 mWallpaperPreferences.clearDailyRotations();
983 }
984
985 private void setImageWallpaperHomeMetadata(int homeWallpaperId) {
986 if (BuildCompat.isAtLeastN()) {
987 mWallpaperPreferences.setHomeWallpaperManagerId(homeWallpaperId);
988 }
989
990 // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely
991 // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that
992 // WallpaperManager doesn't return the old wallpaper drawable. Do this on N+ devices in
993 // addition to saving the wallpaper ID for the purpose of backup & restore.
994 mWallpaperManager.forgetLoadedWallpaper();
995 mBitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap();
996 long bitmapHash = BitmapUtils.generateHashCode(mBitmap);
997
998 mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
999
Santiago Etchebehered1bd5092018-04-18 16:03:30 -07001000 mWallpaperPreferences.setHomeWallpaperAttributions(
1001 mWallpaper.getAttributions(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001002 mWallpaperPreferences.setHomeWallpaperBaseImageUrl(mWallpaper.getBaseImageUrl());
1003 mWallpaperPreferences.setHomeWallpaperActionUrl(mWallpaper.getActionUrl(mAppContext));
Santiago Etchebeheree0810d02018-05-10 17:39:40 -07001004 mWallpaperPreferences.setHomeWallpaperActionLabelRes(
1005 mWallpaper.getActionLabelRes(mAppContext));
1006 mWallpaperPreferences.setHomeWallpaperActionIconRes(
1007 mWallpaper.getActionIconRes(mAppContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -07001008 mWallpaperPreferences.setHomeWallpaperCollectionId(
1009 mWallpaper.getCollectionId(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001010 mWallpaperPreferences.setHomeWallpaperRemoteId(mWallpaper.getWallpaperId());
1011 }
1012
1013 private void setImageWallpaperLockMetadata(int lockWallpaperId) {
1014 mWallpaperPreferences.setLockWallpaperId(lockWallpaperId);
Santiago Etchebehered1bd5092018-04-18 16:03:30 -07001015 mWallpaperPreferences.setLockWallpaperAttributions(
1016 mWallpaper.getAttributions(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001017 mWallpaperPreferences.setLockWallpaperActionUrl(mWallpaper.getActionUrl(mAppContext));
Santiago Etchebeheree0810d02018-05-10 17:39:40 -07001018 mWallpaperPreferences.setHomeWallpaperActionLabelRes(
1019 mWallpaper.getActionLabelRes(mAppContext));
1020 mWallpaperPreferences.setHomeWallpaperActionIconRes(
1021 mWallpaper.getActionIconRes(mAppContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -07001022 mWallpaperPreferences.setLockWallpaperCollectionId(
1023 mWallpaper.getCollectionId(mAppContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001024
1025 // Save the lock wallpaper image's hash code as well for the sake of backup & restore because
1026 // WallpaperManager-generated IDs are specific to a physical device and cannot be used to
1027 // identify a wallpaper image on another device after restore is complete.
1028 saveLockWallpaperHashCode();
1029 }
1030
1031 private void saveLockWallpaperHashCode() {
1032 Bitmap lockBitmap = null;
1033
1034 ParcelFileDescriptor parcelFd = mWallpaperManagerCompat.getWallpaperFile(
1035 WallpaperManagerCompat.FLAG_LOCK);
1036
1037 if (parcelFd == null) {
1038 return;
1039 }
1040
1041 InputStream fileStream = null;
1042 try {
1043 fileStream = new FileInputStream(parcelFd.getFileDescriptor());
1044 lockBitmap = BitmapFactory.decodeStream(fileStream);
1045 parcelFd.close();
1046 } catch (IOException e) {
1047 Log.e(TAG, "IO exception when closing the file descriptor.");
1048 } finally {
1049 if (fileStream != null) {
1050 try {
1051 fileStream.close();
1052 } catch (IOException e) {
1053 Log.e(TAG, "IO exception when closing the input stream for the lock screen WP.");
1054 }
1055 }
1056 }
1057
1058 if (lockBitmap != null) {
1059 long bitmapHash = BitmapUtils.generateHashCode(lockBitmap);
1060 mWallpaperPreferences.setLockWallpaperHashCode(bitmapHash);
1061 }
1062 }
1063 }
1064}