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.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.content.res.Resources; 25 import android.graphics.Bitmap; 26 import android.graphics.BitmapFactory; 27 import android.graphics.BitmapRegionDecoder; 28 import android.graphics.Canvas; 29 import android.graphics.ColorFilter; 30 import android.graphics.Matrix; 31 import android.graphics.Paint; 32 import android.graphics.PixelFormat; 33 import android.graphics.PorterDuff; 34 import android.graphics.PorterDuffXfermode; 35 import android.graphics.Rect; 36 import android.graphics.RectF; 37 import android.graphics.drawable.BitmapDrawable; 38 import android.graphics.drawable.Drawable; 39 import android.net.Uri; 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.os.ParcelFileDescriptor; 46 import android.os.RemoteException; 47 import android.os.ServiceManager; 48 import android.util.Log; 49 import android.view.WindowManagerGlobal; 50 51 import java.io.BufferedInputStream; 52 import java.io.FileOutputStream; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.util.List; 56 57 /** 58 * Provides access to the system wallpaper. With WallpaperManager, you can 59 * get the current wallpaper, get the desired dimensions for the wallpaper, set 60 * the wallpaper, and more. Get an instance of WallpaperManager with 61 * {@link #getInstance(android.content.Context) getInstance()}. 62 */ 63 public class WallpaperManager { 64 private static String TAG = "WallpaperManager"; 65 private static boolean DEBUG = false; 66 private float mWallpaperXStep = -1; 67 private float mWallpaperYStep = -1; 68 69 /** 70 * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct 71 * an intent; instead, use {@link #getCropAndSetWallpaperIntent}. 72 * <p>Input: {@link Intent#getData} is the URI of the image to crop and set as wallpaper. 73 * <p>Output: RESULT_OK if user decided to crop/set the wallpaper, RESULT_CANCEL otherwise 74 * Activities that support this intent should specify a MIME filter of "image/*" 75 */ 76 public static final String ACTION_CROP_AND_SET_WALLPAPER = 77 "android.service.wallpaper.CROP_AND_SET_WALLPAPER"; 78 79 /** 80 * Launch an activity for the user to pick the current global live 81 * wallpaper. 82 */ 83 public static final String ACTION_LIVE_WALLPAPER_CHOOSER 84 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER"; 85 86 /** 87 * Directly launch live wallpaper preview, allowing the user to immediately 88 * confirm to switch to a specific live wallpaper. You must specify 89 * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of 90 * a live wallpaper component that is to be shown. 91 */ 92 public static final String ACTION_CHANGE_LIVE_WALLPAPER 93 = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER"; 94 95 /** 96 * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the 97 * ComponentName of a live wallpaper that should be shown as a preview, 98 * for the user to confirm. 99 */ 100 public static final String EXTRA_LIVE_WALLPAPER_COMPONENT 101 = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT"; 102 103 /** 104 * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER} 105 * which allows them to provide a custom large icon associated with this action. 106 */ 107 public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview"; 108 109 /** 110 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 111 * host when the user taps on an empty area (not performing an action 112 * in the host). The x and y arguments are the location of the tap in 113 * screen coordinates. 114 */ 115 public static final String COMMAND_TAP = "android.wallpaper.tap"; 116 117 /** 118 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 119 * host when the user releases a secondary pointer on an empty area 120 * (not performing an action in the host). The x and y arguments are 121 * the location of the secondary tap in screen coordinates. 122 */ 123 public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap"; 124 125 /** 126 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper 127 * host when the user drops an object into an area of the host. The x 128 * and y arguments are the location of the drop. 129 */ 130 public static final String COMMAND_DROP = "android.home.drop"; 131 132 private final Context mContext; 133 134 /** 135 * Special drawable that draws a wallpaper as fast as possible. Assumes 136 * no scaling or placement off (0,0) of the wallpaper (this should be done 137 * at the time the bitmap is loaded). 138 */ 139 static class FastBitmapDrawable extends Drawable { 140 private final Bitmap mBitmap; 141 private final int mWidth; 142 private final int mHeight; 143 private int mDrawLeft; 144 private int mDrawTop; 145 private final Paint mPaint; 146 147 private FastBitmapDrawable(Bitmap bitmap) { 148 mBitmap = bitmap; 149 mWidth = bitmap.getWidth(); 150 mHeight = bitmap.getHeight(); 151 152 setBounds(0, 0, mWidth, mHeight); 153 154 mPaint = new Paint(); 155 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 156 } 157 158 @Override 159 public void draw(Canvas canvas) { 160 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint); 161 } 162 163 @Override 164 public int getOpacity() { 165 return PixelFormat.OPAQUE; 166 } 167 168 @Override 169 public void setBounds(int left, int top, int right, int bottom) { 170 mDrawLeft = left + (right-left - mWidth) / 2; 171 mDrawTop = top + (bottom-top - mHeight) / 2; 172 } 173 174 @Override 175 public void setAlpha(int alpha) { 176 throw new UnsupportedOperationException("Not supported with this drawable"); 177 } 178 179 @Override 180 public void setColorFilter(ColorFilter cf) { 181 throw new UnsupportedOperationException("Not supported with this drawable"); 182 } 183 184 @Override 185 public void setDither(boolean dither) { 186 throw new UnsupportedOperationException("Not supported with this drawable"); 187 } 188 189 @Override 190 public void setFilterBitmap(boolean filter) { 191 throw new UnsupportedOperationException("Not supported with this drawable"); 192 } 193 194 @Override 195 public int getIntrinsicWidth() { 196 return mWidth; 197 } 198 199 @Override 200 public int getIntrinsicHeight() { 201 return mHeight; 202 } 203 204 @Override 205 public int getMinimumWidth() { 206 return mWidth; 207 } 208 209 @Override 210 public int getMinimumHeight() { 211 return mHeight; 212 } 213 } 214 215 static class Globals extends IWallpaperManagerCallback.Stub { 216 private IWallpaperManager mService; 217 private Bitmap mWallpaper; 218 private Bitmap mDefaultWallpaper; 219 220 private static final int MSG_CLEAR_WALLPAPER = 1; 221 222 private final Handler mHandler; 223 224 Globals(Looper looper) { 225 IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); 226 mService = IWallpaperManager.Stub.asInterface(b); 227 mHandler = new Handler(looper) { 228 @Override 229 public void handleMessage(Message msg) { 230 switch (msg.what) { 231 case MSG_CLEAR_WALLPAPER: 232 synchronized (this) { 233 mWallpaper = null; 234 mDefaultWallpaper = null; 235 } 236 break; 237 } 238 } 239 }; 240 } 241 242 public void onWallpaperChanged() { 243 /* The wallpaper has changed but we shouldn't eagerly load the 244 * wallpaper as that would be inefficient. Reset the cached wallpaper 245 * to null so if the user requests the wallpaper again then we'll 246 * fetch it. 247 */ 248 mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER); 249 } 250 251 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { 252 synchronized (this) { 253 if (mWallpaper != null) { 254 return mWallpaper; 255 } 256 if (mDefaultWallpaper != null) { 257 return mDefaultWallpaper; 258 } 259 mWallpaper = null; 260 try { 261 mWallpaper = getCurrentWallpaperLocked(context); 262 } catch (OutOfMemoryError e) { 263 Log.w(TAG, "No memory load current wallpaper", e); 264 } 265 if (returnDefault) { 266 if (mWallpaper == null) { 267 mDefaultWallpaper = getDefaultWallpaperLocked(context); 268 return mDefaultWallpaper; 269 } else { 270 mDefaultWallpaper = null; 271 } 272 } 273 return mWallpaper; 274 } 275 } 276 277 public void forgetLoadedWallpaper() { 278 synchronized (this) { 279 mWallpaper = null; 280 mDefaultWallpaper = null; 281 mHandler.removeMessages(MSG_CLEAR_WALLPAPER); 282 } 283 } 284 285 private Bitmap getCurrentWallpaperLocked(Context context) { 286 try { 287 Bundle params = new Bundle(); 288 ParcelFileDescriptor fd = mService.getWallpaper(this, params); 289 if (fd != null) { 290 int width = params.getInt("width", 0); 291 int height = params.getInt("height", 0); 292 293 try { 294 BitmapFactory.Options options = new BitmapFactory.Options(); 295 return BitmapFactory.decodeFileDescriptor( 296 fd.getFileDescriptor(), null, options); 297 } catch (OutOfMemoryError e) { 298 Log.w(TAG, "Can't decode file", e); 299 } finally { 300 try { 301 fd.close(); 302 } catch (IOException e) { 303 // Ignore 304 } 305 } 306 } 307 } catch (RemoteException e) { 308 // Ignore 309 } 310 return null; 311 } 312 313 private Bitmap getDefaultWallpaperLocked(Context context) { 314 try { 315 InputStream is = context.getResources().openRawResource( 316 com.android.internal.R.drawable.default_wallpaper); 317 if (is != null) { 318 int width = mService.getWidthHint(); 319 int height = mService.getHeightHint(); 320 321 try { 322 BitmapFactory.Options options = new BitmapFactory.Options(); 323 return BitmapFactory.decodeStream(is, null, options); 324 } catch (OutOfMemoryError e) { 325 Log.w(TAG, "Can't decode stream", e); 326 } finally { 327 try { 328 is.close(); 329 } catch (IOException e) { 330 // Ignore 331 } 332 } 333 } 334 } catch (RemoteException e) { 335 // Ignore 336 } 337 return null; 338 } 339 } 340 341 private static final Object sSync = new Object[0]; 342 private static Globals sGlobals; 343 344 static void initGlobals(Looper looper) { 345 synchronized (sSync) { 346 if (sGlobals == null) { 347 sGlobals = new Globals(looper); 348 } 349 } 350 } 351 352 /*package*/ WallpaperManager(Context context, Handler handler) { 353 mContext = context; 354 initGlobals(context.getMainLooper()); 355 } 356 357 /** 358 * Retrieve a WallpaperManager associated with the given Context. 359 */ 360 public static WallpaperManager getInstance(Context context) { 361 return (WallpaperManager)context.getSystemService( 362 Context.WALLPAPER_SERVICE); 363 } 364 365 /** @hide */ 366 public IWallpaperManager getIWallpaperManager() { 367 return sGlobals.mService; 368 } 369 370 /** 371 * Retrieve the current system wallpaper; if 372 * no wallpaper is set, the system built-in static wallpaper is returned. 373 * This is returned as an 374 * abstract Drawable that you can install in a View to display whatever 375 * wallpaper the user has currently set. 376 * 377 * @return Returns a Drawable object that will draw the wallpaper. 378 */ 379 public Drawable getDrawable() { 380 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true); 381 if (bm != null) { 382 Drawable dr = new BitmapDrawable(mContext.getResources(), bm); 383 dr.setDither(false); 384 return dr; 385 } 386 return null; 387 } 388 389 /** 390 * Returns a drawable for the system built-in static wallpaper . 391 * 392 */ 393 public Drawable getBuiltInDrawable() { 394 return getBuiltInDrawable(0, 0, false, 0, 0); 395 } 396 397 /** 398 * Returns a drawable for the system built-in static wallpaper. Based on the parameters, the 399 * drawable can be cropped and scaled 400 * 401 * @param outWidth The width of the returned drawable 402 * @param outWidth The height of the returned drawable 403 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it 404 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image; 405 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned 406 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image; 407 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned 408 * 409 */ 410 public Drawable getBuiltInDrawable(int outWidth, int outHeight, 411 boolean scaleToFit, float horizontalAlignment, float verticalAlignment) { 412 if (sGlobals.mService == null) { 413 Log.w(TAG, "WallpaperService not running"); 414 return null; 415 } 416 Resources resources = mContext.getResources(); 417 horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment)); 418 verticalAlignment = Math.max(0, Math.min(1, verticalAlignment)); 419 420 InputStream is = new BufferedInputStream( 421 resources.openRawResource(com.android.internal.R.drawable.default_wallpaper)); 422 423 if (is == null) { 424 Log.e(TAG, "default wallpaper input stream is null"); 425 return null; 426 } else { 427 if (outWidth <= 0 || outHeight <= 0) { 428 Bitmap fullSize = BitmapFactory.decodeStream(is, null, null); 429 return new BitmapDrawable(resources, fullSize); 430 } else { 431 int inWidth; 432 int inHeight; 433 { 434 BitmapFactory.Options options = new BitmapFactory.Options(); 435 options.inJustDecodeBounds = true; 436 BitmapFactory.decodeStream(is, null, options); 437 if (options.outWidth != 0 && options.outHeight != 0) { 438 inWidth = options.outWidth; 439 inHeight = options.outHeight; 440 } else { 441 Log.e(TAG, "default wallpaper dimensions are 0"); 442 return null; 443 } 444 } 445 446 is = new BufferedInputStream(resources.openRawResource( 447 com.android.internal.R.drawable.default_wallpaper)); 448 449 RectF cropRectF; 450 451 outWidth = Math.min(inWidth, outWidth); 452 outHeight = Math.min(inHeight, outHeight); 453 if (scaleToFit) { 454 cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight, 455 horizontalAlignment, verticalAlignment); 456 } else { 457 float left = (inWidth - outWidth) * horizontalAlignment; 458 float right = left + outWidth; 459 float top = (inHeight - outHeight) * verticalAlignment; 460 float bottom = top + outHeight; 461 cropRectF = new RectF(left, top, right, bottom); 462 } 463 Rect roundedTrueCrop = new Rect(); 464 cropRectF.roundOut(roundedTrueCrop); 465 466 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 467 Log.w(TAG, "crop has bad values for full size image"); 468 return null; 469 } 470 471 // See how much we're reducing the size of the image 472 int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth, 473 roundedTrueCrop.height() / outHeight); 474 475 // Attempt to open a region decoder 476 BitmapRegionDecoder decoder = null; 477 try { 478 decoder = BitmapRegionDecoder.newInstance(is, true); 479 } catch (IOException e) { 480 Log.w(TAG, "cannot open region decoder for default wallpaper"); 481 } 482 483 Bitmap crop = null; 484 if (decoder != null) { 485 // Do region decoding to get crop bitmap 486 BitmapFactory.Options options = new BitmapFactory.Options(); 487 if (scaleDownSampleSize > 1) { 488 options.inSampleSize = scaleDownSampleSize; 489 } 490 crop = decoder.decodeRegion(roundedTrueCrop, options); 491 decoder.recycle(); 492 } 493 494 if (crop == null) { 495 // BitmapRegionDecoder has failed, try to crop in-memory 496 is = new BufferedInputStream(resources.openRawResource( 497 com.android.internal.R.drawable.default_wallpaper)); 498 Bitmap fullSize = null; 499 if (is != null) { 500 BitmapFactory.Options options = new BitmapFactory.Options(); 501 if (scaleDownSampleSize > 1) { 502 options.inSampleSize = scaleDownSampleSize; 503 } 504 fullSize = BitmapFactory.decodeStream(is, null, options); 505 } 506 if (fullSize != null) { 507 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 508 roundedTrueCrop.top, roundedTrueCrop.width(), 509 roundedTrueCrop.height()); 510 } 511 } 512 513 if (crop == null) { 514 Log.w(TAG, "cannot decode default wallpaper"); 515 return null; 516 } 517 518 // Scale down if necessary 519 if (outWidth > 0 && outHeight > 0 && 520 (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) { 521 Matrix m = new Matrix(); 522 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); 523 RectF returnRect = new RectF(0, 0, outWidth, outHeight); 524 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 525 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 526 (int) returnRect.height(), Bitmap.Config.ARGB_8888); 527 if (tmp != null) { 528 Canvas c = new Canvas(tmp); 529 Paint p = new Paint(); 530 p.setFilterBitmap(true); 531 c.drawBitmap(crop, m, p); 532 crop = tmp; 533 } 534 } 535 536 return new BitmapDrawable(resources, crop); 537 } 538 } 539 } 540 541 private static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight, 542 float horizontalAlignment, float verticalAlignment) { 543 RectF cropRect = new RectF(); 544 // Get a crop rect that will fit this 545 if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 546 cropRect.top = 0; 547 cropRect.bottom = inHeight; 548 float cropWidth = outWidth * (inHeight / (float) outHeight); 549 cropRect.left = (inWidth - cropWidth) * horizontalAlignment; 550 cropRect.right = cropRect.left + cropWidth; 551 } else { 552 cropRect.left = 0; 553 cropRect.right = inWidth; 554 float cropHeight = outHeight * (inWidth / (float) outWidth); 555 cropRect.top = (inHeight - cropHeight) * verticalAlignment; 556 cropRect.bottom = cropRect.top + cropHeight; 557 } 558 return cropRect; 559 } 560 561 /** 562 * Retrieve the current system wallpaper; if there is no wallpaper set, 563 * a null pointer is returned. This is returned as an 564 * abstract Drawable that you can install in a View to display whatever 565 * wallpaper the user has currently set. 566 * 567 * @return Returns a Drawable object that will draw the wallpaper or a 568 * null pointer if these is none. 569 */ 570 public Drawable peekDrawable() { 571 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false); 572 if (bm != null) { 573 Drawable dr = new BitmapDrawable(mContext.getResources(), bm); 574 dr.setDither(false); 575 return dr; 576 } 577 return null; 578 } 579 580 /** 581 * Like {@link #getDrawable()}, but the returned Drawable has a number 582 * of limitations to reduce its overhead as much as possible. It will 583 * never scale the wallpaper (only centering it if the requested bounds 584 * do match the bitmap bounds, which should not be typical), doesn't 585 * allow setting an alpha, color filter, or other attributes, etc. The 586 * bounds of the returned drawable will be initialized to the same bounds 587 * as the wallpaper, so normally you will not need to touch it. The 588 * drawable also assumes that it will be used in a context running in 589 * the same density as the screen (not in density compatibility mode). 590 * 591 * @return Returns a Drawable object that will draw the wallpaper. 592 */ 593 public Drawable getFastDrawable() { 594 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true); 595 if (bm != null) { 596 return new FastBitmapDrawable(bm); 597 } 598 return null; 599 } 600 601 /** 602 * Like {@link #getFastDrawable()}, but if there is no wallpaper set, 603 * a null pointer is returned. 604 * 605 * @return Returns an optimized Drawable object that will draw the 606 * wallpaper or a null pointer if these is none. 607 */ 608 public Drawable peekFastDrawable() { 609 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false); 610 if (bm != null) { 611 return new FastBitmapDrawable(bm); 612 } 613 return null; 614 } 615 616 /** 617 * Like {@link #getDrawable()} but returns a Bitmap. 618 * 619 * @hide 620 */ 621 public Bitmap getBitmap() { 622 return sGlobals.peekWallpaperBitmap(mContext, true); 623 } 624 625 /** 626 * Remove all internal references to the last loaded wallpaper. Useful 627 * for apps that want to reduce memory usage when they only temporarily 628 * need to have the wallpaper. After calling, the next request for the 629 * wallpaper will require reloading it again from disk. 630 */ 631 public void forgetLoadedWallpaper() { 632 sGlobals.forgetLoadedWallpaper(); 633 } 634 635 /** 636 * If the current wallpaper is a live wallpaper component, return the 637 * information about that wallpaper. Otherwise, if it is a static image, 638 * simply return null. 639 */ 640 public WallpaperInfo getWallpaperInfo() { 641 try { 642 if (sGlobals.mService == null) { 643 Log.w(TAG, "WallpaperService not running"); 644 return null; 645 } else { 646 return sGlobals.mService.getWallpaperInfo(); 647 } 648 } catch (RemoteException e) { 649 return null; 650 } 651 } 652 653 /** 654 * Gets an Intent that will launch an activity that crops the given 655 * image and sets the device's wallpaper. If there is a default HOME activity 656 * that supports cropping wallpapers, it will be preferred as the default. 657 * Use this method instead of directly creating a {@link #ACTION_CROP_AND_SET_WALLPAPER} 658 * intent. 659 * 660 * @param imageUri The image URI that will be set in the intent. The must be a content 661 * URI and its provider must resolve its type to "image/*" 662 * 663 * @throws IllegalArgumentException if the URI is not a content URI or its MIME type is 664 * not "image/*" 665 */ 666 public Intent getCropAndSetWallpaperIntent(Uri imageUri) { 667 if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) { 668 throw new IllegalArgumentException("Image URI must be of the " 669 + ContentResolver.SCHEME_CONTENT + " scheme type"); 670 } 671 672 final PackageManager packageManager = mContext.getPackageManager(); 673 Intent cropAndSetWallpaperIntent = 674 new Intent(ACTION_CROP_AND_SET_WALLPAPER, imageUri); 675 cropAndSetWallpaperIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 676 677 // Find out if the default HOME activity supports CROP_AND_SET_WALLPAPER 678 Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); 679 ResolveInfo resolvedHome = packageManager.resolveActivity(homeIntent, 680 PackageManager.MATCH_DEFAULT_ONLY); 681 if (resolvedHome != null) { 682 cropAndSetWallpaperIntent.setPackage(resolvedHome.activityInfo.packageName); 683 684 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 685 cropAndSetWallpaperIntent, 0); 686 if (cropAppList.size() > 0) { 687 return cropAndSetWallpaperIntent; 688 } 689 } 690 691 // fallback crop activity 692 cropAndSetWallpaperIntent.setPackage("com.android.wallpapercropper"); 693 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities( 694 cropAndSetWallpaperIntent, 0); 695 if (cropAppList.size() > 0) { 696 return cropAndSetWallpaperIntent; 697 } 698 // If the URI is not of the right type, or for some reason the system wallpaper 699 // cropper doesn't exist, return null 700 throw new IllegalArgumentException("Cannot use passed URI to set wallpaper; " + 701 "check that the type returned by ContentProvider matches image/*"); 702 } 703 704 /** 705 * Change the current system wallpaper to the bitmap in the given resource. 706 * The resource is opened as a raw data stream and copied into the 707 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent 708 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 709 * 710 * <p>This method requires the caller to hold the permission 711 * {@link android.Manifest.permission#SET_WALLPAPER}. 712 * 713 * @param resid The bitmap to save. 714 * 715 * @throws IOException If an error occurs reverting to the built-in 716 * wallpaper. 717 */ 718 public void setResource(int resid) throws IOException { 719 if (sGlobals.mService == null) { 720 Log.w(TAG, "WallpaperService not running"); 721 return; 722 } 723 try { 724 Resources resources = mContext.getResources(); 725 /* Set the wallpaper to the default values */ 726 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( 727 "res:" + resources.getResourceName(resid)); 728 if (fd != null) { 729 FileOutputStream fos = null; 730 try { 731 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 732 setWallpaper(resources.openRawResource(resid), fos); 733 } finally { 734 if (fos != null) { 735 fos.close(); 736 } 737 } 738 } 739 } catch (RemoteException e) { 740 // Ignore 741 } 742 } 743 744 /** 745 * Change the current system wallpaper to a bitmap. The given bitmap is 746 * converted to a PNG and stored as the wallpaper. On success, the intent 747 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. 748 * 749 * <p>This method requires the caller to hold the permission 750 * {@link android.Manifest.permission#SET_WALLPAPER}. 751 * 752 * @param bitmap The bitmap to save. 753 * 754 * @throws IOException If an error occurs reverting to the built-in 755 * wallpaper. 756 */ 757 public void setBitmap(Bitmap bitmap) throws IOException { 758 if (sGlobals.mService == null) { 759 Log.w(TAG, "WallpaperService not running"); 760 return; 761 } 762 try { 763 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); 764 if (fd == null) { 765 return; 766 } 767 FileOutputStream fos = null; 768 try { 769 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 770 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos); 771 } finally { 772 if (fos != null) { 773 fos.close(); 774 } 775 } 776 } catch (RemoteException e) { 777 // Ignore 778 } 779 } 780 781 /** 782 * Change the current system wallpaper to a specific byte stream. The 783 * give InputStream is copied into persistent storage and will now be 784 * used as the wallpaper. Currently it must be either a JPEG or PNG 785 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 786 * is broadcast. 787 * 788 * <p>This method requires the caller to hold the permission 789 * {@link android.Manifest.permission#SET_WALLPAPER}. 790 * 791 * @param data A stream containing the raw data to install as a wallpaper. 792 * 793 * @throws IOException If an error occurs reverting to the built-in 794 * wallpaper. 795 */ 796 public void setStream(InputStream data) throws IOException { 797 if (sGlobals.mService == null) { 798 Log.w(TAG, "WallpaperService not running"); 799 return; 800 } 801 try { 802 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null); 803 if (fd == null) { 804 return; 805 } 806 FileOutputStream fos = null; 807 try { 808 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); 809 setWallpaper(data, fos); 810 } finally { 811 if (fos != null) { 812 fos.close(); 813 } 814 } 815 } catch (RemoteException e) { 816 // Ignore 817 } 818 } 819 820 private void setWallpaper(InputStream data, FileOutputStream fos) 821 throws IOException { 822 byte[] buffer = new byte[32768]; 823 int amt; 824 while ((amt=data.read(buffer)) > 0) { 825 fos.write(buffer, 0, amt); 826 } 827 } 828 829 /** 830 * Return whether any users are currently set to use the wallpaper 831 * with the given resource ID. That is, their wallpaper has been 832 * set through {@link #setResource(int)} with the same resource id. 833 */ 834 public boolean hasResourceWallpaper(int resid) { 835 if (sGlobals.mService == null) { 836 Log.w(TAG, "WallpaperService not running"); 837 return false; 838 } 839 try { 840 Resources resources = mContext.getResources(); 841 String name = "res:" + resources.getResourceName(resid); 842 return sGlobals.mService.hasNamedWallpaper(name); 843 } catch (RemoteException e) { 844 return false; 845 } 846 } 847 848 /** 849 * Returns the desired minimum width for the wallpaper. Callers of 850 * {@link #setBitmap(android.graphics.Bitmap)} or 851 * {@link #setStream(java.io.InputStream)} should check this value 852 * beforehand to make sure the supplied wallpaper respects the desired 853 * minimum width. 854 * 855 * If the returned value is <= 0, the caller should use the width of 856 * the default display instead. 857 * 858 * @return The desired minimum width for the wallpaper. This value should 859 * be honored by applications that set the wallpaper but it is not 860 * mandatory. 861 */ 862 public int getDesiredMinimumWidth() { 863 if (sGlobals.mService == null) { 864 Log.w(TAG, "WallpaperService not running"); 865 return 0; 866 } 867 try { 868 return sGlobals.mService.getWidthHint(); 869 } catch (RemoteException e) { 870 // Shouldn't happen! 871 return 0; 872 } 873 } 874 875 /** 876 * Returns the desired minimum height for the wallpaper. Callers of 877 * {@link #setBitmap(android.graphics.Bitmap)} or 878 * {@link #setStream(java.io.InputStream)} should check this value 879 * beforehand to make sure the supplied wallpaper respects the desired 880 * minimum height. 881 * 882 * If the returned value is <= 0, the caller should use the height of 883 * the default display instead. 884 * 885 * @return The desired minimum height for the wallpaper. This value should 886 * be honored by applications that set the wallpaper but it is not 887 * mandatory. 888 */ 889 public int getDesiredMinimumHeight() { 890 if (sGlobals.mService == null) { 891 Log.w(TAG, "WallpaperService not running"); 892 return 0; 893 } 894 try { 895 return sGlobals.mService.getHeightHint(); 896 } catch (RemoteException e) { 897 // Shouldn't happen! 898 return 0; 899 } 900 } 901 902 /** 903 * For use only by the current home application, to specify the size of 904 * wallpaper it would like to use. This allows such applications to have 905 * a virtual wallpaper that is larger than the physical screen, matching 906 * the size of their workspace. 907 * 908 * <p>Note developers, who don't seem to be reading this. This is 909 * for <em>home screens</em> to tell what size wallpaper they would like. 910 * Nobody else should be calling this! Certainly not other non-home-screen 911 * apps that change the wallpaper. Those apps are supposed to 912 * <b>retrieve</b> the suggested size so they can construct a wallpaper 913 * that matches it. 914 * 915 * <p>This method requires the caller to hold the permission 916 * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}. 917 * 918 * @param minimumWidth Desired minimum width 919 * @param minimumHeight Desired minimum height 920 */ 921 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { 922 try { 923 if (sGlobals.mService == null) { 924 Log.w(TAG, "WallpaperService not running"); 925 } else { 926 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight); 927 } 928 } catch (RemoteException e) { 929 // Ignore 930 } 931 } 932 933 /** 934 * Set the position of the current wallpaper within any larger space, when 935 * that wallpaper is visible behind the given window. The X and Y offsets 936 * are floating point numbers ranging from 0 to 1, representing where the 937 * wallpaper should be positioned within the screen space. These only 938 * make sense when the wallpaper is larger than the screen. 939 * 940 * @param windowToken The window who these offsets should be associated 941 * with, as returned by {@link android.view.View#getWindowToken() 942 * View.getWindowToken()}. 943 * @param xOffset The offset along the X dimension, from 0 to 1. 944 * @param yOffset The offset along the Y dimension, from 0 to 1. 945 */ 946 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { 947 try { 948 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 949 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 950 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); 951 //Log.v(TAG, "...app returning after sending offsets!"); 952 } catch (RemoteException e) { 953 // Ignore. 954 } 955 } 956 957 /** 958 * For applications that use multiple virtual screens showing a wallpaper, 959 * specify the step size between virtual screens. For example, if the 960 * launcher has 3 virtual screens, it would specify an xStep of 0.5, 961 * since the X offset for those screens are 0.0, 0.5 and 1.0 962 * @param xStep The X offset delta from one screen to the next one 963 * @param yStep The Y offset delta from one screen to the next one 964 */ 965 public void setWallpaperOffsetSteps(float xStep, float yStep) { 966 mWallpaperXStep = xStep; 967 mWallpaperYStep = yStep; 968 } 969 970 /** 971 * Send an arbitrary command to the current active wallpaper. 972 * 973 * @param windowToken The window who these offsets should be associated 974 * with, as returned by {@link android.view.View#getWindowToken() 975 * View.getWindowToken()}. 976 * @param action Name of the command to perform. This must be a scoped 977 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT". 978 * @param x Arbitrary integer argument based on command. 979 * @param y Arbitrary integer argument based on command. 980 * @param z Arbitrary integer argument based on command. 981 * @param extras Optional additional information for the command, or null. 982 */ 983 public void sendWallpaperCommand(IBinder windowToken, String action, 984 int x, int y, int z, Bundle extras) { 985 try { 986 //Log.v(TAG, "Sending new wallpaper offsets from app..."); 987 WindowManagerGlobal.getWindowSession().sendWallpaperCommand( 988 windowToken, action, x, y, z, extras, false); 989 //Log.v(TAG, "...app returning after sending offsets!"); 990 } catch (RemoteException e) { 991 // Ignore. 992 } 993 } 994 995 /** 996 * Clear the offsets previously associated with this window through 997 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts 998 * the window to its default state, where it does not cause the wallpaper 999 * to scroll from whatever its last offsets were. 1000 * 1001 * @param windowToken The window who these offsets should be associated 1002 * with, as returned by {@link android.view.View#getWindowToken() 1003 * View.getWindowToken()}. 1004 */ 1005 public void clearWallpaperOffsets(IBinder windowToken) { 1006 try { 1007 WindowManagerGlobal.getWindowSession().setWallpaperPosition( 1008 windowToken, -1, -1, -1, -1); 1009 } catch (RemoteException e) { 1010 // Ignore. 1011 } 1012 } 1013 1014 /** 1015 * Remove any currently set wallpaper, reverting to the system's built-in 1016 * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} 1017 * is broadcast. 1018 * 1019 * <p>This method requires the caller to hold the permission 1020 * {@link android.Manifest.permission#SET_WALLPAPER}. 1021 * 1022 * @throws IOException If an error occurs reverting to the built-in 1023 * wallpaper. 1024 */ 1025 public void clear() throws IOException { 1026 setResource(com.android.internal.R.drawable.default_wallpaper); 1027 } 1028 } 1029