• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Context;
20 import android.content.Intent;
21 import android.content.res.Resources;
22 import android.graphics.Bitmap;
23 import android.graphics.BitmapFactory;
24 import android.graphics.Canvas;
25 import android.graphics.ColorFilter;
26 import android.graphics.Paint;
27 import android.graphics.PixelFormat;
28 import android.graphics.PorterDuff;
29 import android.graphics.PorterDuffXfermode;
30 import android.graphics.Rect;
31 import android.graphics.drawable.BitmapDrawable;
32 import android.graphics.drawable.Drawable;
33 import android.os.Binder;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.ParcelFileDescriptor;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.util.DisplayMetrics;
43 import android.util.Log;
44 import android.view.ViewRootImpl;
45 
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 
50 /**
51  * Provides access to the system wallpaper. With WallpaperManager, you can
52  * get the current wallpaper, get the desired dimensions for the wallpaper, set
53  * the wallpaper, and more. Get an instance of WallpaperManager with
54  * {@link #getInstance(android.content.Context) getInstance()}.
55  */
56 public class WallpaperManager {
57     private static String TAG = "WallpaperManager";
58     private static boolean DEBUG = false;
59     private float mWallpaperXStep = -1;
60     private float mWallpaperYStep = -1;
61 
62     /**
63      * Launch an activity for the user to pick the current global live
64      * wallpaper.
65      */
66     public static final String ACTION_LIVE_WALLPAPER_CHOOSER
67             = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
68 
69     /**
70      * Directly launch live wallpaper preview, allowing the user to immediately
71      * confirm to switch to a specific live wallpaper.  You must specify
72      * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of
73      * a live wallpaper component that is to be shown.
74      */
75     public static final String ACTION_CHANGE_LIVE_WALLPAPER
76             = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER";
77 
78     /**
79      * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the
80      * ComponentName of a live wallpaper that should be shown as a preview,
81      * for the user to confirm.
82      */
83     public static final String EXTRA_LIVE_WALLPAPER_COMPONENT
84             = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
85 
86     /**
87      * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER}
88      * which allows them to provide a custom large icon associated with this action.
89      */
90     public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
91 
92     /**
93      * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
94      * host when the user taps on an empty area (not performing an action
95      * in the host).  The x and y arguments are the location of the tap in
96      * screen coordinates.
97      */
98     public static final String COMMAND_TAP = "android.wallpaper.tap";
99 
100     /**
101      * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
102      * host when the user releases a secondary pointer on an empty area
103      * (not performing an action in the host).  The x and y arguments are
104      * the location of the secondary tap in screen coordinates.
105      */
106     public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
107 
108     /**
109      * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
110      * host when the user drops an object into an area of the host.  The x
111      * and y arguments are the location of the drop.
112      */
113     public static final String COMMAND_DROP = "android.home.drop";
114 
115     private final Context mContext;
116 
117     /**
118      * Special drawable that draws a wallpaper as fast as possible.  Assumes
119      * no scaling or placement off (0,0) of the wallpaper (this should be done
120      * at the time the bitmap is loaded).
121      */
122     static class FastBitmapDrawable extends Drawable {
123         private final Bitmap mBitmap;
124         private final int mWidth;
125         private final int mHeight;
126         private int mDrawLeft;
127         private int mDrawTop;
128         private final Paint mPaint;
129 
FastBitmapDrawable(Bitmap bitmap)130         private FastBitmapDrawable(Bitmap bitmap) {
131             mBitmap = bitmap;
132             mWidth = bitmap.getWidth();
133             mHeight = bitmap.getHeight();
134 
135             setBounds(0, 0, mWidth, mHeight);
136 
137             mPaint = new Paint();
138             mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
139         }
140 
141         @Override
draw(Canvas canvas)142         public void draw(Canvas canvas) {
143             canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
144         }
145 
146         @Override
getOpacity()147         public int getOpacity() {
148             return PixelFormat.OPAQUE;
149         }
150 
151         @Override
setBounds(int left, int top, int right, int bottom)152         public void setBounds(int left, int top, int right, int bottom) {
153             mDrawLeft = left + (right-left - mWidth) / 2;
154             mDrawTop = top + (bottom-top - mHeight) / 2;
155         }
156 
157         @Override
setAlpha(int alpha)158         public void setAlpha(int alpha) {
159             throw new UnsupportedOperationException("Not supported with this drawable");
160         }
161 
162         @Override
setColorFilter(ColorFilter cf)163         public void setColorFilter(ColorFilter cf) {
164             throw new UnsupportedOperationException("Not supported with this drawable");
165         }
166 
167         @Override
setDither(boolean dither)168         public void setDither(boolean dither) {
169             throw new UnsupportedOperationException("Not supported with this drawable");
170         }
171 
172         @Override
setFilterBitmap(boolean filter)173         public void setFilterBitmap(boolean filter) {
174             throw new UnsupportedOperationException("Not supported with this drawable");
175         }
176 
177         @Override
getIntrinsicWidth()178         public int getIntrinsicWidth() {
179             return mWidth;
180         }
181 
182         @Override
getIntrinsicHeight()183         public int getIntrinsicHeight() {
184             return mHeight;
185         }
186 
187         @Override
getMinimumWidth()188         public int getMinimumWidth() {
189             return mWidth;
190         }
191 
192         @Override
getMinimumHeight()193         public int getMinimumHeight() {
194             return mHeight;
195         }
196     }
197 
198     static class Globals extends IWallpaperManagerCallback.Stub {
199         private IWallpaperManager mService;
200         private Bitmap mWallpaper;
201         private Bitmap mDefaultWallpaper;
202 
203         private static final int MSG_CLEAR_WALLPAPER = 1;
204 
205         private final Handler mHandler;
206 
Globals(Looper looper)207         Globals(Looper looper) {
208             IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
209             mService = IWallpaperManager.Stub.asInterface(b);
210             mHandler = new Handler(looper) {
211                 @Override
212                 public void handleMessage(Message msg) {
213                     switch (msg.what) {
214                         case MSG_CLEAR_WALLPAPER:
215                             synchronized (this) {
216                                 mWallpaper = null;
217                                 mDefaultWallpaper = null;
218                             }
219                             break;
220                     }
221                 }
222             };
223         }
224 
onWallpaperChanged()225         public void onWallpaperChanged() {
226             /* The wallpaper has changed but we shouldn't eagerly load the
227              * wallpaper as that would be inefficient. Reset the cached wallpaper
228              * to null so if the user requests the wallpaper again then we'll
229              * fetch it.
230              */
231             mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
232         }
233 
peekWallpaperBitmap(Context context, boolean returnDefault)234         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
235             synchronized (this) {
236                 if (mWallpaper != null) {
237                     return mWallpaper;
238                 }
239                 if (mDefaultWallpaper != null) {
240                     return mDefaultWallpaper;
241                 }
242                 mWallpaper = null;
243                 try {
244                     mWallpaper = getCurrentWallpaperLocked();
245                 } catch (OutOfMemoryError e) {
246                     Log.w(TAG, "No memory load current wallpaper", e);
247                 }
248                 if (returnDefault) {
249                     if (mWallpaper == null) {
250                         mDefaultWallpaper = getDefaultWallpaperLocked(context);
251                         return mDefaultWallpaper;
252                     } else {
253                         mDefaultWallpaper = null;
254                     }
255                 }
256                 return mWallpaper;
257             }
258         }
259 
forgetLoadedWallpaper()260         public void forgetLoadedWallpaper() {
261             synchronized (this) {
262                 mWallpaper = null;
263                 mDefaultWallpaper = null;
264             }
265         }
266 
getCurrentWallpaperLocked()267         private Bitmap getCurrentWallpaperLocked() {
268             try {
269                 Bundle params = new Bundle();
270                 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
271                 if (fd != null) {
272                     int width = params.getInt("width", 0);
273                     int height = params.getInt("height", 0);
274 
275                     try {
276                         BitmapFactory.Options options = new BitmapFactory.Options();
277                         Bitmap bm = BitmapFactory.decodeFileDescriptor(
278                                 fd.getFileDescriptor(), null, options);
279                         return generateBitmap(bm, width, height);
280                     } catch (OutOfMemoryError e) {
281                         Log.w(TAG, "Can't decode file", e);
282                     } finally {
283                         try {
284                             fd.close();
285                         } catch (IOException e) {
286                             // Ignore
287                         }
288                     }
289                 }
290             } catch (RemoteException e) {
291                 // Ignore
292             }
293             return null;
294         }
295 
getDefaultWallpaperLocked(Context context)296         private Bitmap getDefaultWallpaperLocked(Context context) {
297             try {
298                 InputStream is = context.getResources().openRawResource(
299                         com.android.internal.R.drawable.default_wallpaper);
300                 if (is != null) {
301                     int width = mService.getWidthHint();
302                     int height = mService.getHeightHint();
303 
304                     try {
305                         BitmapFactory.Options options = new BitmapFactory.Options();
306                         Bitmap bm = BitmapFactory.decodeStream(is, null, options);
307                         return generateBitmap(bm, width, height);
308                     } catch (OutOfMemoryError e) {
309                         Log.w(TAG, "Can't decode stream", e);
310                     } finally {
311                         try {
312                             is.close();
313                         } catch (IOException e) {
314                             // Ignore
315                         }
316                     }
317                 }
318             } catch (RemoteException e) {
319                 // Ignore
320             }
321             return null;
322         }
323     }
324 
325     private static final Object sSync = new Object[0];
326     private static Globals sGlobals;
327 
initGlobals(Looper looper)328     static void initGlobals(Looper looper) {
329         synchronized (sSync) {
330             if (sGlobals == null) {
331                 sGlobals = new Globals(looper);
332             }
333         }
334     }
335 
WallpaperManager(Context context, Handler handler)336     /*package*/ WallpaperManager(Context context, Handler handler) {
337         mContext = context;
338         initGlobals(context.getMainLooper());
339     }
340 
341     /**
342      * Retrieve a WallpaperManager associated with the given Context.
343      */
getInstance(Context context)344     public static WallpaperManager getInstance(Context context) {
345         return (WallpaperManager)context.getSystemService(
346                 Context.WALLPAPER_SERVICE);
347     }
348 
349     /** @hide */
getIWallpaperManager()350     public IWallpaperManager getIWallpaperManager() {
351         return sGlobals.mService;
352     }
353 
354     /**
355      * Retrieve the current system wallpaper; if
356      * no wallpaper is set, the system default wallpaper is returned.
357      * This is returned as an
358      * abstract Drawable that you can install in a View to display whatever
359      * wallpaper the user has currently set.
360      *
361      * @return Returns a Drawable object that will draw the wallpaper.
362      */
getDrawable()363     public Drawable getDrawable() {
364         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
365         if (bm != null) {
366             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
367             dr.setDither(false);
368             return dr;
369         }
370         return null;
371     }
372 
373     /**
374      * Retrieve the current system wallpaper; if there is no wallpaper set,
375      * a null pointer is returned. This is returned as an
376      * abstract Drawable that you can install in a View to display whatever
377      * wallpaper the user has currently set.
378      *
379      * @return Returns a Drawable object that will draw the wallpaper or a
380      * null pointer if these is none.
381      */
peekDrawable()382     public Drawable peekDrawable() {
383         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
384         if (bm != null) {
385             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
386             dr.setDither(false);
387             return dr;
388         }
389         return null;
390     }
391 
392     /**
393      * Like {@link #getDrawable()}, but the returned Drawable has a number
394      * of limitations to reduce its overhead as much as possible. It will
395      * never scale the wallpaper (only centering it if the requested bounds
396      * do match the bitmap bounds, which should not be typical), doesn't
397      * allow setting an alpha, color filter, or other attributes, etc.  The
398      * bounds of the returned drawable will be initialized to the same bounds
399      * as the wallpaper, so normally you will not need to touch it.  The
400      * drawable also assumes that it will be used in a context running in
401      * the same density as the screen (not in density compatibility mode).
402      *
403      * @return Returns a Drawable object that will draw the wallpaper.
404      */
getFastDrawable()405     public Drawable getFastDrawable() {
406         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
407         if (bm != null) {
408             return new FastBitmapDrawable(bm);
409         }
410         return null;
411     }
412 
413     /**
414      * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
415      * a null pointer is returned.
416      *
417      * @return Returns an optimized Drawable object that will draw the
418      * wallpaper or a null pointer if these is none.
419      */
peekFastDrawable()420     public Drawable peekFastDrawable() {
421         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
422         if (bm != null) {
423             return new FastBitmapDrawable(bm);
424         }
425         return null;
426     }
427 
428     /**
429      * Like {@link #getDrawable()} but returns a Bitmap.
430      *
431      * @hide
432      */
getBitmap()433     public Bitmap getBitmap() {
434         return sGlobals.peekWallpaperBitmap(mContext, true);
435     }
436 
437     /**
438      * Remove all internal references to the last loaded wallpaper.  Useful
439      * for apps that want to reduce memory usage when they only temporarily
440      * need to have the wallpaper.  After calling, the next request for the
441      * wallpaper will require reloading it again from disk.
442      */
forgetLoadedWallpaper()443     public void forgetLoadedWallpaper() {
444         sGlobals.forgetLoadedWallpaper();
445     }
446 
447     /**
448      * If the current wallpaper is a live wallpaper component, return the
449      * information about that wallpaper.  Otherwise, if it is a static image,
450      * simply return null.
451      */
getWallpaperInfo()452     public WallpaperInfo getWallpaperInfo() {
453         try {
454             if (sGlobals.mService == null) {
455                 Log.w(TAG, "WallpaperService not running");
456                 return null;
457             } else {
458                 return sGlobals.mService.getWallpaperInfo();
459             }
460         } catch (RemoteException e) {
461             return null;
462         }
463     }
464 
465     /**
466      * Change the current system wallpaper to the bitmap in the given resource.
467      * The resource is opened as a raw data stream and copied into the
468      * wallpaper; it must be a valid PNG or JPEG image.  On success, the intent
469      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
470      *
471      * <p>This method requires the caller to hold the permission
472      * {@link android.Manifest.permission#SET_WALLPAPER}.
473      *
474      * @param resid The bitmap to save.
475      *
476      * @throws IOException If an error occurs reverting to the default
477      * wallpaper.
478      */
setResource(int resid)479     public void setResource(int resid) throws IOException {
480         if (sGlobals.mService == null) {
481             Log.w(TAG, "WallpaperService not running");
482             return;
483         }
484         try {
485             Resources resources = mContext.getResources();
486             /* Set the wallpaper to the default values */
487             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
488                     "res:" + resources.getResourceName(resid));
489             if (fd != null) {
490                 FileOutputStream fos = null;
491                 try {
492                     fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
493                     setWallpaper(resources.openRawResource(resid), fos);
494                 } finally {
495                     if (fos != null) {
496                         fos.close();
497                     }
498                 }
499             }
500         } catch (RemoteException e) {
501             // Ignore
502         }
503     }
504 
505     /**
506      * Change the current system wallpaper to a bitmap.  The given bitmap is
507      * converted to a PNG and stored as the wallpaper.  On success, the intent
508      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
509      *
510      * <p>This method requires the caller to hold the permission
511      * {@link android.Manifest.permission#SET_WALLPAPER}.
512      *
513      * @param bitmap The bitmap to save.
514      *
515      * @throws IOException If an error occurs reverting to the default
516      * wallpaper.
517      */
setBitmap(Bitmap bitmap)518     public void setBitmap(Bitmap bitmap) throws IOException {
519         if (sGlobals.mService == null) {
520             Log.w(TAG, "WallpaperService not running");
521             return;
522         }
523         try {
524             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
525             if (fd == null) {
526                 return;
527             }
528             FileOutputStream fos = null;
529             try {
530                 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
531                 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
532             } finally {
533                 if (fos != null) {
534                     fos.close();
535                 }
536             }
537         } catch (RemoteException e) {
538             // Ignore
539         }
540     }
541 
542     /**
543      * Change the current system wallpaper to a specific byte stream.  The
544      * give InputStream is copied into persistent storage and will now be
545      * used as the wallpaper.  Currently it must be either a JPEG or PNG
546      * image.  On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
547      * is broadcast.
548      *
549      * <p>This method requires the caller to hold the permission
550      * {@link android.Manifest.permission#SET_WALLPAPER}.
551      *
552      * @param data A stream containing the raw data to install as a wallpaper.
553      *
554      * @throws IOException If an error occurs reverting to the default
555      * wallpaper.
556      */
setStream(InputStream data)557     public void setStream(InputStream data) throws IOException {
558         if (sGlobals.mService == null) {
559             Log.w(TAG, "WallpaperService not running");
560             return;
561         }
562         try {
563             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
564             if (fd == null) {
565                 return;
566             }
567             FileOutputStream fos = null;
568             try {
569                 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
570                 setWallpaper(data, fos);
571             } finally {
572                 if (fos != null) {
573                     fos.close();
574                 }
575             }
576         } catch (RemoteException e) {
577             // Ignore
578         }
579     }
580 
setWallpaper(InputStream data, FileOutputStream fos)581     private void setWallpaper(InputStream data, FileOutputStream fos)
582             throws IOException {
583         byte[] buffer = new byte[32768];
584         int amt;
585         while ((amt=data.read(buffer)) > 0) {
586             fos.write(buffer, 0, amt);
587         }
588     }
589 
590     /**
591      * Returns the desired minimum width for the wallpaper. Callers of
592      * {@link #setBitmap(android.graphics.Bitmap)} or
593      * {@link #setStream(java.io.InputStream)} should check this value
594      * beforehand to make sure the supplied wallpaper respects the desired
595      * minimum width.
596      *
597      * If the returned value is <= 0, the caller should use the width of
598      * the default display instead.
599      *
600      * @return The desired minimum width for the wallpaper. This value should
601      * be honored by applications that set the wallpaper but it is not
602      * mandatory.
603      */
getDesiredMinimumWidth()604     public int getDesiredMinimumWidth() {
605         if (sGlobals.mService == null) {
606             Log.w(TAG, "WallpaperService not running");
607             return 0;
608         }
609         try {
610             return sGlobals.mService.getWidthHint();
611         } catch (RemoteException e) {
612             // Shouldn't happen!
613             return 0;
614         }
615     }
616 
617     /**
618      * Returns the desired minimum height for the wallpaper. Callers of
619      * {@link #setBitmap(android.graphics.Bitmap)} or
620      * {@link #setStream(java.io.InputStream)} should check this value
621      * beforehand to make sure the supplied wallpaper respects the desired
622      * minimum height.
623      *
624      * If the returned value is <= 0, the caller should use the height of
625      * the default display instead.
626      *
627      * @return The desired minimum height for the wallpaper. This value should
628      * be honored by applications that set the wallpaper but it is not
629      * mandatory.
630      */
getDesiredMinimumHeight()631     public int getDesiredMinimumHeight() {
632         if (sGlobals.mService == null) {
633             Log.w(TAG, "WallpaperService not running");
634             return 0;
635         }
636         try {
637             return sGlobals.mService.getHeightHint();
638         } catch (RemoteException e) {
639             // Shouldn't happen!
640             return 0;
641         }
642     }
643 
644     /**
645      * For use only by the current home application, to specify the size of
646      * wallpaper it would like to use.  This allows such applications to have
647      * a virtual wallpaper that is larger than the physical screen, matching
648      * the size of their workspace.
649      *
650      * <p>Note developers, who don't seem to be reading this.  This is
651      * for <em>home screens</em> to tell what size wallpaper they would like.
652      * Nobody else should be calling this!  Certainly not other non-home-screen
653      * apps that change the wallpaper.  Those apps are supposed to
654      * <b>retrieve</b> the suggested size so they can construct a wallpaper
655      * that matches it.
656      *
657      * <p>This method requires the caller to hold the permission
658      * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}.
659      *
660      * @param minimumWidth Desired minimum width
661      * @param minimumHeight Desired minimum height
662      */
suggestDesiredDimensions(int minimumWidth, int minimumHeight)663     public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
664         try {
665             if (sGlobals.mService == null) {
666                 Log.w(TAG, "WallpaperService not running");
667             } else {
668                 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
669             }
670         } catch (RemoteException e) {
671             // Ignore
672         }
673     }
674 
675     /**
676      * Set the position of the current wallpaper within any larger space, when
677      * that wallpaper is visible behind the given window.  The X and Y offsets
678      * are floating point numbers ranging from 0 to 1, representing where the
679      * wallpaper should be positioned within the screen space.  These only
680      * make sense when the wallpaper is larger than the screen.
681      *
682      * @param windowToken The window who these offsets should be associated
683      * with, as returned by {@link android.view.View#getWindowToken()
684      * View.getWindowToken()}.
685      * @param xOffset The offset along the X dimension, from 0 to 1.
686      * @param yOffset The offset along the Y dimension, from 0 to 1.
687      */
setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset)688     public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
689         try {
690             //Log.v(TAG, "Sending new wallpaper offsets from app...");
691             ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
692                     windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
693             //Log.v(TAG, "...app returning after sending offsets!");
694         } catch (RemoteException e) {
695             // Ignore.
696         }
697     }
698 
699     /**
700      * For applications that use multiple virtual screens showing a wallpaper,
701      * specify the step size between virtual screens. For example, if the
702      * launcher has 3 virtual screens, it would specify an xStep of 0.5,
703      * since the X offset for those screens are 0.0, 0.5 and 1.0
704      * @param xStep The X offset delta from one screen to the next one
705      * @param yStep The Y offset delta from one screen to the next one
706      */
setWallpaperOffsetSteps(float xStep, float yStep)707     public void setWallpaperOffsetSteps(float xStep, float yStep) {
708         mWallpaperXStep = xStep;
709         mWallpaperYStep = yStep;
710     }
711 
712     /**
713      * Send an arbitrary command to the current active wallpaper.
714      *
715      * @param windowToken The window who these offsets should be associated
716      * with, as returned by {@link android.view.View#getWindowToken()
717      * View.getWindowToken()}.
718      * @param action Name of the command to perform.  This must be a scoped
719      * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
720      * @param x Arbitrary integer argument based on command.
721      * @param y Arbitrary integer argument based on command.
722      * @param z Arbitrary integer argument based on command.
723      * @param extras Optional additional information for the command, or null.
724      */
sendWallpaperCommand(IBinder windowToken, String action, int x, int y, int z, Bundle extras)725     public void sendWallpaperCommand(IBinder windowToken, String action,
726             int x, int y, int z, Bundle extras) {
727         try {
728             //Log.v(TAG, "Sending new wallpaper offsets from app...");
729             ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
730                     windowToken, action, x, y, z, extras, false);
731             //Log.v(TAG, "...app returning after sending offsets!");
732         } catch (RemoteException e) {
733             // Ignore.
734         }
735     }
736 
737     /**
738      * Clear the offsets previously associated with this window through
739      * {@link #setWallpaperOffsets(IBinder, float, float)}.  This reverts
740      * the window to its default state, where it does not cause the wallpaper
741      * to scroll from whatever its last offsets were.
742      *
743      * @param windowToken The window who these offsets should be associated
744      * with, as returned by {@link android.view.View#getWindowToken()
745      * View.getWindowToken()}.
746      */
clearWallpaperOffsets(IBinder windowToken)747     public void clearWallpaperOffsets(IBinder windowToken) {
748         try {
749             ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
750                     windowToken, -1, -1, -1, -1);
751         } catch (RemoteException e) {
752             // Ignore.
753         }
754     }
755 
756     /**
757      * Remove any currently set wallpaper, reverting to the system's default
758      * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
759      * is broadcast.
760      *
761      * <p>This method requires the caller to hold the permission
762      * {@link android.Manifest.permission#SET_WALLPAPER}.
763      *
764      * @throws IOException If an error occurs reverting to the default
765      * wallpaper.
766      */
clear()767     public void clear() throws IOException {
768         setResource(com.android.internal.R.drawable.default_wallpaper);
769     }
770 
generateBitmap(Bitmap bm, int width, int height)771     static Bitmap generateBitmap(Bitmap bm, int width, int height) {
772         if (bm == null) {
773             return null;
774         }
775 
776         bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
777 
778         if (width <= 0 || height <= 0
779                 || (bm.getWidth() == width && bm.getHeight() == height)) {
780             return bm;
781         }
782 
783         // This is the final bitmap we want to return.
784         try {
785             Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
786             newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
787 
788             Canvas c = new Canvas(newbm);
789             Rect targetRect = new Rect();
790             targetRect.right = bm.getWidth();
791             targetRect.bottom = bm.getHeight();
792 
793             int deltaw = width - targetRect.right;
794             int deltah = height - targetRect.bottom;
795 
796             if (deltaw > 0 || deltah > 0) {
797                 // We need to scale up so it covers the entire area.
798                 float scale;
799                 if (deltaw > deltah) {
800                     scale = width / (float)targetRect.right;
801                 } else {
802                     scale = height / (float)targetRect.bottom;
803                 }
804                 targetRect.right = (int)(targetRect.right*scale);
805                 targetRect.bottom = (int)(targetRect.bottom*scale);
806                 deltaw = width - targetRect.right;
807                 deltah = height - targetRect.bottom;
808             }
809 
810             targetRect.offset(deltaw/2, deltah/2);
811 
812             Paint paint = new Paint();
813             paint.setFilterBitmap(true);
814             paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
815             c.drawBitmap(bm, null, targetRect, paint);
816 
817             bm.recycle();
818             c.setBitmap(null);
819             return newbm;
820         } catch (OutOfMemoryError e) {
821             Log.w(TAG, "Can't generate default bitmap", e);
822             return bm;
823         }
824     }
825 }
826