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