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 + + +