Live wallpapers become a little more real.

This adds a new theme for having a wallpaper, and fixes up the window manager
to do the right thing when transitioning between a windows with and without
wallpapers (between two windows with wallpapers is not yet addressed).

The wallpaper API now has callbacks to tell you when to start/stop animating.

Also fiddle the image wallpaper to be a little more interesting.
diff --git a/api/current.xml b/api/current.xml
index 853ac9f..cc7dc23 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -9370,6 +9370,17 @@
  visibility="public"
 >
 </field>
+<field name="windowShowWallpaper"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843426"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="windowSoftInputMode"
  type="int"
  transient="false"
@@ -15002,6 +15013,39 @@
  visibility="public"
 >
 </field>
+<field name="Theme_Wallpaper"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973937"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Wallpaper_NoTitleBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973938"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Wallpaper_NoTitleBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973939"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget"
  type="int"
  transient="false"
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dbec1e6..7017514 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@
 import com.android.internal.view.BaseSurfaceHolder;
 
 import android.app.Service;
+import android.app.WallpaperManager;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -55,12 +56,14 @@
     private static final int DO_DETACH = 20;
     
     private static final int MSG_UPDATE_SURFACE = 10000;
+    private static final int MSG_VISIBILITY_CHANGED = 10010;
     
     /**
      * The actual implementation of a wallpaper.  A wallpaper service may
      * have multiple instances running (for example as a real wallpaper
      * and as a preview), each of which is represented by its own Engine
-     * instance.
+     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
+     * to return your concrete Engine implementation.
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
@@ -119,21 +122,79 @@
         };
         
         final BaseIWindow mWindow = new BaseIWindow() {
-            
+            public void dispatchAppVisibility(boolean visible) {
+                Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
+                        visible ? 1 : 0);
+                mCaller.sendMessage(msg);
+            }
         };
         
-        public void onAttach(SurfaceHolder surfaceHolder) {
+        /**
+         * Provides access to the surface in which this wallpaper is drawn.
+         */
+        public SurfaceHolder getSurfaceHolder() {
+            return mSurfaceHolder;
         }
         
-        public void onDetach() {
+        /**
+         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
+         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
+         * that the system would like this wallpaper to run in.
+         */
+        public int getDesiredMinimumWidth() {
+            return mIWallpaperEngine.mReqWidth;
         }
         
+        /**
+         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
+         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
+         * that the system would like this wallpaper to run in.
+         */
+        public int getDesiredMinimumHeight() {
+            return mIWallpaperEngine.mReqHeight;
+        }
+        
+        /**
+         * Called once to initialize the engine.  After returning, the
+         * engine's surface will be created by the framework.
+         */
+        public void onCreate(SurfaceHolder surfaceHolder) {
+        }
+        
+        /**
+         * Called right before the engine is going away.  After this the
+         * surface will be destroyed and this Engine object is no longer
+         * valid.
+         */
+        public void onDestroy() {
+        }
+        
+        /**
+         * Called to inform you of the wallpaper becoming visible or
+         * hidden.  <em>It is very important that a wallpaper only use
+         * CPU while it is visible.</em>.
+         */
+        public void onVisibilityChanged(boolean visible) {
+        }
+        
+        /**
+         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
+         * SurfaceHolder.Callback.surfaceChanged()}.
+         */
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
         }
 
+        /**
+         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
+         * SurfaceHolder.Callback.surfaceCreated()}.
+         */
         public void onSurfaceCreated(SurfaceHolder holder) {
         }
 
+        /**
+         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
+         * SurfaceHolder.Callback.surfaceDestroyed()}.
+         */
         public void onSurfaceDestroyed(SurfaceHolder holder) {
         }
 
@@ -249,14 +310,14 @@
             mSession = ViewRoot.getWindowSession(getMainLooper());
             mWindow.setSession(mSession);
             
-            onAttach(mSurfaceHolder);
+            onCreate(mSurfaceHolder);
             
             mInitializing = false;
             updateSurface(false);
         }
         
         void detach() {
-            onDetach();
+            onDestroy();
             if (mDestroyReportNeeded) {
                 mDestroyReportNeeded = false;
                 SurfaceHolder.Callback callbacks[];
@@ -330,6 +391,11 @@
                 case MSG_UPDATE_SURFACE:
                     mEngine.updateSurface(false);
                     break;
+                case MSG_VISIBILITY_CHANGED:
+                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
+                            + ": " + message.arg1);
+                    mEngine.onVisibilityChanged(message.arg1 != 0);
+                    break;
                 default :
                     Log.w(TAG, "Unknown message type " + message.what);
             }
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
index 9c99e05..7aa4f470 100644
--- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
+++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
@@ -19,7 +19,11 @@
 import android.app.WallpaperManager;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
 import android.service.wallpaper.WallpaperService;
 import android.view.SurfaceHolder;
 
