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