Merge "Implement issue #3201795: Improve transition when keyboard comes up"
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 6f4abef..24a9f87 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -94,13 +94,18 @@
      */
     abstract void setup(int width, int height);
 
+    interface HardwareDrawCallbacks {
+        void onHardwarePreDraw(Canvas canvas);
+        void onHardwarePostDraw(Canvas canvas);
+    }
+
     /**
      * Draws the specified view.
      * 
      * @param view The view to draw.
      * @param attachInfo AttachInfo tied to the specified view.
      */
-    abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
+    abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
 
     /**
      * Creates a new display list that can be used to record batches of
@@ -456,7 +461,7 @@
         }
 
         @Override
-        void draw(View view, View.AttachInfo attachInfo, int yOffset) {
+        void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
             if (canDraw()) {
                 attachInfo.mDrawingTime = SystemClock.uptimeMillis();
                 attachInfo.mIgnoreDirtyState = true;
@@ -473,11 +478,12 @@
 
                 Canvas canvas = mCanvas;
                 int saveCount = canvas.save();
-                canvas.translate(0, -yOffset);
+                callbacks.onHardwarePreDraw(canvas);
 
                 try {
                     view.draw(canvas);
                 } finally {
+                    callbacks.onHardwarePostDraw(canvas);
                     canvas.restoreToCount(saveCount);
                 }
 
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index cb7d0e2..77083a9 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -25,7 +25,9 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -57,6 +59,8 @@
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
@@ -79,7 +83,8 @@
  * {@hide}
  */
 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
-public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks {
+public final class ViewRoot extends Handler implements ViewParent,
+        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
     private static final String TAG = "ViewRoot";
     private static final boolean DBG = false;
     private static final boolean SHOW_FPS = false;
@@ -213,6 +218,10 @@
     int mScrollY;
     int mCurScrollY;
     Scroller mScroller;
+    Bitmap mResizeBitmap;
+    long mResizeBitmapStartTime;
+    int mResizeBitmapDuration;
+    static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
 
     final ViewConfiguration mViewConfiguration;
 
@@ -626,6 +635,13 @@
         return mAppVisible ? mView.getVisibility() : View.GONE;
     }
 
+    void disposeResizeBitmap() {
+        if (mResizeBitmap != null) {
+            mResizeBitmap.recycle();
+            mResizeBitmap = null;
+        }
+    }
+
     private void performTraversals() {
         // cache mView since it is used so much below...
         final View host = mView;
@@ -734,6 +750,48 @@
                 ensureTouchModeLocally(mAddedTouchMode);
             } else {
                 if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
+                    if (mWidth > 0 && mHeight > 0 &&
+                            mSurface != null && mSurface.isValid() &&
+                            mAttachInfo.mHardwareRenderer != null &&
+                            mAttachInfo.mHardwareRenderer.isEnabled() &&
+                            lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
+
+                        disposeResizeBitmap();
+
+                        boolean completed = false;
+                        try {
+                            mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight,
+                                    Bitmap.Config.ARGB_8888);
+                            mResizeBitmap.setHasAlpha(false);
+                            Canvas canvas = new Canvas(mResizeBitmap);
+                            int yoff;
+                            final boolean scrolling = mScroller != null
+                                    && mScroller.computeScrollOffset();
+                            if (scrolling) {
+                                yoff = mScroller.getCurrY();
+                                mScroller.abortAnimation();
+                            } else {
+                                yoff = mScrollY;
+                            }
+                            canvas.translate(0, -yoff);
+                            if (mTranslator != null) {
+                                mTranslator.translateCanvas(canvas);
+                            }
+                            canvas.setScreenDensity(mAttachInfo.mScalingRequired
+                                    ? DisplayMetrics.DENSITY_DEVICE : 0);
+                            mView.draw(canvas);
+                            mResizeBitmapStartTime = SystemClock.uptimeMillis();
+                            mResizeBitmapDuration = mView.getResources().getInteger(
+                                    com.android.internal.R.integer.config_mediumAnimTime);
+                            completed = true;
+                        } catch (OutOfMemoryError e) {
+                            Log.w(TAG, "Not enough memory for content change anim buffer", e);
+                        } finally {
+                            if (!completed) {
+                                mResizeBitmap = null;
+                            }
+                        }
+                    }
                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
                     host.fitSystemWindows(mAttachInfo.mContentInsets);
                     insetsChanged = true;
@@ -787,7 +845,6 @@
                     // Maybe we can just try the next size up, and see if that reduces
                     // the height?
                     if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) {
-                        Log.v(TAG, "Good!");
                         goodMeasure = true;
                     } else {
                         // Didn't fit in that size... try expanding a bit.
@@ -972,6 +1029,7 @@
                     if (mScroller != null) {
                         mScroller.abortAnimation();
                     }
+                    disposeResizeBitmap();
                 }
             } catch (RemoteException e) {
             }
@@ -1310,6 +1368,22 @@
         return measureSpec;
     }
 
