Refactor animation out of WindowState.

Remove the animation stepping from WindowState and move it into a new
class, WindowStateAnimator. Update all references to moved variables
in related files.

Change-Id: I7540d8f897b370c73975f3ffe450140861cb0cd1
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
new file mode 100644
index 0000000..d86d411
--- /dev/null
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -0,0 +1,298 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import com.android.server.wm.WindowManagerService.H;
+
+import java.io.PrintWriter;
+
+/**
+ * @author cmautner@google.com (Craig Mautner)
+ *
+ */
+class WindowStateAnimator {
+
+    final WindowManagerService mService;
+    final WindowState mWin;
+    final WindowState mAttachedWindow;
+
+    // Currently running animation.
+    boolean mAnimating;
+    boolean mLocalAnimating;
+    Animation mAnimation;
+    boolean mAnimationIsEntrance;
+    boolean mHasTransformation;
+    boolean mHasLocalTransformation;
+    final Transformation mTransformation = new Transformation();
+    boolean mWasAnimating;      // Were we animating going into the most recent animation step?
+
+    public WindowStateAnimator(final WindowManagerService service, final WindowState win,
+                               final WindowState attachedWindow) {
+        mService = service;
+        mWin = win;
+        mAttachedWindow = attachedWindow;
+    }
+
+    public void setAnimation(Animation anim) {
+        if (WindowManagerService.localLOGV) Slog.v(
+            WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+        mAnimating = false;
+        mLocalAnimating = false;
+        mAnimation = anim;
+        mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+        mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
+        // Start out animation gone if window is gone, or visible if window is visible.
+        mTransformation.clear();
+        mTransformation.setAlpha(mWin.mLastHidden ? 0 : 1);
+        mHasLocalTransformation = true;
+    }
+
+    public void clearAnimation() {
+        if (mAnimation != null) {
+            mAnimating = true;
+            mLocalAnimating = false;
+            mAnimation.cancel();
+            mAnimation = null;
+        }
+    }
+
+    /** Is the window or its container currently animating? */
+    boolean isAnimating() {
+        final WindowState attached = mAttachedWindow;
+        final AppWindowToken atoken = mWin.mAppToken;
+        return mAnimation != null
+                || (attached != null && attached.mWinAnimator.mAnimation != null)
+                || (atoken != null &&
+                        (atoken.animation != null
+                                || atoken.inPendingTransaction));
+    }
+
+    /** Is this window currently animating? */
+    boolean isWindowAnimating() {
+        return mAnimation != null;
+    }
+
+    // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked()
+    // for avoiding the code duplication.
+    void cancelExitAnimationForNextAnimationLocked() {
+        if (!mWin.mExiting) return;
+        if (mAnimation != null) {
+            mAnimation.cancel();
+            mAnimation = null;
+            mWin.destroySurfaceLocked();
+        }
+        mWin.mExiting = false;
+    }
+
+    private boolean stepAnimation(long currentTime) {
+        if ((mAnimation == null) || !mLocalAnimating) {
+            return false;
+        }
+        mTransformation.clear();
+        final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
+        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+            WindowManagerService.TAG, "Stepped animation in " + this +
+            ": more=" + more + ", xform=" + mTransformation);
+        return more;
+    }
+
+    // This must be called while inside a transaction.  Returns true if
+    // there is more animation to run.
+    boolean stepAnimationLocked(long currentTime) {
+        // Save the animation state as it was before this step so WindowManagerService can tell if
+        // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
+        mWasAnimating = mAnimating;
+        if (mService.okToDisplay()) {
+            // We will run animations as long as the display isn't frozen.
+
+            if (mWin.isDrawnLw() && mAnimation != null) {
+                mHasTransformation = true;
+                mHasLocalTransformation = true;
+                if (!mLocalAnimating) {
+                    if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                        WindowManagerService.TAG, "Starting animation in " + this +
+                        " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
+                        " wh=" + mWin.mFrame.height() +
+                        " dw=" + mWin.mAnimDw + " dh=" + mWin.mAnimDh +
+                        " scale=" + mService.mWindowAnimationScale);
+                    mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mWin.mAnimDw,
+                        mWin.mAnimDh);
+                    mAnimation.setStartTime(currentTime);
+                    mLocalAnimating = true;
+                    mAnimating = true;
+                }
+                if ((mAnimation != null) && mLocalAnimating) {
+                    if (stepAnimation(currentTime)) {
+                        return true;
+                    }
+                }
+                if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                    WindowManagerService.TAG, "Finished animation in " + this +
+                    " @ " + currentTime);
+                //WindowManagerService.this.dump();
+            }
+            mHasLocalTransformation = false;
+            if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
+                    && mWin.mAppToken.animation != null) {
+                // When our app token is animating, we kind-of pretend like
+                // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
+                // part of this check means that we will only do this if
+                // our window is not currently exiting, or it is not
+                // locally animating itself.  The idea being that one that
+                // is exiting and doing a local animation should be removed
+                // once that animation is done.
+                mAnimating = true;
+                mHasTransformation = true;
+                mTransformation.clear();
+                return false;
+            } else if (mHasTransformation) {
+                // Little trick to get through the path below to act like
+                // we have finished an animation.
+                mAnimating = true;
+            } else if (isAnimating()) {
+                mAnimating = true;
+            }
+        } else if (mAnimation != null) {
+            // If the display is frozen, and there is a pending animation,
+            // clear it and make sure we run the cleanup code.
+            mAnimating = true;
+            mLocalAnimating = true;
+            mAnimation.cancel();
+            mAnimation = null;
+        }
+
+        if (!mAnimating && !mLocalAnimating) {
+            return false;
+        }
+
+        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+            WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting
+            + ", reportedVisible="
+            + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+
+        mAnimating = false;
+        mLocalAnimating = false;
+        if (mAnimation != null) {
+            mAnimation.cancel();
+            mAnimation = null;
+        }
+        if (mService.mWindowDetachedWallpaper == mWin) {
+            mService.mWindowDetachedWallpaper = null;
+        }
+        mWin.mAnimLayer = mWin.mLayer;
+        if (mWin.mIsImWindow) {
+            mWin.mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
+        } else if (mWin.mIsWallpaper) {
+            mWin.mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
+        }
+        if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this
+                + " anim layer: " + mWin.mAnimLayer);
+        mHasTransformation = false;
+        mHasLocalTransformation = false;
+        if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
+            if (WindowState.DEBUG_VISIBILITY) {
+                Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": "
+                        + mWin.mPolicyVisibilityAfterAnim);
+            }
+            mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
+            mService.mLayoutNeeded = true;
+            if (!mWin.mPolicyVisibility) {
+                if (mService.mCurrentFocus == mWin) {
+                    mService.mFocusMayChange = true;
+                }
+                // Window is no longer visible -- make sure if we were waiting
+                // for it to be displayed before enabling the display, that
+                // we allow the display to be enabled now.
+                mService.enableScreenIfNeededLocked();
+            }
+        }
+        mTransformation.clear();
+        if (mWin.mHasDrawn
+                && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+                && mWin.mAppToken != null
+                && mWin.mAppToken.firstWindowDrawn
+                && mWin.mAppToken.startingData != null) {
+            if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting "
+                    + mWin.mToken + ": first real window done animating");
+            mService.mFinishedStarting.add(mWin.mAppToken);
+            mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+        }
+
+        finishExit();
+        mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
+
+        if (mWin.mAppToken != null) {
+            mWin.mAppToken.updateReportedVisibilityLocked();
+        }
+
+        return false;
+    }
+
+    void finishExit() {
+        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+                WindowManagerService.TAG, "finishExit in " + this
+                + ": exiting=" + mWin.mExiting
+                + " remove=" + mWin.mRemoveOnExit
+                + " windowAnimating=" + isWindowAnimating());
+
+        final int N = mWin.mChildWindows.size();
+        for (int i=0; i<N; i++) {
+            mWin.mChildWindows.get(i).mWinAnimator.finishExit();
+        }
+
+        if (!mWin.mExiting) {
+            return;
+        }
+
+        if (isWindowAnimating()) {
+            return;
+        }
+
+        if (WindowManagerService.localLOGV) Slog.v(
+                WindowManagerService.TAG, "Exit animation finished in " + this
+                + ": remove=" + mWin.mRemoveOnExit);
+        if (mWin.mSurface != null) {
+            mService.mDestroySurface.add(mWin);
+            mWin.mDestroying = true;
+            if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface(
+                mWin, "HIDE (finishExit)", null);
+            mWin.mSurfaceShown = false;
+            try {
+                mWin.mSurface.hide();
+            } catch (RuntimeException e) {
+                Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e);
+            }
+            mWin.mLastHidden = true;
+        }
+        mWin.mExiting = false;
+        if (mWin.mRemoveOnExit) {
+            mService.mPendingRemove.add(mWin);
+            mWin.mRemoveOnExit = false;
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        if (mAnimating || mLocalAnimating || mAnimationIsEntrance
+                || mAnimation != null) {
+            pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
+                    pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
+                    pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
+                    pw.print(" mAnimation="); pw.println(mAnimation);
+        }
+        if (mHasTransformation || mHasLocalTransformation) {
+            pw.print(prefix); pw.print("XForm: has=");
+                    pw.print(mHasTransformation);
+                    pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
+                    pw.print(" "); mTransformation.printShortString(pw);
+                    pw.println();
+        }
+    }
+
+}