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