@@ -29,30 +33,58 @@
 public class ImageWallpaper extends WallpaperService {
     public WallpaperManager mWallpaperManager;
     
+    static final int MSG_DRAW = 1;
+    
     class MyEngine extends Engine {
-
+        final Paint mTextPaint = new Paint();
+        float mDensity;
         Drawable mBackground;
+        long mAnimStartTime;
+        boolean mAnimLarger;
+        
+        final Handler mHandler = new Handler() {
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_DRAW:
+                        drawFrame(true);
+                        mHandler.sendEmptyMessage(MSG_DRAW);
+                        break;
+                    default:
+                        super.handleMessage(msg);
+                }
+            }
+        };
         
         @Override
-        public void onAttach(SurfaceHolder surfaceHolder) {
-            super.onAttach(surfaceHolder);
+        public void onCreate(SurfaceHolder surfaceHolder) {
+            super.onCreate(surfaceHolder);
             mBackground = mWallpaperManager.getDrawable();
+            mTextPaint.setAntiAlias(true);
+            mDensity = getResources().getDisplayMetrics().density;
+            mTextPaint.setTextSize(30 * mDensity);
+            mTextPaint.setShadowLayer(5*mDensity, 3*mDensity, 3*mDensity, 0xff000000);
+            mTextPaint.setARGB(255, 255, 255, 255);
+            mTextPaint.setTextAlign(Paint.Align.CENTER);
         }
 
         @Override
+        public void onVisibilityChanged(boolean visible) {
+            mHandler.removeMessages(MSG_DRAW);
+            if (visible) {
+                mHandler.sendEmptyMessage(MSG_DRAW);
+                mAnimStartTime = SystemClock.uptimeMillis();
+                mAnimLarger = true;
+            } else {
+                drawFrame(false);
+            }
+        }
+        
+        @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             super.onSurfaceChanged(holder, format, width, height);
-            Canvas c = holder.lockCanvas();
-            mBackground.setBounds(0, 0, width, height);
-            mBackground.draw(c);
-            Paint paint = new Paint();
-            paint.setAntiAlias(true);
-            final float density = getResources().getDisplayMetrics().density;
-            paint.setTextSize(30 * density);
-            paint.setShadowLayer(5*density, 3*density, 3*density, 0xff000000);
-            paint.setARGB(255, 255, 255, 255);
-            c.drawText("Am I live?", 10, 60*density, paint);
-            holder.unlockCanvasAndPost(c);
+            drawFrame(false);
         }
 
         @Override
@@ -65,6 +97,35 @@
             super.onSurfaceDestroyed(holder);
         }
         
+        void drawFrame(boolean drawText) {
+            SurfaceHolder sh = getSurfaceHolder();
+            Canvas c = sh.lockCanvas();
+            if (c != null) {
+                final Rect frame = sh.getSurfaceFrame();
+                mBackground.setBounds(frame);
+                mBackground.draw(c);
+                
+                if (drawText) {
+                    // Figure out animation.
+                    long now = SystemClock.uptimeMillis();
+                    while (mAnimStartTime < (now-1000)) {
+                        mAnimStartTime += 1000;
+                        mAnimLarger = !mAnimLarger;
+                    }
+                    float size = (now-mAnimStartTime) / (float)1000;
+                    if (!mAnimLarger) size = 1-size;
+                    int alpha = (int)(255*(size*size));
+                    mTextPaint.setARGB(alpha, 255, 255, 255);
+                    mTextPaint.setShadowLayer(5*mDensity, 3*mDensity, 3*mDensity,
+                            alpha<<24);
+                    mTextPaint.setTextSize(100 * mDensity * size);
+                    c.drawText("Am I live?",
+                            frame.left + (frame.right-frame.left)/2,
+                            frame.top + (frame.bottom-frame.top)/2, mTextPaint);
+                }
+            }
+            sh.unlockCanvasAndPost(c);
+        }
     }
     
     @Override
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ad6d94f..01253d3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -215,6 +215,9 @@
         <attr name="windowIsFloating" format="boolean" />
         <!-- Flag indicating whether this is a translucent window. -->
         <attr name="windowIsTranslucent" format="boolean" />
