1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import android.annotation.IntDef; 20 import android.annotation.RawRes; 21 import android.annotation.SystemApi; 22 import android.content.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.content.res.Resources; 29 import android.content.res.Resources.NotFoundException; 30 import android.graphics.Bitmap; 31 import android.graphics.BitmapFactory; 32 import android.graphics.BitmapRegionDecoder; 33 import android.graphics.Canvas; 34 import android.graphics.ColorFilter; 35 import android.graphics.Matrix; 36 import android.graphics.Paint; 37 import android.graphics.PixelFormat; 38 import android.graphics.PorterDuff; 39 import android.graphics.PorterDuffXfermode; 40 import android.graphics.Rect; 41 import android.graphics.RectF; 42 import android.graphics.drawable.BitmapDrawable; 43 import android.graphics.drawable.Drawable; 44 import android.net.Uri; 45 import android.os.Bundle; 46 import android.os.DeadSystemException; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Looper; 50 import android.os.ParcelFileDescriptor; 51 import android.os.RemoteException; 52 import android.os.ServiceManager; 53 import android.os.SystemProperties; 54 import android.text.TextUtils; 55 import android.util.Log; 56 import android.view.WindowManagerGlobal; 57 58 import libcore.io.IoUtils; 59 60 import java.io.BufferedInputStream; 61 import java.io.File; 62 import java.io.FileInputStream; 63 import java.io.FileOutputStream; 64 import java.io.IOException; 65 import java.io.InputStream; 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.util.List; 69 import java.util.concurrent.CountDownLatch; 70 import java.util.concurrent.TimeUnit; 71 72 /** 73 * Provides access to the system wallpaper. With WallpaperManager, you can 74 * get the current wallpaper, get the desired dimensions for the wallpaper, set 75 * the wallpaper, and more. Get an instance of WallpaperManager with 76 * {@link #getInstance(android.content.Context) getInstance()}. 77 * 78 * <p> An app can check whether wallpapers are supported for the current user, by calling 79 * {@link #isWallpaperSupported()}, and whether setting of wallpapers is allowed, by calling 80 * {@link #isSetWallpaperAllowed()}. 81 */ 82 public class WallpaperManager { 83 private static String TAG = "WallpaperManager"; 84 private static boolean DEBUG = false; 85 private float mWallpaperXStep = -1; 86 private float mWallpaperYStep = -1; 87 88 /** {@hide} */ 89 private static final String PROP_WALLPAPER = "ro.config.wallpaper"; 90 /** {@hide} */ 91 private static final String PROP_LOCK_WALLPAPER = "ro.config.lock_wallpaper"; 92 /** {@hide} */ 93 private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component"; 94 95 /** 96 * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct 97 * an intent; instead, use {@link #getCropAndSetWallpaperIntent}. 98 * <p>Input: {@link Intent#getData} is the URI of the image to crop and set as wallpaper. 99 * <p>Output: RESULT_OK if user decided to crop/set the wallpaper, RESULT_CANCEL otherwise 100 * Activities that support this intent should specify a MIME filter of "image/*" 101 */ 102 public static final String ACTION_CROP_AND_SET_WALLPAPER = 103 "android.service.wallpaper.CROP_AND_SET_WALLPAPER"; 104 105 /** 106 * Launch an activity for the user to pick the current global live 107 * wallpaper. 108 */ 109 public static final String ACTION_LIVE_WALLPAPER_CHOOSER 110 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER"; 111 112 /** 113 * Directly launch live wallpaper preview, allowing the user to immediately 114 * confirm to switch to a specific live wallpaper. You must specify 115 * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of 116 * a live wallpaper component that is to be shown. 117 */ 118 public static final String ACTION_CHANGE_LIVE_WALLPAPER 119 = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER"; 120 121 /** 122 * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the 123 * ComponentName of a live wallpaper that should be shown as a preview, 124 * for the user to confirm. 125 */ 126 public static final String EXTRA_LIVE_WALLPAPER_COMPONENT 127 = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT"; 128 129 /** 130 * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER} 131 * which allows them to provide a custom large icon associated with this action. 132 */ 133 public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview"; 134 135 /** 136 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 137 * host when the user taps on an empty area (not performing an action 138 * in the host). The x and y arguments are the location of the tap in 139 * screen coordinates. 140 */ 141 public static final String COMMAND_TAP = "android.wallpaper.tap"; 142 143 /** 144 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 145 * host when the user releases a secondary pointer on an empty area 146 * (not performing an action in the host). The x and y arguments are 147 * the location of the secondary tap in screen coordinates. 148 */ 149 public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap"; 150 151 /** 152 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 153 * host when the user drops an object into an area of the host. The x 154 * and y arguments are the location of the drop. 155 */ 156 public static final String COMMAND_DROP = "android.home.drop"; 157 158 /** 159 * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID. 160 * @hide 161 */ 162 public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID"; 163 164 // flags for which kind of wallpaper to act on 165 166 /** @hide */ 167 @IntDef(flag = true, value = { 168 FLAG_SYSTEM, 169 FLAG_LOCK 170 }) 171 @Retention(RetentionPolicy.SOURCE) 172 public @interface SetWallpaperFlags {} 173 174 /** 175 * Flag: set or retrieve the general system wallpaper. 176 */ 177 public static final int FLAG_SYSTEM = 1 << 0; 178 179 /** 180 * Flag: set or retrieve the lock-screen-specific wallpaper. 181 */ 182 public static final int FLAG_LOCK = 1 << 1; 183 184 private final Context mContext; 185 186 /** 187 * Special drawable that draws a wallpaper as fast as possible. Assumes 188 * no scaling or placement off (0,0) of the wallpaper (this should be done 189 * at the time the bitmap is loaded). 190 */ 191 static class FastBitmapDrawable extends Drawable { 192 private final Bitmap mBitmap; 193 private final int mWidth; 194 private final int mHeight; 195 private int mDrawLeft; 196 private int mDrawTop; 197 private final Paint mPaint; 198 FastBitmapDrawable(Bitmap bitmap)199 private FastBitmapDrawable(Bitmap bitmap) { 200 mBitmap = bitmap; 201 mWidth = bitmap.getWidth(); 202 mHeight = bitmap.getHeight(); 203 204 setBounds(0, 0, mWidth, mHeight); 205 206 mPaint = new Paint(); 207 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 208 } 209 210 @Override draw(Canvas canvas)211 public void draw(Canvas canvas) { 212 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint); 213 } 214 215 @Override getOpacity()216 public int getOpacity() { 217 return PixelFormat.OPAQUE; 218 } 219 220 @Override setBounds(int left, int top, int right, int bottom)221 public void setBounds(int left, int top, int right, int bottom) { 222 mDrawLeft = left + (right-left - mWidth) / 2; 223 mDrawTop = top + (bottom-top - mHeight) / 2; 224 } 225 226 @Override setAlpha(int alpha)227 public void setAlpha(int alpha) { 228 throw new UnsupportedOperationException("Not supported with this drawable"); 229 } 230 231 @Override setColorFilter(ColorFilter colorFilter)232 public void setColorFilter(ColorFilter colorFilter) { 233 throw new UnsupportedOperationException("Not supported with this drawable"); 234 } 235 236 @Override setDither(boolean dither)237 public void setDither(boolean dither) { 238 throw new UnsupportedOperationException("Not supported with this drawable"); 239 } 240 241 @Override setFilterBitmap(boolean filter)242 public void setFilterBitmap(boolean filter) { 243 throw new UnsupportedOperationException("Not supported with this drawable"); 244 } 245 246 @Override getIntrinsicWidth()247 public int getIntrinsicWidth() { 248 return mWidth; 249 } 250 251 @Override getIntrinsicHeight()252 public int getIntrinsicHeight() { 253 return mHeight; 254 } 255 256 @Override getMinimumWidth()257 public int getMinimumWidth() { 258 return mWidth; 259 } 260 261 @Override getMinimumHeight()262 public int getMinimumHeight() { 263 return mHeight; 264 } 265 } 266 267 static class Globals extends IWallpaperManagerCallback.Stub { 268 private IWallpaperManager mService; 269 private Bitmap mCachedWallpaper; 270 private int mCachedWallpaperUserId; 271 private Bitmap mDefaultWallpaper; 272 Globals(Looper looper)273 Globals(Looper looper) { 274 IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); 275 mService = IWallpaperManager.Stub.asInterface(b); 276 forgetLoadedWallpaper(); 277 } 278 onWallpaperChanged()279 public void onWallpaperChanged() { 280 /* The wallpaper has changed but we shouldn't eagerly load the 281 * wallpaper as that would be inefficient. Reset the cached wallpaper 282 * to null so if the user requests the wallpaper again then we'll 283 * fetch it. 284 */ 285 forgetLoadedWallpaper(); 286 } 287 peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which)288 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, 289 @SetWallpaperFlags int which) { 290 return peekWallpaperBitmap(context, returnDefault, which, context.getUserId()); 291 } 292 peekWallpaperBitmap(Context context, boolean returnDefault, @SetWallpaperFlags int which, int userId)293 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault, 294 @SetWallpaperFlags int which, int userId) { 295 synchronized (this) { 296 if (mService != null) { 297 try { 298 if (!mService.isWallpaperSupported(context.getOpPackageName())) { 299 return null; 300 } 301 } catch (RemoteException e) { 302 throw e.rethrowFromSystemServer(); 303 } 304 } 305 if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) { 306 return mCachedWallpaper; 307 } 308 mCachedWallpaper = null; 309 mCachedWallpaperUserId = 0; 310 try { 311 mCachedWallpaper = getCurrentWallpaperLocked(userId); 312 mCachedWallpaperUserId = userId; 313 } catch (OutOfMemoryError e) { 314 Log.w(TAG, "No memory load current wallpaper", e); 315 } 316 if (mCachedWallpaper != null) { 317 return mCachedWallpaper; 318 } 319 if (returnDefault) { 320 if (mDefaultWallpaper == null) { 321 mDefaultWallpaper = getDefaultWallpaperLocked(context, which); 322 } 323 return mDefaultWallpaper; 324 } 325 return null; 326 } 327 } 328 forgetLoadedWallpaper()329 public void forgetLoadedWallpaper() { 330 synchronized (this) { 331 mCachedWallpaper = null; 332 mCachedWallpaperUserId = 0; 333 mDefaultWallpaper = null; 334 } 335 } 336 getCurrentWallpaperLocked(int userId)337 private Bitmap getCurrentWallpaperLocked(int userId) { 338 if (mService == null) { 339 Log.w(TAG, "WallpaperService not running"); 340 return null; 341 } 342 343 try { 344 Bundle params = new Bundle(); 345 ParcelFileDescriptor fd = mService.getWallpaper(this, FLAG_SYSTEM, 346 params, userId); 347 if (fd != null) { 348 try { 349 BitmapFactory.Options options = new BitmapFactory.Options(); 350 return BitmapFactory.decodeFileDescriptor( 351 fd.getFileDescriptor(), null, options); 352 } catch (OutOfMemoryError e) { 353 Log.w(TAG, "Can't decode file", e); 354 } finally { 355 IoUtils.closeQuietly(fd); 356 } 357 } 358 } catch (RemoteException e) { 359 throw e.rethrowFromSystemServer(); 360 } 361 return null; 362 } 363 getDefaultWallpaperLocked(Context context, @SetWallpaperFlags int which)364 private Bitmap getDefaultWallpaperLocked(Context context, @SetWallpaperFlags int which) { 365 InputStream is = openDefaultWallpaper(context, which); 366 if (is != null) { 367 try { 368 BitmapFactory.Options options = new BitmapFactory.Options(); 369 return BitmapFactory.decodeStream(is, null, options); 370 } catch (OutOfMemoryError e) { 371 Log.w(TAG, "Can't decode stream", e); 372 } finally { 373 IoUtils.closeQuietly(is); 374 } 375 } 376 return null; 377 } 378 } 379 380 private static final Object sSync = new Object[0]; 381 private static Globals sGlobals; 382 initGlobals(Looper looper)383 static void initGlobals(Looper looper) { 384 synchronized (sSync) { 385 if (sGlobals == null) { 386 sGlobals = new Globals(looper); 387 } 388 } 389 } 390 WallpaperManager(Context context, Handler handler)391 /*package*/ WallpaperManager(Context context, Handler handler) { 392 mContext = context; 393 initGlobals(context.getMainLooper()); 394 } 395 396 /** 397 * Retrieve a WallpaperManager associated with the given Context. 398 */ getInstance(Context context)399 public static WallpaperManager getInstance(Context context) { 400 return (WallpaperManager)context.getSystemService( 401 Context.WALLPAPER_SERVICE); 402 } 403 404 /** @hide */ getIWallpaperManager()405 public IWallpaperManager getIWallpaperManager() { 406 return sGlobals.mService; 407 } 408 409 /** 410 * Retrieve the current system wallpaper; if 411 * no wallpaper is set, the system built-in static wallpaper is returned. 412 * This is returned as an 413 * abstract Drawable that you can install in a View to display whatever 414 * wallpaper the user has currently set. 415 * 416 * @return Returns a Drawable object that will draw the wallpaper. 417 */ getDrawable()418 public Drawable getDrawable() { 419 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM); 420 if (bm != null) { 421 Drawable dr = new BitmapDrawable(mContext.getResources(), bm); 422 dr.setDither(false); 423 return dr; 424 } 425 return null; 426 } 427 428 /** 429 * Obtain a drawable for the built-in static system wallpaper. 430 */ getBuiltInDrawable()431 public Drawable getBuiltInDrawable() { 432 return getBuiltInDrawable(0, 0, false, 0, 0, FLAG_SYSTEM); 433 } 434 435 /** 436 * Obtain a drawable for the specified built-in static system wallpaper. 437 * 438 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 439 * IllegalArgumentException if an invalid wallpaper is requested. 440 * @return A Drawable presenting the specified wallpaper image, or {@code null} 441 * if no built-in default image for that wallpaper type exists. 442 */ getBuiltInDrawable(@etWallpaperFlags int which)443 public Drawable getBuiltInDrawable(@SetWallpaperFlags int which) { 444 return getBuiltInDrawable(0, 0, false, 0, 0, which); 445 } 446 447 /** 448 * Returns a drawable for the system built-in static wallpaper. Based on the parameters, the 449 * drawable can be cropped and scaled 450 * 451 * @param outWidth The width of the returned drawable 452 * @param outWidth The height of the returned drawable 453 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it 454 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image; 455 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned 456 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image; 457 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned 458 * @return A Drawable presenting the built-in default system wallpaper image, 459 * or {@code null} if no such default image is defined on this device. 460 */ getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment)461 public Drawable getBuiltInDrawable(int outWidth, int outHeight, 462 boolean scaleToFit, float horizontalAlignment, float verticalAlignment) { 463 return getBuiltInDrawable(outWidth, outHeight, scaleToFit, 464 horizontalAlignment, verticalAlignment, FLAG_SYSTEM); 465 } 466 467 /** 468 * Returns a drawable for the built-in static wallpaper of the specified type. Based on the 469 * parameters, the drawable can be cropped and scaled. 470 * 471 * @param outWidth The width of the returned drawable 472 * @param outWidth The height of the returned drawable 473 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it 474 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image; 475 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned 476 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image; 477 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned 478 * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws 479 * IllegalArgumentException if an invalid wallpaper is requested. 480 * @return A Drawable presenting the built-in default wallpaper image of the given type, 481 * or {@code null} if no default image of that type is defined on this device. 482 */ getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which)483 public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, 484 float horizontalAlignment, float verticalAlignment, @SetWallpaperFlags int which) { 485 if (sGlobals.mService == null) { 486 Log.w(TAG, "WallpaperService not running"); 487 throw new RuntimeException(new DeadSystemException()); 488 } 489 490 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 491 throw new IllegalArgumentException("Must request exactly one kind of wallpaper"); 492 } 493 494 Resources resources = mContext.getResources(); 495 horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment)); 496 verticalAlignment = Math.max(0, Math.min(1, verticalAlignment)); 497 498 InputStream wpStream = openDefaultWallpaper(mContext, which); 499 if (wpStream == null) { 500 if (DEBUG) { 501 Log.w(TAG, "default wallpaper stream " + which + " is null"); 502 } 503 return null; 504 } else { 505 InputStream is = new BufferedInputStream(wpStream); 506 if (outWidth <= 0 || outHeight <= 0) { 507 Bitmap fullSize = BitmapFactory.decodeStream(is, null, null); 508 return new BitmapDrawable(resources, fullSize); 509 } else { 510 int inWidth; 511 int inHeight; 512 // Just measure this time through... 513 { 514 BitmapFactory.Options options = new BitmapFactory.Options(); 515 options.inJustDecodeBounds = true; 516 BitmapFactory.decodeStream(is, null, options); 517 if (options.outWidth != 0 && options.outHeight != 0) { 518 inWidth = options.outWidth; 519 inHeight = options.outHeight; 520 } else { 521 Log.e(TAG, "default wallpaper dimensions are 0"); 522 return null; 523 } 524 } 525 526 // Reopen the stream to do the full decode. We know at this point 527 // that openDefaultWallpaper() will return non-null. 528 is = new BufferedInputStream(openDefaultWallpaper(mContext, which)); 529 530 RectF cropRectF; 531 532 outWidth = Math.min(inWidth, outWidth); 533 outHeight = Math.min(inHeight, outHeight); 534 if (scaleToFit) { 535 cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight, 536 horizontalAlignment, verticalAlignment); 537 } else { 538 float left = (inWidth - outWidth) * horizontalAlignment; 539 float right = left + outWidth; 540 float top = (inHeight - outHeight) * verticalAlignment; 541 float bottom = top + outHeight; 542 cropRectF = new RectF(left, top, right, bottom); 543 } 544 Rect roundedTrueCrop = new Rect(); 545 cropRectF.roundOut(roundedTrueCrop); 546 547 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 548 Log.w(TAG, "crop has bad values for full size image"); 549 return null; 550 } 551 552 // See how much we're reducing the size of the image 553 int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth, 554 roundedTrueCrop.height() / outHeight); 555 556 // Attempt to open a region decoder 557 BitmapRegionDecoder decoder = null; 558 try { 559 decoder = BitmapRegionDecoder.newInstance(is, true); 560 } catch (IOException e) { 561 Log.w(TAG, "cannot open region decoder for default wallpaper"); 562 } 563 564 Bitmap crop = null; 565 if (decoder != null) { 566 // Do region decoding to get crop bitmap 567 BitmapFactory.Options options = new BitmapFactory.Options(); 568 if (scaleDownSampleSize > 1) { 569 options.inSampleSize = scaleDownSampleSize; 570 } 571 crop = decoder.decodeRegion(roundedTrueCrop, options); 572 decoder.recycle(); 573 } 574 575 if (crop == null) { 576 // BitmapRegionDecoder has failed, try to crop in-memory. We know at 577 // this point that openDefaultWallpaper() will return non-null. 578 is = new BufferedInputStream(openDefaultWallpaper(mContext, which)); 579 Bitmap fullSize = null; 580 BitmapFactory.Options options = new BitmapFactory.Options(); 581 if (scaleDownSampleSize > 1) { 582 options.inSampleSize = scaleDownSampleSize; 583 } 584 fullSize = BitmapFactory.decodeStream(is, null, options); 585 if (fullSize != null) { 586 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 587 roundedTrueCrop.top, roundedTrueCrop.width(), 588 roundedTrueCrop.height()); 589 } 590 } 591 592 if (crop == null) { 593 Log.w(TAG, "cannot decode default wallpaper"); 594 return null; 595 } 596 597 // Scale down if necessary 598 if (outWidth > 0 && outHeight > 0 && 599 (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) { 600 Matrix m = new Matrix(); 601 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); 602 RectF returnRect = new RectF(0, 0, outWidth, outHeight); 603 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 604 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 605 (int) returnRect.height(), Bitmap.Config.ARGB_8888); 606 if (tmp != null) { 607 Canvas c = new Canvas(tmp); 608 Paint p = new Paint(); 609 p.setFilterBitmap(true); 610 c.drawBitmap(crop, m, p); 611 crop = tmp; 612 } 613 } 614 615 return new BitmapDrawable(resources, crop); 616 } 617 } 618 } 619 getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight, float horizontalAlignment, float verticalAlignment)620 private static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight, 621 float horizontalAlignment, float verticalAlignment) { 622 RectF cropRect = new RectF(); 623 // Get a crop rect that will fit this 624 if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 625 cropRect.top = 0; 626 cropRect.bottom = inHeight; 627 float cropWidth = outWidth * (inHeight / (float) outHeight); 628 cropRect.left = (inWidth - cropWidth) * horizontalAlignment; 629 cropRect.right = cropRect.left + cropWidth; 630 } else { 631 cropRect.left = 0; 632 cropRect.right = inWidth; 633 float cropHeight = outHeight * (inWidth / (float) outWidth); 634 cropRect.top = (inHeight - cropHeight) * verticalAlignment; 635 cropRect.bottom = cropRect.top + cropHeight; 636 } 637 return cropRect; 638 } 639 640 /** 641 * Retrieve the current system wallpaper; if there is no wallpaper set, 642 * a null pointer is returned. This is returned as an 643 * abstract Drawable that you can install in a View to display whatever 644 * wallpaper the user has currently set. 645 * 646 * @return Returns a Drawable object that will draw the wallpaper or a 647 * null pointer if these is none. 648 */ peekDrawable()649 public Drawable peekDrawable() { 650 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM); 651 if (bm != null) { 652 Drawable dr = new BitmapDrawable(mContext.getResources(), bm); 653 dr.setDither(false); 654 return dr; 655 } 656 return null; 657 } 658 659 /** 660 * Like {@link #getDrawable()}, but the returned Drawable has a number 661 * of limitations to reduce its overhead as much as possible. It will 662 * never scale the wallpaper (only centering it if the requested bounds 663 * do match the bitmap bounds, which should not be typical), doesn't 664 * allow setting an alpha, color filter, or other attributes, etc. The 665 * bounds of the returned drawable will be initialized to the same bounds 666 * as the wallpaper, so normally you will not need to touch it. The 667 * drawable also assumes that it will be used in a context running in 668 * the same density as the screen (not in density compatibility mode). 669 * 670 * @return Returns a Drawable object that will draw the wallpaper. 671 */ getFastDrawable()672 public Drawable getFastDrawable() { 673 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM); 674 if (bm != null) { 675 return new FastBitmapDrawable(bm); 676 } 677 return null; 678 } 679 680 /** 681 * Like {@link #getFastDrawable()}, but if there is no wallpaper set, 682 * a null pointer is returned. 683 * 684 * @return Returns an optimized Drawable object that will draw the 685 * wallpaper or a null pointer if these is none. 686 */ peekFastDrawable()687 public Drawable peekFastDrawable() { 688 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM); 689 if (bm != null) { 690 return new FastBitmapDrawable(bm); 691 } 692 return null; 693 } 694 695 /** 696 * Like {@link #getDrawable()} but returns a Bitmap. 697 * 698 * @hide 699 */ getBitmap()700 public Bitmap getBitmap() { 701 return getBitmapAsUser(mContext.getUserId()); 702 } 703 704 /** 705 * Like {@link #getDrawable()} but returns a Bitmap for the provided user. 706 * 707 * @hide 708 */ getBitmapAsUser(int userId)709 public Bitmap getBitmapAsUser(int userId) { 710 return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId); 711 } 712 713 /** 714 * Get an open, readable file descriptor to the given wallpaper image file. 715 * The caller is responsible for closing the file descriptor when done ingesting the file. 716 * 717 * <p>If no lock-specific wallpaper has been configured for the given user, then 718 * this method will return {@code null} when requesting {@link #FLAG_LOCK} rather than 719 * returning the system wallpaper's image file. 720 * 721 * @param which The wallpaper whose image file is to be retrieved. Must be a single 722 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 723 * {@link #FLAG_LOCK}. 724 * 725 * @see #FLAG_LOCK 726 * @see #FLAG_SYSTEM 727 */ getWallpaperFile(@etWallpaperFlags int which)728 public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which) { 729 return getWallpaperFile(which, mContext.getUserId()); 730 } 731 732 /** 733 * Version of {@link #getWallpaperFile(int)} that can access the wallpaper data 734 * for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL 735 * permission to access another user's wallpaper data. 736 * 737 * @param which The wallpaper whose image file is to be retrieved. Must be a single 738 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 739 * {@link #FLAG_LOCK}. 740 * @param userId The user or profile whose imagery is to be retrieved 741 * 742 * @see #FLAG_LOCK 743 * @see #FLAG_SYSTEM 744 * 745 * @hide 746 */ getWallpaperFile(@etWallpaperFlags int which, int userId)747 public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, int userId) { 748 if (which != FLAG_SYSTEM && which != FLAG_LOCK) { 749 throw new IllegalArgumentException("Must request exactly one kind of wallpaper"); 750 } 751 752 if (sGlobals.mService == null) { 753 Log.w(TAG, "WallpaperService not running"); 754 throw new RuntimeException(new DeadSystemException()); 755 } else { 756 try { 757 Bundle outParams = new Bundle(); 758 return sGlobals.mService.getWallpaper(null, which, outParams, userId); 759 } catch (RemoteException e) { 760 throw e.rethrowFromSystemServer(); 761 } 762 } 763 } 764 765 /** 766 * Remove all internal references to the last loaded wallpaper. Useful 767 * for apps that want to reduce memory usage when they only temporarily 768 * need to have the wallpaper. After calling, the next request for the 769 * wallpaper will require reloading it again from disk. 770 */ forgetLoadedWallpaper()771 public void forgetLoadedWallpaper() { 772 sGlobals.forgetLoadedWallpaper(); 773 } 774 775 /** 776 * If the current wallpaper is a live wallpaper component, return the 777 * information about that wallpaper. Otherwise, if it is a static image, 778 * simply return null. 779 */ getWallpaperInfo()780 public WallpaperInfo getWallpaperInfo() { 781 try { 782 if (sGlobals.mService == null) { 783 Log.w(TAG, "WallpaperService not running"); 784 throw new RuntimeException(new DeadSystemException()); 785 } else { 786 return sGlobals.mService.getWallpaperInfo(); 787 } 788 } catch (RemoteException e) { 789 throw e.rethrowFromSystemServer(); 790 } 791 } 792 793 /** 794 * Get the ID of the current wallpaper of the given kind. If there is no 795 * such wallpaper configured, returns a negative number. 796 * 797 * <p>Every time the wallpaper image is set, a new ID is assigned to it. 798 * This method allows the caller to determine whether the wallpaper imagery 799 * has changed, regardless of how that change happened. 800 * 801 * @param which The wallpaper whose ID is to be returned. Must be a single 802 * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or 803 * {@link #FLAG_LOCK}. 804 * @return The positive numeric ID of the current wallpaper of the given kind, 805 * or a negative value if no such wallpaper is configured. 806 */ getWallpaperId(@etWallpaperFlags int which)807 public int getWallpaperId(@SetWallpaperFlags int which) { 808 return getWallpaperIdForUser(which, mContext.getUserId()); 809 } 810 811 /** 812 * Get the ID of the given user's current wallpaper of the given kind. If there 813 * is no such wallpaper configured, returns a negative number. 814 * @hide 815 */ getWallpaperIdForUser(@etWallpaperFlags int which, int userId)816 public int getWallpaperIdForUser(@SetWallpaperFlags int which, int userId) { 817 try { 818 if (sGlobals.mService == null) { 819 Log.w(TAG, "WallpaperService not running"); 820 throw new RuntimeException(new DeadSystemException()); 821 } else { 822 return sGlobals.mService.getWallpaperIdForUser(which, userId); 823 } 824 } catch (RemoteException e) { 825 throw e.rethrowFromSystemServer(); 826 } 827 } 828 829 /** 830 * Gets an Intent that will launch an activity that crops the given 831 * image and sets the device's wallpaper. If there is a default HOME activity 832 * that supports cropping wallpapers, it will be preferred as the default. 833 * Use this method instead of directly creating a {@link #ACTION_CROP_AND_SET_WALLPAPER} 834 * intent. 835 * 836 * @param imageUri The image URI that will be set in the intent. The must be a content 837 * URI and its provider must resolve its type to "image/*" 838 * 839 * @throws IllegalArgumentException if the URI is not a content URI or its MIME type is 840 * not "image/*" 841 */ getCropAndSetWallpaperIntent(Uri imageUri)842 public Intent getCropAndSetWallpaperIntent(Uri imageUri) { 843 if (imageUri == null) { 844 throw new IllegalArgumentException("Image URI must not be null"); 845 } 846 847 if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) { 848 throw new IllegalArgumentException("Image URI must be of the " 849 + ContentResolver.SCHEME_CONTENT + " scheme type"); 850 } 851 852 final PackageManager packageManager = mContext.getPackageManager(); 853 Intent cropAndSetWallpaperIntent = 854 new Intent(ACTION_CROP_AND_SET_WALLPAPER, imageUri); 855 cropAndSetWallpaperIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 856 857 // Find out if the default HOME activity supports CROP_AND_SET_WALLPAPER 858 Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); 859 ResolveInfo resolvedHome = packageManager.resolveActivity(homeIntent, 860 PackageManager.MATCH_DEFAULT_ONLY); 861 if (resolvedHome != null) { 862 cropAndSetWallpaperIntent.setPackage(resolvedHome.activityInfo.packageName); 863 864 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 865 cropAndSetWallpaperIntent, 0); 866 if (cropAppList.size() > 0) { 867 return cropAndSetWallpaperIntent; 868 } 869 } 870 871 // fallback crop activity 872 final String cropperPackage = mContext.getString( 873 com.android.internal.R.string.config_wallpaperCropperPackage); 874 cropAndSetWallpaperIntent.setPackage(cropperPackage); 875 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 876 cropAndSetWallpaperIntent, 0); 877 if (cropAppList.size() > 0) { 878 return cropAndSetWallpaperIntent; 879 } 880 // If the URI is not of the right type, or for some reason the system wallpaper 881 // cropper doesn't exist, return null 882 throw new IllegalArgumentException("Cannot use passed URI to set wallpaper; " + 883 "check that the type returned by ContentProvider matches image/*"); 884 } 885 886 /** 887 * Change the current system wallpaper to the bitmap in the given resource. 888 * The resource is opened as a raw data stream and copied into the 889 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent 890 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 891 * 892 * <p>This method requires the caller to hold the permission 893 * {@link android.Manifest.permission#SET_WALLPAPER}. 894 * 895 * @param resid The resource ID of the bitmap to be used as the wallpaper image 896 * 897 * @throws IOException If an error occurs reverting to the built-in 898 * wallpaper. 899 */ setResource(@awRes int resid)900 public void setResource(@RawRes int resid) throws IOException { 901 setResource(resid, FLAG_SYSTEM); 902 } 903 904 /** 905 * Version of {@link #setResource(int)} that allows the caller to specify which 906 * of the supported wallpaper categories to set. 907 * 908 * @param resid The resource ID of the bitmap to be used as the wallpaper image 909 * @param which Flags indicating which wallpaper(s) to configure with the new imagery 910 * 911 * @see #FLAG_LOCK 912 * @see #FLAG_SYSTEM 913 * 914 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 915 * 916 * @throws IOException 917 */ setResource(@awRes int resid, @SetWallpaperFlags int which)918 public int setResource(@RawRes int resid, @SetWallpaperFlags int which) 919 throws IOException { 920 if (sGlobals.mService == null) { 921 Log.w(TAG, "WallpaperService not running"); 922 throw new RuntimeException(new DeadSystemException()); 923 } 924 final Bundle result = new Bundle(); 925 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 926 try { 927 Resources resources = mContext.getResources(); 928 /* Set the wallpaper to the default values */ 929 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( 930 "res:" + resources.getResourceName(resid), 931 mContext.getOpPackageName(), null, false, result, which, completion); 932 if (fd != null) { 933 FileOutputStream fos = null; 934 boolean ok = false; 935 try { 936 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 937 copyStreamToWallpaperFile(resources.openRawResource(resid), fos); 938 // The 'close()' is the trigger for any server-side image manipulation, 939 // so we must do that before waiting for completion. 940 fos.close(); 941 completion.waitForCompletion(); 942 } finally { 943 // Might be redundant but completion shouldn't wait unless the write 944 // succeeded; this is a fallback if it threw past the close+wait. 945 IoUtils.closeQuietly(fos); 946 } 947 } 948 } catch (RemoteException e) { 949 throw e.rethrowFromSystemServer(); 950 } 951 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 952 } 953 954 /** 955 * Change the current system wallpaper to a bitmap. The given bitmap is 956 * converted to a PNG and stored as the wallpaper. On success, the intent 957 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 958 * 959 * <p>This method is equivalent to calling 960 * {@link #setBitmap(Bitmap, Rect, boolean)} and passing {@code null} for the 961 * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup} 962 * parameter. 963 * 964 * <p>This method requires the caller to hold the permission 965 * {@link android.Manifest.permission#SET_WALLPAPER}. 966 * 967 * @param bitmap The bitmap to be used as the new system wallpaper. 968 * 969 * @throws IOException If an error occurs when attempting to set the wallpaper 970 * to the provided image. 971 */ setBitmap(Bitmap bitmap)972 public void setBitmap(Bitmap bitmap) throws IOException { 973 setBitmap(bitmap, null, true); 974 } 975 976 /** 977 * Change the current system wallpaper to a bitmap, specifying a hint about 978 * which subrectangle of the full image is to be visible. The OS will then 979 * try to best present the given portion of the full image as the static system 980 * wallpaper image. On success, the intent 981 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 982 * 983 * <p>Passing {@code null} as the {@code visibleHint} parameter is equivalent to 984 * passing (0, 0, {@code fullImage.getWidth()}, {@code fullImage.getHeight()}). 985 * 986 * <p>This method requires the caller to hold the permission 987 * {@link android.Manifest.permission#SET_WALLPAPER}. 988 * 989 * @param fullImage A bitmap that will supply the wallpaper imagery. 990 * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be 991 * displayed as wallpaper. Passing {@code null} for this parameter means that 992 * the full image should be displayed if possible given the image's and device's 993 * aspect ratios, etc. 994 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 995 * image for restore to a future device; {@code false} otherwise. 996 * 997 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 998 * 999 * @throws IOException If an error occurs when attempting to set the wallpaper 1000 * to the provided image. 1001 * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is 1002 * empty or invalid. 1003 */ setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)1004 public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup) 1005 throws IOException { 1006 return setBitmap(fullImage, visibleCropHint, allowBackup, FLAG_SYSTEM); 1007 } 1008 1009 /** 1010 * Version of {@link #setBitmap(Bitmap, Rect, boolean)} that allows the caller 1011 * to specify which of the supported wallpaper categories to set. 1012 * 1013 * @param fullImage A bitmap that will supply the wallpaper imagery. 1014 * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be 1015 * displayed as wallpaper. Passing {@code null} for this parameter means that 1016 * the full image should be displayed if possible given the image's and device's 1017 * aspect ratios, etc. 1018 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 1019 * image for restore to a future device; {@code false} otherwise. 1020 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 1021 * 1022 * @see #FLAG_LOCK 1023 * @see #FLAG_SYSTEM 1024 * 1025 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 1026 * 1027 * @throws IOException 1028 */ setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which)1029 public int setBitmap(Bitmap fullImage, Rect visibleCropHint, 1030 boolean allowBackup, @SetWallpaperFlags int which) 1031 throws IOException { 1032 validateRect(visibleCropHint); 1033 if (sGlobals.mService == null) { 1034 Log.w(TAG, "WallpaperService not running"); 1035 throw new RuntimeException(new DeadSystemException()); 1036 } 1037 final Bundle result = new Bundle(); 1038 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 1039 try { 1040 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 1041 mContext.getOpPackageName(), visibleCropHint, allowBackup, 1042 result, which, completion); 1043 if (fd != null) { 1044 FileOutputStream fos = null; 1045 try { 1046 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 1047 fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); 1048 fos.close(); 1049 completion.waitForCompletion(); 1050 } finally { 1051 IoUtils.closeQuietly(fos); 1052 } 1053 } 1054 } catch (RemoteException e) { 1055 throw e.rethrowFromSystemServer(); 1056 } 1057 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 1058 } 1059 validateRect(Rect rect)1060 private final void validateRect(Rect rect) { 1061 if (rect != null && rect.isEmpty()) { 1062 throw new IllegalArgumentException("visibleCrop rectangle must be valid and non-empty"); 1063 } 1064 } 1065 1066 /** 1067 * Change the current system wallpaper to a specific byte stream. The 1068 * give InputStream is copied into persistent storage and will now be 1069 * used as the wallpaper. Currently it must be either a JPEG or PNG 1070 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 1071 * is broadcast. 1072 * 1073 * <p>This method is equivalent to calling 1074 * {@link #setStream(InputStream, Rect, boolean)} and passing {@code null} for the 1075 * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup} 1076 * parameter. 1077 * 1078 * <p>This method requires the caller to hold the permission 1079 * {@link android.Manifest.permission#SET_WALLPAPER}. 1080 * 1081 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 1082 * data can be in any format handled by {@link BitmapRegionDecoder}. 1083 * 1084 * @throws IOException If an error occurs when attempting to set the wallpaper 1085 * based on the provided image data. 1086 */ setStream(InputStream bitmapData)1087 public void setStream(InputStream bitmapData) throws IOException { 1088 setStream(bitmapData, null, true); 1089 } 1090 copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)1091 private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos) 1092 throws IOException { 1093 byte[] buffer = new byte[32768]; 1094 int amt; 1095 while ((amt=data.read(buffer)) > 0) { 1096 fos.write(buffer, 0, amt); 1097 } 1098 } 1099 1100 /** 1101 * Change the current system wallpaper to a specific byte stream, specifying a 1102 * hint about which subrectangle of the full image is to be visible. The OS will 1103 * then try to best present the given portion of the full image as the static system 1104 * wallpaper image. The data from the given InputStream is copied into persistent 1105 * storage and will then be used as the system wallpaper. Currently the data must 1106 * be either a JPEG or PNG image. On success, the intent 1107 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 1108 * 1109 * <p>This method requires the caller to hold the permission 1110 * {@link android.Manifest.permission#SET_WALLPAPER}. 1111 * 1112 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 1113 * data can be in any format handled by {@link BitmapRegionDecoder}. 1114 * @param visibleCropHint The rectangular subregion of the streamed image that should be 1115 * displayed as wallpaper. Passing {@code null} for this parameter means that 1116 * the full image should be displayed if possible given the image's and device's 1117 * aspect ratios, etc. 1118 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 1119 * image for restore to a future device; {@code false} otherwise. 1120 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 1121 * 1122 * @see #getWallpaperId(int) 1123 * 1124 * @throws IOException If an error occurs when attempting to set the wallpaper 1125 * based on the provided image data. 1126 * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is 1127 * empty or invalid. 1128 */ setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)1129 public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup) 1130 throws IOException { 1131 return setStream(bitmapData, visibleCropHint, allowBackup, FLAG_SYSTEM); 1132 } 1133 1134 /** 1135 * Version of {@link #setStream(InputStream, Rect, boolean)} that allows the caller 1136 * to specify which of the supported wallpaper categories to set. 1137 * 1138 * @param bitmapData A stream containing the raw data to install as a wallpaper. This 1139 * data can be in any format handled by {@link BitmapRegionDecoder}. 1140 * @param visibleCropHint The rectangular subregion of the streamed image that should be 1141 * displayed as wallpaper. Passing {@code null} for this parameter means that 1142 * the full image should be displayed if possible given the image's and device's 1143 * aspect ratios, etc. 1144 * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper 1145 * image for restore to a future device; {@code false} otherwise. 1146 * @param which Flags indicating which wallpaper(s) to configure with the new imagery. 1147 * @return An integer ID assigned to the newly active wallpaper; or zero on failure. 1148 * 1149 * @see #getWallpaperId(int) 1150 * @see #FLAG_LOCK 1151 * @see #FLAG_SYSTEM 1152 * 1153 * @throws IOException 1154 */ setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which)1155 public int setStream(InputStream bitmapData, Rect visibleCropHint, 1156 boolean allowBackup, @SetWallpaperFlags int which) 1157 throws IOException { 1158 validateRect(visibleCropHint); 1159 if (sGlobals.mService == null) { 1160 Log.w(TAG, "WallpaperService not running"); 1161 throw new RuntimeException(new DeadSystemException()); 1162 } 1163 final Bundle result = new Bundle(); 1164 final WallpaperSetCompletion completion = new WallpaperSetCompletion(); 1165 try { 1166 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, 1167 mContext.getOpPackageName(), visibleCropHint, allowBackup, 1168 result, which, completion); 1169 if (fd != null) { 1170 FileOutputStream fos = null; 1171 try { 1172 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 1173 copyStreamToWallpaperFile(bitmapData, fos); 1174 fos.close(); 1175 completion.waitForCompletion(); 1176 } finally { 1177 IoUtils.closeQuietly(fos); 1178 } 1179 } 1180 } catch (RemoteException e) { 1181 throw e.rethrowFromSystemServer(); 1182 } 1183 1184 return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0); 1185 } 1186 1187 /** 1188 * Return whether any users are currently set to use the wallpaper 1189 * with the given resource ID. That is, their wallpaper has been 1190 * set through {@link #setResource(int)} with the same resource id. 1191 */ hasResourceWallpaper(@awRes int resid)1192 public boolean hasResourceWallpaper(@RawRes int resid) { 1193 if (sGlobals.mService == null) { 1194 Log.w(TAG, "WallpaperService not running"); 1195 throw new RuntimeException(new DeadSystemException()); 1196 } 1197 try { 1198 Resources resources = mContext.getResources(); 1199 String name = "res:" + resources.getResourceName(resid); 1200 return sGlobals.mService.hasNamedWallpaper(name); 1201 } catch (RemoteException e) { 1202 throw e.rethrowFromSystemServer(); 1203 } 1204 } 1205 1206 /** 1207 * Returns the desired minimum width for the wallpaper. Callers of 1208 * {@link #setBitmap(android.graphics.Bitmap)} or 1209 * {@link #setStream(java.io.InputStream)} should check this value 1210 * beforehand to make sure the supplied wallpaper respects the desired 1211 * minimum width. 1212 * 1213 * If the returned value is <= 0, the caller should use the width of 1214 * the default display instead. 1215 * 1216 * @return The desired minimum width for the wallpaper. This value should 1217 * be honored by applications that set the wallpaper but it is not 1218 * mandatory. 1219 */ getDesiredMinimumWidth()1220 public int getDesiredMinimumWidth() { 1221 if (sGlobals.mService == null) { 1222 Log.w(TAG, "WallpaperService not running"); 1223 throw new RuntimeException(new DeadSystemException()); 1224 } 1225 try { 1226 return sGlobals.mService.getWidthHint(); 1227 } catch (RemoteException e) { 1228 throw e.rethrowFromSystemServer(); 1229 } 1230 } 1231 1232 /** 1233 * Returns the desired minimum height for the wallpaper. Callers of 1234 * {@link #setBitmap(android.graphics.Bitmap)} or 1235 * {@link #setStream(java.io.InputStream)} should check this value 1236 * beforehand to make sure the supplied wallpaper respects the desired 1237 * minimum height. 1238 * 1239 * If the returned value is <= 0, the caller should use the height of 1240 * the default display instead. 1241 * 1242 * @return The desired minimum height for the wallpaper. This value should 1243 * be honored by applications that set the wallpaper but it is not 1244 * mandatory. 1245 */ getDesiredMinimumHeight()1246 public int getDesiredMinimumHeight() { 1247 if (sGlobals.mService == null) { 1248 Log.w(TAG, "WallpaperService not running"); 1249 throw new RuntimeException(new DeadSystemException()); 1250 } 1251 try { 1252 return sGlobals.mService.getHeightHint(); 1253 } catch (RemoteException e) { 1254 throw e.rethrowFromSystemServer(); 1255 } 1256 } 1257 1258 /** 1259 * For use only by the current home application, to specify the size of 1260 * wallpaper it would like to use. This allows such applications to have 1261 * a virtual wallpaper that is larger than the physical screen, matching 1262 * the size of their workspace. 1263 * 1264 * <p>Note developers, who don't seem to be reading this. This is 1265 * for <em>home apps</em> to tell what size wallpaper they would like. 1266 * Nobody else should be calling this! Certainly not other non-home 1267 * apps that change the wallpaper. Those apps are supposed to 1268 * <b>retrieve</b> the suggested size so they can construct a wallpaper 1269 * that matches it. 1270 * 1271 * <p>This method requires the caller to hold the permission 1272 * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}. 1273 * 1274 * @param minimumWidth Desired minimum width 1275 * @param minimumHeight Desired minimum height 1276 */ suggestDesiredDimensions(int minimumWidth, int minimumHeight)1277 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { 1278 try { 1279 /** 1280 * The framework makes no attempt to limit the window size 1281 * to the maximum texture size. Any window larger than this 1282 * cannot be composited. 1283 * 1284 * Read maximum texture size from system property and scale down 1285 * minimumWidth and minimumHeight accordingly. 1286 */ 1287 int maximumTextureSize; 1288 try { 1289 maximumTextureSize = SystemProperties.getInt("sys.max_texture_size", 0); 1290 } catch (Exception e) { 1291 maximumTextureSize = 0; 1292 } 1293 1294 if (maximumTextureSize > 0) { 1295 if ((minimumWidth > maximumTextureSize) || 1296 (minimumHeight > maximumTextureSize)) { 1297 float aspect = (float)minimumHeight / (float)minimumWidth; 1298 if (minimumWidth > minimumHeight) { 1299 minimumWidth = maximumTextureSize; 1300 minimumHeight = (int)((minimumWidth * aspect) + 0.5); 1301 } else { 1302 minimumHeight = maximumTextureSize; 1303 minimumWidth = (int)((minimumHeight / aspect) + 0.5); 1304 } 1305 } 1306 } 1307 1308 if (sGlobals.mService == null) { 1309 Log.w(TAG, "WallpaperService not running"); 1310 throw new RuntimeException(new DeadSystemException()); 1311 } else { 1312 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight, 1313 mContext.getOpPackageName()); 1314 } 1315 } catch (RemoteException e) { 1316 throw e.rethrowFromSystemServer(); 1317 } 1318 } 1319 1320 /** 1321 * Specify extra padding that the wallpaper should have outside of the display. 1322 * That is, the given padding supplies additional pixels the wallpaper should extend 1323 * outside of the display itself. 1324 * @param padding The number of pixels the wallpaper should extend beyond the display, 1325 * on its left, top, right, and bottom sides. 1326 * @hide 1327 */ 1328 @SystemApi setDisplayPadding(Rect padding)1329 public void setDisplayPadding(Rect padding) { 1330 try { 1331 if (sGlobals.mService == null) { 1332 Log.w(TAG, "WallpaperService not running"); 1333 throw new RuntimeException(new DeadSystemException()); 1334 } else { 1335 sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName()); 1336 } 1337 } catch (RemoteException e) { 1338 throw e.rethrowFromSystemServer(); 1339 } 1340 } 1341 1342 /** 1343 * Apply a raw offset to the wallpaper window. Should only be used in 1344 * combination with {@link #setDisplayPadding(android.graphics.Rect)} when you 1345 * have ensured that the wallpaper will extend outside of the display area so that 1346 * it can be moved without leaving part of the display uncovered. 1347 * @param x The offset, in pixels, to apply to the left edge. 1348 * @param y The offset, in pixels, to apply to the top edge. 1349 * @hide 1350 */ 1351 @SystemApi setDisplayOffset(IBinder windowToken, int x, int y)1352 public void setDisplayOffset(IBinder windowToken, int x, int y) { 1353 try { 1354 //Log.v(TAG, "Sending new wallpaper display offsets from app..."); 1355 WindowManagerGlobal.getWindowSession().setWallpaperDisplayOffset( 1356 windowToken, x, y); 1357 //Log.v(TAG, "...app returning after sending display offset!"); 1358 } catch (RemoteException e) { 1359 throw e.rethrowFromSystemServer(); 1360 } 1361 } 1362 1363 /** 1364 * Clear the wallpaper. 1365 * 1366 * @hide 1367 */ 1368 @SystemApi clearWallpaper()1369 public void clearWallpaper() { 1370 clearWallpaper(FLAG_SYSTEM, mContext.getUserId()); 1371 } 1372 1373 /** 1374 * Clear the wallpaper for a specific user. The caller must hold the 1375 * INTERACT_ACROSS_USERS_FULL permission to clear another user's 1376 * wallpaper. 1377 * @hide 1378 */ 1379 @SystemApi clearWallpaper(@etWallpaperFlags int which, int userId)1380 public void clearWallpaper(@SetWallpaperFlags int which, int userId) { 1381 if (sGlobals.mService == null) { 1382 Log.w(TAG, "WallpaperService not running"); 1383 throw new RuntimeException(new DeadSystemException()); 1384 } 1385 try { 1386 sGlobals.mService.clearWallpaper(mContext.getOpPackageName(), which, userId); 1387 } catch (RemoteException e) { 1388 throw e.rethrowFromSystemServer(); 1389 } 1390 } 1391 1392 /** 1393 * Set the live wallpaper. 1394 * 1395 * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT 1396 * permission. 1397 * 1398 * @hide 1399 */ 1400 @SystemApi setWallpaperComponent(ComponentName name)1401 public boolean setWallpaperComponent(ComponentName name) { 1402 if (sGlobals.mService == null) { 1403 Log.w(TAG, "WallpaperService not running"); 1404 throw new RuntimeException(new DeadSystemException()); 1405 } 1406 try { 1407 sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName()); 1408 return true; 1409 } catch (RemoteException e) { 1410 throw e.rethrowFromSystemServer(); 1411 } 1412 } 1413 1414 /** 1415 * Set the display position of the current wallpaper within any larger space, when 1416 * that wallpaper is visible behind the given window. The X and Y offsets 1417 * are floating point numbers ranging from 0 to 1, representing where the 1418 * wallpaper should be positioned within the screen space. These only 1419 * make sense when the wallpaper is larger than the display. 1420 * 1421 * @param windowToken The window who these offsets should be associated 1422 * with, as returned by {@link android.view.View#getWindowToken() 1423 * View.getWindowToken()}. 1424 * @param xOffset The offset along the X dimension, from 0 to 1. 1425 * @param yOffset The offset along the Y dimension, from 0 to 1. 1426 */ setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset)1427 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { 1428 try { 1429 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 1430 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 1431 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); 1432 //Log.v(TAG, "...app returning after sending offsets!"); 1433 } catch (RemoteException e) { 1434 throw e.rethrowFromSystemServer(); 1435 } 1436 } 1437 1438 /** 1439 * For applications that use multiple virtual screens showing a wallpaper, 1440 * specify the step size between virtual screens. For example, if the 1441 * launcher has 3 virtual screens, it would specify an xStep of 0.5, 1442 * since the X offset for those screens are 0.0, 0.5 and 1.0 1443 * @param xStep The X offset delta from one screen to the next one 1444 * @param yStep The Y offset delta from one screen to the next one 1445 */ setWallpaperOffsetSteps(float xStep, float yStep)1446 public void setWallpaperOffsetSteps(float xStep, float yStep) { 1447 mWallpaperXStep = xStep; 1448 mWallpaperYStep = yStep; 1449 } 1450 1451 /** 1452 * Send an arbitrary command to the current active wallpaper. 1453 * 1454 * @param windowToken The window who these offsets should be associated 1455 * with, as returned by {@link android.view.View#getWindowToken() 1456 * View.getWindowToken()}. 1457 * @param action Name of the command to perform. This must be a scoped 1458 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT". 1459 * @param x Arbitrary integer argument based on command. 1460 * @param y Arbitrary integer argument based on command. 1461 * @param z Arbitrary integer argument based on command. 1462 * @param extras Optional additional information for the command, or null. 1463 */ sendWallpaperCommand(IBinder windowToken, String action, int x, int y, int z, Bundle extras)1464 public void sendWallpaperCommand(IBinder windowToken, String action, 1465 int x, int y, int z, Bundle extras) { 1466 try { 1467 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 1468 WindowManagerGlobal.getWindowSession().sendWallpaperCommand( 1469 windowToken, action, x, y, z, extras, false); 1470 //Log.v(TAG, "...app returning after sending offsets!"); 1471 } catch (RemoteException e) { 1472 throw e.rethrowFromSystemServer(); 1473 } 1474 } 1475 1476 /** 1477 * Returns whether wallpapers are supported for the calling user. If this function returns 1478 * {@code false}, any attempts to changing the wallpaper will have no effect, 1479 * and any attempt to obtain of the wallpaper will return {@code null}. 1480 */ isWallpaperSupported()1481 public boolean isWallpaperSupported() { 1482 if (sGlobals.mService == null) { 1483 Log.w(TAG, "WallpaperService not running"); 1484 throw new RuntimeException(new DeadSystemException()); 1485 } else { 1486 try { 1487 return sGlobals.mService.isWallpaperSupported(mContext.getOpPackageName()); 1488 } catch (RemoteException e) { 1489 throw e.rethrowFromSystemServer(); 1490 } 1491 } 1492 } 1493 1494 /** 1495 * Returns whether the calling package is allowed to set the wallpaper for the calling user. 1496 * If this function returns {@code false}, any attempts to change the wallpaper will have 1497 * no effect. Always returns {@code true} for device owner and profile owner. 1498 * 1499 * @see android.os.UserManager#DISALLOW_SET_WALLPAPER 1500 */ isSetWallpaperAllowed()1501 public boolean isSetWallpaperAllowed() { 1502 if (sGlobals.mService == null) { 1503 Log.w(TAG, "WallpaperService not running"); 1504 throw new RuntimeException(new DeadSystemException()); 1505 } else { 1506 try { 1507 return sGlobals.mService.isSetWallpaperAllowed(mContext.getOpPackageName()); 1508 } catch (RemoteException e) { 1509 throw e.rethrowFromSystemServer(); 1510 } 1511 } 1512 } 1513 1514 /** 1515 * Clear the offsets previously associated with this window through 1516 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts 1517 * the window to its default state, where it does not cause the wallpaper 1518 * to scroll from whatever its last offsets were. 1519 * 1520 * @param windowToken The window who these offsets should be associated 1521 * with, as returned by {@link android.view.View#getWindowToken() 1522 * View.getWindowToken()}. 1523 */ clearWallpaperOffsets(IBinder windowToken)1524 public void clearWallpaperOffsets(IBinder windowToken) { 1525 try { 1526 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 1527 windowToken, -1, -1, -1, -1); 1528 } catch (RemoteException e) { 1529 throw e.rethrowFromSystemServer(); 1530 } 1531 } 1532 1533 /** 1534 * Remove any currently set system wallpaper, reverting to the system's built-in 1535 * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 1536 * is broadcast. 1537 * 1538 * <p>This method requires the caller to hold the permission 1539 * {@link android.Manifest.permission#SET_WALLPAPER}. 1540 * 1541 * @throws IOException If an error occurs reverting to the built-in 1542 * wallpaper. 1543 */ clear()1544 public void clear() throws IOException { 1545 setStream(openDefaultWallpaper(mContext, FLAG_SYSTEM), null, false); 1546 } 1547 1548 /** 1549 * Remove one or more currently set wallpapers, reverting to the system default 1550 * display for each one. If {@link #FLAG_SYSTEM} is set in the {@code which} 1551 * parameter, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} will be broadcast 1552 * upon success. 1553 * 1554 * @param which A bitwise combination of {@link #FLAG_SYSTEM} or 1555 * {@link #FLAG_LOCK} 1556 * @throws IOException If an error occurs reverting to the built-in wallpaper. 1557 */ clear(@etWallpaperFlags int which)1558 public void clear(@SetWallpaperFlags int which) throws IOException { 1559 if ((which & FLAG_SYSTEM) != 0) { 1560 clear(); 1561 } 1562 if ((which & FLAG_LOCK) != 0) { 1563 clearWallpaper(FLAG_LOCK, mContext.getUserId()); 1564 } 1565 } 1566 1567 /** 1568 * Open stream representing the default static image wallpaper. 1569 * 1570 * If the device defines no default wallpaper of the requested kind, 1571 * {@code null} is returned. 1572 * 1573 * @hide 1574 */ openDefaultWallpaper(Context context, @SetWallpaperFlags int which)1575 public static InputStream openDefaultWallpaper(Context context, @SetWallpaperFlags int which) { 1576 final String whichProp; 1577 final int defaultResId; 1578 if (which == FLAG_LOCK) { 1579 /* Factory-default lock wallpapers are not yet supported 1580 whichProp = PROP_LOCK_WALLPAPER; 1581 defaultResId = com.android.internal.R.drawable.default_lock_wallpaper; 1582 */ 1583 return null; 1584 } else { 1585 whichProp = PROP_WALLPAPER; 1586 defaultResId = com.android.internal.R.drawable.default_wallpaper; 1587 } 1588 final String path = SystemProperties.get(whichProp); 1589 if (!TextUtils.isEmpty(path)) { 1590 final File file = new File(path); 1591 if (file.exists()) { 1592 try { 1593 return new FileInputStream(file); 1594 } catch (IOException e) { 1595 // Ignored, fall back to platform default below 1596 } 1597 } 1598 } 1599 try { 1600 return context.getResources().openRawResource(defaultResId); 1601 } catch (NotFoundException e) { 1602 // no default defined for this device; this is not a failure 1603 } 1604 return null; 1605 } 1606 1607 /** 1608 * Return {@link ComponentName} of the default live wallpaper, or 1609 * {@code null} if none is defined. 1610 * 1611 * @hide 1612 */ getDefaultWallpaperComponent(Context context)1613 public static ComponentName getDefaultWallpaperComponent(Context context) { 1614 String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT); 1615 if (!TextUtils.isEmpty(flat)) { 1616 final ComponentName cn = ComponentName.unflattenFromString(flat); 1617 if (cn != null) { 1618 return cn; 1619 } 1620 } 1621 1622 flat = context.getString(com.android.internal.R.string.default_wallpaper_component); 1623 if (!TextUtils.isEmpty(flat)) { 1624 final ComponentName cn = ComponentName.unflattenFromString(flat); 1625 if (cn != null) { 1626 return cn; 1627 } 1628 } 1629 1630 return null; 1631 } 1632 1633 /** 1634 * Register a callback for lock wallpaper observation. Only the OS may use this. 1635 * 1636 * @return true on success; false on error. 1637 * @hide 1638 */ setLockWallpaperCallback(IWallpaperManagerCallback callback)1639 public boolean setLockWallpaperCallback(IWallpaperManagerCallback callback) { 1640 if (sGlobals.mService == null) { 1641 Log.w(TAG, "WallpaperService not running"); 1642 throw new RuntimeException(new DeadSystemException()); 1643 } 1644 1645 try { 1646 return sGlobals.mService.setLockWallpaperCallback(callback); 1647 } catch (RemoteException e) { 1648 throw e.rethrowFromSystemServer(); 1649 } 1650 } 1651 1652 /** 1653 * Is the current system wallpaper eligible for backup? 1654 * 1655 * Only the OS itself may use this method. 1656 * @hide 1657 */ isWallpaperBackupEligible()1658 public boolean isWallpaperBackupEligible() { 1659 if (sGlobals.mService == null) { 1660 Log.w(TAG, "WallpaperService not running"); 1661 throw new RuntimeException(new DeadSystemException()); 1662 } 1663 try { 1664 return sGlobals.mService.isWallpaperBackupEligible(mContext.getUserId()); 1665 } catch (RemoteException e) { 1666 Log.e(TAG, "Exception querying wallpaper backup eligibility: " + e.getMessage()); 1667 } 1668 return false; 1669 } 1670 1671 // Private completion callback for setWallpaper() synchronization 1672 private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub { 1673 final CountDownLatch mLatch; 1674 WallpaperSetCompletion()1675 public WallpaperSetCompletion() { 1676 mLatch = new CountDownLatch(1); 1677 } 1678 waitForCompletion()1679 public void waitForCompletion() { 1680 try { 1681 mLatch.await(30, TimeUnit.SECONDS); 1682 } catch (InterruptedException e) { 1683 // This might be legit: the crop may take a very long time. Don't sweat 1684 // it in that case; we are okay with display lagging behind in order to 1685 // keep the caller from locking up indeterminately. 1686 } 1687 } 1688 1689 @Override onWallpaperChanged()1690 public void onWallpaperChanged() throws RemoteException { 1691 mLatch.countDown(); 1692 } 1693 } 1694 } 1695