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