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