blob: f81ea81be2379ecb9fa5270b2a7c7a036c70fe4e [file] [log] [blame]
Dianne Hackborn4c62fc02009-08-08 20:40:27 -07001/*
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
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070017package android.app;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
Dianne Hackborn284ac932009-08-28 10:34:25 -070024import android.graphics.Canvas;
Dianne Hackborn19382ac2009-09-11 21:13:37 -070025import android.graphics.ColorFilter;
Dianne Hackborn284ac932009-08-28 10:34:25 -070026import android.graphics.Paint;
Dianne Hackborn19382ac2009-09-11 21:13:37 -070027import android.graphics.PixelFormat;
Jeff Brown24572372011-06-09 19:05:15 -070028import android.graphics.PorterDuff;
29import android.graphics.PorterDuffXfermode;
Dianne Hackborn284ac932009-08-28 10:34:25 -070030import android.graphics.Rect;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070031import android.graphics.drawable.BitmapDrawable;
32import android.graphics.drawable.Drawable;
Dianne Hackborn284ac932009-08-28 10:34:25 -070033import android.os.Bundle;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070034import android.os.Handler;
35import android.os.IBinder;
Dianne Hackborn840c3a22009-09-02 17:35:17 -070036import android.os.Looper;
37import android.os.Message;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070038import android.os.ParcelFileDescriptor;
39import android.os.RemoteException;
40import android.os.ServiceManager;
Dianne Hackborn284ac932009-08-28 10:34:25 -070041import android.util.DisplayMetrics;
Dianne Hackborn7341d7a2009-08-14 11:37:52 -070042import android.util.Log;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -070043import android.view.ViewRootImpl;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070044
45import java.io.FileOutputStream;
46import java.io.IOException;
47import java.io.InputStream;
48
Scott Main8b2e0002009-09-29 18:17:31 -070049/**
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 */
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070055public class WallpaperManager {
56 private static String TAG = "WallpaperManager";
57 private static boolean DEBUG = false;
Marco Nelissenbf6956b2009-11-09 15:21:13 -080058 private float mWallpaperXStep = -1;
59 private float mWallpaperYStep = -1;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070060
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070061 /**
62 * Launch an activity for the user to pick the current global live
63 * wallpaper.
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070064 */
65 public static final String ACTION_LIVE_WALLPAPER_CHOOSER
66 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
Adam Cohen5a242ec2010-12-07 21:07:07 -080067
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
Dianne Hackborn23ef7b42009-11-18 18:20:39 -080074 /**
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
Jeff Brown9f3bdfe2010-10-13 06:01:27 -070084 * 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
Dianne Hackborn23ef7b42009-11-18 18:20:39 -080092 * 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
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070097 private final Context mContext;
98
Dianne Hackborn19382ac2009-09-11 21:13:37 -070099 /**
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;
Romain Guy407ec782011-08-24 17:06:58 -0700110 private final Paint mPaint;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700111
112 private FastBitmapDrawable(Bitmap bitmap) {
113 mBitmap = bitmap;
114 mWidth = bitmap.getWidth();
115 mHeight = bitmap.getHeight();
Romain Guy407ec782011-08-24 17:06:58 -0700116
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700117 setBounds(0, 0, mWidth, mHeight);
Romain Guy407ec782011-08-24 17:06:58 -0700118
119 mPaint = new Paint();
120 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700121 }
122
123 @Override
124 public void draw(Canvas canvas) {
Romain Guy407ec782011-08-24 17:06:58 -0700125 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700126 }
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
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700140 public void setAlpha(int alpha) {
Romain Guy407ec782011-08-24 17:06:58 -0700141 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700142 }
143
144 @Override
145 public void setColorFilter(ColorFilter cf) {
Romain Guy407ec782011-08-24 17:06:58 -0700146 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700147 }
148
149 @Override
150 public void setDither(boolean dither) {
Romain Guy407ec782011-08-24 17:06:58 -0700151 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700152 }
153
154 @Override
155 public void setFilterBitmap(boolean filter) {
Romain Guy407ec782011-08-24 17:06:58 -0700156 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700157 }
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
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700180 static class Globals extends IWallpaperManagerCallback.Stub {
181 private IWallpaperManager mService;
Dianne Hackborn284ac932009-08-28 10:34:25 -0700182 private Bitmap mWallpaper;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700183 private Bitmap mDefaultWallpaper;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700184
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700185 private static final int MSG_CLEAR_WALLPAPER = 1;
186
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700187 private final Handler mHandler;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700188
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700189 Globals(Looper looper) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700190 IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
191 mService = IWallpaperManager.Stub.asInterface(b);
Dianne Hackborn85644d72009-09-03 00:23:04 -0700192 mHandler = new Handler(looper) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700193 @Override
194 public void handleMessage(Message msg) {
195 switch (msg.what) {
196 case MSG_CLEAR_WALLPAPER:
197 synchronized (this) {
198 mWallpaper = null;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700199 mDefaultWallpaper = null;
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700200 }
201 break;
202 }
203 }
204 };
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700205 }
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 */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700213 mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700214 }
215
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700216 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700217 synchronized (this) {
218 if (mWallpaper != null) {
219 return mWallpaper;
220 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700221 if (mDefaultWallpaper != null) {
222 return mDefaultWallpaper;
223 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800224 mWallpaper = null;
225 try {
Romain Guy407ec782011-08-24 17:06:58 -0700226 mWallpaper = getCurrentWallpaperLocked();
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800227 } catch (OutOfMemoryError e) {
228 Log.w(TAG, "No memory load current wallpaper", e);
229 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700230 if (returnDefault) {
231 if (mWallpaper == null) {
232 mDefaultWallpaper = getDefaultWallpaperLocked(context);
233 return mDefaultWallpaper;
234 } else {
235 mDefaultWallpaper = null;
236 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700237 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700238 return mWallpaper;
239 }
240 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700241
242 public void forgetLoadedWallpaper() {
243 synchronized (this) {
244 mWallpaper = null;
245 mDefaultWallpaper = null;
246 }
247 }
248
Romain Guy407ec782011-08-24 17:06:58 -0700249 private Bitmap getCurrentWallpaperLocked() {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700250 try {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700251 Bundle params = new Bundle();
252 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700253 if (fd != null) {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700254 int width = params.getInt("width", 0);
255 int height = params.getInt("height", 0);
Jeff Brown24572372011-06-09 19:05:15 -0700256
257 try {
258 BitmapFactory.Options options = new BitmapFactory.Options();
259 Bitmap bm = BitmapFactory.decodeFileDescriptor(
260 fd.getFileDescriptor(), null, options);
Romain Guy407ec782011-08-24 17:06:58 -0700261 return generateBitmap(bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700262 } catch (OutOfMemoryError e) {
263 Log.w(TAG, "Can't decode file", e);
264 } finally {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700265 try {
266 fd.close();
267 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700268 // Ignore
Dianne Hackborn284ac932009-08-28 10:34:25 -0700269 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700270 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700271 }
272 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700273 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700274 }
275 return null;
276 }
277
278 private Bitmap getDefaultWallpaperLocked(Context context) {
279 try {
280 InputStream is = context.getResources().openRawResource(
281 com.android.internal.R.drawable.default_wallpaper);
282 if (is != null) {
283 int width = mService.getWidthHint();
284 int height = mService.getHeightHint();
Jeff Brown24572372011-06-09 19:05:15 -0700285
286 try {
287 BitmapFactory.Options options = new BitmapFactory.Options();
288 Bitmap bm = BitmapFactory.decodeStream(is, null, options);
Romain Guy407ec782011-08-24 17:06:58 -0700289 return generateBitmap(bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700290 } catch (OutOfMemoryError e) {
291 Log.w(TAG, "Can't decode stream", e);
292 } finally {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700293 try {
294 is.close();
295 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700296 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700297 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800298 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700299 }
300 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700301 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700302 }
303 return null;
304 }
305 }
306
Romain Guy407ec782011-08-24 17:06:58 -0700307 private static final Object sSync = new Object[0];
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700308 private static Globals sGlobals;
309
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700310 static void initGlobals(Looper looper) {
Romain Guy407ec782011-08-24 17:06:58 -0700311 synchronized (sSync) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700312 if (sGlobals == null) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700313 sGlobals = new Globals(looper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700314 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700315 }
316 }
317
318 /*package*/ WallpaperManager(Context context, Handler handler) {
319 mContext = context;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700320 initGlobals(context.getMainLooper());
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700321 }
322
323 /**
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700324 * Retrieve a WallpaperManager associated with the given Context.
325 */
326 public static WallpaperManager getInstance(Context context) {
327 return (WallpaperManager)context.getSystemService(
328 Context.WALLPAPER_SERVICE);
329 }
330
331 /** @hide */
332 public IWallpaperManager getIWallpaperManager() {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700333 return sGlobals.mService;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700334 }
335
336 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700337 * Retrieve the current system wallpaper; if
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700338 * no wallpaper is set, the system default wallpaper is returned.
Scott Main8b2e0002009-09-29 18:17:31 -0700339 * This is returned as an
340 * abstract Drawable that you can install in a View to display whatever
341 * wallpaper the user has currently set.
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700342 *
343 * @return Returns a Drawable object that will draw the wallpaper.
344 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700345 public Drawable getDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700346 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
347 if (bm != null) {
348 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
349 dr.setDither(false);
350 return dr;
351 }
352 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700353 }
354
355 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700356 * Retrieve the current system wallpaper; if there is no wallpaper set,
357 * a null pointer is returned. This is returned as an
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700358 * abstract Drawable that you can install in a View to display whatever
Scott Main8b2e0002009-09-29 18:17:31 -0700359 * wallpaper the user has currently set.
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700360 *
361 * @return Returns a Drawable object that will draw the wallpaper or a
362 * null pointer if these is none.
363 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700364 public Drawable peekDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700365 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
366 if (bm != null) {
367 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
368 dr.setDither(false);
369 return dr;
370 }
371 return null;
372 }
373
374 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700375 * Like {@link #getDrawable()}, but the returned Drawable has a number
376 * of limitations to reduce its overhead as much as possible. It will
377 * never scale the wallpaper (only centering it if the requested bounds
378 * do match the bitmap bounds, which should not be typical), doesn't
379 * allow setting an alpha, color filter, or other attributes, etc. The
380 * bounds of the returned drawable will be initialized to the same bounds
381 * as the wallpaper, so normally you will not need to touch it. The
382 * drawable also assumes that it will be used in a context running in
383 * the same density as the screen (not in density compatibility mode).
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700384 *
385 * @return Returns a Drawable object that will draw the wallpaper.
386 */
387 public Drawable getFastDrawable() {
388 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
389 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700390 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700391 }
392 return null;
393 }
394
395 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700396 * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
397 * a null pointer is returned.
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700398 *
399 * @return Returns an optimized Drawable object that will draw the
400 * wallpaper or a null pointer if these is none.
401 */
402 public Drawable peekFastDrawable() {
403 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
404 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700405 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700406 }
407 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700408 }
409
410 /**
Romain Guy407ec782011-08-24 17:06:58 -0700411 * Like {@link #getDrawable()} but returns a Bitmap.
412 *
413 * @hide
414 */
415 public Bitmap getBitmap() {
416 return sGlobals.peekWallpaperBitmap(mContext, true);
417 }
418
419 /**
Dianne Hackbornba398392011-08-01 16:11:57 -0700420 * Remove all internal references to the last loaded wallpaper. Useful
421 * for apps that want to reduce memory usage when they only temporarily
422 * need to have the wallpaper. After calling, the next request for the
423 * wallpaper will require reloading it again from disk.
424 */
425 public void forgetLoadedWallpaper() {
426 sGlobals.forgetLoadedWallpaper();
427 }
428
429 /**
Dianne Hackborneb034652009-09-07 00:49:58 -0700430 * If the current wallpaper is a live wallpaper component, return the
431 * information about that wallpaper. Otherwise, if it is a static image,
432 * simply return null.
Dianne Hackborneb034652009-09-07 00:49:58 -0700433 */
434 public WallpaperInfo getWallpaperInfo() {
435 try {
436 return sGlobals.mService.getWallpaperInfo();
437 } catch (RemoteException e) {
438 return null;
439 }
440 }
441
442 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700443 * Change the current system wallpaper to the bitmap in the given resource.
444 * The resource is opened as a raw data stream and copied into the
445 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent
446 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
447 *
448 * @param resid The bitmap to save.
449 *
450 * @throws IOException If an error occurs reverting to the default
451 * wallpaper.
452 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700453 public void setResource(int resid) throws IOException {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700454 try {
455 Resources resources = mContext.getResources();
456 /* Set the wallpaper to the default values */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700457 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700458 "res:" + resources.getResourceName(resid));
459 if (fd != null) {
460 FileOutputStream fos = null;
461 try {
462 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
463 setWallpaper(resources.openRawResource(resid), fos);
464 } finally {
465 if (fos != null) {
466 fos.close();
467 }
468 }
469 }
470 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700471 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700472 }
473 }
474
475 /**
476 * Change the current system wallpaper to a bitmap. The given bitmap is
477 * converted to a PNG and stored as the wallpaper. On success, the intent
478 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
479 *
480 * @param bitmap The bitmap to save.
481 *
482 * @throws IOException If an error occurs reverting to the default
483 * wallpaper.
484 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700485 public void setBitmap(Bitmap bitmap) throws IOException {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700486 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700487 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700488 if (fd == null) {
489 return;
490 }
491 FileOutputStream fos = null;
492 try {
493 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
494 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
495 } finally {
496 if (fos != null) {
497 fos.close();
498 }
499 }
500 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700501 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700502 }
503 }
504
505 /**
506 * Change the current system wallpaper to a specific byte stream. The
507 * give InputStream is copied into persistent storage and will now be
508 * used as the wallpaper. Currently it must be either a JPEG or PNG
509 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
510 * is broadcast.
511 *
512 * @param data A stream containing the raw data to install as a wallpaper.
513 *
514 * @throws IOException If an error occurs reverting to the default
515 * wallpaper.
516 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700517 public void setStream(InputStream data) throws IOException {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700518 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700519 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700520 if (fd == null) {
521 return;
522 }
523 FileOutputStream fos = null;
524 try {
525 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
526 setWallpaper(data, fos);
527 } finally {
528 if (fos != null) {
529 fos.close();
530 }
531 }
532 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700533 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700534 }
535 }
536
537 private void setWallpaper(InputStream data, FileOutputStream fos)
538 throws IOException {
539 byte[] buffer = new byte[32768];
540 int amt;
541 while ((amt=data.read(buffer)) > 0) {
542 fos.write(buffer, 0, amt);
543 }
544 }
545
546 /**
547 * Returns the desired minimum width for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700548 * {@link #setBitmap(android.graphics.Bitmap)} or
549 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700550 * beforehand to make sure the supplied wallpaper respects the desired
551 * minimum width.
552 *
553 * If the returned value is <= 0, the caller should use the width of
554 * the default display instead.
555 *
556 * @return The desired minimum width for the wallpaper. This value should
557 * be honored by applications that set the wallpaper but it is not
558 * mandatory.
559 */
560 public int getDesiredMinimumWidth() {
561 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700562 return sGlobals.mService.getWidthHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700563 } catch (RemoteException e) {
564 // Shouldn't happen!
565 return 0;
566 }
567 }
568
569 /**
570 * Returns the desired minimum height for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700571 * {@link #setBitmap(android.graphics.Bitmap)} or
572 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700573 * beforehand to make sure the supplied wallpaper respects the desired
574 * minimum height.
575 *
576 * If the returned value is <= 0, the caller should use the height of
577 * the default display instead.
578 *
579 * @return The desired minimum height for the wallpaper. This value should
580 * be honored by applications that set the wallpaper but it is not
581 * mandatory.
582 */
583 public int getDesiredMinimumHeight() {
584 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700585 return sGlobals.mService.getHeightHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700586 } catch (RemoteException e) {
587 // Shouldn't happen!
588 return 0;
589 }
590 }
591
592 /**
593 * For use only by the current home application, to specify the size of
594 * wallpaper it would like to use. This allows such applications to have
595 * a virtual wallpaper that is larger than the physical screen, matching
596 * the size of their workspace.
597 * @param minimumWidth Desired minimum width
598 * @param minimumHeight Desired minimum height
599 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700600 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700601 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700602 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700603 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700604 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700605 }
606 }
607
608 /**
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700609 * Set the position of the current wallpaper within any larger space, when
610 * that wallpaper is visible behind the given window. The X and Y offsets
611 * are floating point numbers ranging from 0 to 1, representing where the
612 * wallpaper should be positioned within the screen space. These only
613 * make sense when the wallpaper is larger than the screen.
614 *
615 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -0700616 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700617 * View.getWindowToken()}.
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800618 * @param xOffset The offset along the X dimension, from 0 to 1.
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700619 * @param yOffset The offset along the Y dimension, from 0 to 1.
620 */
621 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
622 try {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700623 //Log.v(TAG, "Sending new wallpaper offsets from app...");
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700624 ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800625 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700626 //Log.v(TAG, "...app returning after sending offsets!");
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700627 } catch (RemoteException e) {
628 // Ignore.
629 }
630 }
631
632 /**
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800633 * For applications that use multiple virtual screens showing a wallpaper,
634 * specify the step size between virtual screens. For example, if the
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800635 * launcher has 3 virtual screens, it would specify an xStep of 0.5,
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800636 * since the X offset for those screens are 0.0, 0.5 and 1.0
637 * @param xStep The X offset delta from one screen to the next one
638 * @param yStep The Y offset delta from one screen to the next one
639 */
640 public void setWallpaperOffsetSteps(float xStep, float yStep) {
641 mWallpaperXStep = xStep;
642 mWallpaperYStep = yStep;
643 }
644
645 /**
Dianne Hackborn75804932009-10-20 20:15:20 -0700646 * Send an arbitrary command to the current active wallpaper.
647 *
648 * @param windowToken The window who these offsets should be associated
649 * with, as returned by {@link android.view.View#getWindowToken()
650 * View.getWindowToken()}.
651 * @param action Name of the command to perform. This must be a scoped
652 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
653 * @param x Arbitrary integer argument based on command.
654 * @param y Arbitrary integer argument based on command.
655 * @param z Arbitrary integer argument based on command.
656 * @param extras Optional additional information for the command, or null.
657 */
658 public void sendWallpaperCommand(IBinder windowToken, String action,
659 int x, int y, int z, Bundle extras) {
660 try {
661 //Log.v(TAG, "Sending new wallpaper offsets from app...");
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700662 ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
Dianne Hackborn75804932009-10-20 20:15:20 -0700663 windowToken, action, x, y, z, extras, false);
664 //Log.v(TAG, "...app returning after sending offsets!");
665 } catch (RemoteException e) {
666 // Ignore.
667 }
668 }
669
670 /**
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700671 * Clear the offsets previously associated with this window through
672 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts
673 * the window to its default state, where it does not cause the wallpaper
674 * to scroll from whatever its last offsets were.
675 *
676 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -0700677 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700678 * View.getWindowToken()}.
679 */
680 public void clearWallpaperOffsets(IBinder windowToken) {
681 try {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700682 ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800683 windowToken, -1, -1, -1, -1);
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700684 } catch (RemoteException e) {
685 // Ignore.
686 }
687 }
688
689 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700690 * Remove any currently set wallpaper, reverting to the system's default
691 * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
692 * is broadcast.
693 *
694 * @throws IOException If an error occurs reverting to the default
695 * wallpaper.
696 */
697 public void clear() throws IOException {
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700698 setResource(com.android.internal.R.drawable.default_wallpaper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700699 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700700
Romain Guy407ec782011-08-24 17:06:58 -0700701 static Bitmap generateBitmap(Bitmap bm, int width, int height) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700702 if (bm == null) {
Jeff Brown24572372011-06-09 19:05:15 -0700703 return null;
704 }
705
706 bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
707
Jeff Brown038b7372011-06-10 22:46:21 -0700708 if (width <= 0 || height <= 0
709 || (bm.getWidth() == width && bm.getHeight() == height)) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700710 return bm;
711 }
Jeff Brown24572372011-06-09 19:05:15 -0700712
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700713 // This is the final bitmap we want to return.
Jeff Brown24572372011-06-09 19:05:15 -0700714 try {
715 Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
716 newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
717
718 Canvas c = new Canvas(newbm);
719 Rect targetRect = new Rect();
720 targetRect.right = bm.getWidth();
721 targetRect.bottom = bm.getHeight();
722
723 int deltaw = width - targetRect.right;
724 int deltah = height - targetRect.bottom;
725
726 if (deltaw > 0 || deltah > 0) {
727 // We need to scale up so it covers the entire area.
Romain Guy407ec782011-08-24 17:06:58 -0700728 float scale;
Jeff Brown24572372011-06-09 19:05:15 -0700729 if (deltaw > deltah) {
730 scale = width / (float)targetRect.right;
731 } else {
732 scale = height / (float)targetRect.bottom;
733 }
734 targetRect.right = (int)(targetRect.right*scale);
735 targetRect.bottom = (int)(targetRect.bottom*scale);
736 deltaw = width - targetRect.right;
737 deltah = height - targetRect.bottom;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700738 }
Jeff Brown24572372011-06-09 19:05:15 -0700739
740 targetRect.offset(deltaw/2, deltah/2);
741
742 Paint paint = new Paint();
743 paint.setFilterBitmap(true);
744 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
745 c.drawBitmap(bm, null, targetRect, paint);
746
747 bm.recycle();
Dianne Hackbornba398392011-08-01 16:11:57 -0700748 c.setBitmap(null);
Jeff Brown24572372011-06-09 19:05:15 -0700749 return newbm;
750 } catch (OutOfMemoryError e) {
751 Log.w(TAG, "Can't generate default bitmap", e);
752 return bm;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700753 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700754 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700755}