+    int mHardwareYOffset;
+    int mResizeAlpha;
+    final Paint mResizePaint = new Paint();
+
+    public void onHardwarePreDraw(Canvas canvas) {
+        canvas.translate(0, -mHardwareYOffset);
+    }
+
+    public void onHardwarePostDraw(Canvas canvas) {
+        if (mResizeBitmap != null) {
+            canvas.translate(0, mHardwareYOffset);
+            mResizePaint.setAlpha(mResizeAlpha);
+            canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint);
+        }
+    }
+
     private void draw(boolean fullRedrawNeeded) {
         Surface surface = mSurface;
         if (surface == null || !surface.isValid()) {
@@ -1334,8 +1408,8 @@
         }
 
         int yoff;
-        final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
-        if (scrolling) {
+        boolean animating = mScroller != null && mScroller.computeScrollOffset();
+        if (animating) {
             yoff = mScroller.getCurrY();
         } else {
             yoff = mScrollY;
@@ -1347,10 +1421,29 @@
         float appScale = mAttachInfo.mApplicationScale;
         boolean scalingRequired = mAttachInfo.mScalingRequired;
 
+        int resizeAlpha = 0;
+        if (mResizeBitmap != null) {
+            long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime;
+            if (deltaTime < mResizeBitmapDuration) {
+                float amt = deltaTime/(float)mResizeBitmapDuration;
+                amt = mResizeInterpolator.getInterpolation(amt);
+                animating = true;
+                resizeAlpha = 255 - (int)(amt*255);
+            } else {
+                disposeResizeBitmap();
+            }
+        }
+
         Rect dirty = mDirty;
         if (mSurfaceHolder != null) {
             // The app owns the surface, we won't draw.
             dirty.setEmpty();
+            if (animating) {
+                if (mScroller != null) {
+                    mScroller.abortAnimation();
+                }
+                disposeResizeBitmap();
+            }
             return;
         }
 
@@ -1363,10 +1456,12 @@
             if (!dirty.isEmpty() || mIsAnimating) {
                 mIsAnimating = false;
                 dirty.setEmpty();
-                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff);
+                mHardwareYOffset = yoff;
+                mResizeAlpha = resizeAlpha;
+                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
             }
 
-            if (scrolling) {
+            if (animating) {
                 mFullRedrawNeeded = true;
                 scheduleTraversals();
             }
@@ -1486,7 +1581,7 @@
             Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
         }
 
-        if (scrolling) {
+        if (animating) {
             mFullRedrawNeeded = true;
             scheduleTraversals();
         }
@@ -1600,7 +1695,7 @@
         if (scrollY != mScrollY) {
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
                     + mScrollY + " , new=" + scrollY);
-            if (!immediate) {
+            if (!immediate && mResizeBitmap == null) {
                 if (mScroller == null) {
                     mScroller = new Scroller(mView.getContext());
                 }
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java
index 1cc6a2a..a95a6c7 100644
--- a/services/java/com/android/server/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/ScreenRotationAnimation.java
@@ -122,7 +122,9 @@
         mSurface.unlockCanvasAndPost(c);
         Surface.closeTransaction();
 
-        screenshot.recycle();
+        if (screenshot != null) {
+            screenshot.recycle();
+        }
     }
 
     static int deltaRotation(int oldRotation, int newRotation) {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 5e49404..27ec1af 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -6848,6 +6848,8 @@
             }
 
             if (!mParentFrame.equals(pf)) {
+                //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame
+                //        + " to " + pf);
                 mParentFrame.set(pf);
                 mContentChanged = true;
             }
@@ -7734,12 +7736,10 @@
          * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
          */
         boolean shouldAnimateMove() {
-            return mContentChanged && !mAnimating && !mLastHidden && !mDisplayFrozen
+            return mContentChanged && !mExiting && !mLastHidden && !mDisplayFrozen
                     && (mFrame.top != mLastFrame.top
                             || mFrame.left != mLastFrame.left)
-                    && (mAttachedWindow == null
-                            || (mAttachedWindow.mAnimation == null
-                                    && !mAttachedWindow.shouldAnimateMove()))
+                    && (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove())
                     && mPolicy.isScreenOn();
         }
 
@@ -9223,6 +9223,7 @@
             if (!gone || !win.mHaveFrame) {
                 if (!win.mLayoutAttached) {
                     if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
                         win.mContentChanged = false;
                     }
                     mPolicy.layoutWindowLw(win, win.mAttrs, null);
@@ -9257,6 +9258,7 @@
                 if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
                         || !win.mHaveFrame) {
                     if (initial) {
+                        //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
                         win.mContentChanged = false;
                     }
                     mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
@@ -9455,7 +9457,6 @@
                             w.setAnimation(a);
                             animDw = w.mLastFrame.left - w.mFrame.left;
                             animDh = w.mLastFrame.top - w.mFrame.top;
-                            w.mContentChanged = false;
                         }
 
                         // Execute animation.
@@ -10242,6 +10243,11 @@
                     w.mOrientationChanging = false;
                 }
 
+                if (w.mContentChanged) {
+                    //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+                    w.mContentChanged = false;
+                }
+
                 final boolean canBeSeen = w.isDisplayedLw();
 
                 if (someoneLosingFocus && w == mCurrentFocus && canBeSeen) {