blob: ced72f80a0295a17d2d7bd780d7feb53ea845dc1 [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
Adam Lesinskibba72d12013-10-11 18:10:56 -070019import android.content.ContentResolver;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070020import android.content.Context;
21import android.content.Intent;
Michael Jurkae8d1bf72013-09-09 15:58:54 +020022import android.content.pm.PackageManager;
23import android.content.pm.ResolveInfo;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070024import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.BitmapFactory;
Michael Jurkab668d0b2013-10-04 15:11:05 -070027import android.graphics.BitmapRegionDecoder;
Dianne Hackborn284ac932009-08-28 10:34:25 -070028import android.graphics.Canvas;
Dianne Hackborn19382ac2009-09-11 21:13:37 -070029import android.graphics.ColorFilter;
Michael Jurkab668d0b2013-10-04 15:11:05 -070030import android.graphics.Matrix;
Dianne Hackborn284ac932009-08-28 10:34:25 -070031import android.graphics.Paint;
Dianne Hackborn19382ac2009-09-11 21:13:37 -070032import android.graphics.PixelFormat;
Jeff Brown24572372011-06-09 19:05:15 -070033import android.graphics.PorterDuff;
34import android.graphics.PorterDuffXfermode;
Dianne Hackborn284ac932009-08-28 10:34:25 -070035import android.graphics.Rect;
Michael Jurkab668d0b2013-10-04 15:11:05 -070036import android.graphics.RectF;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070037import android.graphics.drawable.BitmapDrawable;
38import android.graphics.drawable.Drawable;
Michael Jurkae8d1bf72013-09-09 15:58:54 +020039import android.net.Uri;
Dianne Hackborn284ac932009-08-28 10:34:25 -070040import android.os.Bundle;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070041import android.os.Handler;
42import android.os.IBinder;
Dianne Hackborn840c3a22009-09-02 17:35:17 -070043import android.os.Looper;
44import android.os.Message;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070045import android.os.ParcelFileDescriptor;
46import android.os.RemoteException;
47import android.os.ServiceManager;
Dianne Hackborn284ac932009-08-28 10:34:25 -070048import android.util.DisplayMetrics;
Dianne Hackborn7341d7a2009-08-14 11:37:52 -070049import android.util.Log;
Dianne Hackborn908aecc2012-07-31 16:37:34 -070050import android.view.WindowManager;
Jeff Brown98365d72012-08-19 20:30:52 -070051import android.view.WindowManagerGlobal;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070052
Michael Jurkab668d0b2013-10-04 15:11:05 -070053import java.io.BufferedInputStream;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070054import java.io.FileOutputStream;
55import java.io.IOException;
56import java.io.InputStream;
Michael Jurkae8d1bf72013-09-09 15:58:54 +020057import java.util.List;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070058
Scott Main8b2e0002009-09-29 18:17:31 -070059/**
60 * Provides access to the system wallpaper. With WallpaperManager, you can
61 * get the current wallpaper, get the desired dimensions for the wallpaper, set
62 * the wallpaper, and more. Get an instance of WallpaperManager with
63 * {@link #getInstance(android.content.Context) getInstance()}.
64 */
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070065public class WallpaperManager {
66 private static String TAG = "WallpaperManager";
67 private static boolean DEBUG = false;
Marco Nelissenbf6956b2009-11-09 15:21:13 -080068 private float mWallpaperXStep = -1;
69 private float mWallpaperYStep = -1;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -070070
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070071 /**
Michael Jurkae8d1bf72013-09-09 15:58:54 +020072 * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
73 * an intent; instead, use {@link #getCropAndSetWallpaperIntent}.
74 * <p>Input: {@link Intent#getData} is the URI of the image to crop and set as wallpaper.
75 * <p>Output: RESULT_OK if user decided to crop/set the wallpaper, RESULT_CANCEL otherwise
Adam Lesinskibba72d12013-10-11 18:10:56 -070076 * Activities that support this intent should specify a MIME filter of "image/*"
Michael Jurkae8d1bf72013-09-09 15:58:54 +020077 */
78 public static final String ACTION_CROP_AND_SET_WALLPAPER =
79 "android.service.wallpaper.CROP_AND_SET_WALLPAPER";
80
81 /**
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070082 * Launch an activity for the user to pick the current global live
83 * wallpaper.
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070084 */
85 public static final String ACTION_LIVE_WALLPAPER_CHOOSER
86 = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
Adam Cohen5a242ec2010-12-07 21:07:07 -080087
88 /**
Dianne Hackborn7df7d202012-04-19 18:00:04 -070089 * Directly launch live wallpaper preview, allowing the user to immediately
90 * confirm to switch to a specific live wallpaper. You must specify
91 * {@link #EXTRA_LIVE_WALLPAPER_COMPONENT} with the ComponentName of
92 * a live wallpaper component that is to be shown.
93 */
94 public static final String ACTION_CHANGE_LIVE_WALLPAPER
95 = "android.service.wallpaper.CHANGE_LIVE_WALLPAPER";
96
97 /**
98 * Extra in {@link #ACTION_CHANGE_LIVE_WALLPAPER} that specifies the
99 * ComponentName of a live wallpaper that should be shown as a preview,
100 * for the user to confirm.
101 */
102 public static final String EXTRA_LIVE_WALLPAPER_COMPONENT
103 = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
104
105 /**
Adam Cohen5a242ec2010-12-07 21:07:07 -0800106 * Manifest entry for activities that respond to {@link Intent#ACTION_SET_WALLPAPER}
107 * which allows them to provide a custom large icon associated with this action.
108 */
109 public static final String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
110
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800111 /**
112 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
113 * host when the user taps on an empty area (not performing an action
114 * in the host). The x and y arguments are the location of the tap in
115 * screen coordinates.
116 */
117 public static final String COMMAND_TAP = "android.wallpaper.tap";
118
119 /**
120 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
Jeff Brown9f3bdfe2010-10-13 06:01:27 -0700121 * host when the user releases a secondary pointer on an empty area
122 * (not performing an action in the host). The x and y arguments are
123 * the location of the secondary tap in screen coordinates.
124 */
125 public static final String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
126
127 /**
128 * Command for {@link #sendWallpaperCommand}: reported by the wallpaper
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800129 * host when the user drops an object into an area of the host. The x
130 * and y arguments are the location of the drop.
131 */
132 public static final String COMMAND_DROP = "android.home.drop";
133
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700134 private final Context mContext;
135
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700136 /**
137 * Special drawable that draws a wallpaper as fast as possible. Assumes
138 * no scaling or placement off (0,0) of the wallpaper (this should be done
139 * at the time the bitmap is loaded).
140 */
141 static class FastBitmapDrawable extends Drawable {
142 private final Bitmap mBitmap;
143 private final int mWidth;
144 private final int mHeight;
145 private int mDrawLeft;
146 private int mDrawTop;
Romain Guy407ec782011-08-24 17:06:58 -0700147 private final Paint mPaint;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700148
149 private FastBitmapDrawable(Bitmap bitmap) {
150 mBitmap = bitmap;
151 mWidth = bitmap.getWidth();
152 mHeight = bitmap.getHeight();
Romain Guy407ec782011-08-24 17:06:58 -0700153
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700154 setBounds(0, 0, mWidth, mHeight);
Romain Guy407ec782011-08-24 17:06:58 -0700155
156 mPaint = new Paint();
157 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700158 }
159
160 @Override
161 public void draw(Canvas canvas) {
Romain Guy407ec782011-08-24 17:06:58 -0700162 canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, mPaint);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700163 }
164
165 @Override
166 public int getOpacity() {
167 return PixelFormat.OPAQUE;
168 }
169
170 @Override
171 public void setBounds(int left, int top, int right, int bottom) {
172 mDrawLeft = left + (right-left - mWidth) / 2;
173 mDrawTop = top + (bottom-top - mHeight) / 2;
174 }
175
176 @Override
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700177 public void setAlpha(int alpha) {
Romain Guy407ec782011-08-24 17:06:58 -0700178 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700179 }
180
181 @Override
182 public void setColorFilter(ColorFilter cf) {
Romain Guy407ec782011-08-24 17:06:58 -0700183 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700184 }
185
186 @Override
187 public void setDither(boolean dither) {
Romain Guy407ec782011-08-24 17:06:58 -0700188 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700189 }
190
191 @Override
192 public void setFilterBitmap(boolean filter) {
Romain Guy407ec782011-08-24 17:06:58 -0700193 throw new UnsupportedOperationException("Not supported with this drawable");
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700194 }
195
196 @Override
197 public int getIntrinsicWidth() {
198 return mWidth;
199 }
200
201 @Override
202 public int getIntrinsicHeight() {
203 return mHeight;
204 }
205
206 @Override
207 public int getMinimumWidth() {
208 return mWidth;
209 }
210
211 @Override
212 public int getMinimumHeight() {
213 return mHeight;
214 }
215 }
216
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700217 static class Globals extends IWallpaperManagerCallback.Stub {
218 private IWallpaperManager mService;
Dianne Hackborn284ac932009-08-28 10:34:25 -0700219 private Bitmap mWallpaper;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700220 private Bitmap mDefaultWallpaper;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700221
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700222 private static final int MSG_CLEAR_WALLPAPER = 1;
223
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700224 private final Handler mHandler;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700225
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700226 Globals(Looper looper) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700227 IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
228 mService = IWallpaperManager.Stub.asInterface(b);
Dianne Hackborn85644d72009-09-03 00:23:04 -0700229 mHandler = new Handler(looper) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700230 @Override
231 public void handleMessage(Message msg) {
232 switch (msg.what) {
233 case MSG_CLEAR_WALLPAPER:
234 synchronized (this) {
235 mWallpaper = null;
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700236 mDefaultWallpaper = null;
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700237 }
238 break;
239 }
240 }
241 };
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700242 }
243
244 public void onWallpaperChanged() {
245 /* The wallpaper has changed but we shouldn't eagerly load the
246 * wallpaper as that would be inefficient. Reset the cached wallpaper
247 * to null so if the user requests the wallpaper again then we'll
248 * fetch it.
249 */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700250 mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700251 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800252
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700253 public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700254 synchronized (this) {
255 if (mWallpaper != null) {
256 return mWallpaper;
257 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700258 if (mDefaultWallpaper != null) {
259 return mDefaultWallpaper;
260 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800261 mWallpaper = null;
262 try {
Dianne Hackborn908aecc2012-07-31 16:37:34 -0700263 mWallpaper = getCurrentWallpaperLocked(context);
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800264 } catch (OutOfMemoryError e) {
265 Log.w(TAG, "No memory load current wallpaper", e);
266 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700267 if (returnDefault) {
268 if (mWallpaper == null) {
269 mDefaultWallpaper = getDefaultWallpaperLocked(context);
270 return mDefaultWallpaper;
271 } else {
272 mDefaultWallpaper = null;
273 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700274 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700275 return mWallpaper;
276 }
277 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700278
279 public void forgetLoadedWallpaper() {
280 synchronized (this) {
281 mWallpaper = null;
282 mDefaultWallpaper = null;
Michael Wrightb9fca992013-10-15 12:49:44 -0700283 mHandler.removeMessages(MSG_CLEAR_WALLPAPER);
Dianne Hackbornba398392011-08-01 16:11:57 -0700284 }
285 }
286
Dianne Hackborn908aecc2012-07-31 16:37:34 -0700287 private Bitmap getCurrentWallpaperLocked(Context context) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700288 try {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700289 Bundle params = new Bundle();
290 ParcelFileDescriptor fd = mService.getWallpaper(this, params);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700291 if (fd != null) {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700292 int width = params.getInt("width", 0);
293 int height = params.getInt("height", 0);
Jeff Brown24572372011-06-09 19:05:15 -0700294
295 try {
296 BitmapFactory.Options options = new BitmapFactory.Options();
297 Bitmap bm = BitmapFactory.decodeFileDescriptor(
298 fd.getFileDescriptor(), null, options);
Dianne Hackborn908aecc2012-07-31 16:37:34 -0700299 return generateBitmap(context, bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700300 } catch (OutOfMemoryError e) {
301 Log.w(TAG, "Can't decode file", e);
302 } finally {
Dianne Hackborn284ac932009-08-28 10:34:25 -0700303 try {
304 fd.close();
305 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700306 // Ignore
Dianne Hackborn284ac932009-08-28 10:34:25 -0700307 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700308 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700309 }
310 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700311 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700312 }
313 return null;
314 }
315
316 private Bitmap getDefaultWallpaperLocked(Context context) {
317 try {
318 InputStream is = context.getResources().openRawResource(
319 com.android.internal.R.drawable.default_wallpaper);
320 if (is != null) {
321 int width = mService.getWidthHint();
322 int height = mService.getHeightHint();
Jeff Brown24572372011-06-09 19:05:15 -0700323
324 try {
325 BitmapFactory.Options options = new BitmapFactory.Options();
326 Bitmap bm = BitmapFactory.decodeStream(is, null, options);
Dianne Hackborn908aecc2012-07-31 16:37:34 -0700327 return generateBitmap(context, bm, width, height);
Jeff Brown24572372011-06-09 19:05:15 -0700328 } catch (OutOfMemoryError e) {
329 Log.w(TAG, "Can't decode stream", e);
330 } finally {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700331 try {
332 is.close();
333 } catch (IOException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700334 // Ignore
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700335 }
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800336 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700337 }
338 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700339 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700340 }
341 return null;
342 }
343 }
344
Romain Guy407ec782011-08-24 17:06:58 -0700345 private static final Object sSync = new Object[0];
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700346 private static Globals sGlobals;
347
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700348 static void initGlobals(Looper looper) {
Romain Guy407ec782011-08-24 17:06:58 -0700349 synchronized (sSync) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700350 if (sGlobals == null) {
Dianne Hackborn5e802fb2009-09-02 21:50:50 -0700351 sGlobals = new Globals(looper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700352 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700353 }
354 }
355
356 /*package*/ WallpaperManager(Context context, Handler handler) {
357 mContext = context;
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700358 initGlobals(context.getMainLooper());
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700359 }
360
361 /**
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700362 * Retrieve a WallpaperManager associated with the given Context.
363 */
364 public static WallpaperManager getInstance(Context context) {
365 return (WallpaperManager)context.getSystemService(
366 Context.WALLPAPER_SERVICE);
367 }
368
369 /** @hide */
370 public IWallpaperManager getIWallpaperManager() {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700371 return sGlobals.mService;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700372 }
373
374 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700375 * Retrieve the current system wallpaper; if
Michael Jurkab668d0b2013-10-04 15:11:05 -0700376 * no wallpaper is set, the system built-in static wallpaper is returned.
Scott Main8b2e0002009-09-29 18:17:31 -0700377 * This is returned as an
378 * abstract Drawable that you can install in a View to display whatever
379 * wallpaper the user has currently set.
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700380 *
381 * @return Returns a Drawable object that will draw the wallpaper.
382 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700383 public Drawable getDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700384 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
385 if (bm != null) {
386 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
387 dr.setDither(false);
388 return dr;
389 }
390 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700391 }
392
393 /**
Michael Jurkab668d0b2013-10-04 15:11:05 -0700394 * Returns a drawable for the system built-in static wallpaper .
395 *
396 */
397 public Drawable getBuiltInDrawable() {
398 return getBuiltInDrawable(0, 0, false, 0, 0);
399 }
400
401 /**
402 * Returns a drawable for the system built-in static wallpaper. Based on the parameters, the
403 * drawable can be cropped and scaled
404 *
405 * @param outWidth The width of the returned drawable
406 * @param outWidth The height of the returned drawable
407 * @param scaleToFit If true, scale the wallpaper down rather than just cropping it
408 * @param horizontalAlignment A float value between 0 and 1 specifying where to crop the image;
409 * 0 for left-aligned, 0.5 for horizontal center-aligned, and 1 for right-aligned
410 * @param verticalAlignment A float value between 0 and 1 specifying where to crop the image;
411 * 0 for top-aligned, 0.5 for vertical center-aligned, and 1 for bottom-aligned
412 *
413 */
414 public Drawable getBuiltInDrawable(int outWidth, int outHeight,
415 boolean scaleToFit, float horizontalAlignment, float verticalAlignment) {
416 if (sGlobals.mService == null) {
417 Log.w(TAG, "WallpaperService not running");
418 return null;
419 }
420 Resources resources = mContext.getResources();
421 horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment));
422 verticalAlignment = Math.max(0, Math.min(1, verticalAlignment));
423
424 InputStream is = new BufferedInputStream(
425 resources.openRawResource(com.android.internal.R.drawable.default_wallpaper));
426
427 if (is == null) {
428 Log.e(TAG, "default wallpaper input stream is null");
429 return null;
430 } else {
431 if (outWidth <= 0 || outHeight <= 0) {
432 Bitmap fullSize = BitmapFactory.decodeStream(is, null, null);
433 return new BitmapDrawable(resources, fullSize);
434 } else {
435 int inWidth;
436 int inHeight;
437 {
438 BitmapFactory.Options options = new BitmapFactory.Options();
439 options.inJustDecodeBounds = true;
440 BitmapFactory.decodeStream(is, null, options);
441 if (options.outWidth != 0 && options.outHeight != 0) {
442 inWidth = options.outWidth;
443 inHeight = options.outHeight;
444 } else {
445 Log.e(TAG, "default wallpaper dimensions are 0");
446 return null;
447 }
448 }
449
450 is = new BufferedInputStream(resources.openRawResource(
451 com.android.internal.R.drawable.default_wallpaper));
452
453 RectF cropRectF;
454
455 outWidth = Math.min(inWidth, outWidth);
456 outHeight = Math.min(inHeight, outHeight);
457 if (scaleToFit) {
458 cropRectF = getMaxCropRect(inWidth, inHeight, outWidth, outHeight,
459 horizontalAlignment, verticalAlignment);
460 } else {
461 float left = (inWidth - outWidth) * horizontalAlignment;
462 float right = left + outWidth;
463 float top = (inHeight - outHeight) * verticalAlignment;
464 float bottom = top + outHeight;
Michael Jurka05391532013-10-14 20:44:42 -0700465 cropRectF = new RectF(left, top, right, bottom);
Michael Jurkab668d0b2013-10-04 15:11:05 -0700466 }
467 Rect roundedTrueCrop = new Rect();
468 cropRectF.roundOut(roundedTrueCrop);
469
470 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
471 Log.w(TAG, "crop has bad values for full size image");
472 return null;
473 }
474
475 // See how much we're reducing the size of the image
476 int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / outWidth,
477 roundedTrueCrop.height() / outHeight);
478
479 // Attempt to open a region decoder
480 BitmapRegionDecoder decoder = null;
481 try {
482 decoder = BitmapRegionDecoder.newInstance(is, true);
483 } catch (IOException e) {
484 Log.w(TAG, "cannot open region decoder for default wallpaper");
485 }
486
487 Bitmap crop = null;
488 if (decoder != null) {
489 // Do region decoding to get crop bitmap
490 BitmapFactory.Options options = new BitmapFactory.Options();
491 if (scaleDownSampleSize > 1) {
492 options.inSampleSize = scaleDownSampleSize;
493 }
494 crop = decoder.decodeRegion(roundedTrueCrop, options);
495 decoder.recycle();
496 }
497
498 if (crop == null) {
499 // BitmapRegionDecoder has failed, try to crop in-memory
500 is = new BufferedInputStream(resources.openRawResource(
501 com.android.internal.R.drawable.default_wallpaper));
502 Bitmap fullSize = null;
503 if (is != null) {
504 BitmapFactory.Options options = new BitmapFactory.Options();
505 if (scaleDownSampleSize > 1) {
506 options.inSampleSize = scaleDownSampleSize;
507 }
508 fullSize = BitmapFactory.decodeStream(is, null, options);
509 }
510 if (fullSize != null) {
511 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
512 roundedTrueCrop.top, roundedTrueCrop.width(),
513 roundedTrueCrop.height());
514 }
515 }
516
517 if (crop == null) {
518 Log.w(TAG, "cannot decode default wallpaper");
519 return null;
520 }
521
522 // Scale down if necessary
523 if (outWidth > 0 && outHeight > 0 &&
524 (crop.getWidth() != outWidth || crop.getHeight() != outHeight)) {
525 Matrix m = new Matrix();
526 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
527 RectF returnRect = new RectF(0, 0, outWidth, outHeight);
528 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
529 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
530 (int) returnRect.height(), Bitmap.Config.ARGB_8888);
531 if (tmp != null) {
532 Canvas c = new Canvas(tmp);
533 Paint p = new Paint();
534 p.setFilterBitmap(true);
535 c.drawBitmap(crop, m, p);
536 crop = tmp;
537 }
538 }
539
540 return new BitmapDrawable(resources, crop);
541 }
542 }
543 }
544
545 private static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight,
546 float horizontalAlignment, float verticalAlignment) {
547 RectF cropRect = new RectF();
548 // Get a crop rect that will fit this
549 if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
550 cropRect.top = 0;
551 cropRect.bottom = inHeight;
552 float cropWidth = outWidth * (inHeight / (float) outHeight);
553 cropRect.left = (inWidth - cropWidth) * horizontalAlignment;
554 cropRect.right = cropRect.left + cropWidth;
555 } else {
556 cropRect.left = 0;
557 cropRect.right = inWidth;
558 float cropHeight = outHeight * (inWidth / (float) outWidth);
559 cropRect.top = (inHeight - cropHeight) * verticalAlignment;
560 cropRect.bottom = cropRect.top + cropHeight;
561 }
562 return cropRect;
563 }
564
565 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700566 * Retrieve the current system wallpaper; if there is no wallpaper set,
567 * a null pointer is returned. This is returned as an
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700568 * abstract Drawable that you can install in a View to display whatever
Scott Main8b2e0002009-09-29 18:17:31 -0700569 * wallpaper the user has currently set.
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700570 *
571 * @return Returns a Drawable object that will draw the wallpaper or a
572 * null pointer if these is none.
573 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700574 public Drawable peekDrawable() {
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700575 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
576 if (bm != null) {
577 Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
578 dr.setDither(false);
579 return dr;
580 }
581 return null;
582 }
583
584 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700585 * Like {@link #getDrawable()}, but the returned Drawable has a number
586 * of limitations to reduce its overhead as much as possible. It will
587 * never scale the wallpaper (only centering it if the requested bounds
588 * do match the bitmap bounds, which should not be typical), doesn't
589 * allow setting an alpha, color filter, or other attributes, etc. The
590 * bounds of the returned drawable will be initialized to the same bounds
591 * as the wallpaper, so normally you will not need to touch it. The
592 * drawable also assumes that it will be used in a context running in
593 * the same density as the screen (not in density compatibility mode).
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700594 *
595 * @return Returns a Drawable object that will draw the wallpaper.
596 */
597 public Drawable getFastDrawable() {
598 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
599 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700600 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700601 }
602 return null;
603 }
604
605 /**
Scott Main8b2e0002009-09-29 18:17:31 -0700606 * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
607 * a null pointer is returned.
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700608 *
609 * @return Returns an optimized Drawable object that will draw the
610 * wallpaper or a null pointer if these is none.
611 */
612 public Drawable peekFastDrawable() {
613 Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
614 if (bm != null) {
Romain Guy407ec782011-08-24 17:06:58 -0700615 return new FastBitmapDrawable(bm);
Dianne Hackborn19382ac2009-09-11 21:13:37 -0700616 }
617 return null;
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700618 }
619
620 /**
Romain Guy407ec782011-08-24 17:06:58 -0700621 * Like {@link #getDrawable()} but returns a Bitmap.
622 *
623 * @hide
624 */
625 public Bitmap getBitmap() {
626 return sGlobals.peekWallpaperBitmap(mContext, true);
627 }
628
629 /**
Dianne Hackbornba398392011-08-01 16:11:57 -0700630 * Remove all internal references to the last loaded wallpaper. Useful
631 * for apps that want to reduce memory usage when they only temporarily
632 * need to have the wallpaper. After calling, the next request for the
633 * wallpaper will require reloading it again from disk.
634 */
635 public void forgetLoadedWallpaper() {
636 sGlobals.forgetLoadedWallpaper();
637 }
638
639 /**
Dianne Hackborneb034652009-09-07 00:49:58 -0700640 * If the current wallpaper is a live wallpaper component, return the
641 * information about that wallpaper. Otherwise, if it is a static image,
642 * simply return null.
Dianne Hackborneb034652009-09-07 00:49:58 -0700643 */
644 public WallpaperInfo getWallpaperInfo() {
645 try {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400646 if (sGlobals.mService == null) {
647 Log.w(TAG, "WallpaperService not running");
648 return null;
649 } else {
650 return sGlobals.mService.getWallpaperInfo();
651 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700652 } catch (RemoteException e) {
653 return null;
654 }
655 }
Michael Jurkae8d1bf72013-09-09 15:58:54 +0200656
657 /**
658 * Gets an Intent that will launch an activity that crops the given
659 * image and sets the device's wallpaper. If there is a default HOME activity
660 * that supports cropping wallpapers, it will be preferred as the default.
Ying Wang930d4e52013-09-14 11:57:17 -0700661 * Use this method instead of directly creating a {@link #ACTION_CROP_AND_SET_WALLPAPER}
Michael Jurkae8d1bf72013-09-09 15:58:54 +0200662 * intent.
Adam Lesinskibba72d12013-10-11 18:10:56 -0700663 *
664 * @param imageUri The image URI that will be set in the intent. The must be a content
665 * URI and its provider must resolve its type to "image/*"
666 *
667 * @throws IllegalArgumentException if the URI is not a content URI or its MIME type is
668 * not "image/*"
Michael Jurkae8d1bf72013-09-09 15:58:54 +0200669 */
670 public Intent getCropAndSetWallpaperIntent(Uri imageUri) {
Adam Lesinskibba72d12013-10-11 18:10:56 -0700671 if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) {
672 throw new IllegalArgumentException("Image URI must be of the "
673 + ContentResolver.SCHEME_CONTENT + " scheme type");
674 }
675
Michael Jurkae8d1bf72013-09-09 15:58:54 +0200676 final PackageManager packageManager = mContext.getPackageManager();
677 Intent cropAndSetWallpaperIntent =
678 new Intent(ACTION_CROP_AND_SET_WALLPAPER, imageUri);
679 cropAndSetWallpaperIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
680
681 // Find out if the default HOME activity supports CROP_AND_SET_WALLPAPER
682 Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
683 ResolveInfo resolvedHome = packageManager.resolveActivity(homeIntent,
684 PackageManager.MATCH_DEFAULT_ONLY);
685 if (resolvedHome != null) {
686 cropAndSetWallpaperIntent.setPackage(resolvedHome.activityInfo.packageName);
687
688 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities(
689 cropAndSetWallpaperIntent, 0);
690 if (cropAppList.size() > 0) {
691 return cropAndSetWallpaperIntent;
692 }
693 }
694
695 // fallback crop activity
696 cropAndSetWallpaperIntent.setPackage("com.android.wallpapercropper");
Adam Lesinskibba72d12013-10-11 18:10:56 -0700697 List<ResolveInfo> cropAppList = packageManager.queryIntentActivities(
698 cropAndSetWallpaperIntent, 0);
699 if (cropAppList.size() > 0) {
700 return cropAndSetWallpaperIntent;
701 }
702 // If the URI is not of the right type, or for some reason the system wallpaper
703 // cropper doesn't exist, return null
704 throw new IllegalArgumentException("Cannot use passed URI to set wallpaper; " +
705 "check that the type returned by ContentProvider matches image/*");
Michael Jurkae8d1bf72013-09-09 15:58:54 +0200706 }
707
Dianne Hackborneb034652009-09-07 00:49:58 -0700708 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700709 * Change the current system wallpaper to the bitmap in the given resource.
710 * The resource is opened as a raw data stream and copied into the
711 * wallpaper; it must be a valid PNG or JPEG image. On success, the intent
712 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
713 *
Nicolas Falliere9530e3a2012-06-18 17:21:06 -0700714 * <p>This method requires the caller to hold the permission
715 * {@link android.Manifest.permission#SET_WALLPAPER}.
716 *
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700717 * @param resid The bitmap to save.
718 *
Michael Jurkab668d0b2013-10-04 15:11:05 -0700719 * @throws IOException If an error occurs reverting to the built-in
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700720 * wallpaper.
721 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700722 public void setResource(int resid) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400723 if (sGlobals.mService == null) {
724 Log.w(TAG, "WallpaperService not running");
725 return;
726 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700727 try {
728 Resources resources = mContext.getResources();
729 /* Set the wallpaper to the default values */
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700730 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700731 "res:" + resources.getResourceName(resid));
732 if (fd != null) {
733 FileOutputStream fos = null;
734 try {
735 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
736 setWallpaper(resources.openRawResource(resid), fos);
737 } finally {
738 if (fos != null) {
739 fos.close();
740 }
741 }
742 }
743 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700744 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700745 }
746 }
747
748 /**
749 * Change the current system wallpaper to a bitmap. The given bitmap is
750 * converted to a PNG and stored as the wallpaper. On success, the intent
751 * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
752 *
Nicolas Falliere9530e3a2012-06-18 17:21:06 -0700753 * <p>This method requires the caller to hold the permission
754 * {@link android.Manifest.permission#SET_WALLPAPER}.
755 *
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700756 * @param bitmap The bitmap to save.
757 *
Michael Jurkab668d0b2013-10-04 15:11:05 -0700758 * @throws IOException If an error occurs reverting to the built-in
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700759 * wallpaper.
760 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700761 public void setBitmap(Bitmap bitmap) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400762 if (sGlobals.mService == null) {
763 Log.w(TAG, "WallpaperService not running");
764 return;
765 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700766 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700767 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700768 if (fd == null) {
769 return;
770 }
771 FileOutputStream fos = null;
772 try {
773 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
774 bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
775 } finally {
776 if (fos != null) {
777 fos.close();
778 }
779 }
780 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700781 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700782 }
783 }
784
785 /**
786 * Change the current system wallpaper to a specific byte stream. The
787 * give InputStream is copied into persistent storage and will now be
788 * used as the wallpaper. Currently it must be either a JPEG or PNG
789 * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
790 * is broadcast.
791 *
Nicolas Falliere9530e3a2012-06-18 17:21:06 -0700792 * <p>This method requires the caller to hold the permission
793 * {@link android.Manifest.permission#SET_WALLPAPER}.
794 *
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700795 * @param data A stream containing the raw data to install as a wallpaper.
796 *
Michael Jurkab668d0b2013-10-04 15:11:05 -0700797 * @throws IOException If an error occurs reverting to the built-in
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700798 * wallpaper.
799 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700800 public void setStream(InputStream data) throws IOException {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400801 if (sGlobals.mService == null) {
802 Log.w(TAG, "WallpaperService not running");
803 return;
804 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700805 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700806 ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700807 if (fd == null) {
808 return;
809 }
810 FileOutputStream fos = null;
811 try {
812 fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
813 setWallpaper(data, fos);
814 } finally {
815 if (fos != null) {
816 fos.close();
817 }
818 }
819 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700820 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700821 }
822 }
823
824 private void setWallpaper(InputStream data, FileOutputStream fos)
825 throws IOException {
826 byte[] buffer = new byte[32768];
827 int amt;
828 while ((amt=data.read(buffer)) > 0) {
829 fos.write(buffer, 0, amt);
830 }
831 }
832
833 /**
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700834 * Return whether any users are currently set to use the wallpaper
835 * with the given resource ID. That is, their wallpaper has been
836 * set through {@link #setResource(int)} with the same resource id.
837 */
838 public boolean hasResourceWallpaper(int resid) {
839 if (sGlobals.mService == null) {
840 Log.w(TAG, "WallpaperService not running");
841 return false;
842 }
843 try {
844 Resources resources = mContext.getResources();
845 String name = "res:" + resources.getResourceName(resid);
846 return sGlobals.mService.hasNamedWallpaper(name);
847 } catch (RemoteException e) {
848 return false;
849 }
850 }
851
852 /**
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700853 * Returns the desired minimum width for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700854 * {@link #setBitmap(android.graphics.Bitmap)} or
855 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700856 * beforehand to make sure the supplied wallpaper respects the desired
857 * minimum width.
858 *
859 * If the returned value is <= 0, the caller should use the width of
860 * the default display instead.
861 *
862 * @return The desired minimum width for the wallpaper. This value should
863 * be honored by applications that set the wallpaper but it is not
864 * mandatory.
865 */
866 public int getDesiredMinimumWidth() {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400867 if (sGlobals.mService == null) {
868 Log.w(TAG, "WallpaperService not running");
869 return 0;
870 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700871 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700872 return sGlobals.mService.getWidthHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700873 } catch (RemoteException e) {
874 // Shouldn't happen!
875 return 0;
876 }
877 }
878
879 /**
880 * Returns the desired minimum height for the wallpaper. Callers of
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700881 * {@link #setBitmap(android.graphics.Bitmap)} or
882 * {@link #setStream(java.io.InputStream)} should check this value
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700883 * beforehand to make sure the supplied wallpaper respects the desired
884 * minimum height.
885 *
886 * If the returned value is <= 0, the caller should use the height of
887 * the default display instead.
888 *
889 * @return The desired minimum height for the wallpaper. This value should
890 * be honored by applications that set the wallpaper but it is not
891 * mandatory.
892 */
893 public int getDesiredMinimumHeight() {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400894 if (sGlobals.mService == null) {
895 Log.w(TAG, "WallpaperService not running");
896 return 0;
897 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700898 try {
Dianne Hackborn840c3a22009-09-02 17:35:17 -0700899 return sGlobals.mService.getHeightHint();
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700900 } catch (RemoteException e) {
901 // Shouldn't happen!
902 return 0;
903 }
904 }
905
906 /**
907 * For use only by the current home application, to specify the size of
908 * wallpaper it would like to use. This allows such applications to have
909 * a virtual wallpaper that is larger than the physical screen, matching
910 * the size of their workspace.
Dianne Hackbornc5bf7582012-04-25 19:12:07 -0700911 *
912 * <p>Note developers, who don't seem to be reading this. This is
913 * for <em>home screens</em> to tell what size wallpaper they would like.
914 * Nobody else should be calling this! Certainly not other non-home-screen
915 * apps that change the wallpaper. Those apps are supposed to
916 * <b>retrieve</b> the suggested size so they can construct a wallpaper
917 * that matches it.
918 *
Nicolas Falliere9530e3a2012-06-18 17:21:06 -0700919 * <p>This method requires the caller to hold the permission
920 * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}.
921 *
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700922 * @param minimumWidth Desired minimum width
923 * @param minimumHeight Desired minimum height
924 */
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700925 public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700926 try {
Mike Lockwoodc067c9c2011-10-31 12:50:12 -0400927 if (sGlobals.mService == null) {
928 Log.w(TAG, "WallpaperService not running");
929 } else {
930 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
931 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700932 } catch (RemoteException e) {
Romain Guy407ec782011-08-24 17:06:58 -0700933 // Ignore
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700934 }
935 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800936
Dianne Hackborn8cc6a502009-08-05 21:29:42 -0700937 /**
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700938 * Set the position of the current wallpaper within any larger space, when
939 * that wallpaper is visible behind the given window. The X and Y offsets
940 * are floating point numbers ranging from 0 to 1, representing where the
941 * wallpaper should be positioned within the screen space. These only
942 * make sense when the wallpaper is larger than the screen.
943 *
944 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -0700945 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700946 * View.getWindowToken()}.
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800947 * @param xOffset The offset along the X dimension, from 0 to 1.
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700948 * @param yOffset The offset along the Y dimension, from 0 to 1.
949 */
950 public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
Adam Cohen791a6332012-01-12 14:38:38 -0800951 try {
952 //Log.v(TAG, "Sending new wallpaper offsets from app...");
Jeff Brownf9e989d2013-04-04 23:04:03 -0700953 WindowManagerGlobal.getWindowSession().setWallpaperPosition(
Adam Cohen791a6332012-01-12 14:38:38 -0800954 windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
955 //Log.v(TAG, "...app returning after sending offsets!");
956 } catch (RemoteException e) {
957 // Ignore.
958 }
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700959 }
Adam Cohen041a0ba2011-11-09 20:10:27 -0800960
Dianne Hackbornc8a0a752009-08-10 23:05:49 -0700961 /**
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800962 * For applications that use multiple virtual screens showing a wallpaper,
963 * specify the step size between virtual screens. For example, if the
Dianne Hackborn23ef7b42009-11-18 18:20:39 -0800964 * launcher has 3 virtual screens, it would specify an xStep of 0.5,
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800965 * since the X offset for those screens are 0.0, 0.5 and 1.0
966 * @param xStep The X offset delta from one screen to the next one
967 * @param yStep The Y offset delta from one screen to the next one
968 */
969 public void setWallpaperOffsetSteps(float xStep, float yStep) {
970 mWallpaperXStep = xStep;
971 mWallpaperYStep = yStep;
972 }
973
974 /**
Dianne Hackborn75804932009-10-20 20:15:20 -0700975 * Send an arbitrary command to the current active wallpaper.
976 *
977 * @param windowToken The window who these offsets should be associated
978 * with, as returned by {@link android.view.View#getWindowToken()
979 * View.getWindowToken()}.
980 * @param action Name of the command to perform. This must be a scoped
981 * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
982 * @param x Arbitrary integer argument based on command.
983 * @param y Arbitrary integer argument based on command.
984 * @param z Arbitrary integer argument based on command.
985 * @param extras Optional additional information for the command, or null.
986 */
987 public void sendWallpaperCommand(IBinder windowToken, String action,
988 int x, int y, int z, Bundle extras) {
989 try {
990 //Log.v(TAG, "Sending new wallpaper offsets from app...");
Jeff Brownf9e989d2013-04-04 23:04:03 -0700991 WindowManagerGlobal.getWindowSession().sendWallpaperCommand(
Dianne Hackborn75804932009-10-20 20:15:20 -0700992 windowToken, action, x, y, z, extras, false);
993 //Log.v(TAG, "...app returning after sending offsets!");
994 } catch (RemoteException e) {
995 // Ignore.
996 }
997 }
998
999 /**
Dianne Hackborn72c82ab2009-08-11 21:13:54 -07001000 * Clear the offsets previously associated with this window through
1001 * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts
1002 * the window to its default state, where it does not cause the wallpaper
1003 * to scroll from whatever its last offsets were.
1004 *
1005 * @param windowToken The window who these offsets should be associated
Scott Main8b2e0002009-09-29 18:17:31 -07001006 * with, as returned by {@link android.view.View#getWindowToken()
Dianne Hackborn72c82ab2009-08-11 21:13:54 -07001007 * View.getWindowToken()}.
1008 */
1009 public void clearWallpaperOffsets(IBinder windowToken) {
1010 try {
Jeff Brownf9e989d2013-04-04 23:04:03 -07001011 WindowManagerGlobal.getWindowSession().setWallpaperPosition(
Marco Nelissenbf6956b2009-11-09 15:21:13 -08001012 windowToken, -1, -1, -1, -1);
Dianne Hackborn72c82ab2009-08-11 21:13:54 -07001013 } catch (RemoteException e) {
1014 // Ignore.
1015 }
1016 }
1017
1018 /**
Michael Jurkab668d0b2013-10-04 15:11:05 -07001019 * Remove any currently set wallpaper, reverting to the system's built-in
Dianne Hackborn8cc6a502009-08-05 21:29:42 -07001020 * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
1021 * is broadcast.
1022 *
Nicolas Falliere9530e3a2012-06-18 17:21:06 -07001023 * <p>This method requires the caller to hold the permission
1024 * {@link android.Manifest.permission#SET_WALLPAPER}.
1025 *
Michael Jurkab668d0b2013-10-04 15:11:05 -07001026 * @throws IOException If an error occurs reverting to the built-in
Dianne Hackborn8cc6a502009-08-05 21:29:42 -07001027 * wallpaper.
1028 */
1029 public void clear() throws IOException {
Dianne Hackborn4c62fc02009-08-08 20:40:27 -07001030 setResource(com.android.internal.R.drawable.default_wallpaper);
Dianne Hackborn8cc6a502009-08-05 21:29:42 -07001031 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001032
Dianne Hackborn908aecc2012-07-31 16:37:34 -07001033 static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001034 if (bm == null) {
Jeff Brown24572372011-06-09 19:05:15 -07001035 return null;
1036 }
1037
Dianne Hackborn908aecc2012-07-31 16:37:34 -07001038 WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
1039 DisplayMetrics metrics = new DisplayMetrics();
1040 wm.getDefaultDisplay().getMetrics(metrics);
1041 bm.setDensity(metrics.noncompatDensityDpi);
Jeff Brown24572372011-06-09 19:05:15 -07001042
Jeff Brown038b7372011-06-10 22:46:21 -07001043 if (width <= 0 || height <= 0
1044 || (bm.getWidth() == width && bm.getHeight() == height)) {
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001045 return bm;
1046 }
Jeff Brown24572372011-06-09 19:05:15 -07001047
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001048 // This is the final bitmap we want to return.
Jeff Brown24572372011-06-09 19:05:15 -07001049 try {
1050 Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Dianne Hackborn908aecc2012-07-31 16:37:34 -07001051 newbm.setDensity(metrics.noncompatDensityDpi);
Jeff Brown24572372011-06-09 19:05:15 -07001052
1053 Canvas c = new Canvas(newbm);
1054 Rect targetRect = new Rect();
1055 targetRect.right = bm.getWidth();
1056 targetRect.bottom = bm.getHeight();
1057
1058 int deltaw = width - targetRect.right;
1059 int deltah = height - targetRect.bottom;
1060
1061 if (deltaw > 0 || deltah > 0) {
1062 // We need to scale up so it covers the entire area.
Romain Guy407ec782011-08-24 17:06:58 -07001063 float scale;
Jeff Brown24572372011-06-09 19:05:15 -07001064 if (deltaw > deltah) {
1065 scale = width / (float)targetRect.right;
1066 } else {
1067 scale = height / (float)targetRect.bottom;
1068 }
1069 targetRect.right = (int)(targetRect.right*scale);
1070 targetRect.bottom = (int)(targetRect.bottom*scale);
1071 deltaw = width - targetRect.right;
1072 deltah = height - targetRect.bottom;
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001073 }
Jeff Brown24572372011-06-09 19:05:15 -07001074
1075 targetRect.offset(deltaw/2, deltah/2);
1076
1077 Paint paint = new Paint();
1078 paint.setFilterBitmap(true);
1079 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
1080 c.drawBitmap(bm, null, targetRect, paint);
1081
1082 bm.recycle();
Dianne Hackbornba398392011-08-01 16:11:57 -07001083 c.setBitmap(null);
Jeff Brown24572372011-06-09 19:05:15 -07001084 return newbm;
1085 } catch (OutOfMemoryError e) {
1086 Log.w(TAG, "Can't generate default bitmap", e);
1087 return bm;
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001088 }
Dianne Hackborn19382ac2009-09-11 21:13:37 -07001089 }
Dianne Hackborn8cc6a502009-08-05 21:29:42 -07001090}