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;
+ }
+
+}