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