Separate animation into separate class.

Introduction of the class WindowAnimator which takes care of all
animations stepping and Surface operations.

Change-Id: I78f1c269fa57df0616c08adbe156e3059709ae48
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
new file mode 100644
index 0000000..b3dbee1
--- /dev/null
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -0,0 +1,575 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.policy.impl.PhoneWindowManager;
+
+/**
+ * @author cmautner@google.com (Craig Mautner)
+ * Singleton class that carries out the animations and Surface operations in a separate task
+ * on behalf of WindowManagerService.
+ */
+public class WindowAnimator {
+    private static final String TAG = "WindowAnimations";
+
+    final WindowManagerService mService;
+    final Context mContext;
+    final WindowManagerPolicy mPolicy;
+
+    boolean mAnimating;
+    boolean mUpdateRotation;
+    boolean mTokenMayBeDrawn;
+    boolean mForceHiding;
+    WindowState mWindowAnimationBackground;
+    int mWindowAnimationBackgroundColor;
+    int mAdjResult;
+
+    int mPendingLayoutChanges;
+
+    /** Overall window dimensions */
+    int mDw, mDh;
+
+    /** Interior window dimensions */
+    int mInnerDw, mInnerDh;
+
+    /** Time of current animation step. Reset on each iteration */
+    long mCurrentTime;
+
+    /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
+     * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
+    private int mTransactionSequence;
+
+    /** The one and only screen rotation if one is happening */
+    ScreenRotationAnimation mScreenRotationAnimation = null;
+
+    WindowAnimator(final WindowManagerService service, final Context context,
+            final WindowManagerPolicy policy) {
+        mService = service;
+        mContext = context;
+        mPolicy = policy;
+    }
+
+    private void updateWindowsAppsAndRotationAnimationsLocked() {
+        int i;
+        final int NAT = mService.mAppTokens.size();
+        for (i=0; i<NAT; i++) {
+            final AppWindowToken appToken = mService.mAppTokens.get(i);
+            if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+                mAnimating = true;
+            }
+        }
+
+        if (mScreenRotationAnimation != null &&
+                (mScreenRotationAnimation.isAnimating() ||
+                        mScreenRotationAnimation.mFinishAnimReady)) {
+            if (mScreenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
+                mUpdateRotation = false;
+                mAnimating = true;
+            } else {
+                mUpdateRotation = true;
+                mScreenRotationAnimation.kill();
+                mScreenRotationAnimation = null;
+            }
+        }
+    }
+
+    private void updateWindowsAndWallpaperLocked() {
+        ++mTransactionSequence;
+
+        for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
+            WindowState w = mService.mWindows.get(i);
+
+            final WindowManager.LayoutParams attrs = w.mAttrs;
+
+            if (w.mSurface != null) {
+                // Take care of the window being ready to display.
+                if (w.commitFinishDrawingLocked(mCurrentTime)) {
+                    if ((w.mAttrs.flags
+                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+                        if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+                                "First draw done in potential wallpaper target " + w);
+                        mService.mInnerFields.mWallpaperMayChange = true;
+                        mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+                    }
+                }
+
+                // If the window has moved due to its containing
+                // content frame changing, then we'd like to animate
+                // it.  The checks here are ordered by what is least
+                // likely to be true first.
+                if (w.shouldAnimateMove()) {
+                    // Frame has moved, containing content frame
+                    // has also moved, and we're not currently animating...
+                    // let's do something.
+                    Animation a = AnimationUtils.loadAnimation(mContext,
+                            com.android.internal.R.anim.window_move_from_decor);
+                    w.setAnimation(a);
+                    w.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+                    w.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+                } else {
+                    w.mAnimDw = mInnerDw;
+                    w.mAnimDh = mInnerDh;
+                }
+
+                final boolean wasAnimating = w.mWasAnimating;
+                final boolean nowAnimating = w.stepAnimationLocked(mCurrentTime);
+
+                if (WindowManagerService.DEBUG_WALLPAPER) {
+                    Slog.v(TAG, w + ": wasAnimating=" + wasAnimating +
+                            ", nowAnimating=" + nowAnimating);
+                }
+
+                // If this window is animating, make a note that we have
+                // an animating window and take care of a request to run
+                // a detached wallpaper animation.
+                if (nowAnimating) {
+                    if (w.mAnimation != null) {
+                        if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                                && w.mAnimation.getDetachWallpaper()) {
+                            mService.mInnerFields.mDetachedWallpaper = w;
+                        }
+                        if (w.mAnimation.getBackgroundColor() != 0) {
+                            if (mWindowAnimationBackground == null
+                                    || (w.mAnimLayer < mWindowAnimationBackground.mAnimLayer)) {
+                                mWindowAnimationBackground = w;
+                                mWindowAnimationBackgroundColor =
+                                        w.mAnimation.getBackgroundColor();
+                            }
+                        }
+                    }
+                    mAnimating = true;
+                }
+
+                // If this window's app token is running a detached wallpaper
+                // animation, make a note so we can ensure the wallpaper is
+                // displayed behind it.
+                if (w.mAppToken != null && w.mAppToken.animation != null
+                        && w.mAppToken.animating) {
+                    if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
+                            && w.mAppToken.animation.getDetachWallpaper()) {
+                        mService.mInnerFields.mDetachedWallpaper = w;
+                    }
+                    if (w.mAppToken.animation.getBackgroundColor() != 0) {
+                        if (mWindowAnimationBackground == null
+                                || (w.mAnimLayer <
+                                        mWindowAnimationBackground.mAnimLayer)) {
+                            mWindowAnimationBackground = w;
+                            mWindowAnimationBackgroundColor =
+                                    w.mAppToken.animation.getBackgroundColor();
+                        }
+                    }
+                }
+
+                if (wasAnimating && !w.mAnimating && mService.mWallpaperTarget == w) {
+                    mService.mInnerFields.mWallpaperMayChange = true;
+                    mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+                }
+
+                if (mPolicy.doesForceHide(w, attrs)) {
+                    if (!wasAnimating && nowAnimating) {
+                        if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                                "Animation started that could impact force hide: "
+                                + w);
+                        mService.mInnerFields.mWallpaperForceHidingChanged = true;
+                        mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+                        mService.mFocusMayChange = true;
+                    } else if (w.isReadyForDisplay() && w.mAnimation == null) {
+                        mForceHiding = true;
+                    }
+                } else if (mPolicy.canBeForceHidden(w, attrs)) {
+                    boolean changed;
+                    if (mForceHiding) {
+                        changed = w.hideLw(false, false);
+                        if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
+                                "Now policy hidden: " + w);
+                    } else {
+                        changed = w.showLw(false, false);
+                        if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
+                                "Now policy shown: " + w);
+                        if (changed) {
+                            if (mService.mInnerFields.mWallpaperForceHidingChanged
+                                    && w.isVisibleNow() /*w.isReadyForDisplay()*/) {
+                                // Assume we will need to animate.  If
+                                // we don't (because the wallpaper will
+                                // stay with the lock screen), then we will
+                                // clean up later.
+                                Animation a = mPolicy.createForceHideEnterAnimation();
+                                if (a != null) {
+                                    w.setAnimation(a);
+                                }
+                            }
+                            if (mCurrentFocus == null || mCurrentFocus.mLayer < w.mLayer) {
+                                // We are showing on to of the current
+                                // focus, so re-evaluate focus to make
+                                // sure it is correct.
+                                mService.mFocusMayChange = true;
+                            }
+                        }
+                    }
+                    if (changed && (attrs.flags
+                            & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+                        mService.mInnerFields.mWallpaperMayChange = true;
+                        mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+                    }
+                }
+            }
+
+            final AppWindowToken atoken = w.mAppToken;
+            if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+                if (atoken.lastTransactionSequence != mTransactionSequence) {
+                    atoken.lastTransactionSequence = mTransactionSequence;
+                    atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+                    atoken.startingDisplayed = false;
+                }
+                if ((w.isOnScreen() || w.mAttrs.type
+                        == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+                        && !w.mExiting && !w.mDestroying) {
+                    if (WindowManagerService.DEBUG_VISIBILITY ||
+                            WindowManagerService.DEBUG_ORIENTATION) {
+                        Slog.v(TAG, "Eval win " + w + ": isDrawn="
+                                + w.isDrawnLw()
+                                + ", isAnimating=" + w.isAnimating());
+                        if (!w.isDrawnLw()) {
+                            Slog.v(TAG, "Not displayed: s=" + w.mSurface
+                                    + " pv=" + w.mPolicyVisibility
+                                    + " dp=" + w.mDrawPending
+                                    + " cdp=" + w.mCommitDrawPending
+                                    + " ah=" + w.mAttachedHidden
+                                    + " th=" + atoken.hiddenRequested
+                                    + " a=" + w.mAnimating);
+                        }
+                    }
+                    if (w != atoken.startingWindow) {
+                        if (!atoken.freezingScreen || !w.mAppFreezing) {
+                            atoken.numInterestingWindows++;
+                            if (w.isDrawnLw()) {
+                                atoken.numDrawnWindows++;
+                                if (WindowManagerService.DEBUG_VISIBILITY ||
+                                        WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                                        "tokenMayBeDrawn: " + atoken
+                                        + " freezingScreen=" + atoken.freezingScreen
+                                        + " mAppFreezing=" + w.mAppFreezing);
+                                mTokenMayBeDrawn = true;
+                            }
+                        }
+                    } else if (w.isDrawnLw()) {
+                        atoken.startingDisplayed = true;
+                    }
+                }
+            } else if (w.mReadyToShow) {
+                w.performShowLocked();
+                mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+            }
+        } // end forall windows
+    }
+
+    private void testTokenMayBeDrawnLocked() {
+        // See if any windows have been drawn, so they (and others
+        // associated with them) can now be shown.
+        final int NT = mService.mAppTokens.size();
+        for (int i=0; i<NT; i++) {
+            AppWindowToken wtoken = mService.mAppTokens.get(i);
+            if (wtoken.freezingScreen) {
+                int numInteresting = wtoken.numInterestingWindows;
+                if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                    if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                            "allDrawn: " + wtoken
+                            + " interesting=" + numInteresting
+                            + " drawn=" + wtoken.numDrawnWindows);
+                    wtoken.showAllWindowsLocked();
+                    mService.unsetAppFreezingScreenLocked(wtoken, false, true);
+                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+                            "Setting mOrientationChangeComplete=true because wtoken "
+                            + wtoken + " numInteresting=" + numInteresting
+                            + " numDrawn=" + wtoken.numDrawnWindows);
+                    mService.mInnerFields.mOrientationChangeComplete = true;
+                }
+            } else if (!wtoken.allDrawn) {
+                int numInteresting = wtoken.numInterestingWindows;
+                if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
+                    if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                            "allDrawn: " + wtoken
+                            + " interesting=" + numInteresting
+                            + " drawn=" + wtoken.numDrawnWindows);
+                    wtoken.allDrawn = true;
+                    mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+
+                    // We can now show all of the drawn windows!
+                    if (!mService.mOpeningApps.contains(wtoken)) {
+                        mAnimating |= wtoken.showAllWindowsLocked();
+                    }
+                }
+            }
+        }
+    }
+
+    private void performAnimationsLocked() {
+        if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
+                + mTransactionSequence + " mAnimating="
+                + mAnimating);
+
+        mTokenMayBeDrawn = false;
+        mService.mInnerFields.mWallpaperMayChange = false;
+        mForceHiding = false;
+        mService.mInnerFields.mDetachedWallpaper = null;
+        mWindowAnimationBackground = null;
+        mWindowAnimationBackgroundColor = 0;
+
+        updateWindowsAndWallpaperLocked();
+
+        if (mTokenMayBeDrawn) {
+            testTokenMayBeDrawnLocked();
+        }
+
+        if (WindowManagerService.DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+                + Integer.toHexString(mPendingLayoutChanges));
+    }
+
+    public void prepareSurfaceLocked(final WindowState w, final boolean recoveringMemory) {
+        if (w.mSurface == null) {
+            if (w.mOrientationChanging) {
+                if (WindowManagerService.DEBUG_ORIENTATION) {
+                    Slog.v(TAG, "Orientation change skips hidden " + w);
+                }
+                w.mOrientationChanging = false;
+            }
+            return;
+        }
+
+        boolean displayed = false;
+
+        w.computeShownFrameLocked();
+
+        int width, height;
+        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
+            // for a scaled surface, we just want to use
+            // the requested size.
+            width  = w.mRequestedWidth;
+            height = w.mRequestedHeight;
+        } else {
+            width = w.mCompatFrame.width();
+            height = w.mCompatFrame.height();
+        }
+
+        if (width < 1) {
+            width = 1;
+        }
+        if (height < 1) {
+            height = 1;
+        }
+        final boolean surfaceResized = w.mSurfaceW != width || w.mSurfaceH != height;
+        if (surfaceResized) {
+            w.mSurfaceW = width;
+            w.mSurfaceH = height;
+        }
+
+        if (w.mSurfaceX != w.mShownFrame.left
+                || w.mSurfaceY != w.mShownFrame.top) {
+            try {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "POS " + w.mShownFrame.left
+                        + ", " + w.mShownFrame.top, null);
+                w.mSurfaceX = w.mShownFrame.left;
+                w.mSurfaceY = w.mShownFrame.top;
+                w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Error positioning surface of " + w
+                        + " pos=(" + w.mShownFrame.left
+                        + "," + w.mShownFrame.top + ")", e);
+                if (!recoveringMemory) {
+                    mService.reclaimSomeSurfaceMemoryLocked(w, "position", true);
+                }
+            }
+        }
+
+        if (surfaceResized) {
+            try {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "SIZE " + width + "x" + height, null);
+                w.mSurfaceResized = true;
+                w.mSurface.setSize(width, height);
+            } catch (RuntimeException e) {
+                // If something goes wrong with the surface (such
+                // as running out of memory), don't take down the
+                // entire system.
+                Slog.e(TAG, "Error resizing surface of " + w
+                        + " size=(" + width + "x" + height + ")", e);
+                if (!recoveringMemory) {
+                    mService.reclaimSomeSurfaceMemoryLocked(w, "size", true);
+                }
+            }
+        }
+
+        if (w.mAttachedHidden || !w.isReadyForDisplay()) {
+            if (!w.mLastHidden) {
+                //dump();
+                w.mLastHidden = true;
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "HIDE (performLayout)", null);
+                if (w.mSurface != null) {
+                    w.mSurfaceShown = false;
+                    try {
+                        w.mSurface.hide();
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Exception hiding surface in " + w);
+                    }
+                }
+            }
+            // If we are waiting for this window to handle an
+            // orientation change, well, it is hidden, so
+            // doesn't really matter.  Note that this does
+            // introduce a potential glitch if the window
+            // becomes unhidden before it has drawn for the
+            // new orientation.
+            if (w.mOrientationChanging) {
+                w.mOrientationChanging = false;
+                if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                        "Orientation change skips hidden " + w);
+            }
+        } else if (w.mLastLayer != w.mAnimLayer
+                || w.mLastAlpha != w.mShownAlpha
+                || w.mLastDsDx != w.mDsDx
+                || w.mLastDtDx != w.mDtDx
+                || w.mLastDsDy != w.mDsDy
+                || w.mLastDtDy != w.mDtDy
+                || w.mLastHScale != w.mHScale
+                || w.mLastVScale != w.mVScale
+                || w.mLastHidden) {
+            displayed = true;
+            w.mLastAlpha = w.mShownAlpha;
+            w.mLastLayer = w.mAnimLayer;
+            w.mLastDsDx = w.mDsDx;
+            w.mLastDtDx = w.mDtDx;
+            w.mLastDsDy = w.mDsDy;
+            w.mLastDtDy = w.mDtDy;
+            w.mLastHScale = w.mHScale;
+            w.mLastVScale = w.mVScale;
+            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                    "alpha=" + w.mShownAlpha + " layer=" + w.mAnimLayer
+                    + " matrix=[" + (w.mDsDx*w.mHScale)
+                    + "," + (w.mDtDx*w.mVScale)
+                    + "][" + (w.mDsDy*w.mHScale)
+                    + "," + (w.mDtDy*w.mVScale) + "]", null);
+            if (w.mSurface != null) {
+                try {
+                    w.mSurfaceAlpha = w.mShownAlpha;
+                    w.mSurface.setAlpha(w.mShownAlpha);
+                    w.mSurfaceLayer = w.mAnimLayer;
+                    w.mSurface.setLayer(w.mAnimLayer);
+                    w.mSurface.setMatrix(
+                            w.mDsDx*w.mHScale, w.mDtDx*w.mVScale,
+                            w.mDsDy*w.mHScale, w.mDtDy*w.mVScale);
+                } catch (RuntimeException e) {
+                    Slog.w(TAG, "Error updating surface in " + w, e);
+                    if (!recoveringMemory) {
+                        mService.reclaimSomeSurfaceMemoryLocked(w, "update", true);
+                    }
+                }
+            }
+
+            if (w.mLastHidden && w.isDrawnLw()
+                    && !w.mReadyToShow) {
+                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                        "SHOW (performLayout)", null);
+                if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
+                        + " during relayout");
+                if (mService.showSurfaceRobustlyLocked(w)) {
+                    w.mHasDrawn = true;
+                    w.mLastHidden = false;
+                } else {
+                    w.mOrientationChanging = false;
+                }
+            }
+            if (w.mSurface != null) {
+                w.mToken.hasVisible = true;
+            }
+        } else {
+            displayed = true;
+        }
+
+        if (displayed) {
+            if (w.mOrientationChanging) {
+                if (!w.isDrawnLw()) {
+                    mService.mInnerFields.mOrientationChangeComplete = false;
+                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation continue waiting for draw in " + w);
+                } else {
+                    w.mOrientationChanging = false;
+                    if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Orientation change complete in " + w);
+                }
+            }
+            w.mToken.hasVisible = true;
+        }
+    }
+
+    void animate() {
+        mCurrentTime = SystemClock.uptimeMillis();
+
+        // Update animations of all applications, including those
+        // associated with exiting/removed apps
+        Surface.openTransaction();
+
+        try {
+            updateWindowsAppsAndRotationAnimationsLocked();
+            performAnimationsLocked();
+
+            // THIRD LOOP: Update the surfaces of all windows.
+
+            if (mScreenRotationAnimation != null) {
+                mScreenRotationAnimation.updateSurfaces();
+            }
+
+            final int N = mService.mWindows.size();
+            for (int i=N-1; i>=0; i--) {
+                WindowState w = mService.mWindows.get(i);
+                prepareSurfaceLocked(w, true);
+            }
+
+            if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
+                mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
+                            mCurrentTime, !mService.okToDisplay());
+            }
+
+            if (mService.mBlackFrame != null) {
+                if (mScreenRotationAnimation != null) {
+                    mService.mBlackFrame.setMatrix(
+                            mScreenRotationAnimation.getEnterTransformation().getMatrix());
+                } else {
+                    mService.mBlackFrame.clearMatrix();
+                }
+            }
+        } catch (RuntimeException e) {
+            Log.wtf(TAG, "Unhandled exception in Window Manager", e);
+        } finally {
+            Surface.closeTransaction();
+        }
+    }
+
+    WindowState mCurrentFocus;
+    void setCurrentFocus(WindowState currentFocus) {
+        mCurrentFocus = currentFocus;
+    }
+
+    void setDisplayDimensions(final int curWidth, final int curHeight,
+                        final int appWidth, final int appHeight) {
+        mDw = curWidth;
+        mDh = curHeight;
+        mInnerDw = appWidth;
+        mInnerDh = appHeight;
+    }
+
+}