Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a700b13 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Use ValueAnimator in DragState

To remove a depedency on DragState from WindowAnimator, the CL starts
using ValueAnimator in DragState.

Bug: 63914027
Test: android.server.wm.CrossAppDragAndDropTests, manually check the
      rejected animation and the cancel animation.
Change-Id: I231544f35c30260dd45b5d605023aa995d55ee57
parent 17e97359
Loading
Loading
Loading
Loading
+143 −53
Original line number Diff line number Diff line
@@ -22,6 +22,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.animation.Animator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
@@ -29,7 +33,9 @@ import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.input.InputManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -44,16 +50,12 @@ import android.view.InputChannel;
import android.view.InputDevice;
import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;

import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
@@ -78,8 +80,20 @@ class DragState {
            View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION |
            View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION;

    // Property names for animations
    private static final String ANIMATED_PROPERTY_X = "x";
    private static final String ANIMATED_PROPERTY_Y = "y";
    private static final String ANIMATED_PROPERTY_ALPHA = "alpha";
    private static final String ANIMATED_PROPERTY_SCALE = "scale";

    // Messages for Handler.
    private static final int MSG_ANIMATION_END = 0;

    final WindowManagerService mService;
    IBinder mToken;
    /**
     * Do not use the variable from the out of animation thread while mAnimator is not null.
     */
    SurfaceControl mSurfaceControl;
    int mFlags;
    IBinder mLocalWin;
@@ -101,10 +115,10 @@ class DragState {
    boolean mDragInProgress;
    DisplayContent mDisplayContent;

    private Animation mAnimation;
    final Transformation mTransformation = new Transformation();
    @Nullable private ValueAnimator mAnimator;
    private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
    private Point mDisplaySize = new Point();
    private final Handler mHandler;

    DragState(WindowManagerService service, IBinder token, SurfaceControl surface,
            int flags, IBinder localWin) {
@@ -114,9 +128,14 @@ class DragState {
        mFlags = flags;
        mLocalWin = localWin;
        mNotifiedWindows = new ArrayList<WindowState>();
        mHandler = new DragStateHandler(service.mH.getLooper());
    }

    void reset() {
        if (mAnimator != null) {
            Slog.wtf(TAG_WM,
                    "Unexpectedly destroying mSurfaceControl while animation is running");
        }
        if (mSurfaceControl != null) {
            mSurfaceControl.destroy();
        }
@@ -388,11 +407,11 @@ class DragState {
    }

    void endDragLw() {
        if (mAnimation != null) {
        if (mAnimator != null) {
            return;
        }
        if (!mDragResult) {
            mAnimation = createReturnAnimationLocked();
            mAnimator = createReturnAnimationLocked();
            mService.scheduleAnimationLocked();
            return;  // Will call cleanUpDragLw when the animation is done.
        }
@@ -400,11 +419,22 @@ class DragState {
    }

    void cancelDragLw() {
        if (mAnimation != null) {
        if (mAnimator != null) {
            return;
        }
        mAnimation = createCancelAnimationLocked();
        mService.scheduleAnimationLocked();
        if (!mDragInProgress) {
            // This can happen if an app invokes Session#cancelDragAndDrop before
            // Session#performDrag. Reset the drag state:
            // 1. without sending the end broadcast because the start broadcast has not been sent,
            // and
            // 2. without playing the cancel animation because H.DRAG_START_TIMEOUT may be sent to
            //    WindowManagerService, which will cause DragState#reset() while playing the
            //    cancel animation.
            reset();
            mService.mDragState = null;
            return;
        }
        mAnimator = createCancelAnimationLocked();
    }

    private void cleanUpDragLw() {
@@ -422,7 +452,7 @@ class DragState {
    }

    void notifyMoveLw(float x, float y) {
        if (mAnimation != null) {
        if (mAnimator != null) {
            return;
        }
        mCurrentX = x;
@@ -491,7 +521,7 @@ class DragState {
    // dispatch the global drag-ended message, 'false' if we need to wait for a
    // result from the recipient.
    boolean notifyDropLw(float x, float y) {
        if (mAnimation != null) {
        if (mAnimator != null) {
            return false;
        }
        mCurrentX = x;
@@ -560,56 +590,52 @@ class DragState {
                dragAndDropPermissions, result);
    }

    boolean stepAnimationLocked(long currentTimeMs) {
        if (mAnimation == null) {
            return false;
        }

        mTransformation.clear();
        if (!mAnimation.getTransformation(currentTimeMs, mTransformation)) {
            cleanUpDragLw();
            return false;
        }
    private ValueAnimator createReturnAnimationLocked() {
        final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
                PropertyValuesHolder.ofFloat(
                        ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX,
                        mOriginalX - mThumbOffsetX),
                PropertyValuesHolder.ofFloat(
                        ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY,
                        mOriginalY - mThumbOffsetY),
                PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 1),
                PropertyValuesHolder.ofFloat(
                        ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, mOriginalAlpha / 2));

        mTransformation.getMatrix().postTranslate(
                mCurrentX - mThumbOffsetX, mCurrentY - mThumbOffsetY);
        final float tmpFloats[] = mService.mTmpFloats;
        mTransformation.getMatrix().getValues(tmpFloats);
        mSurfaceControl.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
        mSurfaceControl.setAlpha(mTransformation.getAlpha());
        mSurfaceControl.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
        return true;
    }

    private Animation createReturnAnimationLocked() {
        final AnimationSet set = new AnimationSet(false);
        final float translateX = mOriginalX - mCurrentX;
        final float translateY = mOriginalY - mCurrentY;
        set.addAnimation(new TranslateAnimation( 0, translateX, 0, translateY));
        set.addAnimation(new AlphaAnimation(mOriginalAlpha, mOriginalAlpha / 2));
        // Adjust the duration to the travel distance.
        final double travelDistance = Math.sqrt(translateX * translateX + translateY * translateY);
        final double displayDiagonal =
                Math.sqrt(mDisplaySize.x * mDisplaySize.x + mDisplaySize.y * mDisplaySize.y);
        final long duration = MIN_ANIMATION_DURATION_MS + (long) (travelDistance / displayDiagonal
                * (MAX_ANIMATION_DURATION_MS - MIN_ANIMATION_DURATION_MS));
        set.setDuration(duration);
        set.setInterpolator(mCubicEaseOutInterpolator);
        set.initialize(0, 0, 0, 0);
        set.start();  // Will start on the first call to getTransformation.
        return set;
    }

    private Animation createCancelAnimationLocked() {
        final AnimationSet set = new AnimationSet(false);
        set.addAnimation(new ScaleAnimation(1, 0, 1, 0, mThumbOffsetX, mThumbOffsetY));
        set.addAnimation(new AlphaAnimation(mOriginalAlpha, 0));
        set.setDuration(MIN_ANIMATION_DURATION_MS);
        set.setInterpolator(mCubicEaseOutInterpolator);
        set.initialize(0, 0, 0, 0);
        set.start();  // Will start on the first call to getTransformation.
        return set;
        final AnimationListener listener = new AnimationListener();
        animator.setDuration(duration);
        animator.setInterpolator(mCubicEaseOutInterpolator);
        animator.addListener(listener);
        animator.addUpdateListener(listener);

        mService.mAnimationHandler.post(() -> animator.start());
        return animator;
    }

    private ValueAnimator createCancelAnimationLocked() {
        final ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(
                PropertyValuesHolder.ofFloat(
                        ANIMATED_PROPERTY_X, mCurrentX - mThumbOffsetX, mCurrentX),
                PropertyValuesHolder.ofFloat(
                        ANIMATED_PROPERTY_Y, mCurrentY - mThumbOffsetY, mCurrentY),
                PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_SCALE, 1, 0),
                PropertyValuesHolder.ofFloat(ANIMATED_PROPERTY_ALPHA, mOriginalAlpha, 0));
        final AnimationListener listener = new AnimationListener();
        animator.setDuration(MIN_ANIMATION_DURATION_MS);
        animator.setInterpolator(mCubicEaseOutInterpolator);
        animator.addListener(listener);
        animator.addUpdateListener(listener);

        mService.mAnimationHandler.post(() -> animator.start());
        return animator;
    }

    private boolean isFromSource(int source) {
@@ -622,4 +648,68 @@ class DragState {
            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_GRABBING);
        }
    }

    private class DragStateHandler extends Handler {
        DragStateHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ANIMATION_END:
                    synchronized (mService.mWindowMap) {
                        if (mService.mDragState != DragState.this) {
                            Slog.wtf(TAG_WM, "mDragState is updated unexpectedly while " +
                                    "playing animation");
                            return;
                        }
                        if (mAnimator == null) {
                            Slog.wtf(TAG_WM, "Unexpected null mAnimator");
                            return;
                        }
                        mAnimator = null;
                        cleanUpDragLw();
                    }
                    break;
            }
        }
    }

    private class AnimationListener
            implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
                transaction.setPosition(
                        mSurfaceControl,
                        (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_X),
                        (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_Y));
                transaction.setAlpha(
                        mSurfaceControl,
                        (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_ALPHA));
                transaction.setMatrix(
                        mSurfaceControl,
                        (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0,
                        0, (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE));
                transaction.apply();
            }
        }

        @Override
        public void onAnimationStart(Animator animator) {}

        @Override
        public void onAnimationCancel(Animator animator) {}

        @Override
        public void onAnimationRepeat(Animator animator) {}

        @Override
        public void onAnimationEnd(Animator animator) {
            // Updating mDragState requires the WM lock so continues it on the out of
            // AnimationThread.
            mHandler.sendEmptyMessage(MSG_ANIMATION_END);
        }
    }
}
+0 −4
Original line number Diff line number Diff line
@@ -223,10 +223,6 @@ public class WindowAnimator {
                    }
                }

                if (mService.mDragState != null) {
                    mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
                }

                if (!mAnimating) {
                    cancelAnimation();
                }