Craig Mautner | a2c7705 | 2012-03-26 12:14:43 -0700 | [diff] [blame^] | 1 | // Copyright 2012 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | package com.android.server.wm; |
| 4 | |
| 5 | import android.util.Slog; |
| 6 | import android.view.WindowManager; |
| 7 | import android.view.WindowManagerPolicy; |
| 8 | import android.view.animation.Animation; |
| 9 | import android.view.animation.Transformation; |
| 10 | |
| 11 | import com.android.server.wm.WindowManagerService.H; |
| 12 | |
| 13 | import java.io.PrintWriter; |
| 14 | |
| 15 | /** |
| 16 | * @author cmautner@google.com (Craig Mautner) |
| 17 | * |
| 18 | */ |
| 19 | class WindowStateAnimator { |
| 20 | |
| 21 | final WindowManagerService mService; |
| 22 | final WindowState mWin; |
| 23 | final WindowState mAttachedWindow; |
| 24 | |
| 25 | // Currently running animation. |
| 26 | boolean mAnimating; |
| 27 | boolean mLocalAnimating; |
| 28 | Animation mAnimation; |
| 29 | boolean mAnimationIsEntrance; |
| 30 | boolean mHasTransformation; |
| 31 | boolean mHasLocalTransformation; |
| 32 | final Transformation mTransformation = new Transformation(); |
| 33 | boolean mWasAnimating; // Were we animating going into the most recent animation step? |
| 34 | |
| 35 | public WindowStateAnimator(final WindowManagerService service, final WindowState win, |
| 36 | final WindowState attachedWindow) { |
| 37 | mService = service; |
| 38 | mWin = win; |
| 39 | mAttachedWindow = attachedWindow; |
| 40 | } |
| 41 | |
| 42 | public void setAnimation(Animation anim) { |
| 43 | if (WindowManagerService.localLOGV) Slog.v( |
| 44 | WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); |
| 45 | mAnimating = false; |
| 46 | mLocalAnimating = false; |
| 47 | mAnimation = anim; |
| 48 | mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); |
| 49 | mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale); |
| 50 | // Start out animation gone if window is gone, or visible if window is visible. |
| 51 | mTransformation.clear(); |
| 52 | mTransformation.setAlpha(mWin.mLastHidden ? 0 : 1); |
| 53 | mHasLocalTransformation = true; |
| 54 | } |
| 55 | |
| 56 | public void clearAnimation() { |
| 57 | if (mAnimation != null) { |
| 58 | mAnimating = true; |
| 59 | mLocalAnimating = false; |
| 60 | mAnimation.cancel(); |
| 61 | mAnimation = null; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /** Is the window or its container currently animating? */ |
| 66 | boolean isAnimating() { |
| 67 | final WindowState attached = mAttachedWindow; |
| 68 | final AppWindowToken atoken = mWin.mAppToken; |
| 69 | return mAnimation != null |
| 70 | || (attached != null && attached.mWinAnimator.mAnimation != null) |
| 71 | || (atoken != null && |
| 72 | (atoken.animation != null |
| 73 | || atoken.inPendingTransaction)); |
| 74 | } |
| 75 | |
| 76 | /** Is this window currently animating? */ |
| 77 | boolean isWindowAnimating() { |
| 78 | return mAnimation != null; |
| 79 | } |
| 80 | |
| 81 | // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked() |
| 82 | // for avoiding the code duplication. |
| 83 | void cancelExitAnimationForNextAnimationLocked() { |
| 84 | if (!mWin.mExiting) return; |
| 85 | if (mAnimation != null) { |
| 86 | mAnimation.cancel(); |
| 87 | mAnimation = null; |
| 88 | mWin.destroySurfaceLocked(); |
| 89 | } |
| 90 | mWin.mExiting = false; |
| 91 | } |
| 92 | |
| 93 | private boolean stepAnimation(long currentTime) { |
| 94 | if ((mAnimation == null) || !mLocalAnimating) { |
| 95 | return false; |
| 96 | } |
| 97 | mTransformation.clear(); |
| 98 | final boolean more = mAnimation.getTransformation(currentTime, mTransformation); |
| 99 | if (WindowManagerService.DEBUG_ANIM) Slog.v( |
| 100 | WindowManagerService.TAG, "Stepped animation in " + this + |
| 101 | ": more=" + more + ", xform=" + mTransformation); |
| 102 | return more; |
| 103 | } |
| 104 | |
| 105 | // This must be called while inside a transaction. Returns true if |
| 106 | // there is more animation to run. |
| 107 | boolean stepAnimationLocked(long currentTime) { |
| 108 | // Save the animation state as it was before this step so WindowManagerService can tell if |
| 109 | // we just started or just stopped animating by comparing mWasAnimating with isAnimating(). |
| 110 | mWasAnimating = mAnimating; |
| 111 | if (mService.okToDisplay()) { |
| 112 | // We will run animations as long as the display isn't frozen. |
| 113 | |
| 114 | if (mWin.isDrawnLw() && mAnimation != null) { |
| 115 | mHasTransformation = true; |
| 116 | mHasLocalTransformation = true; |
| 117 | if (!mLocalAnimating) { |
| 118 | if (WindowManagerService.DEBUG_ANIM) Slog.v( |
| 119 | WindowManagerService.TAG, "Starting animation in " + this + |
| 120 | " @ " + currentTime + ": ww=" + mWin.mFrame.width() + |
| 121 | " wh=" + mWin.mFrame.height() + |
| 122 | " dw=" + mWin.mAnimDw + " dh=" + mWin.mAnimDh + |
| 123 | " scale=" + mService.mWindowAnimationScale); |
| 124 | mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mWin.mAnimDw, |
| 125 | mWin.mAnimDh); |
| 126 | mAnimation.setStartTime(currentTime); |
| 127 | mLocalAnimating = true; |
| 128 | mAnimating = true; |
| 129 | } |
| 130 | if ((mAnimation != null) && mLocalAnimating) { |
| 131 | if (stepAnimation(currentTime)) { |
| 132 | return true; |
| 133 | } |
| 134 | } |
| 135 | if (WindowManagerService.DEBUG_ANIM) Slog.v( |
| 136 | WindowManagerService.TAG, "Finished animation in " + this + |
| 137 | " @ " + currentTime); |
| 138 | //WindowManagerService.this.dump(); |
| 139 | } |
| 140 | mHasLocalTransformation = false; |
| 141 | if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null |
| 142 | && mWin.mAppToken.animation != null) { |
| 143 | // When our app token is animating, we kind-of pretend like |
| 144 | // we are as well. Note the mLocalAnimating mAnimationIsEntrance |
| 145 | // part of this check means that we will only do this if |
| 146 | // our window is not currently exiting, or it is not |
| 147 | // locally animating itself. The idea being that one that |
| 148 | // is exiting and doing a local animation should be removed |
| 149 | // once that animation is done. |
| 150 | mAnimating = true; |
| 151 | mHasTransformation = true; |
| 152 | mTransformation.clear(); |
| 153 | return false; |
| 154 | } else if (mHasTransformation) { |
| 155 | // Little trick to get through the path below to act like |
| 156 | // we have finished an animation. |
| 157 | mAnimating = true; |
| 158 | } else if (isAnimating()) { |
| 159 | mAnimating = true; |
| 160 | } |
| 161 | } else if (mAnimation != null) { |
| 162 | // If the display is frozen, and there is a pending animation, |
| 163 | // clear it and make sure we run the cleanup code. |
| 164 | mAnimating = true; |
| 165 | mLocalAnimating = true; |
| 166 | mAnimation.cancel(); |
| 167 | mAnimation = null; |
| 168 | } |
| 169 | |
| 170 | if (!mAnimating && !mLocalAnimating) { |
| 171 | return false; |
| 172 | } |
| 173 | |
| 174 | if (WindowManagerService.DEBUG_ANIM) Slog.v( |
| 175 | WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting |
| 176 | + ", reportedVisible=" |
| 177 | + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false)); |
| 178 | |
| 179 | mAnimating = false; |
| 180 | mLocalAnimating = false; |
| 181 | if (mAnimation != null) { |
| 182 | mAnimation.cancel(); |
| 183 | mAnimation = null; |
| 184 | } |
| 185 | if (mService.mWindowDetachedWallpaper == mWin) { |
| 186 | mService.mWindowDetachedWallpaper = null; |
| 187 | } |
| 188 | mWin.mAnimLayer = mWin.mLayer; |
| 189 | if (mWin.mIsImWindow) { |
| 190 | mWin.mAnimLayer += mService.mInputMethodAnimLayerAdjustment; |
| 191 | } else if (mWin.mIsWallpaper) { |
| 192 | mWin.mAnimLayer += mService.mWallpaperAnimLayerAdjustment; |
| 193 | } |
| 194 | if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this |
| 195 | + " anim layer: " + mWin.mAnimLayer); |
| 196 | mHasTransformation = false; |
| 197 | mHasLocalTransformation = false; |
| 198 | if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) { |
| 199 | if (WindowState.DEBUG_VISIBILITY) { |
| 200 | Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": " |
| 201 | + mWin.mPolicyVisibilityAfterAnim); |
| 202 | } |
| 203 | mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim; |
| 204 | mService.mLayoutNeeded = true; |
| 205 | if (!mWin.mPolicyVisibility) { |
| 206 | if (mService.mCurrentFocus == mWin) { |
| 207 | mService.mFocusMayChange = true; |
| 208 | } |
| 209 | // Window is no longer visible -- make sure if we were waiting |
| 210 | // for it to be displayed before enabling the display, that |
| 211 | // we allow the display to be enabled now. |
| 212 | mService.enableScreenIfNeededLocked(); |
| 213 | } |
| 214 | } |
| 215 | mTransformation.clear(); |
| 216 | if (mWin.mHasDrawn |
| 217 | && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING |
| 218 | && mWin.mAppToken != null |
| 219 | && mWin.mAppToken.firstWindowDrawn |
| 220 | && mWin.mAppToken.startingData != null) { |
| 221 | if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting " |
| 222 | + mWin.mToken + ": first real window done animating"); |
| 223 | mService.mFinishedStarting.add(mWin.mAppToken); |
| 224 | mService.mH.sendEmptyMessage(H.FINISHED_STARTING); |
| 225 | } |
| 226 | |
| 227 | finishExit(); |
| 228 | mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; |
| 229 | if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState"); |
| 230 | |
| 231 | if (mWin.mAppToken != null) { |
| 232 | mWin.mAppToken.updateReportedVisibilityLocked(); |
| 233 | } |
| 234 | |
| 235 | return false; |
| 236 | } |
| 237 | |
| 238 | void finishExit() { |
| 239 | if (WindowManagerService.DEBUG_ANIM) Slog.v( |
| 240 | WindowManagerService.TAG, "finishExit in " + this |
| 241 | + ": exiting=" + mWin.mExiting |
| 242 | + " remove=" + mWin.mRemoveOnExit |
| 243 | + " windowAnimating=" + isWindowAnimating()); |
| 244 | |
| 245 | final int N = mWin.mChildWindows.size(); |
| 246 | for (int i=0; i<N; i++) { |
| 247 | mWin.mChildWindows.get(i).mWinAnimator.finishExit(); |
| 248 | } |
| 249 | |
| 250 | if (!mWin.mExiting) { |
| 251 | return; |
| 252 | } |
| 253 | |
| 254 | if (isWindowAnimating()) { |
| 255 | return; |
| 256 | } |
| 257 | |
| 258 | if (WindowManagerService.localLOGV) Slog.v( |
| 259 | WindowManagerService.TAG, "Exit animation finished in " + this |
| 260 | + ": remove=" + mWin.mRemoveOnExit); |
| 261 | if (mWin.mSurface != null) { |
| 262 | mService.mDestroySurface.add(mWin); |
| 263 | mWin.mDestroying = true; |
| 264 | if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface( |
| 265 | mWin, "HIDE (finishExit)", null); |
| 266 | mWin.mSurfaceShown = false; |
| 267 | try { |
| 268 | mWin.mSurface.hide(); |
| 269 | } catch (RuntimeException e) { |
| 270 | Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e); |
| 271 | } |
| 272 | mWin.mLastHidden = true; |
| 273 | } |
| 274 | mWin.mExiting = false; |
| 275 | if (mWin.mRemoveOnExit) { |
| 276 | mService.mPendingRemove.add(mWin); |
| 277 | mWin.mRemoveOnExit = false; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | public void dump(PrintWriter pw, String prefix, boolean dumpAll) { |
| 282 | if (mAnimating || mLocalAnimating || mAnimationIsEntrance |
| 283 | || mAnimation != null) { |
| 284 | pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating); |
| 285 | pw.print(" mLocalAnimating="); pw.print(mLocalAnimating); |
| 286 | pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance); |
| 287 | pw.print(" mAnimation="); pw.println(mAnimation); |
| 288 | } |
| 289 | if (mHasTransformation || mHasLocalTransformation) { |
| 290 | pw.print(prefix); pw.print("XForm: has="); |
| 291 | pw.print(mHasTransformation); |
| 292 | pw.print(" hasLocal="); pw.print(mHasLocalTransformation); |
| 293 | pw.print(" "); mTransformation.printShortString(pw); |
| 294 | pw.println(); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | } |