ScreenRotationAnimation.java revision fd1c5ed3705b885ce50b5ecad04ce699248b1d84
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
17package com.android.server.wm;
18
19import android.content.Context;
20import android.graphics.Matrix;
21import android.graphics.PixelFormat;
22import android.graphics.Rect;
23import android.util.Slog;
24import android.view.Surface;
25import android.view.SurfaceSession;
26import android.view.animation.Animation;
27import android.view.animation.AnimationUtils;
28import android.view.animation.Transformation;
29
30class ScreenRotationAnimation {
31    static final String TAG = "ScreenRotationAnimation";
32    static final boolean DEBUG_STATE = false;
33    static final boolean DEBUG_TRANSFORMS = false;
34
35    static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
36
37    final Context mContext;
38    Surface mSurface;
39    BlackFrame mBlackFrame;
40    int mWidth, mHeight;
41
42    int mSnapshotRotation;
43    int mSnapshotDeltaRotation;
44    int mOriginalRotation;
45    int mOriginalWidth, mOriginalHeight;
46    int mCurRotation;
47
48    // For all animations, "exit" is for the UI elements that are going
49    // away (that is the snapshot of the old screen), and "enter" is for
50    // the new UI elements that are appearing (that is the active windows
51    // in their final orientation).
52
53    // The starting animation for the exiting and entering elements.  This
54    // animation applies a transformation while the rotation is in progress.
55    // It is started immediately, before the new entering UI is ready.
56    Animation mStartExitAnimation;
57    final Transformation mStartExitTransformation = new Transformation();
58    Animation mStartEnterAnimation;
59    final Transformation mStartEnterTransformation = new Transformation();
60
61    // The finishing animation for the exiting and entering elements.  This
62    // animation needs to undo the transformation of the starting animation.
63    // It starts running once the new rotation UI elements are ready to be
64    // displayed.
65    Animation mFinishExitAnimation;
66    final Transformation mFinishExitTransformation = new Transformation();
67    Animation mFinishEnterAnimation;
68    final Transformation mFinishEnterTransformation = new Transformation();
69
70    // The current active animation to move from the old to the new rotated
71    // state.  Which animation is run here will depend on the old and new
72    // rotations.
73    Animation mRotateExitAnimation;
74    final Transformation mRotateExitTransformation = new Transformation();
75    Animation mRotateEnterAnimation;
76    final Transformation mRotateEnterTransformation = new Transformation();
77
78    // A previously running rotate animation.  This will be used if we need
79    // to switch to a new rotation before finishing the previous one.
80    Animation mLastRotateExitAnimation;
81    final Transformation mLastRotateExitTransformation = new Transformation();
82    Animation mLastRotateEnterAnimation;
83    final Transformation mLastRotateEnterTransformation = new Transformation();
84
85    // Complete transformations being applied.
86    final Transformation mExitTransformation = new Transformation();
87    final Transformation mEnterTransformation = new Transformation();
88
89    boolean mStarted;
90    boolean mAnimRunning;
91    boolean mFinishAnimReady;
92    long mFinishAnimStartTime;
93
94    final Matrix mSnapshotInitialMatrix = new Matrix();
95    final Matrix mSnapshotFinalMatrix = new Matrix();
96    final Matrix mTmpMatrix = new Matrix();
97    final float[] mTmpFloats = new float[9];
98
99    public ScreenRotationAnimation(Context context, SurfaceSession session,
100            boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
101        mContext = context;
102
103        // Screenshot does NOT include rotation!
104        mSnapshotRotation = 0;
105        if (originalRotation == Surface.ROTATION_90
106                || originalRotation == Surface.ROTATION_270) {
107            mWidth = originalHeight;
108            mHeight = originalWidth;
109        } else {
110            mWidth = originalWidth;
111            mHeight = originalHeight;
112        }
113
114        mOriginalRotation = originalRotation;
115        mOriginalWidth = originalWidth;
116        mOriginalHeight = originalHeight;
117
118        if (!inTransaction) {
119            if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
120                    ">>> OPEN TRANSACTION ScreenRotationAnimation");
121            Surface.openTransaction();
122        }
123
124        try {
125            try {
126                mSurface = new Surface(session, 0, "FreezeSurface",
127                        -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT | Surface.HIDDEN);
128                if (mSurface == null || !mSurface.isValid()) {
129                    // Screenshot failed, punt.
130                    mSurface = null;
131                    return;
132                }
133                mSurface.setLayer(FREEZE_LAYER + 1);
134                mSurface.show();
135            } catch (Surface.OutOfResourcesException e) {
136                Slog.w(TAG, "Unable to allocate freeze surface", e);
137            }
138
139            if (WindowManagerService.SHOW_TRANSACTIONS ||
140                    WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
141                            "  FREEZE " + mSurface + ": CREATE");
142
143            setRotation(originalRotation);
144        } finally {
145            if (!inTransaction) {
146                Surface.closeTransaction();
147                if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
148                        "<<< CLOSE TRANSACTION ScreenRotationAnimation");
149            }
150        }
151    }
152
153    boolean hasScreenshot() {
154        return mSurface != null;
155    }
156
157    static int deltaRotation(int oldRotation, int newRotation) {
158        int delta = newRotation - oldRotation;
159        if (delta < 0) delta += 4;
160        return delta;
161    }
162
163    void setSnapshotTransform(Matrix matrix, float alpha) {
164        if (mSurface != null) {
165            matrix.getValues(mTmpFloats);
166            mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
167                    mTmpFloats[Matrix.MTRANS_Y]);
168            mSurface.setMatrix(
169                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
170                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
171            mSurface.setAlpha(alpha);
172            if (DEBUG_TRANSFORMS) {
173                float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
174                float[] dstPnts = new float[4];
175                matrix.mapPoints(dstPnts, srcPnts);
176                Slog.i(TAG, "Original  : (" + srcPnts[0] + "," + srcPnts[1]
177                        + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
178                Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
179                        + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
180            }
181        }
182    }
183
184    public static void createRotationMatrix(int rotation, int width, int height,
185            Matrix outMatrix) {
186        switch (rotation) {
187            case Surface.ROTATION_0:
188                outMatrix.reset();
189                break;
190            case Surface.ROTATION_90:
191                outMatrix.setRotate(90, 0, 0);
192                outMatrix.postTranslate(height, 0);
193                break;
194            case Surface.ROTATION_180:
195                outMatrix.setRotate(180, 0, 0);
196                outMatrix.postTranslate(width, height);
197                break;
198            case Surface.ROTATION_270:
199                outMatrix.setRotate(270, 0, 0);
200                outMatrix.postTranslate(0, width);
201                break;
202        }
203    }
204
205    // Must be called while in a transaction.
206    private void setRotation(int rotation) {
207        mCurRotation = rotation;
208
209        // Compute the transformation matrix that must be applied
210        // to the snapshot to make it stay in the same original position
211        // with the current screen rotation.
212        int delta = deltaRotation(rotation, mSnapshotRotation);
213        createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
214
215        if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
216        setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
217    }
218
219    // Must be called while in a transaction.
220    public boolean setRotation(int rotation, SurfaceSession session,
221            long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
222        setRotation(rotation);
223        return startAnimation(session, maxAnimationDuration, animationScale,
224                finalWidth, finalHeight, false);
225    }
226
227    /**
228     * Returns true if animating.
229     */
230    private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
231            float animationScale, int finalWidth, int finalHeight, boolean dismissing) {
232        if (mSurface == null) {
233            // Can't do animation.
234            return false;
235        }
236        if (mStarted) {
237            return true;
238        }
239
240        mStarted = true;
241
242        boolean firstStart = false;
243
244        // Figure out how the screen has moved from the original rotation.
245        int delta = deltaRotation(mCurRotation, mOriginalRotation);
246
247        if (mFinishExitAnimation == null && (!dismissing || delta != Surface.ROTATION_0)) {
248            if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
249            firstStart = true;
250            mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
251                    com.android.internal.R.anim.screen_rotate_start_exit);
252            mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
253                    com.android.internal.R.anim.screen_rotate_start_enter);
254            mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
255                    com.android.internal.R.anim.screen_rotate_finish_exit);
256            mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
257                    com.android.internal.R.anim.screen_rotate_finish_enter);
258        }
259
260        if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
261                + finalWidth + " finalHeight=" + finalHeight
262                + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
263
264        switch (delta) {
265            case Surface.ROTATION_0:
266                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
267                        com.android.internal.R.anim.screen_rotate_0_exit);
268                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
269                        com.android.internal.R.anim.screen_rotate_0_enter);
270                break;
271            case Surface.ROTATION_90:
272                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
273                        com.android.internal.R.anim.screen_rotate_plus_90_exit);
274                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
275                        com.android.internal.R.anim.screen_rotate_plus_90_enter);
276                break;
277            case Surface.ROTATION_180:
278                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
279                        com.android.internal.R.anim.screen_rotate_180_exit);
280                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
281                        com.android.internal.R.anim.screen_rotate_180_enter);
282                break;
283            case Surface.ROTATION_270:
284                mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
285                        com.android.internal.R.anim.screen_rotate_minus_90_exit);
286                mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
287                        com.android.internal.R.anim.screen_rotate_minus_90_enter);
288                break;
289        }
290
291        // Initialize the animations.  This is a hack, redefining what "parent"
292        // means to allow supplying the last and next size.  In this definition
293        // "%p" is the original (let's call it "previous") size, and "%" is the
294        // screen's current/new size.
295        if (firstStart) {
296            if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
297            mStartEnterAnimation.initialize(finalWidth, finalHeight,
298                    mOriginalWidth, mOriginalHeight);
299            mStartExitAnimation.initialize(finalWidth, finalHeight,
300                    mOriginalWidth, mOriginalHeight);
301            mFinishEnterAnimation.initialize(finalWidth, finalHeight,
302                    mOriginalWidth, mOriginalHeight);
303            mFinishExitAnimation.initialize(finalWidth, finalHeight,
304                    mOriginalWidth, mOriginalHeight);
305        }
306        mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
307        mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
308        mAnimRunning = false;
309        mFinishAnimReady = false;
310        mFinishAnimStartTime = -1;
311
312        if (firstStart) {
313            mStartExitAnimation.restrictDuration(maxAnimationDuration);
314            mStartExitAnimation.scaleCurrentDuration(animationScale);
315            mStartEnterAnimation.restrictDuration(maxAnimationDuration);
316            mStartEnterAnimation.scaleCurrentDuration(animationScale);
317            mFinishExitAnimation.restrictDuration(maxAnimationDuration);
318            mFinishExitAnimation.scaleCurrentDuration(animationScale);
319            mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
320            mFinishEnterAnimation.scaleCurrentDuration(animationScale);
321        }
322        mRotateExitAnimation.restrictDuration(maxAnimationDuration);
323        mRotateExitAnimation.scaleCurrentDuration(animationScale);
324        mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
325        mRotateEnterAnimation.scaleCurrentDuration(animationScale);
326
327        if (mBlackFrame == null) {
328            if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
329                    WindowManagerService.TAG,
330                    ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
331            Surface.openTransaction();
332
333            try {
334                Rect outer = new Rect(-finalWidth*1, -finalHeight*1, finalWidth*2, finalHeight*2);
335                Rect inner = new Rect(0, 0, finalWidth, finalHeight);
336                mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
337            } catch (Surface.OutOfResourcesException e) {
338                Slog.w(TAG, "Unable to allocate black surface", e);
339            } finally {
340                Surface.closeTransaction();
341                if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
342                        WindowManagerService.TAG,
343                        "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
344            }
345        }
346
347        return true;
348    }
349
350    /**
351     * Returns true if animating.
352     */
353    public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
354            float animationScale, int finalWidth, int finalHeight) {
355        if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
356        if (mSurface == null) {
357            // Can't do animation.
358            return false;
359        }
360        if (!mStarted) {
361            startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
362                    true);
363        }
364        if (!mStarted) {
365            return false;
366        }
367        if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
368        mFinishAnimReady = true;
369        return true;
370    }
371
372    public void kill() {
373        if (DEBUG_STATE) Slog.v(TAG, "Kill!");
374        if (mSurface != null) {
375            if (WindowManagerService.SHOW_TRANSACTIONS ||
376                    WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
377                            "  FREEZE " + mSurface + ": DESTROY");
378            mSurface.destroy();
379            mSurface = null;
380        }
381        if (mBlackFrame != null) {
382            mBlackFrame.kill();
383            mBlackFrame = null;
384        }
385        if (mStartExitAnimation != null) {
386            mStartExitAnimation.cancel();
387            mStartExitAnimation = null;
388        }
389        if (mStartEnterAnimation != null) {
390            mStartEnterAnimation.cancel();
391            mStartEnterAnimation = null;
392        }
393        if (mFinishExitAnimation != null) {
394            mFinishExitAnimation.cancel();
395            mFinishExitAnimation = null;
396        }
397        if (mStartEnterAnimation != null) {
398            mStartEnterAnimation.cancel();
399            mStartEnterAnimation = null;
400        }
401        if (mRotateExitAnimation != null) {
402            mRotateExitAnimation.cancel();
403            mRotateExitAnimation = null;
404        }
405        if (mRotateEnterAnimation != null) {
406            mRotateEnterAnimation.cancel();
407            mRotateEnterAnimation = null;
408        }
409    }
410
411    public boolean isAnimating() {
412        return mStartEnterAnimation != null || mStartExitAnimation != null
413                && mFinishEnterAnimation != null || mFinishExitAnimation != null
414                && mRotateEnterAnimation != null || mRotateExitAnimation != null;
415    }
416
417    public boolean stepAnimation(long now) {
418        if (!isAnimating()) {
419            if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
420            return false;
421        }
422
423        if (!mAnimRunning) {
424            if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
425            if (mStartEnterAnimation != null) {
426                mStartEnterAnimation.setStartTime(now);
427            }
428            if (mStartExitAnimation != null) {
429                mStartExitAnimation.setStartTime(now);
430            }
431            if (mFinishEnterAnimation != null) {
432                mFinishEnterAnimation.setStartTime(0);
433            }
434            if (mFinishExitAnimation != null) {
435                mFinishExitAnimation.setStartTime(0);
436            }
437            if (mRotateEnterAnimation != null) {
438                mRotateEnterAnimation.setStartTime(now);
439            }
440            if (mRotateExitAnimation != null) {
441                mRotateExitAnimation.setStartTime(now);
442            }
443            mAnimRunning = true;
444        }
445
446        if (mFinishAnimReady && mFinishAnimStartTime < 0) {
447            if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
448            mFinishAnimStartTime = now;
449        }
450
451        // If the start animation is no longer running, we want to keep its
452        // transformation intact until the finish animation also completes.
453
454        boolean moreStartExit = false;
455        if (mStartExitAnimation != null) {
456            mStartExitTransformation.clear();
457            moreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
458            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
459            if (!moreStartExit) {
460                if (DEBUG_STATE) Slog.v(TAG, "Start exit animation done!");
461                mStartExitAnimation.cancel();
462                mStartExitAnimation = null;
463            }
464        }
465
466        boolean moreStartEnter = false;
467        if (mStartEnterAnimation != null) {
468            mStartEnterTransformation.clear();
469            moreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
470            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
471            if (!moreStartEnter) {
472                if (DEBUG_STATE) Slog.v(TAG, "Start enter animation done!");
473                mStartEnterAnimation.cancel();
474                mStartEnterAnimation = null;
475            }
476        }
477
478        long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
479        if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
480
481        mFinishExitTransformation.clear();
482        boolean moreFinishExit = false;
483        if (mFinishExitAnimation != null) {
484            moreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
485            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
486            if (!moreStartExit && !moreFinishExit) {
487                if (DEBUG_STATE) Slog.v(TAG, "Finish exit animation done, clearing start/finish anims!");
488                mStartExitTransformation.clear();
489                mFinishExitAnimation.cancel();
490                mFinishExitAnimation = null;
491                mFinishExitTransformation.clear();
492            }
493        }
494
495        mFinishEnterTransformation.clear();
496        boolean moreFinishEnter = false;
497        if (mFinishEnterAnimation != null) {
498            moreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
499            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
500            if (!moreStartEnter && !moreFinishEnter) {
501                if (DEBUG_STATE) Slog.v(TAG, "Finish enter animation done, clearing start/finish anims!");
502                mStartEnterTransformation.clear();
503                mFinishEnterAnimation.cancel();
504                mFinishEnterAnimation = null;
505                mFinishEnterTransformation.clear();
506            }
507        }
508
509        mRotateExitTransformation.clear();
510        boolean moreRotateExit = false;
511        if (mRotateExitAnimation != null) {
512            moreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
513            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
514        }
515
516        if (!moreFinishExit && !moreRotateExit) {
517            if (DEBUG_STATE) Slog.v(TAG, "Rotate exit animation done!");
518            mRotateExitAnimation.cancel();
519            mRotateExitAnimation = null;
520            mRotateExitTransformation.clear();
521        }
522
523        mRotateEnterTransformation.clear();
524        boolean moreRotateEnter = false;
525        if (mRotateEnterAnimation != null) {
526            moreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
527            if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
528        }
529
530        if (!moreFinishEnter && !moreRotateEnter) {
531            if (DEBUG_STATE) Slog.v(TAG, "Rotate enter animation done!");
532            mRotateEnterAnimation.cancel();
533            mRotateEnterAnimation = null;
534            mRotateEnterTransformation.clear();
535        }
536
537        mExitTransformation.set(mRotateExitTransformation);
538        mExitTransformation.compose(mStartExitTransformation);
539        mExitTransformation.compose(mFinishExitTransformation);
540
541        mEnterTransformation.set(mRotateEnterTransformation);
542        mEnterTransformation.compose(mStartEnterTransformation);
543        mEnterTransformation.compose(mFinishEnterTransformation);
544
545        if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
546        if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
547
548        if (!moreStartExit && !moreFinishExit && !moreRotateExit) {
549            if (mSurface != null) {
550                if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
551                mSurface.hide();
552            }
553        }
554
555        if (!moreStartEnter && !moreFinishEnter && !moreRotateEnter) {
556            if (mBlackFrame != null) {
557                if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, hiding black frame");
558                mBlackFrame.hide();
559            }
560        } else {
561            if (mBlackFrame != null) {
562                mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
563            }
564        }
565
566        mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
567        setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
568
569        final boolean more = moreStartEnter || moreStartExit || moreFinishEnter || moreFinishExit
570                || moreRotateEnter || moreRotateExit || !mFinishAnimReady;
571
572        if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
573
574        return more;
575    }
576
577    public Transformation getEnterTransformation() {
578        return mEnterTransformation;
579    }
580}
581