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

Commit e2a5a722 authored by Hongwei Wang's avatar Hongwei Wang Committed by Android (Google) Code Review
Browse files

Merge "Fix entering PiP transition in button navigation mode" into sc-v2-dev

parents c762b2dc 1ce14dda
Loading
Loading
Loading
Loading
+55 −0
Original line number Original line Diff line number Diff line
@@ -26,10 +26,14 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.app.TaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.Choreographer;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.SurfaceSession;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -253,6 +257,7 @@ public class PipAnimationController {
                mSurfaceControlTransactionFactory;
                mSurfaceControlTransactionFactory;
        private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
        private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
        private @TransitionDirection int mTransitionDirection;
        private @TransitionDirection int mTransitionDirection;
        protected SurfaceControl mContentOverlay;


        private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
        private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
                @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
                @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
@@ -331,6 +336,53 @@ public class PipAnimationController {
            return false;
            return false;
        }
        }


        SurfaceControl getContentOverlay() {
            return mContentOverlay;
        }

        PipTransitionAnimator<T> setUseContentOverlay(Context context) {
            final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
            if (mContentOverlay != null) {
                // remove existing content overlay if there is any.
                tx.remove(mContentOverlay);
                tx.apply();
            }
            mContentOverlay = new SurfaceControl.Builder(new SurfaceSession())
                    .setCallsite("PipAnimation")
                    .setName("PipContentOverlay")
                    .setColorLayer()
                    .build();
            tx.show(mContentOverlay);
            tx.setLayer(mContentOverlay, Integer.MAX_VALUE);
            tx.setColor(mContentOverlay, getContentOverlayColor(context));
            tx.setAlpha(mContentOverlay, 0f);
            tx.reparent(mContentOverlay, mLeash);
            tx.apply();
            return this;
        }

        private float[] getContentOverlayColor(Context context) {
            final TypedArray ta = context.obtainStyledAttributes(new int[] {
                    android.R.attr.colorBackground });
            try {
                int colorAccent = ta.getColor(0, 0);
                return new float[] {
                        Color.red(colorAccent) / 255f,
                        Color.green(colorAccent) / 255f,
                        Color.blue(colorAccent) / 255f };
            } finally {
                ta.recycle();
            }
        }

        /**
         * Clears the {@link #mContentOverlay}, this should be done after the content overlay is
         * faded out, such as in {@link PipTaskOrganizer#fadeOutAndRemoveOverlay}
         */
        void clearContentOverlay() {
            mContentOverlay = null;
        }

        @VisibleForTesting
        @VisibleForTesting
        @TransitionDirection public int getTransitionDirection() {
        @TransitionDirection public int getTransitionDirection() {
            return mTransitionDirection;
            return mTransitionDirection;
@@ -517,6 +569,9 @@ 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 (mContentOverlay != null) {
                        tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
                    }
                    if (rotatedEndRect != null) {
                    if (rotatedEndRect != null) {
                        // Animate the bounds in a different orientation. It only happens when
                        // Animate the bounds in a different orientation. It only happens when
                        // switching between PiP and fullscreen.
                        // switching between PiP and fullscreen.
+54 −18
Original line number Original line Diff line number Diff line
@@ -107,6 +107,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
     */
     */
    private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
    private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;


    /**
     * The fixed start delay in ms when fading out the content overlay from bounds animation.
     * This is to overcome the flicker caused by configuration change when rotating from landscape
     * to portrait PiP in button navigation mode.
     */
    private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;

    private final Context mContext;
    private final Context mContext;
    private final SyncTransactionQueue mSyncTransactionQueue;
    private final SyncTransactionQueue mSyncTransactionQueue;
    private final PipBoundsState mPipBoundsState;
    private final PipBoundsState mPipBoundsState;
@@ -144,6 +151,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            final int direction = animator.getTransitionDirection();
            final int direction = animator.getTransitionDirection();
            final int animationType = animator.getAnimationType();
            final int animationType = animator.getAnimationType();
            final Rect destinationBounds = animator.getDestinationBounds();
            final Rect destinationBounds = animator.getDestinationBounds();
            if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
                fadeOutAndRemoveOverlay(animator.getContentOverlay(),
                        animator::clearContentOverlay, true /* withStartDelay*/);
            }
            if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
            if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
                    && direction == TRANSITION_DIRECTION_TO_PIP) {
                    && direction == TRANSITION_DIRECTION_TO_PIP) {
                // Notify the display to continue the deferred orientation change.
                // Notify the display to continue the deferred orientation change.
@@ -168,17 +179,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                finishResize(tx, destinationBounds, direction, animationType);
                finishResize(tx, destinationBounds, direction, animationType);
                sendOnPipTransitionFinished(direction);
                sendOnPipTransitionFinished(direction);
            }
            }
            if (direction == TRANSITION_DIRECTION_TO_PIP) {
                // TODO (b//169221267): Add jank listener for transactions without buffer updates.
                //InteractionJankMonitor.getInstance().end(
                //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
            }
        }
        }


        @Override
        @Override
        public void onPipAnimationCancel(TaskInfo taskInfo,
        public void onPipAnimationCancel(TaskInfo taskInfo,
                PipAnimationController.PipTransitionAnimator animator) {
                PipAnimationController.PipTransitionAnimator animator) {
            sendOnPipTransitionCancelled(animator.getTransitionDirection());
            final int direction = animator.getTransitionDirection();
            if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
                fadeOutAndRemoveOverlay(animator.getContentOverlay(),
                        animator::clearContentOverlay, true /* withStartDelay */);
            }
            sendOnPipTransitionCancelled(direction);
        }
        }
    };
    };


