WallpaperManager.java revision 72c82ab9923025a91bbabb32e56bfea27bfd083b
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
17package android.app;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.drawable.BitmapDrawable;
25import android.graphics.drawable.Drawable;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.ParcelFileDescriptor;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.view.ViewRoot;
32
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.io.InputStream;
36
37public class WallpaperManager {
38    private static String TAG = "WallpaperManager";
39    private static boolean DEBUG = false;
40
41    /**
42     * Launch an activity for the user to pick the current global live
43     * wallpaper.
44     * @hide
45     */
46    public static final String ACTION_LIVE_WALLPAPER_CHOOSER
47            = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
48
49    private final Context mContext;
50
51    static class Globals extends IWallpaperManagerCallback.Stub {
52        private IWallpaperManager mService;
53        private Drawable mWallpaper;
54
55        Globals() {
56            IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
57            mService = IWallpaperManager.Stub.asInterface(b);
58        }
59
60        public void onWallpaperChanged() {
61            /* The wallpaper has changed but we shouldn't eagerly load the
62             * wallpaper as that would be inefficient. Reset the cached wallpaper
63             * to null so if the user requests the wallpaper again then we'll
64             * fetch it.
65             */
66            synchronized (this) {
67                mWallpaper = null;
68            }
69        }
70
71        public Drawable peekWallpaper(Context context) {
72            synchronized (this) {
73                if (mWallpaper != null) {
74                    return mWallpaper;
75                }
76                mWallpaper = getCurrentWallpaperLocked(context);
77                return mWallpaper;
78            }
79        }
80
81        private Drawable getCurrentWallpaperLocked(Context context) {
82            try {
83                ParcelFileDescriptor fd = mService.getWallpaper(this);
84                if (fd != null) {
85                    Bitmap bm = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
86                    if (bm != null) {
87                        // For now clear the density until we figure out how
88                        // to deal with it for wallpapers.
89                        bm.setDensity(0);
90                        return new BitmapDrawable(context.getResources(), bm);
91                    }
92                }
93            } catch (RemoteException e) {
94            }
95            return null;
96        }
97    }
98
99    private static Object mSync = new Object();
100    private static Globals sGlobals;
101
102    static Globals getGlobals() {
103        synchronized (mSync) {
104            if (sGlobals == null) {
105                sGlobals = new Globals();
106            }
107            return sGlobals;
108        }
109    }
110
111    /*package*/ WallpaperManager(Context context, Handler handler) {
112        mContext = context;
113    }
114
115    /**
116     * Retrieve a WallpaperManager associated with the given Context.
117     */
118    public static WallpaperManager getInstance(Context context) {
119        return (WallpaperManager)context.getSystemService(
120                Context.WALLPAPER_SERVICE);
121    }
122
123    /** @hide */
124    public IWallpaperManager getIWallpaperManager() {
125        return getGlobals().mService;
126    }
127
128    /**
129     * Like {@link #peekDrawable}, but always returns a valid Drawable.  If
130     * no wallpaper is set, the system default wallpaper is returned.
131     *
132     * @return Returns a Drawable object that will draw the wallpaper.
133     */
134    public Drawable getDrawable() {
135        Drawable dr = peekDrawable();
136        return dr != null ? dr : Resources.getSystem().getDrawable(
137                com.android.internal.R.drawable.default_wallpaper);
138    }
139
140    /**
141     * Retrieve the current system wallpaper.  This is returned as an
142     * abstract Drawable that you can install in a View to display whatever
143     * wallpaper the user has currently set.  If there is no wallpaper set,
144     * a null pointer is returned.
145     *
146     * @return Returns a Drawable object that will draw the wallpaper or a
147     * null pointer if these is none.
148     */
149    public Drawable peekDrawable() {
150        return getGlobals().peekWallpaper(mContext);
151    }
152
153    /**
154     * Change the current system wallpaper to the bitmap in the given resource.
155     * The resource is opened as a raw data stream and copied into the
156     * wallpaper; it must be a valid PNG or JPEG image.  On success, the intent
157     * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
158     *
159     * @param resid The bitmap to save.
160     *
161     * @throws IOException If an error occurs reverting to the default
162     * wallpaper.
163     */
164    public void setResource(int resid) throws IOException {
165        try {
166            Resources resources = mContext.getResources();
167            /* Set the wallpaper to the default values */
168            ParcelFileDescriptor fd = getGlobals().mService.setWallpaper(
169                    "res:" + resources.getResourceName(resid));
170            if (fd != null) {
171                FileOutputStream fos = null;
172                try {
173                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
174                    setWallpaper(resources.openRawResource(resid), fos);
175                } finally {
176                    if (fos != null) {
177                        fos.close();
178                    }
179                }
180            }
181        } catch (RemoteException e) {
182        }
183    }
184
185    /**
186     * Change the current system wallpaper to a bitmap.  The given bitmap is
187     * converted to a PNG and stored as the wallpaper.  On success, the intent
188     * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
189     *
190     * @param bitmap The bitmap to save.
191     *
192     * @throws IOException If an error occurs reverting to the default
193     * wallpaper.
194     */
195    public void setBitmap(Bitmap bitmap) throws IOException {
196        try {
197            ParcelFileDescriptor fd = getGlobals().mService.setWallpaper(null);
198            if (fd == null) {
199                return;
200            }
201            FileOutputStream fos = null;
202            try {
203                fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
204                bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
205            } finally {
206                if (fos != null) {
207                    fos.close();
208                }
209            }
210        } catch (RemoteException e) {
211        }
212    }
213
214    /**
215     * Change the current system wallpaper to a specific byte stream.  The
216     * give InputStream is copied into persistent storage and will now be
217     * used as the wallpaper.  Currently it must be either a JPEG or PNG
218     * image.  On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
219     * is broadcast.
220     *
221     * @param data A stream containing the raw data to install as a wallpaper.
222     *
223     * @throws IOException If an error occurs reverting to the default
224     * wallpaper.
225     */
226    public void setStream(InputStream data) throws IOException {
227        try {
228            ParcelFileDescriptor fd = getGlobals().mService.setWallpaper(null);
229            if (fd == null) {
230                return;
231            }
232            FileOutputStream fos = null;
233            try {
234                fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
235                setWallpaper(data, fos);
236            } finally {
237                if (fos != null) {
238                    fos.close();
239                }
240            }
241        } catch (RemoteException e) {
242        }
243    }
244
245    private void setWallpaper(InputStream data, FileOutputStream fos)
246            throws IOException {
247        byte[] buffer = new byte[32768];
248        int amt;
249        while ((amt=data.read(buffer)) > 0) {
250            fos.write(buffer, 0, amt);
251        }
252    }
253
254    /**
255     * Returns the desired minimum width for the wallpaper. Callers of
256     * {@link #setBitmap(android.graphics.Bitmap)} or
257     * {@link #setStream(java.io.InputStream)} should check this value
258     * beforehand to make sure the supplied wallpaper respects the desired
259     * minimum width.
260     *
261     * If the returned value is <= 0, the caller should use the width of
262     * the default display instead.
263     *
264     * @return The desired minimum width for the wallpaper. This value should
265     * be honored by applications that set the wallpaper but it is not
266     * mandatory.
267     */
268    public int getDesiredMinimumWidth() {
269        try {
270            return getGlobals().mService.getWidthHint();
271        } catch (RemoteException e) {
272            // Shouldn't happen!
273            return 0;
274        }
275    }
276
277    /**
278     * Returns the desired minimum height for the wallpaper. Callers of
279     * {@link #setBitmap(android.graphics.Bitmap)} or
280     * {@link #setStream(java.io.InputStream)} should check this value
281     * beforehand to make sure the supplied wallpaper respects the desired
282     * minimum height.
283     *
284     * If the returned value is <= 0, the caller should use the height of
285     * the default display instead.
286     *
287     * @return The desired minimum height for the wallpaper. This value should
288     * be honored by applications that set the wallpaper but it is not
289     * mandatory.
290     */
291    public int getDesiredMinimumHeight() {
292        try {
293            return getGlobals().mService.getHeightHint();
294        } catch (RemoteException e) {
295            // Shouldn't happen!
296            return 0;
297        }
298    }
299
300    /**
301     * For use only by the current home application, to specify the size of
302     * wallpaper it would like to use.  This allows such applications to have
303     * a virtual wallpaper that is larger than the physical screen, matching
304     * the size of their workspace.
305     * @param minimumWidth Desired minimum width
306     * @param minimumHeight Desired minimum height
307     */
308    public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
309        try {
310            getGlobals().mService.setDimensionHints(minimumWidth, minimumHeight);
311        } catch (RemoteException e) {
312        }
313    }
314
315    /**
316     * Set the position of the current wallpaper within any larger space, when
317     * that wallpaper is visible behind the given window.  The X and Y offsets
318     * are floating point numbers ranging from 0 to 1, representing where the
319     * wallpaper should be positioned within the screen space.  These only
320     * make sense when the wallpaper is larger than the screen.
321     *
322     * @param windowToken The window who these offsets should be associated
323     * with, as returned by {@link android.view.View#getWindowVisibility()
324     * View.getWindowToken()}.
325     * @param xOffset The offset olong the X dimension, from 0 to 1.
326     * @param yOffset The offset along the Y dimension, from 0 to 1.
327     */
328    public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
329        try {
330            ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
331                    windowToken, xOffset, yOffset);
332        } catch (RemoteException e) {
333            // Ignore.
334        }
335    }
336
337    /**
338     * Clear the offsets previously associated with this window through
339     * {@link #setWallpaperOffsets(IBinder, float, float)}.  This reverts
340     * the window to its default state, where it does not cause the wallpaper
341     * to scroll from whatever its last offsets were.
342     *
343     * @param windowToken The window who these offsets should be associated
344     * with, as returned by {@link android.view.View#getWindowVisibility()
345     * View.getWindowToken()}.
346     */
347    public void clearWallpaperOffsets(IBinder windowToken) {
348        try {
349            ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
350                    windowToken, -1, -1);
351        } catch (RemoteException e) {
352            // Ignore.
353        }
354    }
355
356    /**
357     * Remove any currently set wallpaper, reverting to the system's default
358     * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
359     * is broadcast.
360     *
361     * @throws IOException If an error occurs reverting to the default
362     * wallpaper.
363     */
364    public void clear() throws IOException {
365        setResource(com.android.internal.R.drawable.default_wallpaper);
366    }
367}
368