New screen rotation animation design.
We now have an animation that can start as soon as we detect to
rotate the screen, and have a different phase once the new UI
is ready to complete the transition into it.
Change-Id: I05b9c12a699232acbddc36b4a994a4452db71476
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 8fc9a70..55fb038 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -17,13 +17,8 @@
package com.android.server.wm;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Surface;
@@ -34,7 +29,8 @@
class ScreenRotationAnimation {
static final String TAG = "ScreenRotationAnimation";
- static final boolean DEBUG = false;
+ static final boolean DEBUG_STATE = false;
+ static final boolean DEBUG_TRANSFORMS = false;
static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
@@ -49,11 +45,51 @@
int mOriginalWidth, mOriginalHeight;
int mCurRotation;
- Animation mExitAnimation;
+ // For all animations, "exit" is for the UI elements that are going
+ // away (that is the snapshot of the old screen), and "enter" is for
+ // the new UI elements that are appearing (that is the active windows
+ // in their final orientation).
+
+ // The starting animation for the exiting and entering elements. This
+ // animation applies a transformation while the rotation is in progress.
+ // It is started immediately, before the new entering UI is ready.
+ Animation mStartExitAnimation;
+ final Transformation mStartExitTransformation = new Transformation();
+ Animation mStartEnterAnimation;
+ final Transformation mStartEnterTransformation = new Transformation();
+
+ // The finishing animation for the exiting and entering elements. This
+ // animation needs to undo the transformation of the starting animation.
+ // It starts running once the new rotation UI elements are ready to be
+ // displayed.
+ Animation mFinishExitAnimation;
+ final Transformation mFinishExitTransformation = new Transformation();
+ Animation mFinishEnterAnimation;
+ final Transformation mFinishEnterTransformation = new Transformation();
+
+ // The current active animation to move from the old to the new rotated
+ // state. Which animation is run here will depend on the old and new
+ // rotations.
+ Animation mRotateExitAnimation;
+ final Transformation mRotateExitTransformation = new Transformation();
+ Animation mRotateEnterAnimation;
+ final Transformation mRotateEnterTransformation = new Transformation();
+
+ // A previously running rotate animation. This will be used if we need
+ // to switch to a new rotation before finishing the previous one.
+ Animation mLastRotateExitAnimation;
+ final Transformation mLastRotateExitTransformation = new Transformation();
+ Animation mLastRotateEnterAnimation;
+ final Transformation mLastRotateEnterTransformation = new Transformation();
+
+ // Complete transformations being applied.
final Transformation mExitTransformation = new Transformation();
- Animation mEnterAnimation;
final Transformation mEnterTransformation = new Transformation();
+
boolean mStarted;
+ boolean mAnimRunning;
+ boolean mFinishAnimReady;
+ long mFinishAnimStartTime;
final Matrix mSnapshotInitialMatrix = new Matrix();
final Matrix mSnapshotFinalMatrix = new Matrix();
@@ -133,7 +169,7 @@
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
mSurface.setAlpha(alpha);
- if (DEBUG) {
+ if (DEBUG_TRANSFORMS) {
float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
float[] dstPnts = new float[4];
matrix.mapPoints(dstPnts, srcPnts);
@@ -167,7 +203,7 @@
}
// Must be called while in a transaction.
- public void setRotation(int rotation) {
+ private void setRotation(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -176,46 +212,78 @@
int delta = deltaRotation(rotation, mSnapshotRotation);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
- if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
+ if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
}
+ // Must be called while in a transaction.
+ public boolean setRotation(int rotation, SurfaceSession session,
+ long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
+ setRotation(rotation);
+ return startAnimation(session, maxAnimationDuration, animationScale,
+ finalWidth, finalHeight, false);
+ }
+
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
- float animationScale, int finalWidth, int finalHeight) {
+ private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight, boolean dismissing) {
if (mSurface == null) {
// Can't do animation.
return false;
}
+ if (mStarted) {
+ return true;
+ }
+
+ mStarted = true;
+
+ boolean firstStart = false;
// Figure out how the screen has moved from the original rotation.
int delta = deltaRotation(mCurRotation, mOriginalRotation);
+ if (mFinishExitAnimation == null && (!dismissing || delta != Surface.ROTATION_0)) {
+ if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
+ firstStart = true;
+ mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_exit);
+ mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_enter);
+ mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_exit);
+ mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_enter);
+ }
+
+ if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
+ + finalWidth + " finalHeight=" + finalHeight
+ + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
+
switch (delta) {
case Surface.ROTATION_0:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
@@ -224,35 +292,85 @@
// 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(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mStarted = false;
+ if (firstStart) {
+ if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
+ mStartEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mStartExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ }
+ mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mAnimRunning = false;
+ mFinishAnimReady = false;
+ mFinishAnimStartTime = -1;
- mExitAnimation.restrictDuration(maxAnimationDuration);
- mExitAnimation.scaleCurrentDuration(animationScale);
- mEnterAnimation.restrictDuration(maxAnimationDuration);
- mEnterAnimation.scaleCurrentDuration(animationScale);
+ if (firstStart) {
+ mStartExitAnimation.restrictDuration(maxAnimationDuration);
+ mStartExitAnimation.scaleCurrentDuration(animationScale);
+ mStartEnterAnimation.restrictDuration(maxAnimationDuration);
+ mStartEnterAnimation.scaleCurrentDuration(animationScale);
+ mFinishExitAnimation.restrictDuration(maxAnimationDuration);
+ mFinishExitAnimation.scaleCurrentDuration(animationScale);
+ mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
+ mFinishEnterAnimation.scaleCurrentDuration(animationScale);
+ }
+ mRotateExitAnimation.restrictDuration(maxAnimationDuration);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
- Surface.openTransaction();
+ if (mBlackFrame == null) {
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
+ Surface.openTransaction();
- try {
- Rect outer = new Rect(-finalWidth, -finalHeight, finalWidth * 2, finalHeight * 2);
- Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
- } catch (Surface.OutOfResourcesException e) {
- Slog.w(TAG, "Unable to allocate black surface", e);
- } finally {
- Surface.closeTransaction();
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- "<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss");
+ try {
+ Rect outer = new Rect(-finalWidth*1, -finalHeight*1, finalWidth*2, finalHeight*2);
+ Rect inner = new Rect(0, 0, finalWidth, finalHeight);
+ mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate black surface", e);
+ } finally {
+ Surface.closeTransaction();
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
+ }
}
return true;
}
+ /**
+ * Returns true if animating.
+ */
+ public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight) {
+ if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
+ if (mSurface == null) {
+ // Can't do animation.
+ return false;
+ }
+ if (!mStarted) {
+ startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ true);
+ }
+ if (!mStarted) {
+ return false;
+ }
+ if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
+ mFinishAnimReady = true;
+ return true;
+ }
+
public void kill() {
+ if (DEBUG_STATE) Slog.v(TAG, "Kill!");
if (mSurface != null) {
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
@@ -262,74 +380,198 @@
}
if (mBlackFrame != null) {
mBlackFrame.kill();
+ mBlackFrame = null;
}
- if (mExitAnimation != null) {
- mExitAnimation.cancel();
- mExitAnimation = null;
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
}
- if (mEnterAnimation != null) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ }
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
}
}
public boolean isAnimating() {
- return mEnterAnimation != null || mExitAnimation != null;
+ return mStartEnterAnimation != null || mStartExitAnimation != null
+ && mFinishEnterAnimation != null || mFinishExitAnimation != null
+ && mRotateEnterAnimation != null || mRotateExitAnimation != null;
}
public boolean stepAnimation(long now) {
- if (mEnterAnimation == null && mExitAnimation == null) {
+ if (!isAnimating()) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
return false;
}
- if (!mStarted) {
- if (mEnterAnimation != null) {
- mEnterAnimation.setStartTime(now);
+ if (!mAnimRunning) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.setStartTime(now);
}
- if (mExitAnimation != null) {
- mExitAnimation.setStartTime(now);
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.setStartTime(now);
}
- mStarted = true;
+ if (mFinishEnterAnimation != null) {
+ mFinishEnterAnimation.setStartTime(0);
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.setStartTime(0);
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.setStartTime(now);
+ }
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.setStartTime(now);
+ }
+ mAnimRunning = 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.hide();
- }
+ if (mFinishAnimReady && mFinishAnimStartTime < 0) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
+ mFinishAnimStartTime = now;
+ }
+
+ // If the start animation is no longer running, we want to keep its
+ // transformation intact until the finish animation also completes.
+
+ boolean moreStartExit = false;
+ if (mStartExitAnimation != null) {
+ mStartExitTransformation.clear();
+ moreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
+ if (!moreStartExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start exit animation done!");
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
}
}
- mEnterTransformation.clear();
- boolean moreEnter = false;
- if (mEnterAnimation != null) {
- moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
- if (!moreEnter) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
- mEnterTransformation.clear();
- if (mBlackFrame != null) {
- mBlackFrame.hide();
- }
- } else {
- if (mBlackFrame != null) {
- mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
- }
+ boolean moreStartEnter = false;
+ if (mStartEnterAnimation != null) {
+ mStartEnterTransformation.clear();
+ moreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
+ if (!moreStartEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start enter animation done!");
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ }
+
+ long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
+
+ mFinishExitTransformation.clear();
+ boolean moreFinishExit = false;
+ if (mFinishExitAnimation != null) {
+ moreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
+ if (!moreStartExit && !moreFinishExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish exit animation done, clearing start/finish anims!");
+ mStartExitTransformation.clear();
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ mFinishExitTransformation.clear();
+ }
+ }
+
+ mFinishEnterTransformation.clear();
+ boolean moreFinishEnter = false;
+ if (mFinishEnterAnimation != null) {
+ moreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
+ if (!moreStartEnter && !moreFinishEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish enter animation done, clearing start/finish anims!");
+ mStartEnterTransformation.clear();
+ mFinishEnterAnimation.cancel();
+ mFinishEnterAnimation = null;
+ mFinishEnterTransformation.clear();
+ }
+ }
+
+ mRotateExitTransformation.clear();
+ boolean moreRotateExit = false;
+ if (mRotateExitAnimation != null) {
+ moreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
+ }
+
+ if (!moreFinishExit && !moreRotateExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate exit animation done!");
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ mRotateExitTransformation.clear();
+ }
+
+ mRotateEnterTransformation.clear();
+ boolean moreRotateEnter = false;
+ if (mRotateEnterAnimation != null) {
+ moreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
+ }
+
+ if (!moreFinishEnter && !moreRotateEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate enter animation done!");
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
+ mRotateEnterTransformation.clear();
+ }
+
+ mExitTransformation.set(mRotateExitTransformation);
+ mExitTransformation.compose(mStartExitTransformation);
+ mExitTransformation.compose(mFinishExitTransformation);
+
+ mEnterTransformation.set(mRotateEnterTransformation);
+ mEnterTransformation.compose(mStartEnterTransformation);
+ mEnterTransformation.compose(mFinishEnterTransformation);
+
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
+
+ if (!moreStartExit && !moreFinishExit && !moreRotateExit) {
+ if (mSurface != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
+ mSurface.hide();
+ }
+ }
+
+ if (!moreStartEnter && !moreFinishEnter && !moreRotateEnter) {
+ if (mBlackFrame != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, hiding black frame");
+ mBlackFrame.hide();
+ }
+ } else {
+ if (mBlackFrame != null) {
+ mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
}
}
mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
- return moreEnter || moreExit;
+ final boolean more = moreStartEnter || moreStartExit || moreFinishEnter || moreFinishExit
+ || moreRotateEnter || moreRotateExit || !mFinishAnimReady;
+
+ if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
+
+ return more;
}
public Transformation getEnterTransformation() {