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