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

Commit ca7f1099 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Apply fixed rotation animation for pip to fullscreen" into sc-dev

parents 5bb86857 c0e5b3d9
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -5685,6 +5685,13 @@ public final class ActivityThread extends ClientTransactionHandler {
        final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
        final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                amOverrideConfig, contextThemeWrapperOverrideConfig);
                amOverrideConfig, contextThemeWrapperOverrideConfig);
        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
        final Resources res = activity.getResources();
        if (res.hasOverrideDisplayAdjustments()) {
            // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
            // configuration of activity may be sent later than the adjustments. In this case, the
            // adjustments need to be updated for the consistency of display info.
            res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
        }


        activity.mConfigChangeFlags = 0;
        activity.mConfigChangeFlags = 0;
        activity.mCurrentConfig = new Configuration(newConfig);
        activity.mCurrentConfig = new Configuration(newConfig);
+51 −4
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package com.android.wm.shell.pip;
package com.android.wm.shell.pip;


import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;

import android.animation.AnimationHandler;
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@ import android.annotation.IntDef;
import android.app.TaskInfo;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayLayout;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@ public class PipAnimationController {
     * leash bounds before transformation/any animation. This is so when we try to construct
     * leash bounds before transformation/any animation. This is so when we try to construct
     * the different transformation matrices for the animation, we are constructing this based off
     * the different transformation matrices for the animation, we are constructing this based off
     * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
     * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
     *
     * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
     * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
     * rotation change.
     */
     */
    @VisibleForTesting
    @VisibleForTesting
    public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
    public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
            Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
            Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
            @PipAnimationController.TransitionDirection int direction, float startingAngle) {
            @PipAnimationController.TransitionDirection int direction, float startingAngle,
            @Surface.Rotation int rotationDelta) {
        if (mCurrentAnimator == null) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
                    PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
                            endBounds, sourceHintRect, direction, 0 /* startingAngle */));
                            endBounds, sourceHintRect, direction, 0 /* startingAngle */,
                            rotationDelta));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                && mCurrentAnimator.isRunning()) {
                && mCurrentAnimator.isRunning()) {
            // If we are still animating the fade into pip, then just move the surface and ensure
            // If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@ public class PipAnimationController {
            mCurrentAnimator.cancel();
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
                    PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
                            endBounds, sourceHintRect, direction, startingAngle));
                            endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
        }
        }
        return mCurrentAnimator;
        return mCurrentAnimator;
    }
    }
@@ -410,7 +421,8 @@ public class PipAnimationController {


        static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
        static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
                Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
                Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
                @PipAnimationController.TransitionDirection int direction, float startingAngle) {
                @PipAnimationController.TransitionDirection int direction, float startingAngle,
                @Surface.Rotation int rotationDelta) {
            // Just for simplicity we'll interpolate between the source rect hint insets and empty
            // Just for simplicity we'll interpolate between the source rect hint insets and empty
            // insets to calculate the window crop
            // insets to calculate the window crop
            final Rect initialSourceValue;
            final Rect initialSourceValue;
@@ -431,6 +443,16 @@ public class PipAnimationController {
            }
            }
            final Rect sourceInsets = new Rect(0, 0, 0, 0);
            final Rect sourceInsets = new Rect(0, 0, 0, 0);


            final Rect rotatedEndRect;
            if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
                // Rotate the end bounds according to the rotation delta because the display will
                // be rotated to the same orientation.
                rotatedEndRect = new Rect(endValue);
                DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
            } else {
                rotatedEndRect = null;
            }

            // construct new Rect instances in case they are recycled
            // construct new Rect instances in case they are recycled
            return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
            return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@ public class PipAnimationController {
                    final Rect base = getBaseValue();
                    final Rect base = getBaseValue();
                    final Rect start = getStartValue();
                    final Rect start = getStartValue();
                    final Rect end = getEndValue();
                    final Rect end = getEndValue();
                    if (rotatedEndRect != null) {
                        // Animate the bounds in a different orientation. It only happens when
                        // leaving PiP to fullscreen.
                        applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
                        return;
                    }
                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                    Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
                    float angle = (1.0f - fraction) * startingAngle;
                    float angle = (1.0f - fraction) * startingAngle;
                    setCurrentValue(bounds);
                    setCurrentValue(bounds);
@@ -469,6 +497,25 @@ public class PipAnimationController {
                    tx.apply();
                    tx.apply();
                }
                }


                private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
                        float fraction, Rect start, Rect end, Rect rotatedEndRect) {
                    final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
                    setCurrentValue(bounds);
                    final float degree, x, y;
                    if (rotationDelta == ROTATION_90) {
                        degree = 90 * fraction;
                        x = fraction * (end.right - start.left) + start.left;
                        y = fraction * (end.top - start.top) + start.top;
                    } else {
                        degree = -90 * fraction;
                        x = fraction * (end.left - start.left) + start.left;
                        y = fraction * (end.bottom - start.top) + start.top;
                    }
                    getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
                            rotatedEndRect, degree, x, y);
                    tx.apply();
                }

                @Override
                @Override
                void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
                    getSurfaceTransactionHelper()
                    getSurfaceTransactionHelper()
