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