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