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