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