+        <!-- Flag indicating that this window's background should be the
+        	 user's current wallpaper. -->
+        <attr name="windowShowWallpaper" format="boolean" />
         <!-- This Drawable is overlaid over the foreground of the Window's content area, usually
              to place a shadow below the title.  -->
         <attr name="windowContentOverlay" format="reference" />
@@ -900,6 +903,7 @@
         <attr name="windowFullscreen" />
         <attr name="windowIsFloating" />
         <attr name="windowIsTranslucent" />
+        <attr name="windowShowWallpaper" />
         <attr name="windowAnimationStyle" />
         <attr name="windowSoftInputMode" />
         <attr name="windowDisablePreview" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f8dd4c1..ef87b17 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1167,9 +1167,13 @@
 
   <public type="attr" name="accountType" />
   <public type="attr" name="contentAuthority" />
+  <public type="attr" name="windowShowWallpaper" />
 
   <public type="drawable" name="stat_sys_vp_phone_call" />
   <public type="drawable" name="stat_sys_vp_phone_call_on_hold" />
   
+  <public type="style" name="Theme.Wallpaper" />
+  <public type="style" name="Theme.Wallpaper.NoTitleBar" />
+  <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" />
 
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index e3fffb7..1e3a4a8 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -280,6 +280,25 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
     
+    <!-- Default theme for windows that want to have the user's selected
+    	 wallpaper appear behind them.  -->
+    <style name="Theme.Wallpaper">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowShowWallpaper">true</item>
+    </style>
+
+    <!-- Variant of the translucent theme with no title bar -->
+    <style name="Theme.Wallpaper.NoTitleBar">
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Variant of the translucent theme that has no title bar and
+         fills the entire screen -->
+    <style name="Theme.Wallpaper.NoTitleBar.Fullscreen">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Default theme for translucent activities, that is windows that allow you
          to see through them to the windows behind.  This sets up the translucent
          flag and appropriate animations for your windows.  -->
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 1b7efeb..69c48d3 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -403,6 +403,11 @@
 
     final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
     
+    // If non-null, this is the currently visible window that is associated
+    // with the wallpaper.
+    WindowState mWallpaperTarget = null;
+    int mWallpaperAnimLayerAdjustment;
+    
     AppWindowToken mFocusedApp = null;
 
     PowerManagerService mPowerManager;
@@ -1180,15 +1185,23 @@
         int N = localmWindows.size();
         WindowState w = null;
         int i = N;
+        boolean visible = false;
         while (i > 0) {
             i--;
             w = (WindowState)localmWindows.get(i);
-            if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isVisibleOrAdding()) {
+            if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()) {
+                visible = true;
                 break;
             }
         }
 
