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