diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-11-24 12:35:25 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-11-28 18:28:57 -0800 |
commit | f9d0be917b6f80efad29dce88ad2d2f117986c57 (patch) | |
tree | 6615af240495c9e61cd97218c3ba1fad62321528 /services/java | |
parent | 5a6b4f826515c65d816e711266f0eac5ae3d37df (diff) | |
download | frameworks_base-f9d0be917b6f80efad29dce88ad2d2f117986c57.zip frameworks_base-f9d0be917b6f80efad29dce88ad2d2f117986c57.tar.gz frameworks_base-f9d0be917b6f80efad29dce88ad2d2f117986c57.tar.bz2 |
Implement rotation animations.
This introduces a small new feature for ScaleAnimation allowing
the scaling factor to be expressed as a percentage of the object
(which is the same as the existing float interpretation), a
percentage of the container, or a fixed dimension. Maybe not
useful for anything else, but I needed it for this.
Also fix a bug in how transformation matrices were propagated
from the Animation to Surface Flinger, so that rotate and skew
animations will actually work. :p
Change-Id: I301f4caa2147aa35564b5e511cb9c0b368d2425d
Diffstat (limited to 'services/java')
-rw-r--r-- | services/java/com/android/server/ScreenRotationAnimation.java | 238 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 41 |
2 files changed, 235 insertions, 44 deletions
diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java index 299567a..1cc6a2a 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -16,6 +16,7 @@ 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; @@ -28,38 +29,60 @@ 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 { - private static final String TAG = "ScreenRotationAnimation"; + static final String TAG = "ScreenRotationAnimation"; + static final boolean DEBUG = false; + final Context mContext; + final Display mDisplay; Surface mSurface; int mWidth, mHeight; - int mBaseRotation; + int mSnapshotRotation; + int mSnapshotDeltaRotation; + int mOriginalRotation; + int mOriginalWidth, mOriginalHeight; int mCurRotation; - int mDeltaRotation; - final Matrix mMatrix = new Matrix(); + 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(Display display, SurfaceSession session) { - final DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); + 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! - mBaseRotation = 0; + mSnapshotRotation = 0; mWidth = screenshot.getWidth(); mHeight = screenshot.getHeight(); } else { // Just in case. - mBaseRotation = display.getRotation(); - mWidth = dm.widthPixels; - mHeight = dm.heightPixels; + 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(); @@ -102,51 +125,190 @@ class ScreenRotationAnimation { 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; - int delta = mCurRotation - mBaseRotation; - if (delta < 0) delta += 4; - mDeltaRotation = delta; + // 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: - mMatrix.reset(); + mSnapshotInitialMatrix.reset(); break; case Surface.ROTATION_90: - mMatrix.setRotate(90, 0, 0); - mMatrix.postTranslate(0, mWidth); + mSnapshotInitialMatrix.setRotate(90, 0, 0); + mSnapshotInitialMatrix.postTranslate(mHeight, 0); break; case Surface.ROTATION_180: - mMatrix.setRotate(180, 0, 0); - mMatrix.postTranslate(mWidth, mHeight); + mSnapshotInitialMatrix.setRotate(180, 0, 0); + mSnapshotInitialMatrix.postTranslate(mWidth, mHeight); break; case Surface.ROTATION_270: - mMatrix.setRotate(270, 0, 0); - mMatrix.postTranslate(mHeight, 0); + mSnapshotInitialMatrix.setRotate(270, 0, 0); + mSnapshotInitialMatrix.postTranslate(0, mWidth); break; } - mMatrix.getValues(mTmpFloats); - mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X], - (int)mTmpFloats[Matrix.MTRANS_Y]); - mSurface.setMatrix( - mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_X], - mTmpFloats[Matrix.MSKEW_Y], mTmpFloats[Matrix.MSCALE_Y]); + if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta); + setSnapshotTransform(mSnapshotInitialMatrix, 1.0f); + } - if (false) { - float[] srcPnts = new float[] { 0, 0, mWidth, mHeight }; - float[] dstPnts = new float[8]; - mMatrix.mapPoints(dstPnts, srcPnts); - Slog.i(TAG, "**** ROTATION: " + delta); - Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1] - + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")"); - Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1] - + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")"); + /** + * 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 void dismiss() { - mSurface.destroy(); + public Transformation getEnterTransformation() { + return mEnterTransformation; } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 89512ae..cbb35c6 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -7487,8 +7487,10 @@ public class WindowManagerService extends IWindowManager.Stub } } + final boolean screenAnimation = mScreenRotationAnimation != null + && mScreenRotationAnimation.isAnimating(); if (selfTransformation || attachedTransformation != null - || appTransformation != null) { + || appTransformation != null || screenAnimation) { // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; @@ -7506,6 +7508,10 @@ public class WindowManagerService extends IWindowManager.Stub if (appTransformation != null) { tmpMatrix.postConcat(appTransformation.getMatrix()); } + if (screenAnimation) { + tmpMatrix.postConcat( + mScreenRotationAnimation.getEnterTransformation().getMatrix()); + } // "convert" it into SurfaceFlinger's format // (a 2x2 matrix + an offset) @@ -7515,8 +7521,8 @@ public class WindowManagerService extends IWindowManager.Stub tmpMatrix.getValues(tmpFloats); mDsDx = tmpFloats[Matrix.MSCALE_X]; - mDtDx = tmpFloats[Matrix.MSKEW_X]; - mDsDy = tmpFloats[Matrix.MSKEW_Y]; + mDtDx = tmpFloats[Matrix.MSKEW_Y]; + mDsDy = tmpFloats[Matrix.MSKEW_X]; mDtDy = tmpFloats[Matrix.MSCALE_Y]; int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset; int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset; @@ -7544,6 +7550,10 @@ public class WindowManagerService extends IWindowManager.Stub if (appTransformation != null) { mShownAlpha *= appTransformation.getAlpha(); } + if (screenAnimation) { + mShownAlpha *= + mScreenRotationAnimation.getEnterTransformation().getAlpha(); + } } else { //Slog.i(TAG, "Not applying alpha transform"); } @@ -9397,6 +9407,16 @@ public class WindowManagerService extends IWindowManager.Stub animating = tokensAnimating; + if (mScreenRotationAnimation != null) { + if (mScreenRotationAnimation.isAnimating()) { + if (mScreenRotationAnimation.stepAnimation(currentTime)) { + animating = true; + } else { + mScreenRotationAnimation = null; + } + } + } + boolean tokenMayBeDrawn = false; boolean wallpaperMayChange = false; boolean forceHiding = false; @@ -10841,8 +10861,13 @@ public class WindowManagerService extends IWindowManager.Stub } if (CUSTOM_SCREEN_ROTATION) { + if (mScreenRotationAnimation != null && mScreenRotationAnimation.isAnimating()) { + mScreenRotationAnimation.kill(); + mScreenRotationAnimation = null; + } if (mScreenRotationAnimation == null) { - mScreenRotationAnimation = new ScreenRotationAnimation(mDisplay, mFxSession); + mScreenRotationAnimation = new ScreenRotationAnimation(mContext, + mDisplay, mFxSession); } } else { Surface.freezeDisplay(0); @@ -10866,8 +10891,12 @@ public class WindowManagerService extends IWindowManager.Stub if (CUSTOM_SCREEN_ROTATION) { if (mScreenRotationAnimation != null) { - mScreenRotationAnimation.dismiss(); - mScreenRotationAnimation = null; + if (mScreenRotationAnimation.dismiss(MAX_ANIMATION_DURATION, + mTransitionAnimationScale)) { + requestAnimationLocked(0); + } else { + mScreenRotationAnimation = null; + } } } else { Surface.unfreezeDisplay(0); |