blob: 3824f44ffc4d34698538548a35c28416d1fab3fe [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;
Amith Yamasani37ce3a82012-02-06 12:04:42 -080033import android.os.Binder;
Dianne Hackborn284ac932009-08-28 10:34:25 -070034import android.os.Bundle;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070035import android.os.Handler;
36import android.os.IBinder;
Dianne Hackborn840c3a22009-09-02 17:35:17 -070037import android.os.Looper;
38import android.os.Message;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070039import android.os.ParcelFileDescriptor;
40import android.os.RemoteException;
41import android.os.ServiceManager;
Dianne Hackborn284ac932009-08-28 10:34:25 -070042import android.util.DisplayMetrics;
Dianne Hackborn7341d7a2009-08-14 11:37:52 -070043import android.util.Log;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -070044import android.view.ViewRootImpl;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070045
46import java.io.FileOutputStream;
47import java.io.IOException;
48import java.io.InputStream;
49
Scott Main8b2e0002009-09-29 18:17:31 -070050/**
51 * Provides access to the system wallpaper. With WallpaperManager, you can
52 * get the current wallpaper, get the desired dimensions for the wallpaper, set
53 * the wallpaper, and more. Get an instance of WallpaperManager with
54 * {@link #getInstance(android.content.Context) getInstance()}.
55 */
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070056public class WallpaperManager {
57 private static String TAG = "WallpaperManager";
58 private static boolean DEBUG = false;
Marco Nelissenbf6956b2009-11-09 15:21:13 -080059 private float mWallpaperXStep = -1;
60 private float mWallpaperYStep = -1;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070061
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070062 /**
63 * Launch an activity for the user to pick the current global live
64 * wallpaper.
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070065 */
66 public static final String ACTION_LIVE_WALLPAPER_CHOOSER
67 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
Adam Cohen5a242ec2010-12-07 21:07:07 -080068
69 /**
Dianne Hackborn7df7d202012-04-19 18:00:04 -070070 * Directly launch live wallpaper preview, allowing the user to immediately
71 * confirm to switch to a specific live wallpaper. You must specify
72 * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of
73 * a live wallpaper component that is to be shown.
74 */
75 public static final String ACTION_CHANGE_LIVE_WALLPAPER
76 = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER";
77
78 /**
79 * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the
80 * ComponentName of a live wallpaper that should be shown as a preview,
81 * for the user to confirm.
82 */
83 public static final String EXTRA_LIVE_WALLPAPER_COMPONENT
84 = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
85
86 /**
Adam Cohen5a242ec2010-12-07 21:07:07 -080087 * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER}
88 * which allows them to provide a custom large icon associated with this action.
89 */
90 public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
91
Dianne Hackborn23ef7b42009-11-18 18:20:39 -080092 /**
93 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
94 * host when the user taps on an empty area (not performing an action
95 * in the host). The x and y arguments are the location of the tap in
96 * screen coordinates.
97 */
98 public static final String COMMAND_TAP = "android.wallpaper.tap";
99
100 /**
101 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
Jeff Brown9f3bdfe2010-10-13 06:01:27 -0700102 * host when the user releases a secondary pointer on an empty area
103 * (not performing an action in the host). The x and y arguments are
104 * the location of the secondary tap in screen coordinates.
105 */
106 public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
107
108 /**
109 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800110 * host when the user drops an object into an area of the host. The x
111 * and y arguments are the location of the drop.
112 */
113 public static final String COMMAND_DROP = "android.home.drop";
114
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700115 private final Context mContext;
116
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700117 /**
118 * Special drawable that draws a wallpaper as fast as possible. Assumes
119 * no scaling or placement off (0,0) of the wallpaper (this should be done
120 * at the time the bitmap is loaded).
121 */
122 static class FastBitmapDrawable extends Drawable {
123 private final Bitmap mBitmap;
124 private final int mWidth;
125 private final int mHeight;
126 private int mDrawLeft;
127 private int mDrawTop;
Romain Guy407ec782011-08-24 17:06:58 -0700128 private final Paint mPaint;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700129
130 private FastBitmapDrawable(Bitmap bitmap) {
131 mBitmap = bitmap;
132 mWidth = bitmap.getWidth();
133 mHeight = bitmap.getHeight();
Romain Guy407ec782011-08-24 17:06:58 -0700134
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700135 setBounds(0, 0, mWidth, mHeight);
Romain Guy407ec782011-08-24 17:06:58 -0700136
137 mPaint = new Paint();
138 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700139 }
140
141 @Override
142 public void draw(Canvas canvas) {
Romain Guy407ec782011-08-24 17:06:58 -0700143 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700144 }
145
146 @Override
147 public int getOpacity() {
148 return PixelFormat.OPAQUE;
149 }
150
151 @Override
152 public void setBounds(int left, int top, int right, int bottom) {
153 mDrawLeft = left + (right-left - mWidth) / 2;
154 mDrawTop = top + (bottom-top - mHeight) / 2;
155 }
156
157 @Override
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700158 public void setAlpha(int alpha) {
Romain Guy407ec782011-08-24 17:06:58 -0700159 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700160 }
161
162 @Override
163 public void setColorFilter(ColorFilter cf) {
Romain Guy407ec782011-08-24 17:06:58 -0700164 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700165 }
166
167 @Override
168 public void setDither(boolean dither) {
Romain Guy407ec782011-08-24 17:06:58 -0700169 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700170 }
171
172 @Override
173 public void setFilterBitmap(boolean filter) {
Romain Guy407ec782011-08-24 17:06:58 -0700174 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700175 }
176
177 @Override
178 public int getIntrinsicWidth() {
179 return mWidth;
180 }
181
182 @Override
183 public int getIntrinsicHeight() {
184 return mHeight;
185 }
186
187 @Override
188 public int getMinimumWidth() {
189 return mWidth;
190 }
191
192 @Override
193 public int getMinimumHeight() {
194 return mHeight;
195 }
196 }
197
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700198 static class Globals extends IWallpaperManagerCallback.Stub {
199 private IWallpaperManager mService;
Dianne Hackborn284ac932009-08-28 10:34:25 -0700200 private Bitmap mWallpaper;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700201 private Bitmap mDefaultWallpaper;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700202
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700203 private static final int MSG_CLEAR_WALLPAPER = 1;
204
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700205 private final Handler mHandler;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700206
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700207 Globals(Looper looper) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700208 IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
209 mService = IWallpaperManager.Stub.asInterface(b);
Dianne Hackborn85644d72009-09-03 00:23:04 -0700210 mHandler = new Handler(looper) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700211 @Override
212 public void handleMessage(Message msg) {
213 switch (msg.what) {
214 case MSG_CLEAR_WALLPAPER:
215 synchronized (this) {
216 mWallpaper = null;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700217 mDefaultWallpaper = null;
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700218 }
219 break;
220 }
221 }
222 };
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700223 }
224
225 public void onWallpaperChanged() {
226 /* The wallpaper has changed but we shouldn't eagerly load the
227 * wallpaper as that would be inefficient. Reset the cached wallpaper
228 * to null so if the user requests the wallpaper again then we'll
229 * fetch it.
230 */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700231 mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700232 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800233
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700234 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700235 synchronized (this) {
236 if (mWallpaper != null) {
237 return mWallpaper;
238 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700239 if (mDefaultWallpaper != null) {
240 return mDefaultWallpaper;
241 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800242 mWallpaper = null;
243 try {
Romain Guy407ec782011-08-24 17:06:58 -0700244 mWallpaper = getCurrentWallpaperLocked();
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800245 } catch (OutOfMemoryError e) {
246 Log.w(TAG, "No memory load current wallpaper", e);
247 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700248 if (returnDefault) {
249 if (mWallpaper == null) {
250 mDefaultWallpaper = getDefaultWallpaperLocked(context);
251 return mDefaultWallpaper;
252 } else {
253 mDefaultWallpaper = null;
254 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700255 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700256 return mWallpaper;
257 }
258 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700259
260 public void forgetLoadedWallpaper() {
261 synchronized (this) {
262 mWallpaper = null;
263 mDefaultWallpaper = null;
264 }
265 }
266
Romain Guy407ec782011-08-24 17:06:58 -0700267 private Bitmap getCurrentWallpaperLocked() {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700268 try {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700269 Bundle params = new Bundle();
270 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700271 if (fd != null) {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700272 int width = params.getInt("width", 0);
273 int height = params.getInt("height", 0);
Jeff Brown24572372011-06-09 19:05:15 -0700274
275 try {
276 BitmapFactory.Options options = new BitmapFactory.Options();
277 Bitmap bm = BitmapFactory.decodeFileDescriptor(
278 fd.getFileDescriptor(), null, options);
Romain Guy407ec782011-08-24 17:06:58 -0700279 return generateBitmap(bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700280 } catch (OutOfMemoryError e) {
281 Log.w(TAG, "Can't decode file", e);
282 } finally {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700283 try {
284 fd.close();
285 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700286 // Ignore
Dianne Hackborn284ac932009-08-28 10:34:25 -0700287 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700288 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700289 }
290 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700291 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700292 }
293 return null;
294 }
295
296 private Bitmap getDefaultWallpaperLocked(Context context) {
297 try {
298 InputStream is = context.getResources().openRawResource(
299 com.android.internal.R.drawable.default_wallpaper);
300 if (is != null) {
301 int width = mService.getWidthHint();
302 int height = mService.getHeightHint();
Jeff Brown24572372011-06-09 19:05:15 -0700303
304 try {
305 BitmapFactory.Options options = new BitmapFactory.Options();
306 Bitmap bm = BitmapFactory.decodeStream(is, null, options);
Romain Guy407ec782011-08-24 17:06:58 -0700307 return generateBitmap(bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700308 } catch (OutOfMemoryError e) {
309 Log.w(TAG, "Can't decode stream", e);
310 } finally {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700311 try {
312 is.close();
313 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700314 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700315 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800316 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700317 }
318 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700319 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700320 }
321 return null;
322 }
323 }
324
Romain Guy407ec782011-08-24 17:06:58 -0700325 private static final Object sSync = new Object[0];
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700326 private static Globals sGlobals;
327
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700328 static void initGlobals(Looper looper) {
Romain Guy407ec782011-08-24 17:06:58 -0700329 synchronized (sSync) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700330 if (sGlobals == null) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700331 sGlobals = new Globals(looper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700332 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700333 }
334 }
335
336 /*package*/ WallpaperManager(Context context, Handler handler) {
337 mContext = context;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700338 initGlobals(context.getMainLooper());
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700339 }
340
341 /**
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700342 * Retrieve a WallpaperManager associated with the given Context.
343 */
344 public static WallpaperManager getInstance(Context context) {
345 return (WallpaperManager)context.getSystemService(
346 Context.WALLPAPER_SERVICE);
347 }
348
349 /** @hide */
350 public IWallpaperManager getIWallpaperManager() {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700351 return sGlobals.mService;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700352 }
353
354 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700355 * Retrieve the current system wallpaper; if
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700356 * no wallpaper is set, the system default wallpaper is returned.
Scott Main8b2e0002009-09-29 18:17:31 -0700357 * This is returned as an
358 * abstract Drawable that you can install in a View to display whatever
359 * 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.
362 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700363 public Drawable getDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700364 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
365 if (bm != null) {
366 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
367 dr.setDither(false);
368 return dr;
369 }
370 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700371 }
372
373 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700374 * Retrieve the current system wallpaper; if there is no wallpaper set,
375 * a null pointer is returned. This is returned as an
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700376 * abstract Drawable that you can install in a View to display whatever
Scott Main8b2e0002009-09-29 18:17:31 -0700377 * wallpaper the user has currently set.
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700378 *
379 * @return Returns a Drawable object that will draw the wallpaper or a
380 * null pointer if these is none.
381 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700382 public Drawable peekDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700383 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
384 if (bm != null) {
385 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
386 dr.setDither(false);
387 return dr;
388 }
389 return null;
390 }
391
392 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700393 * Like {@link #getDrawable()}, but the returned Drawable has a number
394 * of limitations to reduce its overhead as much as possible. It will
395 * never scale the wallpaper (only centering it if the requested bounds
396 * do match the bitmap bounds, which should not be typical), doesn't
397 * allow setting an alpha, color filter, or other attributes, etc. The
398 * bounds of the returned drawable will be initialized to the same bounds
399 * as the wallpaper, so normally you will not need to touch it. The
400 * drawable also assumes that it will be used in a context running in
401 * the same density as the screen (not in density compatibility mode).
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700402 *
403 * @return Returns a Drawable object that will draw the wallpaper.
404 */
405 public Drawable getFastDrawable() {
406 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
407 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700408 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700409 }
410 return null;
411 }
412
413 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700414 * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
415 * a null pointer is returned.
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700416 *
417 * @return Returns an optimized Drawable object that will draw the
418 * wallpaper or a null pointer if these is none.
419 */
420 public Drawable peekFastDrawable() {
421 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
422 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700423 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700424 }
425 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700426 }
427
428 /**
Romain Guy407ec782011-08-24 17:06:58 -0700429 * Like {@link #getDrawable()} but returns a Bitmap.
430 *
431 * @hide
432 */
433 public Bitmap getBitmap() {
434 return sGlobals.peekWallpaperBitmap(mContext, true);
435 }
436
437 /**
Dianne Hackbornba398392011-08-01 16:11:57 -0700438 * Remove all internal references to the last loaded wallpaper. Useful
439 * for apps that want to reduce memory usage when they only temporarily
440 * need to have the wallpaper. After calling, the next request for the
441 * wallpaper will require reloading it again from disk.
442 */
443 public void forgetLoadedWallpaper() {
444 sGlobals.forgetLoadedWallpaper();
445 }
446
447 /**
Dianne Hackborneb034652009-09-07 00:49:58 -0700448 * If the current wallpaper is a live wallpaper component, return the
449 * information about that wallpaper. Otherwise, if it is a static image,
450 * simply return null.
Dianne Hackborneb034652009-09-07 00:49:58 -0700451 */
452 public WallpaperInfo getWallpaperInfo() {
453 try {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400454 if (sGlobals.mService == null) {
455 Log.w(TAG, "WallpaperService not running");
456 return null;
457 } else {
458 return sGlobals.mService.getWallpaperInfo();
459 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700460 } catch (RemoteException e) {
461 return null;
462 }
463 }
464
465 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700466 * Change the current system wallpaper to the bitmap in the given resource.
467 * The resource is opened as a raw data stream and copied into the
468 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent
469 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
470 *
471 * @param resid The bitmap to save.
472 *
473 * @throws IOException If an error occurs reverting to the default
474 * wallpaper.
475 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700476 public void setResource(int resid) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400477 if (sGlobals.mService == null) {
478 Log.w(TAG, "WallpaperService not running");
479 return;
480 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700481 try {
482 Resources resources = mContext.getResources();
483 /* Set the wallpaper to the default values */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700484 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700485 "res:" + resources.getResourceName(resid));
486 if (fd != null) {
487 FileOutputStream fos = null;
488 try {
489 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
490 setWallpaper(resources.openRawResource(resid), fos);
491 } finally {
492 if (fos != null) {
493 fos.close();
494 }
495 }
496 }
497 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700498 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700499 }
500 }
501
502 /**
503 * Change the current system wallpaper to a bitmap. The given bitmap is
504 * converted to a PNG and stored as the wallpaper. On success, the intent
505 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
506 *
507 * @param bitmap The bitmap to save.
508 *
509 * @throws IOException If an error occurs reverting to the default
510 * wallpaper.
511 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700512 public void setBitmap(Bitmap bitmap) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400513 if (sGlobals.mService == null) {
514 Log.w(TAG, "WallpaperService not running");
515 return;
516 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700517 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700518 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700519 if (fd == null) {
520 return;
521 }
522 FileOutputStream fos = null;
523 try {
524 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
525 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
526 } finally {
527 if (fos != null) {
528 fos.close();
529 }
530 }
531 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700532 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700533 }
534 }
535
536 /**
537 * Change the current system wallpaper to a specific byte stream. The
538 * give InputStream is copied into persistent storage and will now be
539 * used as the wallpaper. Currently it must be either a JPEG or PNG
540 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
541 * is broadcast.
542 *
543 * @param data A stream containing the raw data to install as a wallpaper.
544 *
545 * @throws IOException If an error occurs reverting to the default
546 * wallpaper.
547 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700548 public void setStream(InputStream data) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400549 if (sGlobals.mService == null) {
550 Log.w(TAG, "WallpaperService not running");
551 return;
552 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700553 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700554 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700555 if (fd == null) {
556 return;
557 }
558 FileOutputStream fos = null;
559 try {
560 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
561 setWallpaper(data, fos);
562 } finally {
563 if (fos != null) {
564 fos.close();
565 }
566 }
567 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700568 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700569 }
570 }
571
572 private void setWallpaper(InputStream data, FileOutputStream fos)
573 throws IOException {
574 byte[] buffer = new byte[32768];
575 int amt;
576 while ((amt=data.read(buffer)) > 0) {
577 fos.write(buffer, 0, amt);
578 }
579 }
580
581 /**
582 * Returns the desired minimum width for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700583 * {@link #setBitmap(android.graphics.Bitmap)} or
584 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700585 * beforehand to make sure the supplied wallpaper respects the desired
586 * minimum width.
587 *
588 * If the returned value is <= 0, the caller should use the width of
589 * the default display instead.
590 *
591 * @return The desired minimum width for the wallpaper. This value should
592 * be honored by applications that set the wallpaper but it is not
593 * mandatory.
594 */
595 public int getDesiredMinimumWidth() {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400596 if (sGlobals.mService == null) {
597 Log.w(TAG, "WallpaperService not running");
598 return 0;
599 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700600 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700601 return sGlobals.mService.getWidthHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700602 } catch (RemoteException e) {
603 // Shouldn't happen!
604 return 0;
605 }
606 }
607
608 /**
609 * Returns the desired minimum height for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700610 * {@link #setBitmap(android.graphics.Bitmap)} or
611 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700612 * beforehand to make sure the supplied wallpaper respects the desired
613 * minimum height.
614 *
615 * If the returned value is <= 0, the caller should use the height of
616 * the default display instead.
617 *
618 * @return The desired minimum height for the wallpaper. This value should
619 * be honored by applications that set the wallpaper but it is not
620 * mandatory.
621 */
622 public int getDesiredMinimumHeight() {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400623 if (sGlobals.mService == null) {
624 Log.w(TAG, "WallpaperService not running");
625 return 0;
626 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700627 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700628 return sGlobals.mService.getHeightHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700629 } catch (RemoteException e) {
630 // Shouldn't happen!
631 return 0;
632 }
633 }
634
635 /**
636 * For use only by the current home application, to specify the size of
637 * wallpaper it would like to use. This allows such applications to have
638 * a virtual wallpaper that is larger than the physical screen, matching
639 * the size of their workspace.
Dianne Hackbornc5bf7582012-04-25 19:12:07 -0700640 *
641 * <p>Note developers, who don't seem to be reading this. This is
642 * for <em>home screens</em> to tell what size wallpaper they would like.
643 * Nobody else should be calling this! Certainly not other non-home-screen
644 * apps that change the wallpaper. Those apps are supposed to
645 * <b>retrieve</b> the suggested size so they can construct a wallpaper
646 * that matches it.
647 *
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700648 * @param minimumWidth Desired minimum width
649 * @param minimumHeight Desired minimum height
650 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700651 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700652 try {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400653 if (sGlobals.mService == null) {
654 Log.w(TAG, "WallpaperService not running");
655 } else {
656 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
657 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700658 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700659 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700660 }
661 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800662
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700663 /**
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700664 * Set the position of the current wallpaper within any larger space, when
665 * that wallpaper is visible behind the given window. The X and Y offsets
666 * are floating point numbers ranging from 0 to 1, representing where the
667 * wallpaper should be positioned within the screen space. These only
668 * make sense when the wallpaper is larger than the screen.
669 *
670 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -0700671 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700672 * View.getWindowToken()}.
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800673 * @param xOffset The offset along the X dimension, from 0 to 1.
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700674 * @param yOffset The offset along the Y dimension, from 0 to 1.
675 */
676 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
Adam Cohen791a6332012-01-12 14:38:38 -0800677 try {
678 //Log.v(TAG, "Sending new wallpaper offsets from app...");
679 ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
680 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
681 //Log.v(TAG, "...app returning after sending offsets!");
682 } catch (RemoteException e) {
683 // Ignore.
684 }
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700685 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800686
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700687 /**
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800688 * For applications that use multiple virtual screens showing a wallpaper,
689 * specify the step size between virtual screens. For example, if the
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800690 * launcher has 3 virtual screens, it would specify an xStep of 0.5,
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800691 * since the X offset for those screens are 0.0, 0.5 and 1.0
692 * @param xStep The X offset delta from one screen to the next one
693 * @param yStep The Y offset delta from one screen to the next one
694 */
695 public void setWallpaperOffsetSteps(float xStep, float yStep) {
696 mWallpaperXStep = xStep;
697 mWallpaperYStep = yStep;
698 }
699
700 /**
Dianne Hackborn75804932009-10-20 20:15:20 -0700701 * Send an arbitrary command to the current active wallpaper.
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 action Name of the command to perform. This must be a scoped
707 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
708 * @param x Arbitrary integer argument based on command.
709 * @param y Arbitrary integer argument based on command.
710 * @param z Arbitrary integer argument based on command.
711 * @param extras Optional additional information for the command, or null.
712 */
713 public void sendWallpaperCommand(IBinder windowToken, String action,
714 int x, int y, int z, Bundle extras) {
715 try {
716 //Log.v(TAG, "Sending new wallpaper offsets from app...");
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700717 ViewRootImpl.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
Dianne Hackborn75804932009-10-20 20:15:20 -0700718 windowToken, action, x, y, z, extras, false);
719 //Log.v(TAG, "...app returning after sending offsets!");
720 } catch (RemoteException e) {
721 // Ignore.
722 }
723 }
724
725 /**
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700726 * Clear the offsets previously associated with this window through
727 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts
728 * the window to its default state, where it does not cause the wallpaper
729 * to scroll from whatever its last offsets were.
730 *
731 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -0700732 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700733 * View.getWindowToken()}.
734 */
735 public void clearWallpaperOffsets(IBinder windowToken) {
736 try {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700737 ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800738 windowToken, -1, -1, -1, -1);
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700739 } catch (RemoteException e) {
740 // Ignore.
741 }
742 }
743
744 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700745 * Remove any currently set wallpaper, reverting to the system's default
746 * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
747 * is broadcast.
748 *
749 * @throws IOException If an error occurs reverting to the default
750 * wallpaper.
751 */
752 public void clear() throws IOException {
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700753 setResource(com.android.internal.R.drawable.default_wallpaper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700754 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700755
Romain Guy407ec782011-08-24 17:06:58 -0700756 static Bitmap generateBitmap(Bitmap bm, int width, int height) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700757 if (bm == null) {
Jeff Brown24572372011-06-09 19:05:15 -0700758 return null;
759 }
760
761 bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
762
Jeff Brown038b7372011-06-10 22:46:21 -0700763 if (width <= 0 || height <= 0
764 || (bm.getWidth() == width && bm.getHeight() == height)) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700765 return bm;
766 }
Jeff Brown24572372011-06-09 19:05:15 -0700767
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700768 // This is the final bitmap we want to return.
Jeff Brown24572372011-06-09 19:05:15 -0700769 try {
770 Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
771 newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
772
773 Canvas c = new Canvas(newbm);
774 Rect targetRect = new Rect();
775 targetRect.right = bm.getWidth();
776 targetRect.bottom = bm.getHeight();
777
778 int deltaw = width - targetRect.right;
779 int deltah = height - targetRect.bottom;
780
781 if (deltaw > 0 || deltah > 0) {
782 // We need to scale up so it covers the entire area.
Romain Guy407ec782011-08-24 17:06:58 -0700783 float scale;
Jeff Brown24572372011-06-09 19:05:15 -0700784 if (deltaw > deltah) {
785 scale = width / (float)targetRect.right;
786 } else {
787 scale = height / (float)targetRect.bottom;
788 }
789 targetRect.right = (int)(targetRect.right*scale);
790 targetRect.bottom = (int)(targetRect.bottom*scale);
791 deltaw = width - targetRect.right;
792 deltah = height - targetRect.bottom;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700793 }
Jeff Brown24572372011-06-09 19:05:15 -0700794
795 targetRect.offset(deltaw/2, deltah/2);
796
797 Paint paint = new Paint();
798 paint.setFilterBitmap(true);
799 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
800 c.drawBitmap(bm, null, targetRect, paint);
801
802 bm.recycle();
Dianne Hackbornba398392011-08-01 16:11:57 -0700803 c.setBitmap(null);
Jeff Brown24572372011-06-09 19:05:15 -0700804 return newbm;
805 } catch (OutOfMemoryError e) {
806 Log.w(TAG, "Can't generate default bitmap", e);
807 return bm;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700808 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700809 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700810}