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