@@ -635,7 +646,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,


            // Remove the swipe to home overlay
            // Remove the swipe to home overlay
            if (swipeToHomeOverlay != null) {
            if (swipeToHomeOverlay != null) {
                fadeOutAndRemoveOverlay(swipeToHomeOverlay);
                fadeOutAndRemoveOverlay(swipeToHomeOverlay,
                        null /* callback */, false /* withStartDelay */);
            }
            }
        }, tx);
        }, tx);
        mInSwipePipToHomeTransition = false;
        mInSwipePipToHomeTransition = false;
@@ -718,9 +730,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
        }
        }


        final PipAnimationController.PipTransitionAnimator animator =
        final PipAnimationController.PipTransitionAnimator<?> animator =
                mPipAnimationController.getCurrentAnimator();
                mPipAnimationController.getCurrentAnimator();
        if (animator != null) {
        if (animator != null) {
            if (animator.getContentOverlay() != null) {
                removeContentOverlay(animator.getContentOverlay(), animator::clearContentOverlay);
            }
            animator.removeAllUpdateListeners();
            animator.removeAllUpdateListeners();
            animator.removeAllListeners();
            animator.removeAllListeners();
            animator.cancel();
            animator.cancel();
@@ -1195,7 +1210,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                            snapshotDest);
                            snapshotDest);


                    // Start animation to fade out the snapshot.
                    // Start animation to fade out the snapshot.
                    fadeOutAndRemoveOverlay(snapshotSurface);
                    fadeOutAndRemoveOverlay(snapshotSurface,
                            null /* callback */, false /* withStartDelay */);
                });
                });
            } else {
            } else {
                applyFinishBoundsResize(wct, direction);
                applyFinishBoundsResize(wct, direction);
@@ -1286,15 +1302,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        animator.setTransitionDirection(direction)
        animator.setTransitionDirection(direction)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setDuration(durationMs)
                .setDuration(durationMs);
                .start();
        if (isInPipDirection(direction)) {
        if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
            // Similar to auto-enter-pip transition, we use content overlay when there is no
            // source rect hint to enter PiP use bounds animation.
            if (sourceHintRect == null) animator.setUseContentOverlay(mContext);
            // The destination bounds are used for the end rect of animation and the final bounds
            // The destination bounds are used for the end rect of animation and the final bounds
            // after animation finishes. So after the animation is started, the destination bounds
            // after animation finishes. So after the animation is started, the destination bounds
            // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
            // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
            // without affecting the animation.
            // without affecting the animation.
            if (rotationDelta != Surface.ROTATION_0) {
                animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
                animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
            }
            }
        }
        animator.start();
        return animator;
        return animator;
    }
    }


@@ -1307,6 +1328,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
            // Transform the destination bounds to current display coordinates.
            // Transform the destination bounds to current display coordinates.
            rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation);
            rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation);
            // When entering PiP (from button navigation mode), adjust the source rect hint by
            // display cutout if applicable.
            if (sourceHintRect != null && mTaskInfo.displayCutoutInsets != null) {
                if (rotationDelta == Surface.ROTATION_270) {
                    sourceHintRect.offset(mTaskInfo.displayCutoutInsets.left,
                            mTaskInfo.displayCutoutInsets.top);
                }
            }
        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
