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