Report wallpaper offset to the wallpaper, use this in the image wallpaper.

Wallpapers can now be just the size of the screen, and get told when their
scroll position should change to do the updating on their own.
diff --git a/api/current.xml b/api/current.xml
index b9fb85a..397e25d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26818,6 +26818,19 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
+<method name="clearWallpaperOffsets"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="windowToken" type="android.os.IBinder">
+</parameter>
+</method>
 <method name="getDesiredMinimumHeight"
  return="int"
  abstract="false"
@@ -277743,6 +277756,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="name" type="java.lang.String">
+</parameter>
 </constructor>
 <constructor name="Timer"
  type="java.util.Timer"
@@ -277763,8 +277778,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="name" type="java.lang.String">
-</parameter>
 </constructor>
 <method name="cancel"
  return="void"
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 78b6cf1..fd8776f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -335,6 +335,25 @@
     }
     
     /**
+     * Clear the offsets previously associated with this window through
+     * {@link #setWallpaperOffsets(IBinder, float, float)}.  This reverts
+     * the window to its default state, where it does not cause the wallpaper
+     * to scroll from whatever its last offsets were.
+     * 
+     * @param windowToken The window who these offsets should be associated
+     * with, as returned by {@link android.view.View#getWindowVisibility()
+     * View.getWindowToken()}.
+     */
+    public void clearWallpaperOffsets(IBinder windowToken) {
+        try {
+            ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+                    windowToken, -1, -1);
+        } catch (RemoteException e) {
+            // Ignore.
+        }
+    }
+    
+    /**
      * Remove any currently set wallpaper, reverting to the system's default
      * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
      * is broadcast.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 7017514..595b10c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,7 +24,6 @@
 import android.app.WallpaperManager;
 import android.content.Intent;
 import android.graphics.Rect;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
@@ -33,6 +32,7 @@
 import android.view.IWindowSession;
 import android.view.SurfaceHolder;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewRoot;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -50,13 +50,14 @@
         "android.service.wallpaper.WallpaperService";
 
     static final String TAG = "WallpaperService";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
     
     private static final int DO_ATTACH = 10;
     private static final int DO_DETACH = 20;
     
     private static final int MSG_UPDATE_SURFACE = 10000;
     private static final int MSG_VISIBILITY_CHANGED = 10010;
+    private static final int MSG_WALLPAPER_OFFSETS = 10020;
     
     /**
      * The actual implementation of a wallpaper.  A wallpaper service may
@@ -83,6 +84,8 @@
         int mHeight;
         int mFormat;
         int mType;
+        int mCurWidth;
+        int mCurHeight;
         boolean mDestroyReportNeeded;
         final Rect mVisibleInsets = new Rect();
         final Rect mWinFrame = new Rect();
@@ -92,6 +95,11 @@
                 = new WindowManager.LayoutParams();
         IWindowSession mSession;
 
+        final Object mLock = new Object();
+        boolean mOffsetMessageEnqueued;
+        float mPendingXOffset;
+        float mPendingYOffset;
+        
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
 
             @Override
@@ -127,6 +135,20 @@
                         visible ? 1 : 0);
                 mCaller.sendMessage(msg);
             }
+
+            @Override
+            public void dispatchWallpaperOffsets(float x, float y) {
+                synchronized (mLock) {
+                    mPendingXOffset = x;
+                    mPendingYOffset = y;
+                    if (!mOffsetMessageEnqueued) {
+                        mOffsetMessageEnqueued = true;
+                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
+                        mCaller.sendMessage(msg);
+                    }
+                }
+            }
+            
         };
         
         /**
@@ -178,6 +200,16 @@
         }
         
         /**
+         * Called to inform you of the wallpaper's offsets changing
+         * within its contain, corresponding to the container's
+         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
+         * WallpaperManager.setWallpaperOffsets()}.
+         */
+        public void onOffsetsChanged(float xOffset, float yOffset,
+                int xPixelOffset, int yPixelOffset) {
+        }
+        
+        /**
          * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
          * SurfaceHolder.Callback.surfaceChanged()}.
          */
@@ -200,9 +232,9 @@
 
         void updateSurface(boolean force) {
             int myWidth = mSurfaceHolder.getRequestedWidth();
-            if (myWidth <= 0) myWidth = mIWallpaperEngine.mReqWidth;
+            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT;
             int myHeight = mSurfaceHolder.getRequestedHeight();
-            if (myHeight <= 0) myHeight = mIWallpaperEngine.mReqHeight;
+            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT;
             
             final boolean creating = !mCreated;
             final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
@@ -254,6 +286,9 @@
                     if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
                     
+                    mCurWidth = mWinFrame.width();
+                    mCurHeight = mWinFrame.height();
+                    
                     mSurfaceHolder.mSurfaceLock.unlock();
 
                     try {
@@ -278,10 +313,12 @@
                             }
                         }
                         if (creating || formatChanged || sizeChanged) {
-                            onSurfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+                            onSurfaceChanged(mSurfaceHolder, mFormat,
+                                    mCurWidth, mCurHeight);
                             if (callbacks != null) {
                                 for (SurfaceHolder.Callback c : callbacks) {
-                                    c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+                                    c.surfaceChanged(mSurfaceHolder, mFormat,
+                                            mCurWidth, mCurHeight);
                                 }
                             }
                         }