@@ -1345,7 +1374,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    /**
    /**
     * Fades out and removes an overlay surface.
     * Fades out and removes an overlay surface.
     */
     */
    private void fadeOutAndRemoveOverlay(SurfaceControl surface) {
    private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
            boolean withStartDelay) {
        if (surface == null) {
        if (surface == null) {
            return;
            return;
        }
        }
@@ -1362,13 +1392,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        animator.addListener(new AnimatorListenerAdapter() {
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            @Override
            public void onAnimationEnd(Animator animation) {
            public void onAnimationEnd(Animator animation) {
                removeContentOverlay(surface, callback);
            }
        });
        animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0);
        animator.start();
    }

    private void removeContentOverlay(SurfaceControl surface, Runnable callback) {
        final SurfaceControl.Transaction tx =
        final SurfaceControl.Transaction tx =
                mSurfaceControlTransactionFactory.getTransaction();
                mSurfaceControlTransactionFactory.getTransaction();
        tx.remove(surface);
        tx.remove(surface);
        tx.apply();
        tx.apply();
            }
        if (callback != null) callback.run();
        });
        animator.start();
    }
    }


    /**
    /**
+1 −1
Original line number Original line Diff line number Diff line
@@ -1901,7 +1901,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            forAllWindows(w -> {
            forAllWindows(w -> {
                w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
                w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
            }, true /* traverseTopToBottom */);
            }, true /* traverseTopToBottom */);
            mPinnedTaskController.startSeamlessRotationIfNeeded(transaction);
            mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
        }
        }


        mWmService.mDisplayManagerInternal.performTraversal(transaction);
        mWmService.mDisplayManagerInternal.performTraversal(transaction);
+15 −1
Original line number Original line Diff line number Diff line
@@ -27,13 +27,16 @@ import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;
import android.util.RotationUtils;
import android.util.Slog;
import android.util.Slog;
import android.view.IPinnedTaskListener;
import android.view.IPinnedTaskListener;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.PictureInPictureSurfaceTransaction;


@@ -237,7 +240,8 @@ class PinnedTaskController {
     * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
     * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
     * receives the callback of fixed rotation completion.
     * receives the callback of fixed rotation completion.
     */
     */
    void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t) {
    void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
            int oldRotation, int newRotation) {
        final Rect bounds = mDestRotatedBounds;
        final Rect bounds = mDestRotatedBounds;
        final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
        final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
        if (bounds == null && pipTx == null) {
        if (bounds == null && pipTx == null) {
@@ -280,6 +284,16 @@ class PinnedTaskController {
                ? params.getSourceRectHint()
                ? params.getSourceRectHint()
                : null;
                : null;
        Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
        Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
        final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
        // Adjust for display cutout if applicable.
        if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
            if (pinnedTask.getDisplayCutoutInsets() != null) {
                final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
                final Rect displayCutoutInsets = RotationUtils.rotateInsets(
                        Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
                sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
            }
        }
        final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
        final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
                ? sourceHintRect : areaBounds;
                ? sourceHintRect : areaBounds;
        final int w = contentBounds.width();
        final int w = contentBounds.width();
+5 −6
Original line number Original line Diff line number Diff line
@@ -3341,7 +3341,7 @@ class Task extends TaskFragment {
        info.positionInParent = getRelativePosition();
        info.positionInParent = getRelativePosition();


        info.pictureInPictureParams = getPictureInPictureParams(top);
        info.pictureInPictureParams = getPictureInPictureParams(top);
        info.displayCutoutInsets = getDisplayCutoutInsets(top);
        info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null;
        info.topActivityInfo = mReuseActivitiesReport.top != null
        info.topActivityInfo = mReuseActivitiesReport.top != null
                ? mReuseActivitiesReport.top.info
                ? mReuseActivitiesReport.top.info
                : null;
                : null;
@@ -3377,16 +3377,15 @@ class Task extends TaskFragment {
                ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
                ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
    }
    }


    private Rect getDisplayCutoutInsets(Task top) {
    Rect getDisplayCutoutInsets() {
        if (top == null || top.mDisplayContent == null
        if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
                || top.getDisplayInfo().displayCutout == null) return null;
        final WindowState w = getTopVisibleAppMainWindow();
        final WindowState w = top.getTopVisibleAppMainWindow();
        final int displayCutoutMode = w == null
        final int displayCutoutMode = w == null
                ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
                : w.getAttrs().layoutInDisplayCutoutMode;
                : w.getAttrs().layoutInDisplayCutoutMode;
        return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
        return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
                || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
                || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
                ? null : top.getDisplayInfo().displayCutout.getSafeInsets();
                ? null : getDisplayInfo().displayCutout.getSafeInsets();
    }
    }


    /**
    /**