OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / app / WallpaperManager.java
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 }