@@ -305,7 +342,10 @@
             mCaller = wrapper.mCaller;
             mConnection = wrapper.mConnection;
             mWindowToken = wrapper.mWindowToken;
-            mSurfaceHolder.setSizeFromLayout();
+            // XXX temp -- should run in size from layout (screen) mode.
+            mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth,
+                    mIWallpaperEngine.mReqHeight);
+            //mSurfaceHolder.setSizeFromLayout();
             mInitializing = true;
             mSession = ViewRoot.getWindowSession(getMainLooper());
             mWindow.setSession(mSession);
@@ -396,6 +436,22 @@
                             + ": " + message.arg1);
                     mEngine.onVisibilityChanged(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);
+                } break;
                 default :
                     Log.w(TAG, "Unknown message type " + message.what);
             }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 99d5c0c..ec2036e 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -56,4 +56,9 @@
      * to date on the current state showing navigational focus (touch mode) too.
      */
     void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+    
+    /**
+     * Called for wallpaper windows when their offsets change.
+     */
+    void dispatchWallpaperOffsets(float x, float y);
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4546572..92e813f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import com.android.internal.view.BaseIWindow;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.CompatibilityInfo.Translator;
@@ -435,7 +437,7 @@
         updateWindow(false);
     }
 
-    private static class MyWindow extends IWindow.Stub {
+    private static class MyWindow extends BaseIWindow {
         private final WeakReference<SurfaceView> mSurfaceView;
 
         public MyWindow(SurfaceView surfaceView) {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 216fc5e..4623bb5 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -2858,6 +2858,9 @@
                 }
             }
         }
+        
+        public void dispatchWallpaperOffsets(float x, float y) {
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 2ab9e09..e4c473d 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -93,6 +93,10 @@
         mH.sendMessage(msg);
     }
     
+    public boolean hasMessages(int what) {
+        return mH.hasMessages(what);
+    }
+    
     public void sendMessage(Message msg) {
         mH.sendMessage(msg);
     }
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
index 6d50840..e1bf5ca 100644
--- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
+++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.service.wallpaper.WallpaperService;
+import android.util.Log;
 import android.view.SurfaceHolder;
 import android.content.Context;
 import android.content.IntentFilter;
@@ -63,12 +64,23 @@
 
     class DrawableEngine extends Engine {
         private final Object mLock = new Object();
+        private final Rect mBounds = new Rect();
         Drawable mBackground;
+        int mXOffset;
+        int mYOffset;
 
         @Override
         public void onCreate(SurfaceHolder surfaceHolder) {
             super.onCreate(surfaceHolder);
             mBackground = mWallpaperManager.getDrawable();
+            mBounds.left = mBounds.top = 0;
+            mBounds.right = mBackground.getIntrinsicWidth();
+            mBounds.bottom = mBackground.getIntrinsicHeight();
+            int offx = (getDesiredMinimumWidth() - mBounds.right) / 2;
+            int offy = (getDesiredMinimumHeight() - mBounds.bottom) / 2;
+            mBounds.offset(offx, offy);
+            mBackground.setBounds(mBounds);
+            surfaceHolder.setSizeFromLayout();
         }
 
         @Override
@@ -77,6 +89,14 @@
         }
         
         @Override
+        public void onOffsetsChanged(float xOffset, float yOffset,
+                int xPixels, int yPixels) {
+            mXOffset = xPixels;
+            mYOffset = yPixels;
+            drawFrame();
+        }
+
+        @Override
         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             super.onSurfaceChanged(holder, format, width, height);
             drawFrame();
