blob: 2ad9cbe97d41ef63ed6ea091b11702086d21dd58 [file] [log] [blame]
Dianne Hackborna1111872010-11-23 20:55:11 -08001/*
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; // TODO: use com.android.server.wm, once things move there
18
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080019import android.content.Context;
Dianne Hackborna1111872010-11-23 20:55:11 -080020import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Color;
23import android.graphics.Matrix;
24import android.graphics.Paint;
25import android.graphics.PixelFormat;
26import android.graphics.Rect;
27import android.util.DisplayMetrics;
28import android.util.Slog;
29import android.view.Display;
30import android.view.Surface;
31import android.view.SurfaceSession;
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080032import android.view.animation.Animation;
33import android.view.animation.AnimationUtils;
34import android.view.animation.Transformation;
Dianne Hackborna1111872010-11-23 20:55:11 -080035
36class ScreenRotationAnimation {
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080037 static final String TAG = "ScreenRotationAnimation";
38 static final boolean DEBUG = false;
Dianne Hackborna1111872010-11-23 20:55:11 -080039
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080040 final Context mContext;
41 final Display mDisplay;
Dianne Hackborna1111872010-11-23 20:55:11 -080042 Surface mSurface;
Dianne Hackborn352cc982011-01-04 11:34:18 -080043 Surface mBlackSurface;
Dianne Hackborna1111872010-11-23 20:55:11 -080044 int mWidth, mHeight;
45
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080046 int mSnapshotRotation;
47 int mSnapshotDeltaRotation;
48 int mOriginalRotation;
49 int mOriginalWidth, mOriginalHeight;
Dianne Hackborna1111872010-11-23 20:55:11 -080050 int mCurRotation;
Dianne Hackborna1111872010-11-23 20:55:11 -080051
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080052 Animation mExitAnimation;
53 final Transformation mExitTransformation = new Transformation();
54 Animation mEnterAnimation;
55 final Transformation mEnterTransformation = new Transformation();
56 boolean mStarted;
57
58 final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
59 final Matrix mSnapshotInitialMatrix = new Matrix();
60 final Matrix mSnapshotFinalMatrix = new Matrix();
Dianne Hackborna1111872010-11-23 20:55:11 -080061 final float[] mTmpFloats = new float[9];
62
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -080063 public ScreenRotationAnimation(Context context, Display display, SurfaceSession session,
64 boolean inTransaction) {
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080065 mContext = context;
66 mDisplay = display;
67
68 display.getMetrics(mDisplayMetrics);
Dianne Hackborna1111872010-11-23 20:55:11 -080069
70 Bitmap screenshot = Surface.screenshot(0, 0);
71
72 if (screenshot != null) {
73 // Screenshot does NOT include rotation!
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080074 mSnapshotRotation = 0;
Dianne Hackborna1111872010-11-23 20:55:11 -080075 mWidth = screenshot.getWidth();
76 mHeight = screenshot.getHeight();
77 } else {
78 // Just in case.
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080079 mSnapshotRotation = display.getRotation();
80 mWidth = mDisplayMetrics.widthPixels;
81 mHeight = mDisplayMetrics.heightPixels;
Dianne Hackborna1111872010-11-23 20:55:11 -080082 }
83
Dianne Hackbornf9d0be92010-11-24 12:35:25 -080084 mOriginalRotation = display.getRotation();
85 mOriginalWidth = mDisplayMetrics.widthPixels;
86 mOriginalHeight = mDisplayMetrics.heightPixels;
87
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -080088 if (!inTransaction) {
89 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
90 ">>> OPEN TRANSACTION ScreenRotationAnimation");
91 Surface.openTransaction();
92 }
Dianne Hackborn352cc982011-01-04 11:34:18 -080093
Dianne Hackborna1111872010-11-23 20:55:11 -080094 try {
Dianne Hackborn352cc982011-01-04 11:34:18 -080095 try {
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -080096 mSurface = new Surface(session, 0, "FreezeSurface",
97 -1, mWidth, mHeight, PixelFormat.OPAQUE, 0);
98 mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200);
Dianne Hackborn352cc982011-01-04 11:34:18 -080099 } catch (Surface.OutOfResourcesException e) {
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -0800100 Slog.w(TAG, "Unable to allocate freeze surface", e);
Dianne Hackborn352cc982011-01-04 11:34:18 -0800101 }
Dianne Hackborna1111872010-11-23 20:55:11 -0800102
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -0800103 if (false) {
104 try {
105 int size = mOriginalWidth > mOriginalHeight ? mOriginalWidth : mOriginalHeight;
106 mBlackSurface = new Surface(session, 0, "BlackSurface",
107 -1, size, size, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM);
108 mBlackSurface.setAlpha(1.0f);
109 mBlackSurface.setLayer(0);
110 } catch (Surface.OutOfResourcesException e) {
111 Slog.w(TAG, "Unable to allocate black surface", e);
112 }
Dianne Hackborn352cc982011-01-04 11:34:18 -0800113 }
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -0800114
115 setRotation(display.getRotation());
116
117 if (mSurface != null) {
118 Rect dirty = new Rect(0, 0, mWidth, mHeight);
119 Canvas c = null;
120 try {
121 c = mSurface.lockCanvas(dirty);
122 } catch (IllegalArgumentException e) {
123 Slog.w(TAG, "Unable to lock surface", e);
124 return;
125 } catch (Surface.OutOfResourcesException e) {
126 Slog.w(TAG, "Unable to lock surface", e);
127 return;
128 }
129 if (c == null) {
130 Slog.w(TAG, "Null surface");
131 return;
132 }
133
134 if (screenshot != null) {
135 c.drawBitmap(screenshot, 0, 0, new Paint(0));
136 } else {
137 c.drawColor(Color.GREEN);
138 }
139
140 mSurface.unlockCanvasAndPost(c);
141 }
142 } finally {
143 if (!inTransaction) {
144 Surface.closeTransaction();
145 if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
146 "<<< CLOSE TRANSACTION ScreenRotationAnimation");
Dianne Hackborn352cc982011-01-04 11:34:18 -0800147 }
148
149 if (screenshot != null) {
Dianne Hackborn94cb2eb2011-01-13 21:09:44 -0800150 screenshot.recycle();
Dianne Hackborn352cc982011-01-04 11:34:18 -0800151 }
Dianne Hackborn0f761d62010-11-30 22:06:10 -0800152 }
Dianne Hackborna1111872010-11-23 20:55:11 -0800153 }
154
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800155 static int deltaRotation(int oldRotation, int newRotation) {
156 int delta = newRotation - oldRotation;
Dianne Hackborna1111872010-11-23 20:55:11 -0800157 if (delta < 0) delta += 4;
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800158 return delta;
159 }
Dianne Hackborna1111872010-11-23 20:55:11 -0800160
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800161 void setSnapshotTransform(Matrix matrix, float alpha) {
Dianne Hackborn352cc982011-01-04 11:34:18 -0800162 if (mSurface != null) {
163 matrix.getValues(mTmpFloats);
164 mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
165 (int)mTmpFloats[Matrix.MTRANS_Y]);
166 mSurface.setMatrix(
167 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
168 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
169 mSurface.setAlpha(alpha);
170 if (DEBUG) {
171 float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
172 float[] dstPnts = new float[4];
173 matrix.mapPoints(dstPnts, srcPnts);
174 Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1]
175 + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
176 Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
177 + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
178 }
Dianne Hackborna1111872010-11-23 20:55:11 -0800179 }
180 }
181
Dianne Hackborn0aae2d42010-12-07 23:51:29 -0800182 public static void createRotationMatrix(int rotation, int width, int height,
183 Matrix outMatrix) {
184 switch (rotation) {
185 case Surface.ROTATION_0:
186 outMatrix.reset();
187 break;
188 case Surface.ROTATION_90:
189 outMatrix.setRotate(90, 0, 0);
190 outMatrix.postTranslate(height, 0);
191 break;
192 case Surface.ROTATION_180:
193 outMatrix.setRotate(180, 0, 0);
194 outMatrix.postTranslate(width, height);
195 break;
196 case Surface.ROTATION_270:
197 outMatrix.setRotate(270, 0, 0);
198 outMatrix.postTranslate(0, width);
199 break;
200 }
201 }
202
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800203 // Must be called while in a transaction.
204 public void setRotation(int rotation) {
205 mCurRotation = rotation;
206
207 // Compute the transformation matrix that must be applied
208 // to the snapshot to make it stay in the same original position
209 // with the current screen rotation.
210 int delta = deltaRotation(rotation, mSnapshotRotation);
Dianne Hackborn0aae2d42010-12-07 23:51:29 -0800211 createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800212
213 if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
214 setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
215 }
216
217 /**
218 * Returns true if animating.
219 */
220 public boolean dismiss(long maxAnimationDuration, float animationScale) {
221 // Figure out how the screen has moved from the original rotation.
222 int delta = deltaRotation(mCurRotation, mOriginalRotation);
223 if (false && delta == 0) {
224 // Nothing changed, just remove the snapshot.
225 if (mSurface != null) {
226 mSurface.destroy();
227 mSurface = null;
228 }
229 return false;
230 }
231
232 switch (delta) {
233 case Surface.ROTATION_0:
234 mExitAnimation = AnimationUtils.loadAnimation(mContext,
235 com.android.internal.R.anim.screen_rotate_0_exit);
236 mEnterAnimation = AnimationUtils.loadAnimation(mContext,
237 com.android.internal.R.anim.screen_rotate_0_enter);
238 break;
239 case Surface.ROTATION_90:
240 mExitAnimation = AnimationUtils.loadAnimation(mContext,
241 com.android.internal.R.anim.screen_rotate_plus_90_exit);
242 mEnterAnimation = AnimationUtils.loadAnimation(mContext,
243 com.android.internal.R.anim.screen_rotate_plus_90_enter);
244 break;
245 case Surface.ROTATION_180:
246 mExitAnimation = AnimationUtils.loadAnimation(mContext,
247 com.android.internal.R.anim.screen_rotate_180_exit);
248 mEnterAnimation = AnimationUtils.loadAnimation(mContext,
249 com.android.internal.R.anim.screen_rotate_180_enter);
250 break;
251 case Surface.ROTATION_270:
252 mExitAnimation = AnimationUtils.loadAnimation(mContext,
253 com.android.internal.R.anim.screen_rotate_minus_90_exit);
254 mEnterAnimation = AnimationUtils.loadAnimation(mContext,
255 com.android.internal.R.anim.screen_rotate_minus_90_enter);
256 break;
257 }
258
259 mDisplay.getMetrics(mDisplayMetrics);
260
261 // Initialize the animations. This is a hack, redefining what "parent"
262 // means to allow supplying the last and next size. In this definition
263 // "%p" is the original (let's call it "previous") size, and "%" is the
264 // screen's current/new size.
265 mEnterAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
266 mOriginalWidth, mOriginalHeight);
267 mExitAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
268 mOriginalWidth, mOriginalHeight);
269 mStarted = false;
270
271 mExitAnimation.restrictDuration(maxAnimationDuration);
272 mExitAnimation.scaleCurrentDuration(animationScale);
273 mEnterAnimation.restrictDuration(maxAnimationDuration);
274 mEnterAnimation.scaleCurrentDuration(animationScale);
275
276 return true;
277 }
278
279 public void kill() {
280 if (mSurface != null) {
281 mSurface.destroy();
282 mSurface = null;
283 }
Dianne Hackborn352cc982011-01-04 11:34:18 -0800284 if (mBlackSurface != null) {
285 mBlackSurface.destroy();
286 mBlackSurface = null;
287 }
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800288 if (mExitAnimation != null) {
289 mExitAnimation.cancel();
290 mExitAnimation = null;
291 }
292 if (mEnterAnimation != null) {
293 mEnterAnimation.cancel();
294 mEnterAnimation = null;
295 }
296 }
297
298 public boolean isAnimating() {
299 return mEnterAnimation != null || mExitAnimation != null;
300 }
301
302 public boolean stepAnimation(long now) {
303 if (mEnterAnimation == null && mExitAnimation == null) {
304 return false;
305 }
306
307 if (!mStarted) {
308 mEnterAnimation.setStartTime(now);
309 mExitAnimation.setStartTime(now);
310 mStarted = true;
311 }
312
313 mExitTransformation.clear();
314 boolean moreExit = false;
315 if (mExitAnimation != null) {
316 moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
317 if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
318 if (!moreExit) {
319 if (DEBUG) Slog.v(TAG, "Exit animation done!");
320 mExitAnimation.cancel();
321 mExitAnimation = null;
322 mExitTransformation.clear();
323 if (mSurface != null) {
324 mSurface.destroy();
325 mSurface = null;
326 }
Dianne Hackborn352cc982011-01-04 11:34:18 -0800327 if (mBlackSurface != null) {
328 mBlackSurface.destroy();
329 mBlackSurface = null;
330 }
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800331 }
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 }
343 }
344
Dianne Hackborn352cc982011-01-04 11:34:18 -0800345 mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
346 setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
Dianne Hackbornf9d0be92010-11-24 12:35:25 -0800347
348 return moreEnter || moreExit;
349 }
350
351 public Transformation getEnterTransformation() {
352 return mEnterTransformation;
Dianne Hackborna1111872010-11-23 20:55:11 -0800353 }
354}