diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/WallpaperManager.java | 277 | ||||
-rw-r--r-- | core/java/android/content/res/Resources.java | 4 | ||||
-rw-r--r-- | core/java/android/os/HandlerThread.java | 17 | ||||
-rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 43 | ||||
-rw-r--r-- | core/java/android/view/IWindow.aidl | 2 | ||||
-rw-r--r-- | core/java/android/view/IWindowSession.aidl | 2 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 8 | ||||
-rw-r--r-- | core/java/com/android/internal/os/HandlerCaller.java | 7 | ||||
-rw-r--r-- | core/java/com/android/internal/service/wallpaper/ImageWallpaper.java | 23 | ||||
-rw-r--r-- | core/java/com/android/internal/view/BaseIWindow.java | 8 |
10 files changed, 333 insertions, 58 deletions
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index da40c8a..38cac87 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -22,7 +22,9 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -56,9 +58,96 @@ public class WallpaperManager { private final Context mContext; + /** + * Special drawable that draws a wallpaper as fast as possible. Assumes + * no scaling or placement off (0,0) of the wallpaper (this should be done + * at the time the bitmap is loaded). + */ + static class FastBitmapDrawable extends Drawable { + private final Bitmap mBitmap; + private final int mWidth; + private final int mHeight; + private int mDrawLeft; + private int mDrawTop; + + private FastBitmapDrawable(Bitmap bitmap) { + mBitmap = bitmap; + mWidth = bitmap.getWidth(); + mHeight = bitmap.getHeight(); + setBounds(0, 0, mWidth, mHeight); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, null); + } + + @Override + public int getOpacity() { + return PixelFormat.OPAQUE; + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + mDrawLeft = left + (right-left - mWidth) / 2; + mDrawTop = top + (bottom-top - mHeight) / 2; + } + + @Override + public void setBounds(Rect bounds) { + // TODO Auto-generated method stub + super.setBounds(bounds); + } + + @Override + public void setAlpha(int alpha) { + throw new UnsupportedOperationException( + "Not supported with this drawable"); + } + + @Override + public void setColorFilter(ColorFilter cf) { + throw new UnsupportedOperationException( + "Not supported with this drawable"); + } + + @Override + public void setDither(boolean dither) { + throw new UnsupportedOperationException( + "Not supported with this drawable"); + } + + @Override + public void setFilterBitmap(boolean filter) { + throw new UnsupportedOperationException( + "Not supported with this drawable"); + } + + @Override + public int getIntrinsicWidth() { + return mWidth; + } + + @Override + public int getIntrinsicHeight() { + return mHeight; + } + + @Override + public int getMinimumWidth() { + return mWidth; + } + + @Override + public int getMinimumHeight() { + return mHeight; + } + } + static class Globals extends IWallpaperManagerCallback.Stub { private IWallpaperManager mService; private Bitmap mWallpaper; + private Bitmap mDefaultWallpaper; private static final int MSG_CLEAR_WALLPAPER = 1; @@ -74,6 +163,7 @@ public class WallpaperManager { case MSG_CLEAR_WALLPAPER: synchronized (this) { mWallpaper = null; + mDefaultWallpaper = null; } break; } @@ -90,12 +180,19 @@ public class WallpaperManager { mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER); } - public Bitmap peekWallpaperBitmap(Context context) { + public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { synchronized (this) { if (mWallpaper != null) { return mWallpaper; } + if (mDefaultWallpaper != null) { + return mDefaultWallpaper; + } mWallpaper = getCurrentWallpaperLocked(context); + if (mWallpaper == null && returnDefault) { + mDefaultWallpaper = getDefaultWallpaperLocked(context); + return mDefaultWallpaper; + } return mWallpaper; } } @@ -134,48 +231,48 @@ public class WallpaperManager { fd.close(); } catch (IOException e) { } - if (bm == null) { - return bm; - } - bm.setDensity(DisplayMetrics.DENSITY_DEVICE); - - // This is the final bitmap we want to return. - Bitmap newbm = Bitmap.createBitmap(width, height, - bm.getConfig()); - newbm.setDensity(DisplayMetrics.DENSITY_DEVICE); - Canvas c = new Canvas(newbm); - c.setDensity(DisplayMetrics.DENSITY_DEVICE); - Rect targetRect = new Rect(); - targetRect.left = targetRect.top = 0; - targetRect.right = bm.getWidth(); - targetRect.bottom = bm.getHeight(); - int deltaw = width - targetRect.right; - int deltah = height - targetRect.bottom; + return generateBitmap(context, bm, width, height); + } + } catch (RemoteException e) { + } + return null; + } + + private Bitmap getDefaultWallpaperLocked(Context context) { + try { + InputStream is = context.getResources().openRawResource( + com.android.internal.R.drawable.default_wallpaper); + if (is != null) { + int width = mService.getWidthHint(); + int height = mService.getHeightHint(); - if (deltaw > 0 || deltah > 0) { - // We need to scale up so it covers the entire - // area. - float scale = 1.0f; - if (deltaw > deltah) { - scale = width / (float)targetRect.right; - } else { - scale = height / (float)targetRect.bottom; + if (width <= 0 || height <= 0) { + // Degenerate case: no size requested, just load + // bitmap as-is. + Bitmap bm = BitmapFactory.decodeStream(is, null, null); + try { + is.close(); + } catch (IOException e) { } - targetRect.right = (int)(targetRect.right*scale); - targetRect.bottom = (int)(targetRect.bottom*scale); - deltaw = width - targetRect.right; - deltah = height - targetRect.bottom; + if (bm != null) { + bm.setDensity(DisplayMetrics.DENSITY_DEVICE); + } + return bm; } - targetRect.offset(deltaw/2, deltah/2); - Paint paint = new Paint(); - paint.setFilterBitmap(true); - paint.setDither(true); - c.drawBitmap(bm, null, targetRect, paint); + // Load the bitmap with full color depth, to preserve + // quality for later processing. + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inDither = false; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + Bitmap bm = BitmapFactory.decodeStream(is, null, options); + try { + is.close(); + } catch (IOException e) { + } - bm.recycle(); - return newbm; + return generateBitmap(context, bm, width, height); } } catch (RemoteException e) { } @@ -219,9 +316,13 @@ public class WallpaperManager { * @return Returns a Drawable object that will draw the wallpaper. */ public Drawable getDrawable() { - Drawable dr = peekDrawable(); - return dr != null ? dr : Resources.getSystem().getDrawable( - com.android.internal.R.drawable.default_wallpaper); + Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true); + if (bm != null) { + Drawable dr = new BitmapDrawable(mContext.getResources(), bm); + dr.setDither(false); + return dr; + } + return null; } /** @@ -234,8 +335,51 @@ public class WallpaperManager { * null pointer if these is none. */ public Drawable peekDrawable() { - Bitmap bm = sGlobals.peekWallpaperBitmap(mContext); - return bm != null ? new BitmapDrawable(mContext.getResources(), bm) : null; + Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false); + if (bm != null) { + Drawable dr = new BitmapDrawable(mContext.getResources(), bm); + dr.setDither(false); + return dr; + } + return null; + } + + /** + * Like {@link #peekFastDrawable}, but always returns a valid Drawable. If + * no wallpaper is set, the system default wallpaper is returned. + * + * @return Returns a Drawable object that will draw the wallpaper. + */ + public Drawable getFastDrawable() { + Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true); + if (bm != null) { + Drawable dr = new FastBitmapDrawable(bm); + return dr; + } + return null; + } + + /** + * Like {@link #peekDrawable()}, but the returned Drawable has a number + * of limitations to reduce its overhead as much as possible: it will + * never scale the wallpaper (only centering it if the requested bounds + * do match the bitmap bounds, which should not be typical), doesn't + * allow setting an alpha, color filter, or other attributes, etc. The + * bounds of the returned drawable will be initialized to the same bounds + * as the wallpaper, so normally you will not need to touch it. The + * drawable also assumes that it will be used in a context running in + * the same density as the screen (not in density compatibility mode). + * + * @return Returns an optimized Drawable object that will draw the + * wallpaper or a null pointer if these is none. + */ + public Drawable peekFastDrawable() { + Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false); + if (bm != null) { + Drawable dr = new FastBitmapDrawable(bm); + return dr; + } + return null; } /** @@ -429,8 +573,10 @@ public class WallpaperManager { */ public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { try { + //Log.v(TAG, "Sending new wallpaper offsets from app..."); ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( windowToken, xOffset, yOffset); + //Log.v(TAG, "...app returning after sending offsets!"); } catch (RemoteException e) { // Ignore. } @@ -466,4 +612,51 @@ public class WallpaperManager { public void clear() throws IOException { setResource(com.android.internal.R.drawable.default_wallpaper); } + + static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) { + if (bm == null) { + return bm; + } + bm.setDensity(DisplayMetrics.DENSITY_DEVICE); + + // This is the final bitmap we want to return. + // XXX We should get the pixel depth from the system (to match the + // physical display depth), when there is a way. + Bitmap newbm = Bitmap.createBitmap(width, height, + Bitmap.Config.RGB_565); + newbm.setDensity(DisplayMetrics.DENSITY_DEVICE); + Canvas c = new Canvas(newbm); + c.setDensity(DisplayMetrics.DENSITY_DEVICE); + Rect targetRect = new Rect(); + targetRect.left = targetRect.top = 0; + targetRect.right = bm.getWidth(); + targetRect.bottom = bm.getHeight(); + + int deltaw = width - targetRect.right; + int deltah = height - targetRect.bottom; + + if (deltaw > 0 || deltah > 0) { + // We need to scale up so it covers the entire + // area. + float scale = 1.0f; + if (deltaw > deltah) { + scale = width / (float)targetRect.right; + } else { + scale = height / (float)targetRect.bottom; + } + targetRect.right = (int)(targetRect.right*scale); + targetRect.bottom = (int)(targetRect.bottom*scale); + deltaw = width - targetRect.right; + deltah = height - targetRect.bottom; + } + + targetRect.offset(deltaw/2, deltah/2); + Paint paint = new Paint(); + paint.setFilterBitmap(true); + paint.setDither(true); + c.drawBitmap(bm, null, targetRect, paint); + + bm.recycle(); + return newbm; + } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ba5c9ed..9d370fc 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -22,8 +22,6 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.content.pm.ApplicationInfo; -import android.graphics.BitmapFactory; import android.graphics.Movie; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; @@ -1699,7 +1697,7 @@ public class Resources { } else { try { InputStream is = mAssets.openNonAsset( - value.assetCookie, file, AssetManager.ACCESS_BUFFER); + value.assetCookie, file, AssetManager.ACCESS_STREAMING); // System.out.println("Opened file " + file + ": " + is); dr = Drawable.createFromResourceStream(this, value, is, file, null); diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index 0ce86db..65301e4 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -64,7 +64,7 @@ public class HandlerThread extends Thread { /** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread - * has been started, this method will blocked until the looper has been initialized. + * has been started, this method will block until the looper has been initialized. * @return The looper. */ public Looper getLooper() { @@ -85,6 +85,21 @@ public class HandlerThread extends Thread { } /** + * Ask the currently running looper to quit. If the thread has not + * been started or has finished (that is if {@link #getLooper} returns + * null), then false is returned. Otherwise the looper is asked to + * quit and true is returned. + */ + public boolean quit() { + Looper looper = getLooper(); + if (looper != null) { + looper.quit(); + return true; + } + return false; + } + + /** * Returns the identifier of this thread. See Process.myTid(). */ public int getThreadId() { diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e5659d5..cd5cf10 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -28,9 +28,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.util.LogPrinter; import android.view.Gravity; import android.view.IWindowSession; import android.view.MotionEvent; @@ -74,6 +76,8 @@ public abstract class WallpaperService extends Service { private static final int MSG_WINDOW_RESIZED = 10030; private static final int MSG_TOUCH_EVENT = 10040; + private Looper mCallbackLooper; + /** * The actual implementation of a wallpaper. A wallpaper service may * have multiple instances running (for example as a real wallpaper @@ -120,6 +124,7 @@ public abstract class WallpaperService extends Service { boolean mOffsetMessageEnqueued; float mPendingXOffset; float mPendingYOffset; + boolean mPendingSync; MotionEvent mPendingMove; final BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -212,10 +217,14 @@ public abstract class WallpaperService extends Service { } @Override - public void dispatchWallpaperOffsets(float x, float y) { + public void dispatchWallpaperOffsets(float x, float y, boolean sync) { synchronized (mLock) { + if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); mPendingXOffset = x; mPendingYOffset = y; + if (sync) { + mPendingSync = true; + } if (!mOffsetMessageEnqueued) { mOffsetMessageEnqueued = true; Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); @@ -551,9 +560,12 @@ public abstract class WallpaperService extends Service { float xOffset; float yOffset; + boolean sync; synchronized (mLock) { xOffset = mPendingXOffset; yOffset = mPendingYOffset; + sync = mPendingSync; + mPendingSync = false; mOffsetMessageEnqueued = false; } if (DEBUG) Log.v(TAG, "Offsets change in " + this @@ -563,6 +575,14 @@ public abstract class WallpaperService extends Service { final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; onOffsetsChanged(xOffset, yOffset, xPixels, yPixels); + + if (sync) { + try { + if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); + mSession.wallpaperOffsetsComplete(mWindow.asBinder()); + } catch (RemoteException e) { + } + } } void detach() { @@ -622,7 +642,13 @@ public abstract class WallpaperService extends Service { IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight) { - mCaller = new HandlerCaller(context, this); + if (DEBUG && mCallbackLooper != null) { + mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG)); + } + mCaller = new HandlerCaller(context, + mCallbackLooper != null + ? mCallbackLooper : context.getMainLooper(), + this); mConnection = conn; mWindowToken = windowToken; mWindowType = windowType; @@ -736,5 +762,18 @@ public abstract class WallpaperService extends Service { return new IWallpaperServiceWrapper(this); } + /** + * This allows subclasses to change the thread that most callbacks + * occur on. Currently hidden because it is mostly needed for the + * image wallpaper (which runs in the system process and doesn't want + * to get stuck running on that seriously in use main thread). Not + * exposed right now because the semantics of this are not totally + * well defined and some callbacks can still happen on the main thread). + * @hide + */ + public void setCallbackLooper(Looper looper) { + mCallbackLooper = looper; + } + public abstract Engine onCreateEngine(); } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index ebc5f7b..b7953af 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -60,5 +60,5 @@ oneway interface IWindow { /** * Called for wallpaper windows when their offsets change. */ - void dispatchWallpaperOffsets(float x, float y); + void dispatchWallpaperOffsets(float x, float y, boolean sync); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 4d662d2..9b8b6d4 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -114,4 +114,6 @@ interface IWindowSession { * larger than the screen, set the offset within the screen. */ void setWallpaperPosition(IBinder windowToken, float x, float y); + + void wallpaperOffsetsComplete(IBinder window); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index b61465a..6748ade 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -2868,7 +2868,13 @@ public final class ViewRoot extends Handler implements ViewParent, } } - public void dispatchWallpaperOffsets(float x, float y) { + public void dispatchWallpaperOffsets(float x, float y, boolean sync) { + if (sync) { + try { + sWindowSession.wallpaperOffsetsComplete(asBinder()); + } catch (RemoteException e) { + } + } } } diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java index 5825024..35b9251 100644 --- a/core/java/com/android/internal/os/HandlerCaller.java +++ b/core/java/com/android/internal/os/HandlerCaller.java @@ -57,6 +57,13 @@ public class HandlerCaller { mCallback = callback; } + public HandlerCaller(Context context, Looper looper, Callback callback) { + mContext = context; + mMainLooper = looper; + mH = new MyHandler(mMainLooper); + mCallback = callback; + } + public SomeArgs obtainArgs() { synchronized (mH) { SomeArgs args = mArgsPool; diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java index 5357469..0bc70de 100644 --- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java +++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java @@ -20,6 +20,8 @@ import android.app.WallpaperManager; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.HandlerThread; +import android.os.Process; import android.service.wallpaper.WallpaperService; import android.view.MotionEvent; import android.view.SurfaceHolder; @@ -33,20 +35,29 @@ import android.content.BroadcastReceiver; */ public class ImageWallpaper extends WallpaperService { WallpaperManager mWallpaperManager; + private HandlerThread mThread; @Override public void onCreate() { super.onCreate(); mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE); + mThread = new HandlerThread("Wallpaper", Process.THREAD_PRIORITY_FOREGROUND); + mThread.start(); + setCallbackLooper(mThread.getLooper()); } public Engine onCreateEngine() { return new DrawableEngine(); } + @Override + public void onDestroy() { + super.onDestroy(); + mThread.quit(); + } + class DrawableEngine extends Engine { private final Object mLock = new Object(); - private final Rect mBounds = new Rect(); private WallpaperObserver mReceiver; Drawable mBackground; float mXOffset; @@ -56,6 +67,9 @@ public class ImageWallpaper extends WallpaperService { public void onReceive(Context context, Intent intent) { updateWallpaper(); drawFrame(); + // Assume we are the only one using the wallpaper in this + // process, and force a GC now to release the old wallpaper. + System.gc(); } } @@ -67,7 +81,6 @@ public class ImageWallpaper extends WallpaperService { registerReceiver(mReceiver, filter); updateWallpaper(); surfaceHolder.setSizeFromLayout(); - //setTouchEventsEnabled(true); } @Override @@ -137,11 +150,7 @@ public class ImageWallpaper extends WallpaperService { void updateWallpaper() { synchronized (mLock) { - mBackground = mWallpaperManager.getDrawable(); - mBounds.left = mBounds.top = 0; - mBounds.right = mBackground.getIntrinsicWidth(); - mBounds.bottom = mBackground.getIntrinsicHeight(); - mBackground.setBounds(mBounds); + mBackground = mWallpaperManager.getFastDrawable(); } } } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index f4f6297..b8d19ac 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -90,6 +90,12 @@ public class BaseIWindow extends IWindow.Stub { public void executeCommand(String command, String parameters, ParcelFileDescriptor out) { } - public void dispatchWallpaperOffsets(float x, float y) { + public void dispatchWallpaperOffsets(float x, float y, boolean sync) { + if (sync) { + try { + mSession.wallpaperOffsetsComplete(asBinder()); + } catch (RemoteException e) { + } + } } } |