@@ -94,19 +114,16 @@
         
         void drawFrame() {
             SurfaceHolder sh = getSurfaceHolder();
-            Canvas c = null;
-            try {
-                c = sh.lockCanvas();
-                if (c != null) {
-                    final Rect frame = sh.getSurfaceFrame();
-                    synchronized (mLock) {
-                        final Drawable background = mBackground;
-                        background.setBounds(frame);
-                        background.draw(c);
-                    }
+            Canvas c = sh.lockCanvas();
+            if (c != null) {
+                //final Rect frame = sh.getSurfaceFrame();
+                synchronized (mLock) {
+                    final Drawable background = mBackground;
+                    //background.setBounds(frame);
+                    c.translate(mXOffset, mYOffset);
+                    background.draw(c);
                 }
-            } finally {
-                if (c != null) sh.unlockCanvasAndPost(c);
+                sh.unlockCanvasAndPost(c);
             }
         }
 
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 7449067..a030db7 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -65,4 +65,7 @@
 
     public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
     }
+    
+    public void dispatchWallpaperOffsets(float x, float y) {
+    }
 }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 641f251..b0f049e 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -1237,6 +1237,10 @@
                 curWallpaperIndex--;
                 WindowState wallpaper = token.windows.get(curWallpaperIndex);
                 
+                if (visible) {
+                    updateWallpaperOffsetLocked(mWallpaperTarget, wallpaper, dw, dh);                        
+                }
+                
                 // First, make sure the client has the current visibility
                 // state.
                 if (wallpaper.mWallpaperVisible != visible) {
@@ -1250,10 +1254,6 @@
                     }
                 }
                 
-                if (visible) {
-                    updateWallpaperOffsetLocked(mWallpaperTarget, wallpaper, dw, dh);                        
-                }
-                
                 wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
                 if (DEBUG_LAYERS) Log.v(TAG, "Wallpaper win " + wallpaper
                         + " anim layer: " + wallpaper.mAnimLayer);
@@ -1306,18 +1306,35 @@
 
     boolean updateWallpaperOffsetLocked(WindowState target,
             WindowState wallpaperWin, int dw, int dh) {
-        int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
-        int offset = availw > 0 ? -(int)(availw*target.mWallpaperX+.5f) : 0;
-        boolean changed = wallpaperWin.mXOffset != offset;
-        if (changed) {
-            wallpaperWin.mXOffset = offset;
+        boolean changed = false;
+        if (target.mWallpaperX >= 0) {
+            int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
+            int offset = availw > 0 ? -(int)(availw*target.mWallpaperX+.5f) : 0;
+            changed = wallpaperWin.mXOffset != offset;
+            if (changed) {
+                wallpaperWin.mXOffset = offset;
+            }
         }
-        int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
-        offset = availh > 0 ? -(int)(availh*target.mWallpaperY+.5f) : 0;
-        if (wallpaperWin.mYOffset != offset) {
-            changed = true;
-            wallpaperWin.mYOffset = offset;
+        if (target.mWallpaperY >= 0) {
+            int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
+            int offset = availh > 0 ? -(int)(availh*target.mWallpaperY+.5f) : 0;
+            if (wallpaperWin.mYOffset != offset) {
+                changed = true;
+                wallpaperWin.mYOffset = offset;
+            }
         }
+        
+        if (wallpaperWin.mWallpaperX != target.mWallpaperX
+                || wallpaperWin.mWallpaperY != target.mWallpaperY) {
+            wallpaperWin.mWallpaperX = target.mWallpaperX;
+            wallpaperWin.mWallpaperY = target.mWallpaperY;
+            try {
+                wallpaperWin.mClient.dispatchWallpaperOffsets(
+                        wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY);
+            } catch (RemoteException e) {
+            }
+        }
+        
         return changed;
     }
     
@@ -5975,8 +5992,6 @@
         int mRequestedHeight;
         int mLastRequestedWidth;
         int mLastRequestedHeight;
-        int mXOffset;
-        int mYOffset;
         int mLayer;
         int mAnimLayer;
         int mLastLayer;
@@ -6061,8 +6076,14 @@
         boolean mHasLocalTransformation;
         final Transformation mTransformation = new Transformation();
 
-        float mWallpaperX = 0;
-        float mWallpaperY = 0;
+        // If a window showing a wallpaper: the requested offset for the
+        // wallpaper; if a wallpaper window: the currently applied offset.
+        float mWallpaperX = -1;
+        float mWallpaperY = -1;
+        
+        // Wallpaper windows: pixels offset based on above variables.
+        int mXOffset;
+        int mYOffset;
         
         // This is set after IWindowSession.relayout() has been called at
         // least once for the window.  It allows us to detect the situation
@@ -7181,7 +7202,7 @@
                 pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
                         pw.print(" mVScale="); pw.println(mVScale);
             }
-            if (mWallpaperX != 0 || mWallpaperY != 0) {
+            if (mWallpaperX != -1 || mWallpaperY != -1) {
                 pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
                         pw.print(" mWallpaperY="); pw.println(mWallpaperY);
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 8e7d48f..8d44ac08 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -1047,6 +1047,11 @@
             // pass for now.
         }
 
+        @SuppressWarnings("unused")
+        public void dispatchWallpaperOffsets(float x, float y) {
+            // pass for now.
+        }
+
         public IBinder asBinder() {
             // pass for now.
             return null;