From 284ac93aa30642fda87d5c40263a1263677c21cd Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 28 Aug 2009 10:34:25 -0700 Subject: [PATCH] More work on wallpapers: animations, lifecycle, scaling, etc. Yet more work on improving the behavior of wallpapers. This fixes a few problems in their lifecycle (corresponding change in the picker also required for this), makes their animations better for hardware that supports alpha fades, adds animations for the wallpapers themselves, eliminates fixed size wallpapers, and adjusts the API for retrieving a wallpaper bitmap to take care of scaling the raw wallpaper image to match the current desired width and height. Change-Id: If1c0aaceba4ea4e175dcb7a8416ca7ddbb9bfa6f --- core/java/android/app/IWallpaperManager.aidl | 4 +- core/java/android/app/WallpaperManager.java | 94 ++++++++-- .../service/wallpaper/IWallpaperEngine.aidl | 2 + .../service/wallpaper/WallpaperService.java | 162 ++++++++++++++---- .../service/wallpaper/ImageWallpaper.java | 38 ++-- core/res/AndroidManifest.xml | 2 +- .../anim/wallpaper_activity_close_enter.xml | 2 + .../anim/wallpaper_activity_close_exit.xml | 2 + .../anim/wallpaper_activity_open_enter.xml | 2 + .../res/anim/wallpaper_activity_open_exit.xml | 2 + core/res/res/anim/wallpaper_enter.xml | 29 ++++ core/res/res/anim/wallpaper_exit.xml | 29 ++++ core/res/res/values/styles.xml | 6 + .../server/WallpaperManagerService.java | 18 +- .../android/server/WindowManagerService.java | 70 ++++++-- 15 files changed, 376 insertions(+), 86 deletions(-) create mode 100644 core/res/res/anim/wallpaper_enter.xml create mode 100644 core/res/res/anim/wallpaper_exit.xml diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 77416688b8f08..4d1e2540b8ee7 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -16,6 +16,7 @@ package android.app; +import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.app.IWallpaperManagerCallback; import android.content.ComponentName; @@ -36,7 +37,8 @@ interface IWallpaperManager { /** * Get the wallpaper. */ - ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb); + ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, + out Bundle outParams); /** * Clear the wallpaper. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index c5ca0a3ac3d05..7669306a157f2 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -21,13 +21,18 @@ import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.DisplayMetrics; import android.util.Log; import android.view.ViewRoot; @@ -51,7 +56,7 @@ public class WallpaperManager { static class Globals extends IWallpaperManagerCallback.Stub { private IWallpaperManager mService; - private Drawable mWallpaper; + private Bitmap mWallpaper; Globals() { IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); @@ -69,7 +74,7 @@ public class WallpaperManager { } } - public Drawable peekWallpaper(Context context) { + public Bitmap peekWallpaperBitmap(Context context) { synchronized (this) { if (mWallpaper != null) { return mWallpaper; @@ -79,18 +84,82 @@ public class WallpaperManager { } } - private Drawable getCurrentWallpaperLocked(Context context) { + private Bitmap getCurrentWallpaperLocked(Context context) { try { - ParcelFileDescriptor fd = mService.getWallpaper(this); + Bundle params = new Bundle(); + ParcelFileDescriptor fd = mService.getWallpaper(this, params); if (fd != null) { - Bitmap bm = BitmapFactory.decodeFileDescriptor( - fd.getFileDescriptor(), null, null); - if (bm != null) { - // For now clear the density until we figure out how - // to deal with it for wallpapers. - bm.setDensity(0); - return new BitmapDrawable(context.getResources(), bm); + int width = params.getInt("width", 0); + int height = params.getInt("height", 0); + + if (width <= 0 || height <= 0) { + // Degenerate case: no size requested, just load + // bitmap as-is. + Bitmap bm = BitmapFactory.decodeFileDescriptor( + fd.getFileDescriptor(), null, null); + try { + fd.close(); + } catch (IOException e) { + } + if (bm != null) { + bm.setDensity(DisplayMetrics.DENSITY_DEVICE); + } + return bm; } + + // 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.decodeFileDescriptor( + fd.getFileDescriptor(), null, options); + try { + 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; + + 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; } } catch (RemoteException e) { } @@ -149,7 +218,8 @@ public class WallpaperManager { * null pointer if these is none. */ public Drawable peekDrawable() { - return getGlobals().peekWallpaper(mContext); + Bitmap bm = getGlobals().peekWallpaperBitmap(mContext); + return bm != null ? new BitmapDrawable(mContext.getResources(), bm) : null; } /** diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index 9586e34e40894..bbd9ddeada9b8 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -20,5 +20,7 @@ package android.service.wallpaper; * @hide */ oneway interface IWallpaperEngine { + void setDesiredSize(int width, int height); + void setVisibility(boolean visible); void destroy(); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 629e97e8f5d42..2cdfc665f69f1 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -55,6 +55,7 @@ public abstract class WallpaperService extends Service { private static final int DO_ATTACH = 10; private static final int DO_DETACH = 20; + private static final int DO_SET_DESIRED_SIZE = 30; private static final int MSG_UPDATE_SURFACE = 10000; private static final int MSG_VISIBILITY_CHANGED = 10010; @@ -78,6 +79,8 @@ public abstract class WallpaperService extends Service { IBinder mWindowToken; boolean mInitializing = true; + boolean mVisible; + boolean mDestroyed; // Current window state. boolean mCreated; @@ -129,8 +132,15 @@ public abstract class WallpaperService extends Service { return mIsCreating; } + @Override + public void setFixedSize(int width, int height) { + throw new UnsupportedOperationException( + "Wallpapers currently only support sizing from layout"); + } + public void setKeepScreenOn(boolean screenOn) { - // Ignore. + throw new UnsupportedOperationException( + "Wallpapers do not support keep screen on"); } }; @@ -166,9 +176,13 @@ public abstract class WallpaperService extends Service { @Override public void dispatchAppVisibility(boolean visible) { - Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, - visible ? 1 : 0); - mCaller.sendMessage(msg); + // We don't do this in preview mode; we'll let the preview + // activity tell us when to run. + if (!mIWallpaperEngine.mIsPreview) { + Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, + visible ? 1 : 0); + mCaller.sendMessage(msg); + } } @Override @@ -211,6 +225,15 @@ public abstract class WallpaperService extends Service { return mIWallpaperEngine.mReqHeight; } + /** + * Return whether the wallpaper is currently visible to the user, + * this is the last value supplied to + * {@link #onVisibilityChanged(boolean)}. + */ + public boolean isVisible() { + return mVisible; + } + /** * Returns true if this engine is running in preview mode -- that is, * it is being shown to the user before they select it as the actual @@ -279,6 +302,13 @@ public abstract class WallpaperService extends Service { int xPixelOffset, int yPixelOffset) { } + /** + * Called when an application has changed the desired virtual size of + * the wallpaper. + */ + public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { + } + /** * Convenience for {@link SurfaceHolder.Callback#surfaceChanged * SurfaceHolder.Callback.surfaceChanged()}. @@ -301,6 +331,10 @@ public abstract class WallpaperService extends Service { } void updateSurface(boolean forceRelayout, boolean forceReport) { + if (mDestroyed) { + Log.w(TAG, "Ignoring updateSurface: destroyed"); + } + int myWidth = mSurfaceHolder.getRequestedWidth(); if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT; int myHeight = mSurfaceHolder.getRequestedHeight(); @@ -314,7 +348,7 @@ public abstract class WallpaperService extends Service { if (forceRelayout || creating || formatChanged || sizeChanged || typeChanged || flagsChanged) { - if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + if (DEBUG) Log.v(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged); try { @@ -343,6 +377,8 @@ public abstract class WallpaperService extends Service { if (!mCreated) { mLayout.type = mIWallpaperEngine.mWindowType; mLayout.gravity = Gravity.LEFT|Gravity.TOP; + mLayout.windowAnimations = + com.android.internal.R.style.Animation_Wallpaper; mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets); } @@ -354,7 +390,7 @@ public abstract class WallpaperService extends Service { View.VISIBLE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurfaceHolder.mSurface); - if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface + if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrame); int w = mWinFrame.width(); @@ -384,6 +420,8 @@ public abstract class WallpaperService extends Service { if (!mCreated) { mIsCreating = true; + if (DEBUG) Log.v(TAG, "onSurfaceCreated(" + + mSurfaceHolder + "): " + this); onSurfaceCreated(mSurfaceHolder); if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { @@ -399,6 +437,10 @@ public abstract class WallpaperService extends Service { + " formatChanged=" + formatChanged + " sizeChanged=" + sizeChanged, e); } + if (DEBUG) Log.v(TAG, "onSurfaceChanged(" + + mSurfaceHolder + ", " + mFormat + + ", " + mCurWidth + ", " + mCurHeight + + "): " + this); onSurfaceChanged(mSurfaceHolder, mFormat, mCurWidth, mCurHeight); if (callbacks != null) { @@ -425,26 +467,73 @@ public abstract class WallpaperService extends Service { void attach(IWallpaperEngineWrapper wrapper) { if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); + if (mDestroyed) { + return; + } + mIWallpaperEngine = wrapper; mCaller = wrapper.mCaller; mConnection = wrapper.mConnection; mWindowToken = wrapper.mWindowToken; - // XXX temp -- should run in size from layout (screen) mode. - mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth, - mIWallpaperEngine.mReqHeight); - //mSurfaceHolder.setSizeFromLayout(); + mSurfaceHolder.setSizeFromLayout(); mInitializing = true; mSession = ViewRoot.getWindowSession(getMainLooper()); mWindow.setSession(mSession); + if (DEBUG) Log.v(TAG, "onCreate(): " + this); onCreate(mSurfaceHolder); mInitializing = false; updateSurface(false, false); } + void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { + if (!mDestroyed) { + if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" + + desiredWidth + "," + desiredHeight + "): " + this); + onDesiredSizeChanged(desiredWidth, desiredHeight); + } + } + + void doVisibilityChanged(boolean visible) { + if (!mDestroyed) { + mVisible = visible; + if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible + + "): " + this); + onVisibilityChanged(visible); + } + } + + void doOffsetsChanged() { + if (mDestroyed) { + return; + } + + float xOffset; + float yOffset; + synchronized (mLock) { + xOffset = mPendingXOffset; + yOffset = mPendingYOffset; + mOffsetMessageEnqueued = false; + } + if (DEBUG) Log.v(TAG, "Offsets change in " + this + + ": " + xOffset + "," + yOffset); + final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; + final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; + final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; + final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; + onOffsetsChanged(xOffset, yOffset, xPixels, yPixels); + } + void detach() { - onDestroy(); + mDestroyed = true; + + if (mVisible) { + mVisible = false; + if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); + onVisibilityChanged(false); + } + if (mDestroyReportNeeded) { mDestroyReportNeeded = false; SurfaceHolder.Callback callbacks[]; @@ -456,7 +545,14 @@ public abstract class WallpaperService extends Service { for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); } + if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" + + mSurfaceHolder + "): " + this); + onSurfaceDestroyed(mSurfaceHolder); } + + if (DEBUG) Log.v(TAG, "onDestroy(): " + this); + onDestroy(); + if (mCreated) { try { mSession.remove(mWindow); @@ -492,16 +588,21 @@ public abstract class WallpaperService extends Service { mReqWidth = reqWidth; mReqHeight = reqHeight; - try { - conn.attachEngine(this); - } catch (RemoteException e) { - destroy(); - } - Message msg = mCaller.obtainMessage(DO_ATTACH); mCaller.sendMessage(msg); } + public void setDesiredSize(int width, int height) { + Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); + mCaller.sendMessage(msg); + } + + public void setVisibility(boolean visible) { + Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, + visible ? 1 : 0); + mCaller.sendMessage(msg); + } + public void destroy() { Message msg = mCaller.obtainMessage(DO_DETACH); mCaller.sendMessage(msg); @@ -510,6 +611,12 @@ public abstract class WallpaperService extends Service { public void executeMessage(Message message) { switch (message.what) { case DO_ATTACH: { + try { + mConnection.attachEngine(this); + } catch (RemoteException e) { + Log.w(TAG, "Wallpaper host disappeared", e); + return; + } Engine engine = onCreateEngine(); mEngine = engine; engine.attach(this); @@ -519,29 +626,20 @@ public abstract class WallpaperService extends Service { mEngine.detach(); return; } + case DO_SET_DESIRED_SIZE: { + mEngine.doDesiredSizeChanged(message.arg1, message.arg2); + return; + } case MSG_UPDATE_SURFACE: mEngine.updateSurface(true, false); break; case MSG_VISIBILITY_CHANGED: if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine + ": " + message.arg1); - mEngine.onVisibilityChanged(message.arg1 != 0); + mEngine.doVisibilityChanged(message.arg1 != 0); break; case MSG_WALLPAPER_OFFSETS: { - float xOffset; - float yOffset; - synchronized (mEngine.mLock) { - xOffset = mEngine.mPendingXOffset; - yOffset = mEngine.mPendingYOffset; - mEngine.mOffsetMessageEnqueued = false; - } - if (DEBUG) Log.v(TAG, "Offsets change in " + mEngine - + ": " + xOffset + "," + yOffset); - final int availw = mReqWidth-mEngine.mCurWidth; - final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; - final int availh = mReqHeight-mEngine.mCurHeight; - final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; - mEngine.onOffsetsChanged(xOffset, yOffset, xPixels, yPixels); + mEngine.doOffsetsChanged(); } break; case MSG_WINDOW_RESIZED: { final boolean reportDraw = message.arg1 != 0; diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java index c4eb31f4aa9eb..5357469a2f367 100644 --- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java +++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java @@ -33,51 +33,49 @@ import android.content.BroadcastReceiver; */ public class ImageWallpaper extends WallpaperService { WallpaperManager mWallpaperManager; - ImageWallpaper.DrawableEngine mEngine; - private WallpaperObserver mReceiver; @Override public void onCreate() { super.onCreate(); mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE); - IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); - mReceiver = new WallpaperObserver(); - registerReceiver(mReceiver, filter); - } - - @Override - public void onDestroy() { - super.onDestroy(); - unregisterReceiver(mReceiver); } public Engine onCreateEngine() { - mEngine = new DrawableEngine(); - return mEngine; - } - - class WallpaperObserver extends BroadcastReceiver { - public void onReceive(Context context, Intent intent) { - mEngine.updateWallpaper(); - mEngine.drawFrame(); - } + return new DrawableEngine(); } class DrawableEngine extends Engine { private final Object mLock = new Object(); private final Rect mBounds = new Rect(); + private WallpaperObserver mReceiver; Drawable mBackground; float mXOffset; float mYOffset; + class WallpaperObserver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + updateWallpaper(); + drawFrame(); + } + } + @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); + IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); + mReceiver = new WallpaperObserver(); + registerReceiver(mReceiver, filter); updateWallpaper(); surfaceHolder.setSizeFromLayout(); //setTouchEventsEnabled(true); } + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } + @Override public void onVisibilityChanged(boolean visible) { drawFrame(); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4f789dd0819b3..1ea5fa32eb2ba 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -898,7 +898,7 @@ + android:protectionLevel="signatureOrSystem" /> diff --git a/core/res/res/anim/wallpaper_activity_close_enter.xml b/core/res/res/anim/wallpaper_activity_close_enter.xml index fc6e332a381bc..9e9bd802fe1a0 100644 --- a/core/res/res/anim/wallpaper_activity_close_enter.xml +++ b/core/res/res/anim/wallpaper_activity_close_enter.xml @@ -27,4 +27,6 @@ android:duration="@android:integer/config_mediumAnimTime" /> + diff --git a/core/res/res/anim/wallpaper_activity_close_exit.xml b/core/res/res/anim/wallpaper_activity_close_exit.xml index edd00fd1d7a6b..badbbf02c0068 100644 --- a/core/res/res/anim/wallpaper_activity_close_exit.xml +++ b/core/res/res/anim/wallpaper_activity_close_exit.xml @@ -26,4 +26,6 @@ android:duration="@android:integer/config_mediumAnimTime" /> + diff --git a/core/res/res/anim/wallpaper_activity_open_enter.xml b/core/res/res/anim/wallpaper_activity_open_enter.xml index 5b44d971d9cde..e60bac28776eb 100644 --- a/core/res/res/anim/wallpaper_activity_open_enter.xml +++ b/core/res/res/anim/wallpaper_activity_open_enter.xml @@ -26,4 +26,6 @@ android:duration="@android:integer/config_mediumAnimTime" /> + diff --git a/core/res/res/anim/wallpaper_activity_open_exit.xml b/core/res/res/anim/wallpaper_activity_open_exit.xml index fa39beefdc9af..01abbb717a8f0 100644 --- a/core/res/res/anim/wallpaper_activity_open_exit.xml +++ b/core/res/res/anim/wallpaper_activity_open_exit.xml @@ -27,4 +27,6 @@ android:duration="@android:integer/config_mediumAnimTime" /> + diff --git a/core/res/res/anim/wallpaper_enter.xml b/core/res/res/anim/wallpaper_enter.xml new file mode 100644 index 0000000000000..981f5f686b19a --- /dev/null +++ b/core/res/res/anim/wallpaper_enter.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/core/res/res/anim/wallpaper_exit.xml b/core/res/res/anim/wallpaper_exit.xml new file mode 100644 index 0000000000000..23060718617ee --- /dev/null +++ b/core/res/res/anim/wallpaper_exit.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 3950cb16d5dbe..55f81675ec3af 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -160,6 +160,12 @@ @anim/fade_out + + +