-        if (w != null) {
+        if (!visible) w = null;
+        mWallpaperTarget = w;
+        
+        if (visible) {
+            mWallpaperAnimLayerAdjustment = w.mAppToken != null
+                    ? w.mAppToken.animLayerAdjustment : 0;
+            
             // Now w is the window we are supposed to be behind...  but we
             // need to be sure to also be behind any of its attached windows,
             // AND any starting window associated with it.
@@ -1220,6 +1233,24 @@
             while (curWallpaperIndex > 0) {
                 curWallpaperIndex--;
                 WindowState wallpaper = token.windows.get(curWallpaperIndex);
+                
+                // First, make sure the client has the current visibility
+                // state.
+                if (wallpaper.mWallpaperVisible != visible) {
+                    wallpaper.mWallpaperVisible = visible;
+                    try {
+                        if (DEBUG_VISIBILITY) Log.v(TAG,
+                                "Setting visibility of wallpaper " + wallpaper
+                                + ": " + visible);
+                        wallpaper.mClient.dispatchAppVisibility(visible);
+                    } catch (RemoteException e) {
+                    }
+                }
+                
+                wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
+                if (DEBUG_LAYERS) Log.v(TAG, "Wallpaper win " + wallpaper
+                        + " anim layer: " + wallpaper.mAnimLayer);
+                
                 // First, if this window is at the current index, then all
                 // is well.
                 if (wallpaper == w) {
@@ -1248,6 +1279,24 @@
         return changed;
     }
 
+    void setWallpaperAnimLayerAdjustment(int adj) {
+        if (DEBUG_LAYERS) Log.v(TAG, "Setting wallpaper layer adj to " + adj);
+        mWallpaperAnimLayerAdjustment = adj;
+        int curTokenIndex = mWallpaperTokens.size();
+        while (curTokenIndex > 0) {
+            curTokenIndex--;
+            WindowToken token = mWallpaperTokens.get(curTokenIndex);
+            int curWallpaperIndex = token.windows.size();
+            while (curWallpaperIndex > 0) {
+                curWallpaperIndex--;
+                WindowState wallpaper = token.windows.get(curWallpaperIndex);
+                wallpaper.mAnimLayer = wallpaper.mLayer + adj;
+                if (DEBUG_LAYERS) Log.v(TAG, "Wallpaper win " + wallpaper
+                        + " anim layer: " + wallpaper.mAnimLayer);
+            }
+        }
+    }
+
     public int addWindow(Session session, IWindow client,
             WindowManager.LayoutParams attrs, int viewVisibility,
             Rect outContentInsets) {
@@ -1867,6 +1916,9 @@
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client);
             if (win != null && win.finishDrawingLocked()) {
+                if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+                    adjustWallpaperWindowsLocked();
+                }
                 mLayoutNeeded = true;
                 performLayoutAndPlaceSurfacesLocked();
             }
@@ -5842,6 +5894,7 @@
         Surface mSurface;
         boolean mAttachedHidden;    // is our parent window hidden?
         boolean mLastHidden;        // was this window last hidden?
+        boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
         int mRequestedWidth;
         int mRequestedHeight;
         int mLastRequestedWidth;
@@ -6518,6 +6571,8 @@
             mAnimLayer = mLayer;
             if (mIsImWindow) {
                 mAnimLayer += mInputMethodAnimLayerAdjustment;
+            } else if (mIsWallpaper) {
+                mAnimLayer += mWallpaperAnimLayerAdjustment;
             }
             if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
                     + " anim layer: " + mAnimLayer);
@@ -6604,6 +6659,19 @@
             Transformation appTransformation =
                     (mAppToken != null && mAppToken.hasTransformation)
                     ? mAppToken.transformation : null;
+            
+            // Wallpapers are animated based on the "real" window they
+            // are currently targeting.
+            if (mAttrs.type == TYPE_WALLPAPER && mWallpaperTarget != null) {
+                if (mWallpaperTarget.mHasLocalTransformation) {
+                    attachedTransformation = mWallpaperTarget.mTransformation;
+                }
+                if (mWallpaperTarget.mAppToken != null &&
+                        mWallpaperTarget.mAppToken.hasTransformation) {
+                    appTransformation = mWallpaperTarget.mAppToken.transformation;
+                }
+            }
+            
             if (selfTransformation || attachedTransformation != null
                     || appTransformation != null) {
                 // cache often used attributes locally
@@ -6926,7 +6994,8 @@
             if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
                 pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
                         pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
-                        pw.print(" mIsFloatingLayer="); pw.println(mIsFloatingLayer);
+                        pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
+                        pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
             }
             pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
                     pw.print(" mSubLayer="); pw.print(mSubLayer);
@@ -7212,6 +7281,9 @@
                 if (w == mInputMethodTarget) {
                     setInputMethodAnimLayerAdjustment(adj);
                 }
+                if (w == mWallpaperTarget) {
+                    setWallpaperAnimLayerAdjustment(adj);
+                }
             }
         }
 
@@ -7978,6 +8050,8 @@
             }
             if (w.mIsImWindow) {
                 w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+            } else if (w.mIsWallpaper) {
+                w.mAnimLayer += mWallpaperAnimLayerAdjustment;
             }
             if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
                     + w.mAnimLayer);
@@ -8659,7 +8733,8 @@
                     }
 
                     boolean opaqueDrawn = w.isOpaqueDrawn();
-                    if (opaqueDrawn && w.isFullscreen(dw, dh)) {
+                    if ((opaqueDrawn && w.isFullscreen(dw, dh))
+                            || attrs.type == TYPE_WALLPAPER) {
                         // This window completely covers everything behind it,
                         // so we want to leave all of them as unblurred (for
                         // performance reasons).
@@ -9316,7 +9391,9 @@
                 pw.print( "  no DimAnimator ");
             }
             pw.print("  mInputMethodAnimLayerAdjustment=");
-                    pw.println(mInputMethodAnimLayerAdjustment);
+                    pw.print(mInputMethodAnimLayerAdjustment);
+                    pw.print("  mWallpaperAnimLayerAdjustment=");
+                    pw.println(mWallpaperAnimLayerAdjustment);
             pw.print("  mDisplayFrozen="); pw.print(mDisplayFrozen);
                     pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
                     pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen);