1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
20 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
26 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
27 
28 import android.content.Context;
29 import android.graphics.Matrix;
30 import android.graphics.Rect;
31 import android.util.Slog;
32 import android.util.proto.ProtoOutputStream;
33 import android.view.Display;
34 import android.view.DisplayInfo;
35 import android.view.Surface;
36 import android.view.Surface.OutOfResourcesException;
37 import android.view.SurfaceControl;
38 import android.view.animation.Animation;
39 import android.view.animation.AnimationUtils;
40 import android.view.animation.Transformation;
41 
42 import java.io.PrintWriter;
43 
44 class ScreenRotationAnimation {
45     static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
46     static final boolean DEBUG_STATE = false;
47     static final boolean DEBUG_TRANSFORMS = false;
48     static final boolean TWO_PHASE_ANIMATION = false;
49     static final boolean USE_CUSTOM_BLACK_FRAME = false;
50 
51     /*
52      * Layers for screen rotation animation. We put these layers above
53      * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
54      */
55     static final int SCREEN_FREEZE_LAYER_BASE       = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
56     static final int SCREEN_FREEZE_LAYER_ENTER      = SCREEN_FREEZE_LAYER_BASE;
57     static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
58     static final int SCREEN_FREEZE_LAYER_EXIT       = SCREEN_FREEZE_LAYER_BASE + 2;
59     static final int SCREEN_FREEZE_LAYER_CUSTOM     = SCREEN_FREEZE_LAYER_BASE + 3;
60 
61     final Context mContext;
62     final DisplayContent mDisplayContent;
63     SurfaceControl mSurfaceControl;
64     BlackFrame mCustomBlackFrame;
65     BlackFrame mExitingBlackFrame;
66     BlackFrame mEnteringBlackFrame;
67     int mWidth, mHeight;
68 
69     int mOriginalRotation;
70     int mOriginalWidth, mOriginalHeight;
71     int mCurRotation;
72     Rect mOriginalDisplayRect = new Rect();
73     Rect mCurrentDisplayRect = new Rect();
74 
75     // For all animations, "exit" is for the UI elements that are going
76     // away (that is the snapshot of the old screen), and "enter" is for
77     // the new UI elements that are appearing (that is the active windows
78     // in their final orientation).
79 
80     // The starting animation for the exiting and entering elements.  This
81     // animation applies a transformation while the rotation is in progress.
82     // It is started immediately, before the new entering UI is ready.
83     Animation mStartExitAnimation;
84     final Transformation mStartExitTransformation = new Transformation();
85     Animation mStartEnterAnimation;
86     final Transformation mStartEnterTransformation = new Transformation();
87     Animation mStartFrameAnimation;
88     final Transformation mStartFrameTransformation = new Transformation();
89 
90     // The finishing animation for the exiting and entering elements.  This
91     // animation needs to undo the transformation of the starting animation.
92     // It starts running once the new rotation UI elements are ready to be
93     // displayed.
94     Animation mFinishExitAnimation;
95     final Transformation mFinishExitTransformation = new Transformation();
96     Animation mFinishEnterAnimation;
97     final Transformation mFinishEnterTransformation = new Transformation();
98     Animation mFinishFrameAnimation;
99     final Transformation mFinishFrameTransformation = new Transformation();
100 
101     // The current active animation to move from the old to the new rotated
102     // state.  Which animation is run here will depend on the old and new
103     // rotations.
104     Animation mRotateExitAnimation;
105     final Transformation mRotateExitTransformation = new Transformation();
106     Animation mRotateEnterAnimation;
107     final Transformation mRotateEnterTransformation = new Transformation();
108     Animation mRotateFrameAnimation;
109     final Transformation mRotateFrameTransformation = new Transformation();
110 
111     // A previously running rotate animation.  This will be used if we need
112     // to switch to a new rotation before finishing the previous one.
113     Animation mLastRotateExitAnimation;
114     final Transformation mLastRotateExitTransformation = new Transformation();
115     Animation mLastRotateEnterAnimation;
116     final Transformation mLastRotateEnterTransformation = new Transformation();
117     Animation mLastRotateFrameAnimation;
118     final Transformation mLastRotateFrameTransformation = new Transformation();
119 
120     // Complete transformations being applied.
121     final Transformation mExitTransformation = new Transformation();
122     final Transformation mEnterTransformation = new Transformation();
123     final Transformation mFrameTransformation = new Transformation();
124 
125     boolean mStarted;
126     boolean mAnimRunning;
127     boolean mFinishAnimReady;
128     long mFinishAnimStartTime;
129     boolean mForceDefaultOrientation;
130 
131     final Matrix mFrameInitialMatrix = new Matrix();
132     final Matrix mSnapshotInitialMatrix = new Matrix();
133     final Matrix mSnapshotFinalMatrix = new Matrix();
134     final Matrix mExitFrameFinalMatrix = new Matrix();
135     final Matrix mTmpMatrix = new Matrix();
136     final float[] mTmpFloats = new float[9];
137     private boolean mMoreRotateEnter;
138     private boolean mMoreRotateExit;
139     private boolean mMoreRotateFrame;
140     private boolean mMoreFinishEnter;
141     private boolean mMoreFinishExit;
142     private boolean mMoreFinishFrame;
143     private boolean mMoreStartEnter;
144     private boolean mMoreStartExit;
145     private boolean mMoreStartFrame;
146     long mHalfwayPoint;
147 
148     private final WindowManagerService mService;
149 
printTo(String prefix, PrintWriter pw)150     public void printTo(String prefix, PrintWriter pw) {
151         pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
152                 pw.print(" mWidth="); pw.print(mWidth);
153                 pw.print(" mHeight="); pw.println(mHeight);
154         if (USE_CUSTOM_BLACK_FRAME) {
155             pw.print(prefix); pw.print("mCustomBlackFrame="); pw.println(mCustomBlackFrame);
156             if (mCustomBlackFrame != null) {
157                 mCustomBlackFrame.printTo(prefix + "  ", pw);
158             }
159         }
160         pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
161         if (mExitingBlackFrame != null) {
162             mExitingBlackFrame.printTo(prefix + "  ", pw);
163         }
164         pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame);
165         if (mEnteringBlackFrame != null) {
166             mEnteringBlackFrame.printTo(prefix + "  ", pw);
167         }
168         pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation);
169                 pw.print(" mOriginalRotation="); pw.println(mOriginalRotation);
170         pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth);
171                 pw.print(" mOriginalHeight="); pw.println(mOriginalHeight);
172         pw.print(prefix); pw.print("mStarted="); pw.print(mStarted);
173                 pw.print(" mAnimRunning="); pw.print(mAnimRunning);
174                 pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady);
175                 pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime);
176         pw.print(prefix); pw.print("mStartExitAnimation="); pw.print(mStartExitAnimation);
177                 pw.print(" "); mStartExitTransformation.printShortString(pw); pw.println();
178         pw.print(prefix); pw.print("mStartEnterAnimation="); pw.print(mStartEnterAnimation);
179                 pw.print(" "); mStartEnterTransformation.printShortString(pw); pw.println();
180         pw.print(prefix); pw.print("mStartFrameAnimation="); pw.print(mStartFrameAnimation);
181                 pw.print(" "); mStartFrameTransformation.printShortString(pw); pw.println();
182         pw.print(prefix); pw.print("mFinishExitAnimation="); pw.print(mFinishExitAnimation);
183                 pw.print(" "); mFinishExitTransformation.printShortString(pw); pw.println();
184         pw.print(prefix); pw.print("mFinishEnterAnimation="); pw.print(mFinishEnterAnimation);
185                 pw.print(" "); mFinishEnterTransformation.printShortString(pw); pw.println();
186         pw.print(prefix); pw.print("mFinishFrameAnimation="); pw.print(mFinishFrameAnimation);
187                 pw.print(" "); mFinishFrameTransformation.printShortString(pw); pw.println();
188         pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation);
189                 pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
190         pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
191                 pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
192         pw.print(prefix); pw.print("mRotateFrameAnimation="); pw.print(mRotateFrameAnimation);
193                 pw.print(" "); mRotateFrameTransformation.printShortString(pw); pw.println();
194         pw.print(prefix); pw.print("mExitTransformation=");
195                 mExitTransformation.printShortString(pw); pw.println();
196         pw.print(prefix); pw.print("mEnterTransformation=");
197                 mEnterTransformation.printShortString(pw); pw.println();
198         pw.print(prefix); pw.print("mFrameTransformation=");
199                 mFrameTransformation.printShortString(pw); pw.println();
200         pw.print(prefix); pw.print("mFrameInitialMatrix=");
201                 mFrameInitialMatrix.printShortString(pw);
202                 pw.println();
203         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
204                 mSnapshotInitialMatrix.printShortString(pw);
205                 pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
206                 pw.println();
207         pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
208                 mExitFrameFinalMatrix.printShortString(pw);
209                 pw.println();
210         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
211         if (mForceDefaultOrientation) {
212             pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
213             pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
214         }
215     }
216 
writeToProto(ProtoOutputStream proto, long fieldId)217     public void writeToProto(ProtoOutputStream proto, long fieldId) {
218         final long token = proto.start(fieldId);
219         proto.write(STARTED, mStarted);
220         proto.write(ANIMATION_RUNNING, mAnimRunning);
221         proto.end(token);
222     }
223 
ScreenRotationAnimation(Context context, DisplayContent displayContent, boolean fixedToUserRotation, boolean isSecure, WindowManagerService service)224     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
225             boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
226         mService = service;
227         mContext = context;
228         mDisplayContent = displayContent;
229         displayContent.getBounds(mOriginalDisplayRect);
230 
231         // Screenshot does NOT include rotation!
232         final Display display = displayContent.getDisplay();
233         int originalRotation = display.getRotation();
234         final int originalWidth;
235         final int originalHeight;
236         DisplayInfo displayInfo = displayContent.getDisplayInfo();
237         if (fixedToUserRotation) {
238             // Emulated orientation.
239             mForceDefaultOrientation = true;
240             originalWidth = displayContent.mBaseDisplayWidth;
241             originalHeight = displayContent.mBaseDisplayHeight;
242         } else {
243             // Normal situation
244             originalWidth = displayInfo.logicalWidth;
245             originalHeight = displayInfo.logicalHeight;
246         }
247         if (originalRotation == Surface.ROTATION_90
248                 || originalRotation == Surface.ROTATION_270) {
249             mWidth = originalHeight;
250             mHeight = originalWidth;
251         } else {
252             mWidth = originalWidth;
253             mHeight = originalHeight;
254         }
255 
256         mOriginalRotation = originalRotation;
257         mOriginalWidth = originalWidth;
258         mOriginalHeight = originalHeight;
259 
260         final SurfaceControl.Transaction t = mService.mTransactionFactory.make();
261         try {
262             mSurfaceControl = displayContent.makeOverlay()
263                     .setName("ScreenshotSurface")
264                     .setBufferSize(mWidth, mHeight)
265                     .setSecure(isSecure)
266                     .build();
267 
268             // In case display bounds change, screenshot buffer and surface may mismatch so set a
269             // scaling mode.
270             SurfaceControl.Transaction t2 = mService.mTransactionFactory.make();
271             t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
272             t2.apply(true /* sync */);
273 
274             // Capture a screenshot into the surface we just created.
275             final int displayId = display.getDisplayId();
276             final Surface surface = mService.mSurfaceFactory.make();
277             surface.copyFrom(mSurfaceControl);
278             SurfaceControl.ScreenshotGraphicBuffer gb =
279                 mService.mDisplayManagerInternal.screenshot(displayId);
280             if (gb != null) {
281                 try {
282                     surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
283                             gb.getColorSpace());
284                 } catch (RuntimeException e) {
285                     Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
286                 }
287                 // If the screenshot contains secure layers, we have to make sure the
288                 // screenshot surface we display it in also has FLAG_SECURE so that
289                 // the user can not screenshot secure layers via the screenshot surface.
290                 if (gb.containsSecureLayers()) {
291                     t.setSecure(mSurfaceControl, true);
292                 }
293                 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
294                 t.setAlpha(mSurfaceControl, 0);
295                 t.show(mSurfaceControl);
296             } else {
297                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
298             }
299             surface.destroy();
300         } catch (OutOfResourcesException e) {
301             Slog.w(TAG, "Unable to allocate freeze surface", e);
302         }
303 
304         if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
305                 "  FREEZE " + mSurfaceControl + ": CREATE");
306         setRotation(t, originalRotation);
307         t.apply();
308     }
309 
hasScreenshot()310     boolean hasScreenshot() {
311         return mSurfaceControl != null;
312     }
313 
setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha)314     private void setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha) {
315         if (mSurfaceControl != null) {
316             matrix.getValues(mTmpFloats);
317             float x = mTmpFloats[Matrix.MTRANS_X];
318             float y = mTmpFloats[Matrix.MTRANS_Y];
319             if (mForceDefaultOrientation) {
320                 mDisplayContent.getBounds(mCurrentDisplayRect);
321                 x -= mCurrentDisplayRect.left;
322                 y -= mCurrentDisplayRect.top;
323             }
324             t.setPosition(mSurfaceControl, x, y);
325             t.setMatrix(mSurfaceControl,
326                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
327                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
328             t.setAlpha(mSurfaceControl, alpha);
329             if (DEBUG_TRANSFORMS) {
330                 float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
331                 float[] dstPnts = new float[4];
332                 matrix.mapPoints(dstPnts, srcPnts);
333                 Slog.i(TAG, "Original  : (" + srcPnts[0] + "," + srcPnts[1]
334                         + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
335                 Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
336                         + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
337             }
338         }
339     }
340 
createRotationMatrix(int rotation, int width, int height, Matrix outMatrix)341     public static void createRotationMatrix(int rotation, int width, int height,
342             Matrix outMatrix) {
343         switch (rotation) {
344             case Surface.ROTATION_0:
345                 outMatrix.reset();
346                 break;
347             case Surface.ROTATION_90:
348                 outMatrix.setRotate(90, 0, 0);
349                 outMatrix.postTranslate(height, 0);
350                 break;
351             case Surface.ROTATION_180:
352                 outMatrix.setRotate(180, 0, 0);
353                 outMatrix.postTranslate(width, height);
354                 break;
355             case Surface.ROTATION_270:
356                 outMatrix.setRotate(270, 0, 0);
357                 outMatrix.postTranslate(0, width);
358                 break;
359         }
360     }
361 
setRotation(SurfaceControl.Transaction t, int rotation)362     private void setRotation(SurfaceControl.Transaction t, int rotation) {
363         mCurRotation = rotation;
364 
365         // Compute the transformation matrix that must be applied
366         // to the snapshot to make it stay in the same original position
367         // with the current screen rotation.
368         int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
369         createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
370 
371         if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
372         setSnapshotTransform(t, mSnapshotInitialMatrix, 1.0f);
373     }
374 
setRotation(SurfaceControl.Transaction t, int rotation, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight)375     public boolean setRotation(SurfaceControl.Transaction t, int rotation,
376             long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
377         setRotation(t, rotation);
378         if (TWO_PHASE_ANIMATION) {
379             return startAnimation(t, maxAnimationDuration, animationScale,
380                     finalWidth, finalHeight, false, 0, 0);
381         }
382 
383         // Don't start animation yet.
384         return false;
385     }
386 
387     /**
388      * Returns true if animating.
389      */
startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, boolean dismissing, int exitAnim, int enterAnim)390     private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
391             float animationScale, int finalWidth, int finalHeight, boolean dismissing,
392             int exitAnim, int enterAnim) {
393         if (mSurfaceControl == null) {
394             // Can't do animation.
395             return false;
396         }
397         if (mStarted) {
398             return true;
399         }
400 
401         mStarted = true;
402 
403         boolean firstStart = false;
404 
405         // Figure out how the screen has moved from the original rotation.
406         int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
407 
408         if (TWO_PHASE_ANIMATION && mFinishExitAnimation == null
409                 && (!dismissing || delta != Surface.ROTATION_0)) {
410             if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
411             firstStart = true;
412             mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
413                     com.android.internal.R.anim.screen_rotate_start_exit);
414             mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
415                     com.android.internal.R.anim.screen_rotate_start_enter);
416             if (USE_CUSTOM_BLACK_FRAME) {
417                 mStartFrameAnimation = AnimationUtils.loadAnimation(mContext,
418                         com.android.internal.R.anim.screen_rotate_start_frame);
419             }
420             mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
421                     com.android.internal.R.anim.screen_rotate_finish_exit);
422             mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
423                     com.android.internal.R.anim.screen_rotate_finish_enter);
424             if (USE_CUSTOM_BLACK_FRAME) {
425                 mFinishFrameAnimation = AnimationUtils.loadAnimation(mContext,
426                         com.android.internal.R.anim.screen_rotate_finish_frame);
427             }
428         }
429 
430         if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
431                 + finalWidth + " finalHeight=" + finalHeight
432                 + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
433 
434         final boolean customAnim;
435         if (exitAnim != 0 && enterAnim != 0) {
436             customAnim = true;
437             mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
438             mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
439         } else {
440             customAnim = false;
441             switch (delta) {
442                 case Surface.ROTATION_0:
443                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
444                             com.android.internal.R.anim.screen_rotate_0_exit);
445                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
446                             com.android.internal.R.anim.screen_rotate_0_enter);
447                     if (USE_CUSTOM_BLACK_FRAME) {
448                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
449                                 com.android.internal.R.anim.screen_rotate_0_frame);
450                     }
451                     break;
452                 case Surface.ROTATION_90:
453                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
454                             com.android.internal.R.anim.screen_rotate_plus_90_exit);
455                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
456                             com.android.internal.R.anim.screen_rotate_plus_90_enter);
457                     if (USE_CUSTOM_BLACK_FRAME) {
458                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
459                                 com.android.internal.R.anim.screen_rotate_plus_90_frame);
460                     }
461                     break;
462                 case Surface.ROTATION_180:
463                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
464                             com.android.internal.R.anim.screen_rotate_180_exit);
465                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
466                             com.android.internal.R.anim.screen_rotate_180_enter);
467                     if (USE_CUSTOM_BLACK_FRAME) {
468                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
469                                 com.android.internal.R.anim.screen_rotate_180_frame);
470                     }
471                     break;
472                 case Surface.ROTATION_270:
473                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
474                             com.android.internal.R.anim.screen_rotate_minus_90_exit);
475                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
476                             com.android.internal.R.anim.screen_rotate_minus_90_enter);
477                     if (USE_CUSTOM_BLACK_FRAME) {
478                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
479                                 com.android.internal.R.anim.screen_rotate_minus_90_frame);
480                     }
481                     break;
482             }
483         }
484 
485         // Initialize the animations.  This is a hack, redefining what "parent"
486         // means to allow supplying the last and next size.  In this definition
487         // "%p" is the original (let's call it "previous") size, and "%" is the
488         // screen's current/new size.
489         if (TWO_PHASE_ANIMATION && firstStart) {
490             // Compute partial steps between original and final sizes.  These
491             // are used for the dimensions of the exiting and entering elements,
492             // so they are never stretched too significantly.
493             final int halfWidth = (finalWidth + mOriginalWidth) / 2;
494             final int halfHeight = (finalHeight + mOriginalHeight) / 2;
495 
496             if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
497             mStartEnterAnimation.initialize(finalWidth, finalHeight,
498                     halfWidth, halfHeight);
499             mStartExitAnimation.initialize(halfWidth, halfHeight,
500                     mOriginalWidth, mOriginalHeight);
501             mFinishEnterAnimation.initialize(finalWidth, finalHeight,
502                     halfWidth, halfHeight);
503             mFinishExitAnimation.initialize(halfWidth, halfHeight,
504                     mOriginalWidth, mOriginalHeight);
505             if (USE_CUSTOM_BLACK_FRAME) {
506                 mStartFrameAnimation.initialize(finalWidth, finalHeight,
507                         mOriginalWidth, mOriginalHeight);
508                 mFinishFrameAnimation.initialize(finalWidth, finalHeight,
509                         mOriginalWidth, mOriginalHeight);
510             }
511         }
512         mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
513         mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
514         if (USE_CUSTOM_BLACK_FRAME) {
515             mRotateFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth,
516                     mOriginalHeight);
517         }
518         mAnimRunning = false;
519         mFinishAnimReady = false;
520         mFinishAnimStartTime = -1;
521 
522         if (TWO_PHASE_ANIMATION && firstStart) {
523             mStartExitAnimation.restrictDuration(maxAnimationDuration);
524             mStartExitAnimation.scaleCurrentDuration(animationScale);
525             mStartEnterAnimation.restrictDuration(maxAnimationDuration);
526             mStartEnterAnimation.scaleCurrentDuration(animationScale);
527             mFinishExitAnimation.restrictDuration(maxAnimationDuration);
528             mFinishExitAnimation.scaleCurrentDuration(animationScale);
529             mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
530             mFinishEnterAnimation.scaleCurrentDuration(animationScale);
531             if (USE_CUSTOM_BLACK_FRAME) {
532                 mStartFrameAnimation.restrictDuration(maxAnimationDuration);
533                 mStartFrameAnimation.scaleCurrentDuration(animationScale);
534                 mFinishFrameAnimation.restrictDuration(maxAnimationDuration);
535                 mFinishFrameAnimation.scaleCurrentDuration(animationScale);
536             }
537         }
538         mRotateExitAnimation.restrictDuration(maxAnimationDuration);
539         mRotateExitAnimation.scaleCurrentDuration(animationScale);
540         mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
541         mRotateEnterAnimation.scaleCurrentDuration(animationScale);
542         if (USE_CUSTOM_BLACK_FRAME) {
543             mRotateFrameAnimation.restrictDuration(maxAnimationDuration);
544             mRotateFrameAnimation.scaleCurrentDuration(animationScale);
545         }
546 
547         final int layerStack = mDisplayContent.getDisplay().getLayerStack();
548         if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) {
549             // Compute the transformation matrix that must be applied
550             // the the black frame to make it stay in the initial position
551             // before the new screen rotation.  This is different than the
552             // snapshot transformation because the snapshot is always based
553             // of the native orientation of the screen, not the orientation
554             // we were last in.
555             createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
556 
557             try {
558                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
559                         mOriginalWidth*2, mOriginalHeight*2);
560                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
561                 mCustomBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
562                         SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
563                 mCustomBlackFrame.setMatrix(t, mFrameInitialMatrix);
564             } catch (OutOfResourcesException e) {
565                 Slog.w(TAG, "Unable to allocate black surface", e);
566             }
567         }
568 
569         if (!customAnim && mExitingBlackFrame == null) {
570             try {
571                 // Compute the transformation matrix that must be applied
572                 // the the black frame to make it stay in the initial position
573                 // before the new screen rotation.  This is different than the
574                 // snapshot transformation because the snapshot is always based
575                 // of the native orientation of the screen, not the orientation
576                 // we were last in.
577                 createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
578 
579                 final Rect outer;
580                 final Rect inner;
581                 if (mForceDefaultOrientation) {
582                     // Going from a smaller Display to a larger Display, add curtains to sides
583                     // or top and bottom. Going from a larger to smaller display will result in
584                     // no BlackSurfaces being constructed.
585                     outer = mCurrentDisplayRect;
586                     inner = mOriginalDisplayRect;
587                 } else {
588                     outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
589                             mOriginalWidth*2, mOriginalHeight*2);
590                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
591                 }
592                 mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
593                         SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
594                 mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix);
595             } catch (OutOfResourcesException e) {
596                 Slog.w(TAG, "Unable to allocate black surface", e);
597             }
598         }
599 
600         if (customAnim && mEnteringBlackFrame == null) {
601             try {
602                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
603                         finalWidth*2, finalHeight*2);
604                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
605                 mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
606                         SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
607             } catch (OutOfResourcesException e) {
608                 Slog.w(TAG, "Unable to allocate black surface", e);
609             }
610         }
611 
612         return true;
613     }
614 
615     /**
616      * Returns true if animating.
617      */
dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)618     public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
619             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
620         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
621         if (mSurfaceControl == null) {
622             // Can't do animation.
623             return false;
624         }
625         if (!mStarted) {
626             startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
627                     true, exitAnim, enterAnim);
628         }
629         if (!mStarted) {
630             return false;
631         }
632         if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
633         mFinishAnimReady = true;
634         return true;
635     }
636 
kill()637     public void kill() {
638         if (DEBUG_STATE) Slog.v(TAG, "Kill!");
639         if (mSurfaceControl != null) {
640             if (SHOW_TRANSACTIONS ||
641                     SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
642                             "  FREEZE " + mSurfaceControl + ": DESTROY");
643             mService.mTransactionFactory.make().remove(mSurfaceControl).apply();
644             mSurfaceControl = null;
645         }
646         if (mCustomBlackFrame != null) {
647             mCustomBlackFrame.kill();
648             mCustomBlackFrame = null;
649         }
650         if (mExitingBlackFrame != null) {
651             mExitingBlackFrame.kill();
652             mExitingBlackFrame = null;
653         }
654         if (mEnteringBlackFrame != null) {
655             mEnteringBlackFrame.kill();
656             mEnteringBlackFrame = null;
657         }
658         if (TWO_PHASE_ANIMATION) {
659             if (mStartExitAnimation != null) {
660                 mStartExitAnimation.cancel();
661                 mStartExitAnimation = null;
662             }
663             if (mStartEnterAnimation != null) {
664                 mStartEnterAnimation.cancel();
665                 mStartEnterAnimation = null;
666             }
667             if (mFinishExitAnimation != null) {
668                 mFinishExitAnimation.cancel();
669                 mFinishExitAnimation = null;
670             }
671             if (mFinishEnterAnimation != null) {
672                 mFinishEnterAnimation.cancel();
673                 mFinishEnterAnimation = null;
674             }
675         }
676         if (USE_CUSTOM_BLACK_FRAME) {
677             if (mStartFrameAnimation != null) {
678                 mStartFrameAnimation.cancel();
679                 mStartFrameAnimation = null;
680             }
681             if (mRotateFrameAnimation != null) {
682                 mRotateFrameAnimation.cancel();
683                 mRotateFrameAnimation = null;
684             }
685             if (mFinishFrameAnimation != null) {
686                 mFinishFrameAnimation.cancel();
687                 mFinishFrameAnimation = null;
688             }
689         }
690         if (mRotateExitAnimation != null) {
691             mRotateExitAnimation.cancel();
692             mRotateExitAnimation = null;
693         }
694         if (mRotateEnterAnimation != null) {
695             mRotateEnterAnimation.cancel();
696             mRotateEnterAnimation = null;
697         }
698     }
699 
isAnimating()700     public boolean isAnimating() {
701         return hasAnimations() || (TWO_PHASE_ANIMATION && mFinishAnimReady);
702     }
703 
isRotating()704     public boolean isRotating() {
705         return mCurRotation != mOriginalRotation;
706     }
707 
hasAnimations()708     private boolean hasAnimations() {
709         return (TWO_PHASE_ANIMATION &&
710                     (mStartEnterAnimation != null || mStartExitAnimation != null
711                     || mFinishEnterAnimation != null || mFinishExitAnimation != null))
712                 || (USE_CUSTOM_BLACK_FRAME &&
713                         (mStartFrameAnimation != null || mRotateFrameAnimation != null
714                         || mFinishFrameAnimation != null))
715                 || mRotateEnterAnimation != null || mRotateExitAnimation != null;
716     }
717 
stepAnimation(long now)718     private boolean stepAnimation(long now) {
719         if (now > mHalfwayPoint) {
720             mHalfwayPoint = Long.MAX_VALUE;
721         }
722         if (mFinishAnimReady && mFinishAnimStartTime < 0) {
723             if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
724             mFinishAnimStartTime = now;
725         }
726 
727         if (TWO_PHASE_ANIMATION) {
728             mMoreStartExit = false;
729             if (mStartExitAnimation != null) {
730                 mMoreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
731                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
732             }
733 
734             mMoreStartEnter = false;
735             if (mStartEnterAnimation != null) {
736                 mMoreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
737                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
738             }
739         }
740         if (USE_CUSTOM_BLACK_FRAME) {
741             mMoreStartFrame = false;
742             if (mStartFrameAnimation != null) {
743                 mMoreStartFrame = mStartFrameAnimation.getTransformation(now, mStartFrameTransformation);
744                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start frame: " + mStartFrameTransformation);
745             }
746         }
747 
748         long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
749         if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
750 
751         if (TWO_PHASE_ANIMATION) {
752             mMoreFinishExit = false;
753             if (mFinishExitAnimation != null) {
754                 mMoreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
755                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
756             }
757 
758             mMoreFinishEnter = false;
759             if (mFinishEnterAnimation != null) {
760                 mMoreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
761                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
762             }
763         }
764         if (USE_CUSTOM_BLACK_FRAME) {
765             mMoreFinishFrame = false;
766             if (mFinishFrameAnimation != null) {
767                 mMoreFinishFrame = mFinishFrameAnimation.getTransformation(finishNow, mFinishFrameTransformation);
768                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish frame: " + mFinishFrameTransformation);
769             }
770         }
771 
772         mMoreRotateExit = false;
773         if (mRotateExitAnimation != null) {
774             mMoreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
775             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
776         }
777 
778         mMoreRotateEnter = false;
779         if (mRotateEnterAnimation != null) {
780             mMoreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
781             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
782         }
783 
784         if (USE_CUSTOM_BLACK_FRAME) {
785             mMoreRotateFrame = false;
786             if (mRotateFrameAnimation != null) {
787                 mMoreRotateFrame = mRotateFrameAnimation.getTransformation(now, mRotateFrameTransformation);
788                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate frame: " + mRotateFrameTransformation);
789             }
790         }
791 
792         if (!mMoreRotateExit && (!TWO_PHASE_ANIMATION || (!mMoreStartExit && !mMoreFinishExit))) {
793             if (TWO_PHASE_ANIMATION) {
794                 if (mStartExitAnimation != null) {
795                     if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing start exit anim!");
796                     mStartExitAnimation.cancel();
797                     mStartExitAnimation = null;
798                     mStartExitTransformation.clear();
799                 }
800                 if (mFinishExitAnimation != null) {
801                     if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing finish exit anim!");
802                     mFinishExitAnimation.cancel();
803                     mFinishExitAnimation = null;
804                     mFinishExitTransformation.clear();
805                 }
806             }
807             if (mRotateExitAnimation != null) {
808                 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing rotate exit anim!");
809                 mRotateExitAnimation.cancel();
810                 mRotateExitAnimation = null;
811                 mRotateExitTransformation.clear();
812             }
813         }
814 
815         if (!mMoreRotateEnter && (!TWO_PHASE_ANIMATION || (!mMoreStartEnter && !mMoreFinishEnter))) {
816             if (TWO_PHASE_ANIMATION) {
817                 if (mStartEnterAnimation != null) {
818                     if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing start enter anim!");
819                     mStartEnterAnimation.cancel();
820                     mStartEnterAnimation = null;
821                     mStartEnterTransformation.clear();
822                 }
823                 if (mFinishEnterAnimation != null) {
824                     if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing finish enter anim!");
825                     mFinishEnterAnimation.cancel();
826                     mFinishEnterAnimation = null;
827                     mFinishEnterTransformation.clear();
828                 }
829             }
830             if (mRotateEnterAnimation != null) {
831                 if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing rotate enter anim!");
832                 mRotateEnterAnimation.cancel();
833                 mRotateEnterAnimation = null;
834                 mRotateEnterTransformation.clear();
835             }
836         }
837 
838         if (USE_CUSTOM_BLACK_FRAME && !mMoreStartFrame && !mMoreRotateFrame && !mMoreFinishFrame) {
839             if (mStartFrameAnimation != null) {
840                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing start frame anim!");
841                 mStartFrameAnimation.cancel();
842                 mStartFrameAnimation = null;
843                 mStartFrameTransformation.clear();
844             }
845             if (mFinishFrameAnimation != null) {
846                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing finish frame anim!");
847                 mFinishFrameAnimation.cancel();
848                 mFinishFrameAnimation = null;
849                 mFinishFrameTransformation.clear();
850             }
851             if (mRotateFrameAnimation != null) {
852                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing rotate frame anim!");
853                 mRotateFrameAnimation.cancel();
854                 mRotateFrameAnimation = null;
855                 mRotateFrameTransformation.clear();
856             }
857         }
858 
859         mExitTransformation.set(mRotateExitTransformation);
860         mEnterTransformation.set(mRotateEnterTransformation);
861         if (TWO_PHASE_ANIMATION) {
862             mExitTransformation.compose(mStartExitTransformation);
863             mExitTransformation.compose(mFinishExitTransformation);
864 
865             mEnterTransformation.compose(mStartEnterTransformation);
866             mEnterTransformation.compose(mFinishEnterTransformation);
867         }
868 
869         if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
870         if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
871 
872         if (USE_CUSTOM_BLACK_FRAME) {
873             //mFrameTransformation.set(mRotateExitTransformation);
874             //mFrameTransformation.compose(mStartExitTransformation);
875             //mFrameTransformation.compose(mFinishExitTransformation);
876             mFrameTransformation.set(mRotateFrameTransformation);
877             mFrameTransformation.compose(mStartFrameTransformation);
878             mFrameTransformation.compose(mFinishFrameTransformation);
879             mFrameTransformation.getMatrix().preConcat(mFrameInitialMatrix);
880             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final frame: " + mFrameTransformation);
881         }
882 
883         final boolean more = (TWO_PHASE_ANIMATION
884                     && (mMoreStartEnter || mMoreStartExit || mMoreFinishEnter || mMoreFinishExit))
885                 || (USE_CUSTOM_BLACK_FRAME
886                         && (mMoreStartFrame || mMoreRotateFrame || mMoreFinishFrame))
887                 || mMoreRotateEnter || mMoreRotateExit
888                 || !mFinishAnimReady;
889 
890         mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
891 
892         if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
893 
894         return more;
895     }
896 
updateSurfaces(SurfaceControl.Transaction t)897     void updateSurfaces(SurfaceControl.Transaction t) {
898         if (!mStarted) {
899             return;
900         }
901 
902         if (mSurfaceControl != null) {
903             if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) {
904                 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
905                 t.hide(mSurfaceControl);
906             }
907         }
908 
909         if (mCustomBlackFrame != null) {
910             if (!mMoreStartFrame && !mMoreFinishFrame && !mMoreRotateFrame) {
911                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding black frame");
912                 mCustomBlackFrame.hide(t);
913             } else {
914                 mCustomBlackFrame.setMatrix(t, mFrameTransformation.getMatrix());
915             }
916         }
917 
918         if (mExitingBlackFrame != null) {
919             if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) {
920                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding exiting frame");
921                 mExitingBlackFrame.hide(t);
922             } else {
923                 mExitFrameFinalMatrix.setConcat(mExitTransformation.getMatrix(), mFrameInitialMatrix);
924                 mExitingBlackFrame.setMatrix(t, mExitFrameFinalMatrix);
925                 if (mForceDefaultOrientation) {
926                     mExitingBlackFrame.setAlpha(t, mExitTransformation.getAlpha());
927                 }
928             }
929         }
930 
931         if (mEnteringBlackFrame != null) {
932             if (!mMoreStartEnter && !mMoreFinishEnter && !mMoreRotateEnter) {
933                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding entering frame");
934                 mEnteringBlackFrame.hide(t);
935             } else {
936                 mEnteringBlackFrame.setMatrix(t, mEnterTransformation.getMatrix());
937             }
938         }
939 
940         t.setEarlyWakeup();
941         setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
942     }
943 
stepAnimationLocked(long now)944     public boolean stepAnimationLocked(long now) {
945         if (!hasAnimations()) {
946             if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
947             mFinishAnimReady = false;
948             return false;
949         }
950 
951         if (!mAnimRunning) {
952             if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
953             if (TWO_PHASE_ANIMATION) {
954                 if (mStartEnterAnimation != null) {
955                     mStartEnterAnimation.setStartTime(now);
956                 }
957                 if (mStartExitAnimation != null) {
958                     mStartExitAnimation.setStartTime(now);
959                 }
960                 if (mFinishEnterAnimation != null) {
961                     mFinishEnterAnimation.setStartTime(0);
962                 }
963                 if (mFinishExitAnimation != null) {
964                     mFinishExitAnimation.setStartTime(0);
965                 }
966             }
967             if (USE_CUSTOM_BLACK_FRAME) {
968                 if (mStartFrameAnimation != null) {
969                     mStartFrameAnimation.setStartTime(now);
970                 }
971                 if (mFinishFrameAnimation != null) {
972                     mFinishFrameAnimation.setStartTime(0);
973                 }
974                 if (mRotateFrameAnimation != null) {
975                     mRotateFrameAnimation.setStartTime(now);
976                 }
977             }
978             if (mRotateEnterAnimation != null) {
979                 mRotateEnterAnimation.setStartTime(now);
980             }
981             if (mRotateExitAnimation != null) {
982                 mRotateExitAnimation.setStartTime(now);
983             }
984             mAnimRunning = true;
985             mHalfwayPoint = now + mRotateEnterAnimation.getDuration() / 2;
986         }
987 
988         return stepAnimation(now);
989     }
990 
getEnterTransformation()991     public Transformation getEnterTransformation() {
992         return mEnterTransformation;
993     }
994 }
995