/** Returns true if animating. */
  public boolean dismiss(
      SurfaceSession session,
      long maxAnimationDuration,
      float animationScale,
      int finalWidth,
      int finalHeight) {
    if (mSurface == null) {
      // Can't do animation.
      return false;
    }

    // Figure out how the screen has moved from the original rotation.
    int delta = deltaRotation(mCurRotation, mOriginalRotation);

    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;
    }

    // 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(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
    mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
    mStarted = false;

    mExitAnimation.restrictDuration(maxAnimationDuration);
    mExitAnimation.scaleCurrentDuration(animationScale);
    mEnterAnimation.restrictDuration(maxAnimationDuration);
    mEnterAnimation.scaleCurrentDuration(animationScale);

    if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS)
      Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
    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");
    }

    return true;
  }
  /** Returns true if animating. */
  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 (TWO_PHASE_ANIMATION
        && 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);
      if (USE_CUSTOM_BLACK_FRAME) {
        mStartFrameAnimation =
            AnimationUtils.loadAnimation(
                mContext, com.android.internal.R.anim.screen_rotate_start_frame);
      }
      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 (USE_CUSTOM_BLACK_FRAME) {
        mFinishFrameAnimation =
            AnimationUtils.loadAnimation(
                mContext, com.android.internal.R.anim.screen_rotate_finish_frame);
      }
    }

    if (DEBUG_STATE)
      Slog.v(
          TAG,
          "Rotation delta: "
              + delta
              + " finalWidth="
              + finalWidth
              + " finalHeight="
              + finalHeight
              + " origWidth="
              + mOriginalWidth
              + " origHeight="
              + mOriginalHeight);

    final boolean customAnim;
    if (mExitAnimId != 0 && mEnterAnimId != 0) {
      customAnim = true;
      mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, mExitAnimId);
      mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, mEnterAnimId);
    } else {
      customAnim = false;
      switch (delta) {
        case Surface.ROTATION_0:
          mRotateExitAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_0_exit);
          mRotateEnterAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_0_enter);
          if (USE_CUSTOM_BLACK_FRAME) {
            mRotateFrameAnimation =
                AnimationUtils.loadAnimation(
                    mContext, com.android.internal.R.anim.screen_rotate_0_frame);
          }
          break;
        case Surface.ROTATION_90:
          mRotateExitAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_plus_90_exit);
          mRotateEnterAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_plus_90_enter);
          if (USE_CUSTOM_BLACK_FRAME) {
            mRotateFrameAnimation =
                AnimationUtils.loadAnimation(
                    mContext, com.android.internal.R.anim.screen_rotate_plus_90_frame);
          }
          break;
        case Surface.ROTATION_180:
          mRotateExitAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_180_exit);
          mRotateEnterAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_180_enter);
          if (USE_CUSTOM_BLACK_FRAME) {
            mRotateFrameAnimation =
                AnimationUtils.loadAnimation(
                    mContext, com.android.internal.R.anim.screen_rotate_180_frame);
          }
          break;
        case Surface.ROTATION_270:
          mRotateExitAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_minus_90_exit);
          mRotateEnterAnimation =
              AnimationUtils.loadAnimation(
                  mContext, com.android.internal.R.anim.screen_rotate_minus_90_enter);
          if (USE_CUSTOM_BLACK_FRAME) {
            mRotateFrameAnimation =
                AnimationUtils.loadAnimation(
                    mContext, com.android.internal.R.anim.screen_rotate_minus_90_frame);
          }
          break;
      }
    }

    // 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.
    if (TWO_PHASE_ANIMATION && firstStart) {
      // Compute partial steps between original and final sizes.  These
      // are used for the dimensions of the exiting and entering elements,
      // so they are never stretched too significantly.
      final int halfWidth = (finalWidth + mOriginalWidth) / 2;
      final int halfHeight = (finalHeight + mOriginalHeight) / 2;

      if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
      mStartEnterAnimation.initialize(finalWidth, finalHeight, halfWidth, halfHeight);
      mStartExitAnimation.initialize(halfWidth, halfHeight, mOriginalWidth, mOriginalHeight);
      mFinishEnterAnimation.initialize(finalWidth, finalHeight, halfWidth, halfHeight);
      mFinishExitAnimation.initialize(halfWidth, halfHeight, mOriginalWidth, mOriginalHeight);
      if (USE_CUSTOM_BLACK_FRAME) {
        mStartFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
        mFinishFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
      }
    }
    mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
    mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
    if (USE_CUSTOM_BLACK_FRAME) {
      mRotateFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
    }
    mAnimRunning = false;
    mFinishAnimReady = false;
    mFinishAnimStartTime = -1;

    if (TWO_PHASE_ANIMATION && 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);
      if (USE_CUSTOM_BLACK_FRAME) {
        mStartFrameAnimation.restrictDuration(maxAnimationDuration);
        mStartFrameAnimation.scaleCurrentDuration(animationScale);
        mFinishFrameAnimation.restrictDuration(maxAnimationDuration);
        mFinishFrameAnimation.scaleCurrentDuration(animationScale);
      }
    }
    mRotateExitAnimation.restrictDuration(maxAnimationDuration);
    mRotateExitAnimation.scaleCurrentDuration(animationScale);
    mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
    mRotateEnterAnimation.scaleCurrentDuration(animationScale);
    if (USE_CUSTOM_BLACK_FRAME) {
      mRotateFrameAnimation.restrictDuration(maxAnimationDuration);
      mRotateFrameAnimation.scaleCurrentDuration(animationScale);
    }

    final int layerStack = mDisplay.getLayerStack();
    if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) {
      if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE)
        Slog.i(
            WindowManagerService.TAG,
            ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
      Surface.openTransaction();

      // Compute the transformation matrix that must be applied
      // the the black frame to make it stay in the initial position
      // before the new screen rotation.  This is different than the
      // snapshot transformation because the snapshot is always based
      // of the native orientation of the screen, not the orientation
      // we were last in.
      createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);

      try {
        Rect outer =
            new Rect(
                -mOriginalWidth * 1, -mOriginalHeight * 1, mOriginalWidth * 2, mOriginalHeight * 2);
        Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
        mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3, layerStack);
        mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
      } 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");
      }
    }

    if (!customAnim && mExitingBlackFrame == null) {
      if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE)
        Slog.i(
            WindowManagerService.TAG,
            ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
      Surface.openTransaction();
      try {
        // Compute the transformation matrix that must be applied
        // the the black frame to make it stay in the initial position
        // before the new screen rotation.  This is different than the
        // snapshot transformation because the snapshot is always based
        // of the native orientation of the screen, not the orientation
        // we were last in.
        createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);

        Rect outer =
            new Rect(
                -mOriginalWidth * 1, -mOriginalHeight * 1, mOriginalWidth * 2, mOriginalHeight * 2);
        Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
        mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2, layerStack);
        mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
      } 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");
      }
    }

    if (customAnim && mEnteringBlackFrame == null) {
      if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE)
        Slog.i(
            WindowManagerService.TAG,
            ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
      Surface.openTransaction();

      try {
        Rect outer = new Rect(-finalWidth * 1, -finalHeight * 1, finalWidth * 2, finalHeight * 2);
        Rect inner = new Rect(0, 0, finalWidth, finalHeight);
        mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER, layerStack);
      } 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;
  }