ScreenRotationAnimation.java revision bc68a59c024bdb745dac8e2ec7408a9f30595f1a
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.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Matrix;
23import android.graphics.Paint;
24import android.graphics.PixelFormat;
25import android.graphics.PorterDuff;
26import android.graphics.PorterDuffXfermode;
27import android.graphics.Rect;
28import android.util.Slog;
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 = false;
38
39    static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
40
41    final Context mContext;
42    Surface mSurface;
43    BlackFrame mBlackFrame;
44    int mWidth, mHeight;
45
46    int mSnapshotRotation;
47    int mSnapshotDeltaRotation;
48    int mOriginalRotation;
49    int mOriginalWidth, mOriginalHeight;
50    int mCurRotation;
51
52    Animation mExitAnimation;
53    final Transformation mExitTransformation = new Transformation();
54    Animation mEnterAnimation;
55    final Transformation mEnterTransformation = new Transformation();
56    boolean mStarted;
57
58    final Matrix mSnapshotInitialMatrix = new Matrix();
59    final Matrix mSnapshotFinalMatrix = new Matrix();
60    final Matrix mTmpMatrix = new Matrix();
61    final float[] mTmpFloats = new float[9];
62
63    public ScreenRotationAnimation(Context context, SurfaceSession session,
64            boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
65        mContext = context;
66
67        Bitmap screenshot = Surface.screenshot(0, 0);
68
69        if (screenshot == null) {
70            // Device is not capable of screenshots...  we can't do an animation.
71            return;
72        }
73
74        // Screenshot does NOT include rotation!
75        mSnapshotRotation = 0;
76        mWidth = screenshot.getWidth();
77        mHeight = screenshot.getHeight();
78
79        mOriginalRotation = originalRotation;
80        mOriginalWidth = originalWidth;
81        mOriginalHeight = originalHeight;
82
83        if (!inTransaction) {
84            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
85                    ">>> OPEN TRANSACTION ScreenRotationAnimation");
86            Surface.openTransaction();
87        }
88
89        try {
90            try {
91                mSurface = new Surface(session, 0, "FreezeSurface",
92                        -1, mWidth, mHeight, PixelFormat.OPAQUE, 0);
93                mSurface.setLayer(FREEZE_LAYER + 1);
94            } catch (Surface.OutOfResourcesException e) {
95                Slog.w(TAG, "Unable to allocate freeze surface", e);
96            }
97
98            if (WindowManagerService.SHOW_TRANSACTIONS ||
99                    WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
100                            "  FREEZE " + mSurface + ": CREATE");
101
102            setRotation(originalRotation);
103
104            if (mSurface != null) {
105                Rect dirty = new Rect(0, 0, mWidth, mHeight);
106                Canvas c = null;
107                try {
108                    c = mSurface.lockCanvas(dirty);
109                } catch (IllegalArgumentException e) {
110                    Slog.w(TAG, "Unable to lock surface", e);
111                } catch (Surface.OutOfResourcesException e) {
112                    Slog.w(TAG, "Unable to lock surface", e);
113                }
114                if (c == null) {
115                    Slog.w(TAG, "Null surface canvas");
116                    mSurface.destroy();
117                    mSurface = null;
118                    return;
119                }
120
121                Paint paint = new Paint(0);
122                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
123                c.drawBitmap(screenshot, 0, 0, paint);
124
125                mSurface.unlockCanvasAndPost(c);
126            }
127        } finally {
128            if (!inTransaction) {
129                Surface.closeTransaction();
130                if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
131                        "<<< CLOSE TRANSACTION ScreenRotationAnimation");
132            }
133
134            screenshot.recycle();
135        }
136    }
137
138    boolean hasScreenshot() {
139        return mSurface != null;
140    }
141
142    static int deltaRotation(int oldRotation, int newRotation) {
143        int delta = newRotation - oldRotation;
144        if (delta < 0) delta += 4;
145        return delta;
146    }
147
148    void setSnapshotTransform(Matrix matrix, float alpha) {
149        if (mSurface != null) {
150            matrix.getValues(mTmpFloats);
151            mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
152                    (int)mTmpFloats[Matrix.MTRANS_Y]);
153            mSurface.setMatrix(
154                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
155                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
156            mSurface.setAlpha(alpha);
157            if (DEBUG) {
158                float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
159                float[] dstPnts = new float[4];
160                matrix.mapPoints(dstPnts, srcPnts);
161                Slog.i(TAG, "Original  : (" + srcPnts[0] + "," + srcPnts[1]
162                        + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
163                Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
164                        + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
165            }
166        }
167    }
168
169    public static void createRotationMatrix(int rotation, int width, int height,
170            Matrix outMatrix) {
171        switch (rotation) {
172            case Surface.ROTATION_0:
173                outMatrix.reset();
174                break;
175            case Surface.ROTATION_90:
176                outMatrix.setRotate(90, 0, 0);
177                outMatrix.postTranslate(height, 0);
178                break;
179            case Surface.ROTATION_180:
180                outMatrix.setRotate(180, 0, 0);
181                outMatrix.postTranslate(width, height);
182                break;
183            case Surface.ROTATION_270:
184                outMatrix.setRotate(270, 0, 0);
185                outMatrix.postTranslate(0, width);
186                break;
187        }
188    }
189
190    // Must be called while in a transaction.
191    public void setRotation(int rotation) {
192        mCurRotation = rotation;
193
194        // Compute the transformation matrix that must be applied
195        // to the snapshot to make it stay in the same original position
196        // with the current screen rotation.
197        int delta = deltaRotation(rotation, mSnapshotRotation);
198        createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
199
200        if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
201        setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
202    }
203
204    /**
205     * Returns true if animating.
206     */
207    public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
208            float animationScale, int finalWidth, int finalHeight) {
209        if (mSurface == null) {
210            // Can't do animation.
211            return false;
212        }
213
214        // Figure out how the screen has moved from the original rotation.
215        int delta = deltaRotation(mCurRotation, mOriginalRotation);
216
217        switch (delta) {
218            case Surface.ROTATION_0:
219                mExitAnimation = AnimationUtils.loadAnimation(mContext,
220                        com.android.internal.R.anim.screen_rotate_0_exit);
221                mEnterAnimation = AnimationUtils.loadAnimation(mContext,
222                        com.android.internal.R.anim.screen_rotate_0_enter);
223                break;
224            case Surface.ROTATION_90:
225                mExitAnimation = AnimationUtils.loadAnimation(mContext,
226                        com.android.internal.R.anim.screen_rotate_plus_90_exit);
227                mEnterAnimation = AnimationUtils.loadAnimation(mContext,
228                        com.android.internal.R.anim.screen_rotate_plus_90_enter);
229                break;
230            case Surface.ROTATION_180:
231                mExitAnimation = AnimationUtils.loadAnimation(mContext,
232                        com.android.internal.R.anim.screen_rotate_180_exit);
233                mEnterAnimation = AnimationUtils.loadAnimation(mContext,
234                        com.android.internal.R.anim.screen_rotate_180_enter);
235                break;
236            case Surface.ROTATION_270:
237                mExitAnimation = AnimationUtils.loadAnimation(mContext,
238                        com.android.internal.R.anim.screen_rotate_minus_90_exit);
239                mEnterAnimation = AnimationUtils.loadAnimation(mContext,
240                        com.android.internal.R.anim.screen_rotate_minus_90_enter);
241                break;
242        }
243
244        // Initialize the animations.  This is a hack, redefining what "parent"
245        // means to allow supplying the last and next size.  In this definition
246        // "%p" is the original (let's call it "previous") size, and "%" is the
247        // screen's current/new size.
248        mEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
249        mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
250        mStarted = false;
251
252        mExitAnimation.restrictDuration(maxAnimationDuration);
253        mExitAnimation.scaleCurrentDuration(animationScale);
254        mEnterAnimation.restrictDuration(maxAnimationDuration);
255        mEnterAnimation.scaleCurrentDuration(animationScale);
256
257        if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
258                ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
259        Surface.openTransaction();
260
261        try {
262            Rect outer = new Rect(-finalWidth, -finalHeight, finalWidth * 2, finalHeight * 2);
263            Rect inner = new Rect(0, 0, finalWidth, finalHeight);
264            mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
265        } catch (Surface.OutOfResourcesException e) {
266            Slog.w(TAG, "Unable to allocate black surface", e);
267        } finally {
268            Surface.closeTransaction();
269            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
270                    "<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss");
271        }
272
273        return true;
274    }
275
276    public void kill() {
277        if (mSurface != null) {
278            if (WindowManagerService.SHOW_TRANSACTIONS ||
279                    WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
280                            "  FREEZE " + mSurface + ": DESTROY");
281            mSurface.destroy();
282            mSurface = null;
283        }
284        if (mBlackFrame != null) {
285            mBlackFrame.kill();
286        }
287        if (mExitAnimation != null) {
288            mExitAnimation.cancel();
289            mExitAnimation = null;
290        }
291        if (mEnterAnimation != null) {
292            mEnterAnimation.cancel();
293            mEnterAnimation = null;
294        }
295    }
296
297    public boolean isAnimating() {
298        return mEnterAnimation != null || mExitAnimation != null;
299    }
300
301    public boolean stepAnimation(long now) {
302        if (mEnterAnimation == null && mExitAnimation == null) {
303            return false;
304        }
305
306        if (!mStarted) {
307            mEnterAnimation.setStartTime(now);
308            mExitAnimation.setStartTime(now);
309            mStarted = true;
310        }
311
312        mExitTransformation.clear();
313        boolean moreExit = false;
314        if (mExitAnimation != null) {
315            moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
316            if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
317            if (!moreExit) {
318                if (DEBUG) Slog.v(TAG, "Exit animation done!");
319                mExitAnimation.cancel();
320                mExitAnimation = null;
321                mExitTransformation.clear();
322                if (mSurface != null) {
323                    mSurface.hide();
324                }
325            }
326        }
327
328        mEnterTransformation.clear();
329        boolean moreEnter = false;
330        if (mEnterAnimation != null) {
331            moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
332            if (!moreEnter) {
333                mEnterAnimation.cancel();
334                mEnterAnimation = null;
335                mEnterTransformation.clear();
336                if (mBlackFrame != null) {
337                    mBlackFrame.hide();
338                }
339            } else {
340                if (mBlackFrame != null) {
341                    mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
342                }
343            }
344        }
345
346        mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
347        setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
348
349        return moreEnter || moreExit;
350    }
351
352    public Transformation getEnterTransformation() {
353        return mEnterTransformation;
354    }
355}
356