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