+26 −0
Original line number Original line Diff line number Diff line
@@ -126,6 +126,32 @@ public class PipSurfaceTransactionHelper {
        return this;
        return this;
    }
    }


    /**
     * Operates the rotation according to the given degrees and scale (setMatrix) according to the
     * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
            SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
            float positionX, float positionY) {
        mTmpDestinationRect.set(sourceBounds);
        final int dw = destinationBounds.width();
        final int dh = destinationBounds.height();
        // Scale by the short side so there won't be empty area if the aspect ratio of source and
        // destination are different.
        final float scale = dw <= dh
                ? (float) sourceBounds.width() / dw
                : (float) sourceBounds.height() / dh;
        // Inverse scale for crop to fit in screen coordinates.
        mTmpDestinationRect.scale(1 / scale);
        mTmpTransform.setRotate(degrees);
        mTmpTransform.postScale(scale, scale);
        mTmpTransform.postTranslate(positionX, positionY);
        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
        return this;
    }

    /**
    /**
     * Resets the scale (setMatrix) on a given transaction and leash if there's any
     * Resets the scale (setMatrix) on a given transaction and leash if there's any
     *
     *
+41 −63
Original line number Original line Diff line number Diff line
@@ -48,13 +48,12 @@ import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;
import android.util.Rational;
import android.util.Rational;
import android.view.Display;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerToken;
@@ -72,8 +71,6 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.transition.Transitions;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
import java.util.Optional;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Consumer;
@@ -134,7 +131,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    private final PipUiEventLogger mPipUiEventLoggerLogger;
    private final PipUiEventLogger mPipUiEventLoggerLogger;
    private final int mEnterExitAnimationDuration;
    private final int mEnterExitAnimationDuration;
    private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
    private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
    private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
    private final Optional<LegacySplitScreen> mSplitScreenOptional;
    private final Optional<LegacySplitScreen> mSplitScreenOptional;
    protected final ShellTaskOrganizer mTaskOrganizer;
    protected final ShellTaskOrganizer mTaskOrganizer;
    protected final ShellExecutor mMainExecutor;
    protected final ShellExecutor mMainExecutor;
@@ -191,6 +187,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
     */
     */
    private boolean mWaitForFixedRotation;
    private boolean mWaitForFixedRotation;


    /**
     * The rotation that the display will apply after expanding PiP to fullscreen. This is only
     * meaningful if {@link #mWaitForFixedRotation} is true.
     */
    private @Surface.Rotation int mNextRotation;

    /**
    /**
     * If set to {@code true}, no entering PiP transition would be kicked off and most likely
     * If set to {@code true}, no entering PiP transition would be kicked off and most likely
     * it's due to the fact that Launcher is handling the transition directly when swiping
     * it's due to the fact that Launcher is handling the transition directly when swiping
@@ -313,35 +315,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            return;
            return;
        }
        }


        final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
        if (initialConfig == null) {
            Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
            return;
        }
        mPipUiEventLoggerLogger.log(
        mPipUiEventLoggerLogger.log(
                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
        final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
                != mPipBoundsState.getDisplayLayout().rotation();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
        final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
        final int direction = syncWithSplitScreenBounds(destinationBounds)
        final int direction = syncWithSplitScreenBounds(destinationBounds)
                ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                : TRANSITION_DIRECTION_LEAVE_PIP;
                : TRANSITION_DIRECTION_LEAVE_PIP;
        if (orientationDiffers) {
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
            mState = State.EXITING_PIP;
        mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
            // Send started callback though animation is ignored.
            sendOnPipTransitionStarted(direction);
            // Don't bother doing an animation if the display rotation differs or if it's in
            // a non-supported windowing mode
            applyWindowingModeChangeOnExit(wct, direction);
            mTaskOrganizer.applyTransaction(wct);
            // Send finished callback though animation is ignored.
            sendOnPipTransitionFinished(direction);
        } else {
            final SurfaceControl.Transaction tx =
                    mSurfaceControlTransactionFactory.getTransaction();
            mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
                    mPipBoundsState.getBounds());
        tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
        tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
        // We set to fullscreen here for now, but later it will be set to UNDEFINED for
        // We set to fullscreen here for now, but later it will be set to UNDEFINED for
        // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
        // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
@@ -368,7 +350,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            }
            }
        });
        });
    }
    }
    }


    private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
    private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
        // Reset the final windowing mode.
        // Reset the final windowing mode.
@@ -399,7 +380,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setDuration(mEnterExitAnimationDuration)
                .setDuration(mEnterExitAnimationDuration)
                .start();
                .start();
        mInitialState.remove(mToken.asBinder());
        mState = State.EXITING_PIP;
        mState = State.EXITING_PIP;
    }
    }


@@ -424,7 +404,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mToken = mTaskInfo.token;
        mToken = mTaskInfo.token;
        mState = State.TASK_APPEARED;
        mState = State.TASK_APPEARED;
        mLeash = leash;
        mLeash = leash;
        mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
        mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
        mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
        setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
        setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
                mTaskInfo.topActivityInfo);
                mTaskInfo.topActivityInfo);
@@ -606,6 +585,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,


    @Override
    @Override
    public void onFixedRotationStarted(int displayId, int newRotation) {
    public void onFixedRotationStarted(int displayId, int newRotation) {
        mNextRotation = newRotation;
        mWaitForFixedRotation = true;
        mWaitForFixedRotation = true;
    }
    }


@@ -645,7 +625,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                mPipAnimationController.getCurrentAnimator();
                mPipAnimationController.getCurrentAnimator();
        if (animator == null || !animator.isRunning()
        if (animator == null || !animator.isRunning()
                || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
                || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
            if (mState.isInPip() && fromRotation) {
            if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
                // Update bounds state to final destination first. It's important to do this
                // Update bounds state to final destination first. It's important to do this
                // before finishing & cancelling the transition animation so that the MotionHelper
                // before finishing & cancelling the transition animation so that the MotionHelper
                // bounds are synchronized to the destination bounds when the animation ends.
                // bounds are synchronized to the destination bounds when the animation ends.
@@ -1052,11 +1032,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            Log.w(TAG, "Abort animation, invalid leash");
            Log.w(TAG, "Abort animation, invalid leash");
            return;
            return;
        }
        }
        final int rotationDelta = mWaitForFixedRotation
                ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
                : Surface.ROTATION_0;
        Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
        Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                ? mPipBoundsState.getBounds() : currentBounds;
                ? mPipBoundsState.getBounds() : currentBounds;
        mPipAnimationController
        mPipAnimationController
                .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
                .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
                        sourceHintRect, direction, startingAngle)
                        sourceHintRect, direction, startingAngle, rotationDelta)
                .setTransitionDirection(direction)
                .setTransitionDirection(direction)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setDuration(durationMs)
                .setDuration(durationMs)
@@ -1101,11 +1084,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        pw.println(innerPrefix + "mState=" + mState);
        pw.println(innerPrefix + "mState=" + mState);
        pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
        pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
        pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
        pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
        pw.println(innerPrefix + "mInitialState:");
        for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
            pw.println(innerPrefix + "  binder=" + e.getKey()
                    + " winConfig=" + e.getValue().windowConfiguration);
        }
    }
    }


    @Override
    @Override
+2 −1
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.app.TaskInfo;
import android.content.Context;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@ public class PipTransition extends PipTransitionController {
                            taskInfo.pictureInPictureParams, currentBounds);
                            taskInfo.pictureInPictureParams, currentBounds);
            animator = mPipAnimationController.getAnimator(taskInfo, leash,
            animator = mPipAnimationController.getAnimator(taskInfo, leash,
                    currentBounds, currentBounds, destinationBounds, sourceHintRect,
                    currentBounds, currentBounds, destinationBounds, sourceHintRect,
                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
                    TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
            t.setAlpha(leash, 0f);
            t.setAlpha(leash, 0f);
            t.apply();
            t.apply();
Loading