From 0f761d6b8f5d5a607c87dbcdca6fe0ec7911970e Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 30 Nov 2010 22:06:10 -0800 Subject: [PATCH] Implement issue #3201795: Improve transition when keyboard comes up ViewRoot now does a fade animation between a snapshot of the previous layout to the new one when its content rect changes. Also tweaked some things in the window manager to fix problems in deciding when to animate the movement of a window and when not to. Change-Id: I9b4b3bd53c8258bd39a2f2fc315e77cfc56a409c --- core/java/android/view/HardwareRenderer.java | 12 +- core/java/android/view/ViewRoot.java | 111 ++++++++++++++++-- .../server/ScreenRotationAnimation.java | 4 +- .../android/server/WindowManagerService.java | 16 ++- 4 files changed, 126 insertions(+), 17 deletions(-) diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 6f4abefbddfd7..24a9f870ec875 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -94,13 +94,18 @@ public abstract class HardwareRenderer { */ 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 @@ public abstract class HardwareRenderer { } @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 @@ public abstract class HardwareRenderer { 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 cb7d0e277e623..77083a9021125 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -25,7 +25,9 @@ import android.content.pm.PackageManager; 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.util.TypedValue; 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 @@ import java.util.ArrayList; * {@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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn // 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (mScroller != null) { mScroller.abortAnimation(); } + disposeResizeBitmap(); } } catch (RemoteException e) { } @@ -1310,6 +1368,22 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost"); } - if (scrolling) { + if (animating) { mFullRedrawNeeded = true; scheduleTraversals(); } @@ -1600,7 +1695,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn 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 1cc6a2a551219..a95a6c715774f 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -122,7 +122,9 @@ class ScreenRotationAnimation { 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 5e4940400ef35..27ec1af5eda0e 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -6848,6 +6848,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (!mParentFrame.equals(pf)) { + //Slog.i(TAG, "Window " + this + " content frame from " + mParentFrame + // + " to " + pf); mParentFrame.set(pf); mContentChanged = true; } @@ -7734,12 +7736,10 @@ public class WindowManagerService extends IWindowManager.Stub * 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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) {