Files
frameworks_base/services/java/com/android/server/ScreenRotationAnimation.java
Dianne Hackborn 0f761d6b8f Implement issue #3201795: Improve transition when keyboard comes up
ViewRoot now does a fade animation between a snapshot of the previous
layout to the new one when its content rect changes.

Also tweaked some things in the window manager to fix problems in
deciding when to animate the movement of a window and when not to.

Change-Id: I9b4b3bd53c8258bd39a2f2fc315e77cfc56a409c
2010-12-01 11:00:42 -08:00

317 lines
11 KiB
Java

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server; // TODO: use com.android.server.wm, once things move there
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceSession;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
class ScreenRotationAnimation {
static final String TAG = "ScreenRotationAnimation";
static final boolean DEBUG = false;
final Context mContext;
final Display mDisplay;
Surface mSurface;
int mWidth, mHeight;
int mSnapshotRotation;
int mSnapshotDeltaRotation;
int mOriginalRotation;
int mOriginalWidth, mOriginalHeight;
int mCurRotation;
Animation mExitAnimation;
final Transformation mExitTransformation = new Transformation();
Animation mEnterAnimation;
final Transformation mEnterTransformation = new Transformation();
boolean mStarted;
final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
final Matrix mSnapshotInitialMatrix = new Matrix();
final Matrix mSnapshotFinalMatrix = new Matrix();
final float[] mTmpFloats = new float[9];
public ScreenRotationAnimation(Context context, Display display, SurfaceSession session) {
mContext = context;
mDisplay = display;
display.getMetrics(mDisplayMetrics);
Bitmap screenshot = Surface.screenshot(0, 0);
if (screenshot != null) {
// Screenshot does NOT include rotation!
mSnapshotRotation = 0;
mWidth = screenshot.getWidth();
mHeight = screenshot.getHeight();
} else {
// Just in case.
mSnapshotRotation = display.getRotation();
mWidth = mDisplayMetrics.widthPixels;
mHeight = mDisplayMetrics.heightPixels;
}
mOriginalRotation = display.getRotation();
mOriginalWidth = mDisplayMetrics.widthPixels;
mOriginalHeight = mDisplayMetrics.heightPixels;
Surface.openTransaction();
if (mSurface != null) {
mSurface.destroy();
mSurface = null;
}
try {
mSurface = new Surface(session, 0, "FreezeSurface",
-1, mWidth, mHeight, PixelFormat.OPAQUE, 0);
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200);
setRotation(display.getRotation());
Rect dirty = new Rect(0, 0, mWidth, mHeight);
Canvas c = null;
try {
c = mSurface.lockCanvas(dirty);
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unable to lock surface", e);
return;
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to lock surface", e);
return;
}
if (c == null) {
Slog.w(TAG, "Null surface");
return;
}
if (screenshot != null) {
c.drawBitmap(screenshot, 0, 0, new Paint(0));
} else {
c.drawColor(Color.GREEN);
}
mSurface.unlockCanvasAndPost(c);
Surface.closeTransaction();
if (screenshot != null) {
screenshot.recycle();
}
}
static int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
return delta;
}
void setSnapshotTransform(Matrix matrix, float alpha) {
matrix.getValues(mTmpFloats);
mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
(int)mTmpFloats[Matrix.MTRANS_Y]);
mSurface.setMatrix(
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
mSurface.setAlpha(alpha);
if (DEBUG) {
float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
float[] dstPnts = new float[4];
matrix.mapPoints(dstPnts, srcPnts);
Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1]
+ ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
+ ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
}
}
// Must be called while in a transaction.
public void setRotation(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
int delta = deltaRotation(rotation, mSnapshotRotation);
switch (delta) {
case Surface.ROTATION_0:
mSnapshotInitialMatrix.reset();
break;
case Surface.ROTATION_90:
mSnapshotInitialMatrix.setRotate(90, 0, 0);
mSnapshotInitialMatrix.postTranslate(mHeight, 0);
break;
case Surface.ROTATION_180:
mSnapshotInitialMatrix.setRotate(180, 0, 0);
mSnapshotInitialMatrix.postTranslate(mWidth, mHeight);
break;
case Surface.ROTATION_270:
mSnapshotInitialMatrix.setRotate(270, 0, 0);
mSnapshotInitialMatrix.postTranslate(0, mWidth);
break;
}
if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
}
/**
* Returns true if animating.
*/
public boolean dismiss(long maxAnimationDuration, float animationScale) {
// Figure out how the screen has moved from the original rotation.
int delta = deltaRotation(mCurRotation, mOriginalRotation);
if (false && delta == 0) {
// Nothing changed, just remove the snapshot.
if (mSurface != null) {
mSurface.destroy();
mSurface = null;
}
return false;
}
switch (delta) {
case Surface.ROTATION_0:
mExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_exit);
mEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
mExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_exit);
mEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
mExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_exit);
mEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
mExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_exit);
mEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
mDisplay.getMetrics(mDisplayMetrics);
// Initialize the animations. This is a hack, redefining what "parent"
// means to allow supplying the last and next size. In this definition
// "%p" is the original (let's call it "previous") size, and "%" is the
// screen's current/new size.
mEnterAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
mOriginalWidth, mOriginalHeight);
mExitAnimation.initialize(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
mOriginalWidth, mOriginalHeight);
mStarted = false;
mExitAnimation.restrictDuration(maxAnimationDuration);
mExitAnimation.scaleCurrentDuration(animationScale);
mEnterAnimation.restrictDuration(maxAnimationDuration);
mEnterAnimation.scaleCurrentDuration(animationScale);
return true;
}
public void kill() {
if (mSurface != null) {
mSurface.destroy();
mSurface = null;
}
if (mExitAnimation != null) {
mExitAnimation.cancel();
mExitAnimation = null;
}
if (mEnterAnimation != null) {
mEnterAnimation.cancel();
mEnterAnimation = null;
}
}
public boolean isAnimating() {
return mEnterAnimation != null || mExitAnimation != null;
}
public boolean stepAnimation(long now) {
if (mEnterAnimation == null && mExitAnimation == null) {
return false;
}
if (!mStarted) {
mEnterAnimation.setStartTime(now);
mExitAnimation.setStartTime(now);
mStarted = true;
}
mExitTransformation.clear();
boolean moreExit = false;
if (mExitAnimation != null) {
moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
if (!moreExit) {
if (DEBUG) Slog.v(TAG, "Exit animation done!");
mExitAnimation.cancel();
mExitAnimation = null;
mExitTransformation.clear();
if (mSurface != null) {
mSurface.destroy();
mSurface = null;
}
}
}
mEnterTransformation.clear();
boolean moreEnter = false;
if (mEnterAnimation != null) {
moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
if (!moreEnter) {
mEnterAnimation.cancel();
mEnterAnimation = null;
mEnterTransformation.clear();
}
}
if (mSurface != null) {
mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
return moreEnter || moreExit;
}
public Transformation getEnterTransformation() {
return mEnterTransformation;
}
}