Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f761d6b authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

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
parent 7423c09f
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -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);
                }

+103 −8
Original line number Diff line number Diff line
@@ -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());
                }
+3 −1
Original line number Diff line number Diff line
@@ -122,8 +122,10 @@ class ScreenRotationAnimation {
        mSurface.unlockCanvasAndPost(c);
        Surface.closeTransaction();

        if (screenshot != null) {
            screenshot.recycle();
        }
    }

    static int deltaRotation(int oldRotation, int newRotation) {
        int delta = newRotation - oldRotation;
+11 −5
Original line number Diff line number Diff line
@@ -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) {