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 Handler getHandler() {
    217             return mHandler;
    218         }
    219 
    220         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
    221             synchronized (this) {
    222                 if (mWallpaper != null) {
    223                     return mWallpaper;
    224                 }
    225                 if (mDefaultWallpaper != null) {
    226                     return mDefaultWallpaper;
    227                 }
    228                 mWallpaper = null;
    229                 try {
    230                     mWallpaper = getCurrentWallpaperLocked();
    231                 } catch (OutOfMemoryError e) {
    232                     Log.w(TAG, "No memory load current wallpaper", e);
    233                 }
    234                 if (returnDefault) {
    235                     if (mWallpaper == null) {
    236                         mDefaultWallpaper = getDefaultWallpaperLocked(context);
    237                         return mDefaultWallpaper;
    238                     } else {
    239                         mDefaultWallpaper = null;
    240                     }
    241                 }
    242                 return mWallpaper;
    243             }
    244         }
    245 
    246         public void forgetLoadedWallpaper() {
    247             synchronized (this) {
    248                 mWallpaper = null;
    249                 mDefaultWallpaper = null;
    250             }
    251         }
    252 
    253         private Bitmap getCurrentWallpaperLocked() {
    254             try {
    255                 Bundle params = new Bundle();
    256                 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
    257                 if (fd != null) {
    258                     int width = params.getInt("width", 0);
    259                     int height = params.getInt("height", 0);
    260 
    261                     try {
    262                         BitmapFactory.Options options = new BitmapFactory.Options();
    263                         Bitmap bm = BitmapFactory.decodeFileDescriptor(
    264                                 fd.getFileDescriptor(), null, options);
    265                         return generateBitmap(bm, width, height);
    266                     } catch (OutOfMemoryError e) {
    267                         Log.w(TAG, "Can't decode file", e);
    268                     } finally {
    269                         try {
    270                             fd.close();
    271                         } catch (IOException e) {
    272                             // Ignore
    273                         }
    274                     }
    275                 }
    276             } catch (RemoteException e) {
    277                 // Ignore
    278             }
    279             return null;
    280         }
    281 
    282         private Bitmap getDefaultWallpaperLocked(Context context) {
    283             try {
    284                 InputStream is = context.getResources().openRawResource(
    285                         com.android.internal.R.drawable.default_wallpaper);
    286                 if (is != null) {
    287                     int width = mService.getWidthHint();
    288                     int height = mService.getHeightHint();
    289 
    290                     try {
    291                         BitmapFactory.Options options = new BitmapFactory.Options();
    292                         Bitmap bm = BitmapFactory.decodeStream(is, null, options);
    293                         return generateBitmap(bm, width, height);
    294                     } catch (OutOfMemoryError e) {
    295                         Log.w(TAG, "Can't decode stream", e);
    296                     } finally {
    297                         try {
    298                             is.close();
    299                         } catch (IOException e) {
    300                             // Ignore
    301                         }
    302                     }
    303                 }
    304             } catch (RemoteException e) {
    305                 // Ignore
    306             }
    307             return null;
    308         }
    309     }
    310 
    311     private static final Object sSync = new Object[0];
    312     private static Globals sGlobals;
    313 
    314     static void initGlobals(Looper looper) {
    315         synchronized (sSync) {
    316             if (sGlobals == null) {
    317                 sGlobals = new Globals(looper);
    318             }
    319         }
    320     }
    321 
    322     /*package*/ WallpaperManager(Context context, Handler handler) {
    323         mContext = context;
    324         initGlobals(context.getMainLooper());
    325     }
    326 
    327     /**
    328      * Retrieve a WallpaperManager associated with the given Context.
    329      */
    330     public static WallpaperManager getInstance(Context context) {
    331         return (WallpaperManager)context.getSystemService(
    332                 Context.WALLPAPER_SERVICE);
    333     }
    334 
    335     /** @hide */
    336     public IWallpaperManager getIWallpaperManager() {
    337         return sGlobals.mService;
    338     }
    339 
    340     /**
    341      * Retrieve the current system wallpaper; if
    342      * no wallpaper is set, the system default wallpaper is returned.
    343      * This is returned as an
    344      * abstract Drawable that you can install in a View to display whatever
    345      * wallpaper the user has currently set.
    346      *
    347      * @return Returns a Drawable object that will draw the wallpaper.
    348      */
    349     public Drawable getDrawable() {
    350         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
    351         if (bm != null) {
    352             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
    353             dr.setDither(false);
    354             return dr;
    355         }
    356         return null;
    357     }
    358 
    359     /**
    360      * Retrieve the current system wallpaper; if there is no wallpaper set,
    361      * a null pointer is returned. This is returned as an
    362      * abstract Drawable that you can install in a View to display whatever
    363      * wallpaper the user has currently set.
    364      *
    365      * @return Returns a Drawable object that will draw the wallpaper or a
    366      * null pointer if these is none.
    367      */
    368     public Drawable peekDrawable() {
    369         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
    370         if (bm != null) {
    371             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
    372             dr.setDither(false);
    373             return dr;
    374         }
    375         return null;
    376     }
    377 
    378     /**
    379      * Like {@link #getDrawable()}, but the returned Drawable has a number
    380      * of limitations to reduce its overhead as much as possible. It will
    381      * never scale the wallpaper (only centering it if the requested bounds
    382      * do match the bitmap bounds, which should not be typical), doesn't
    383      * allow setting an alpha, color filter, or other attributes, etc.  The
    384      * bounds of the returned drawable will be initialized to the same bounds
    385      * as the wallpaper, so normally you will not need to touch it.  The
    386      * drawable also assumes that it will be used in a context running in
    387      * the same density as the screen (not in density compatibility mode).
    388      *
    389      * @return Returns a Drawable object that will draw the wallpaper.
    390      */
    391     public Drawable getFastDrawable() {
    392         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
    393         if (bm != null) {
    394             return new FastBitmapDrawable(bm);
    395         }
    396         return null;
    397     }
    398 
    399     /**
    400      * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
    401      * a null pointer is returned.
    402      *
    403      * @return Returns an optimized Drawable object that will draw the
    404      * wallpaper or a null pointer if these is none.
    405      */
    406     public Drawable peekFastDrawable() {
    407         Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
    408         if (bm != null) {
    409             return new FastBitmapDrawable(bm);
    410         }
    411         return null;
    412     }
    413 
    414     /**
    415      * Like {@link #getDrawable()} but returns a Bitmap.
    416      *
    417      * @hide
    418      */
    419     public Bitmap getBitmap() {
    420         return sGlobals.peekWallpaperBitmap(mContext, true);
    421     }
    422 
    423     /**
    424      * Remove all internal references to the last loaded wallpaper.  Useful
    425      * for apps that want to reduce memory usage when they only temporarily
    426      * need to have the wallpaper.  After calling, the next request for the
    427      * wallpaper will require reloading it again from disk.
    428      */
    429     public void forgetLoadedWallpaper() {
    430         sGlobals.forgetLoadedWallpaper();
    431     }
    432 
    433     /**
    434      * If the current wallpaper is a live wallpaper component, return the
    435      * information about that wallpaper.  Otherwise, if it is a static image,
    436      * simply return null.
    437      */
    438     public WallpaperInfo getWallpaperInfo() {
    439         try {
    440             return sGlobals.mService.getWallpaperInfo();
    441         } catch (RemoteException e) {
    442             return null;
    443         }
    444     }
    445 
    446     /**
    447      * Change the current system wallpaper to the bitmap in the given resource.
    448      * The resource is opened as a raw data stream and copied into the
    449      * wallpaper; it must be a valid PNG or JPEG image.  On success, the intent
    450      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
    451      *
    452      * @param resid The bitmap to save.
    453      *
    454      * @throws IOException If an error occurs reverting to the default
    455      * wallpaper.
    456      */
    457     public void setResource(int resid) throws IOException {
    458         try {
    459             Resources resources = mContext.getResources();
    460             /* Set the wallpaper to the default values */
    461             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
    462                     "res:" + resources.getResourceName(resid));
    463             if (fd != null) {
    464                 FileOutputStream fos = null;
    465                 try {
    466                     fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
    467                     setWallpaper(resources.openRawResource(resid), fos);
    468                 } finally {
    469                     if (fos != null) {
    470                         fos.close();
    471                     }
    472                 }
    473             }
    474         } catch (RemoteException e) {
    475             // Ignore
    476         }
    477     }
    478 
    479     /**
    480      * Change the current system wallpaper to a bitmap.  The given bitmap is
    481      * converted to a PNG and stored as the wallpaper.  On success, the intent
    482      * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
    483      *
    484      * @param bitmap The bitmap to save.
    485      *
    486      * @throws IOException If an error occurs reverting to the default
    487      * wallpaper.
    488      */
    489     public void setBitmap(Bitmap bitmap) throws IOException {
    490         try {
    491             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
    492             if (fd == null) {
    493                 return;
    494             }
    495             FileOutputStream fos = null;
    496             try {
    497                 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
    498                 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
    499             } finally {
    500                 if (fos != null) {
    501                     fos.close();
    502                 }
    503             }
    504         } catch (RemoteException e) {
    505             // Ignore
    506         }
    507     }
    508 
    509     /**
    510      * Change the current system wallpaper to a specific byte stream.  The
    511      * give InputStream is copied into persistent storage and will now be
    512      * used as the wallpaper.  Currently it must be either a JPEG or PNG
    513      * image.  On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
    514      * is broadcast.
    515      *
    516      * @param data A stream containing the raw data to install as a wallpaper.
    517      *
    518      * @throws IOException If an error occurs reverting to the default
    519      * wallpaper.
    520      */
    521     public void setStream(InputStream data) throws IOException {
    522         try {
    523             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
    524             if (fd == null) {
    525                 return;
    526             }
    527             FileOutputStream fos = null;
    528             try {
    529                 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
    530                 setWallpaper(data, fos);
    531             } finally {
    532                 if (fos != null) {
    533                     fos.close();
    534                 }
    535             }
    536         } catch (RemoteException e) {
    537             // Ignore
    538         }
    539     }
    540 
    541     private void setWallpaper(InputStream data, FileOutputStream fos)
    542             throws IOException {
    543         byte[] buffer = new byte[32768];
    544         int amt;
    545         while ((amt=data.read(buffer)) > 0) {
    546             fos.write(buffer, 0, amt);
    547         }
    548     }
    549 
    550     /**
    551      * Returns the desired minimum width for the wallpaper. Callers of
    552      * {@link #setBitmap(android.graphics.Bitmap)} or
    553      * {@link #setStream(java.io.InputStream)} should check this value
    554      * beforehand to make sure the supplied wallpaper respects the desired
    555      * minimum width.
    556      *
    557      * If the returned value is <= 0, the caller should use the width of
    558      * the default display instead.
    559      *
    560      * @return The desired minimum width for the wallpaper. This value should
    561      * be honored by applications that set the wallpaper but it is not
    562      * mandatory.
    563      */
    564     public int getDesiredMinimumWidth() {
    565         try {
    566             return sGlobals.mService.getWidthHint();
    567         } catch (RemoteException e) {
    568             // Shouldn't happen!
    569             return 0;
    570         }
    571     }
    572 
    573     /**
    574      * Returns the desired minimum height for the wallpaper. Callers of
    575      * {@link #setBitmap(android.graphics.Bitmap)} or
    576      * {@link #setStream(java.io.InputStream)} should check this value
    577      * beforehand to make sure the supplied wallpaper respects the desired
    578      * minimum height.
    579      *
    580      * If the returned value is <= 0, the caller should use the height of
    581      * the default display instead.
    582      *
    583      * @return The desired minimum height for the wallpaper. This value should
    584      * be honored by applications that set the wallpaper but it is not
    585      * mandatory.
    586      */
    587     public int getDesiredMinimumHeight() {
    588         try {
    589             return sGlobals.mService.getHeightHint();
    590         } catch (RemoteException e) {
    591             // Shouldn't happen!
    592             return 0;
    593         }
    594     }
    595 
    596     /**
    597      * For use only by the current home application, to specify the size of
    598      * wallpaper it would like to use.  This allows such applications to have
    599      * a virtual wallpaper that is larger than the physical screen, matching
    600      * the size of their workspace.
    601      * @param minimumWidth Desired minimum width
    602      * @param minimumHeight Desired minimum height
    603      */
    604     public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
    605         try {
    606             sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
    607         } catch (RemoteException e) {
    608             // Ignore
    609         }
    610     }
    611 
    612     /**
    613      * Set the position of the current wallpaper within any larger space, when
    614      * that wallpaper is visible behind the given window.  The X and Y offsets
    615      * are floating point numbers ranging from 0 to 1, representing where the
    616      * wallpaper should be positioned within the screen space.  These only
    617      * make sense when the wallpaper is larger than the screen.
    618      *
    619      * @param windowToken The window who these offsets should be associated
    620      * with, as returned by {@link android.view.View#getWindowToken()
    621      * View.getWindowToken()}.
    622      * @param xOffset The offset along the X dimension, from 0 to 1.
    623      * @param yOffset The offset along the Y dimension, from 0 to 1.
    624      */
    625     public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
    626         final IBinder fWindowToken = windowToken;
    627         final float fXOffset = xOffset;
    628         final float fYOffset = yOffset;
    629         sGlobals.getHandler().post(new Runnable() {
    630             public void run() {
    631                 try {
    632                     //Log.v(TAG, "Sending new wallpaper offsets from app...");
    633                     ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
    634                             fWindowToken, fXOffset, fYOffset, mWallpaperXStep, mWallpaperYStep);
    635                     //Log.v(TAG, "...app returning after sending offsets!");
    636                 } catch (RemoteException e) {
    637                     // Ignore.
    638                 } catch (IllegalArgumentException e) {
    639                     // Since this is being posted, it's possible that this windowToken is no longer
    640                     // valid, for example, if setWallpaperOffsets is called just before rotation.
    641                 }
    642             }
    643         });
    644     }
    645 
    646     /**
    647      * For applications that use multiple virtual screens showing a wallpaper,
    648      * specify the step size between virtual screens. For example, if the
    649      * launcher has 3 virtual screens, it would specify an xStep of 0.5,
    650      * since the X offset for those screens are 0.0, 0.5 and 1.0
    651      * @param xStep The X offset delta from one screen to the next one
    652      * @param yStep The Y offset delta from one screen to the next one
    653      */
    654     public void setWallpaperOffsetSteps(float xStep, float yStep) {
    655         mWallpaperXStep = xStep;
    656         mWallpaperYStep = yStep;
    657     }
    658 
    659     /**
    660      * Send an arbitrary command to the current active wallpaper.
    661      *
    662      * @param windowToken The window who these offsets should be associated
    663      * with, as returned by {@link android.view.View#getWindowToken()
    664      * View.getWindowToken()}.
    665      * @param action Name of the command to perform.  This must be a scoped
    666      * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
    667      * @param x Arbitrary integer argument based on command.
    668      * @param y Arbitrary integer argument based on command.
    669      * @param z Arbitrary integer argument based on command.
    670      * @param extras Optional additional information for the command, or null.
    671      */
    672     public void sendWallpaperCommand(IBinder windowToken, String action,
    673             int x, int y, int z, Bundle extras) {
    674         try {
    675             //Log.v(TAG, "Sending new wallpaper offsets from app...");
    676             ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
    677                     windowToken, action, x, y, z, extras, false);
    678             //Log.v(TAG, "...app returning after sending offsets!");
    679         } catch (RemoteException e) {
    680             // Ignore.
    681         }
    682     }
    683 
    684     /**
    685      * Clear the offsets previously associated with this window through
    686      * {@link #setWallpaperOffsets(IBinder, float, float)}.  This reverts
    687      * the window to its default state, where it does not cause the wallpaper
    688      * to scroll from whatever its last offsets were.
    689      *
    690      * @param windowToken The window who these offsets should be associated
    691      * with, as returned by {@link android.view.View#getWindowToken()
    692      * View.getWindowToken()}.
    693      */
    694     public void clearWallpaperOffsets(IBinder windowToken) {
    695         try {
    696             ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
    697                     windowToken, -1, -1, -1, -1);
    698         } catch (RemoteException e) {
    699             // Ignore.
    700         }
    701     }
    702 
    703     /**
    704      * Remove any currently set wallpaper, reverting to the system's default
    705      * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
    706      * is broadcast.
    707      *
    708      * @throws IOException If an error occurs reverting to the default
    709      * wallpaper.
    710      */
    711     public void clear() throws IOException {
    712         setResource(com.android.internal.R.drawable.default_wallpaper);
    713     }
    714 
    715     static Bitmap generateBitmap(Bitmap bm, int width, int height) {
    716         if (bm == null) {
    717             return null;
    718         }
    719 
    720         bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
    721 
    722         if (width <= 0 || height <= 0
    723                 || (bm.getWidth() == width && bm.getHeight() == height)) {
    724             return bm;
    725         }
    726 
    727         // This is the final bitmap we want to return.
    728         try {
    729             Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    730             newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
    731 
    732             Canvas c = new Canvas(newbm);
    733             Rect targetRect = new Rect();
    734             targetRect.right = bm.getWidth();
    735             targetRect.bottom = bm.getHeight();
    736 
    737             int deltaw = width - targetRect.right;
    738             int deltah = height - targetRect.bottom;
    739 
    740             if (deltaw > 0 || deltah > 0) {
    741                 // We need to scale up so it covers the entire area.
    742                 float scale;
    743                 if (deltaw > deltah) {
    744                     scale = width / (float)targetRect.right;
    745                 } else {
    746                     scale = height / (float)targetRect.bottom;
    747                 }
    748                 targetRect.right = (int)(targetRect.right*scale);
    749                 targetRect.bottom = (int)(targetRect.bottom*scale);
    750                 deltaw = width - targetRect.right;
    751                 deltah = height - targetRect.bottom;
    752             }
    753 
    754             targetRect.offset(deltaw/2, deltah/2);
    755 
    756             Paint paint = new Paint();
    757             paint.setFilterBitmap(true);
    758             paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
    759             c.drawBitmap(bm, null, targetRect, paint);
    760 
    761             bm.recycle();
    762             c.setBitmap(null);
    763             return newbm;
    764         } catch (OutOfMemoryError e) {
    765             Log.w(TAG, "Can't generate default bitmap", e);
    766             return bm;
    767         }
    768     }
    769 }
    770