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