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

Commit 732ad85e authored by Hongwei Wang's avatar Hongwei Wang Committed by Automerger Merge Worker
Browse files

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

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15340838

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

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

        private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
                @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
@@ -331,6 +336,53 @@ public class PipAnimationController {
            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
        @TransitionDirection public int getTransitionDirection() {
            return mTransitionDirection;
@@ -517,6 +569,9 @@ public class PipAnimationController {
                    final Rect base = getBaseValue();
                    final Rect start = getStartValue();
                    final Rect end = getEndValue();
                    if (mContentOverlay != null) {
                        tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
                    }
                    if (rotatedEndRect != null) {
                        // Animate the bounds in a different orientation. It only happens when
                        // switching between PiP and fullscreen.
+54 −18
Original line number 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;

    /**
     * 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 SyncTransactionQueue mSyncTransactionQueue;
    private final PipBoundsState mPipBoundsState;
@@ -144,6 +151,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            final int direction = animator.getTransitionDirection();
            final int animationType = animator.getAnimationType();
            final Rect destinationBounds = animator.getDestinationBounds();
            if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
                fadeOutAndRemoveOverlay(animator.getContentOverlay(),
                        animator::clearContentOverlay, true /* withStartDelay*/);
            }
            if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
                    && direction == TRANSITION_DIRECTION_TO_PIP) {
                // Notify the display to continue the deferred orientation change.
@@ -168,17 +179,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                finishResize(tx, destinationBounds, direction, animationType);
                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
        public void onPipAnimationCancel(TaskInfo taskInfo,
                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
            if (swipeToHomeOverlay != null) {
                fadeOutAndRemoveOverlay(swipeToHomeOverlay);
                fadeOutAndRemoveOverlay(swipeToHomeOverlay,
                        null /* callback */, false /* withStartDelay */);
            }
        }, tx);
        mInSwipePipToHomeTransition = false;
@@ -718,9 +730,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
        }

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

                    // Start animation to fade out the snapshot.
                    fadeOutAndRemoveOverlay(snapshotSurface);
                    fadeOutAndRemoveOverlay(snapshotSurface,
                            null /* callback */, false /* withStartDelay */);
                });
            } else {
                applyFinishBoundsResize(wct, direction);
@@ -1286,15 +1302,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        animator.setTransitionDirection(direction)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setPipTransactionHandler(mPipTransactionHandler)
                .setDuration(durationMs)
                .start();
        if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
                .setDuration(durationMs);
        if (isInPipDirection(direction)) {
            // 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
            // after animation finishes. So after the animation is started, the destination bounds
            // can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
            // without affecting the animation.
            if (rotationDelta != Surface.ROTATION_0) {
                animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
            }
        }
        animator.start();
        return animator;
    }

@@ -1307,6 +1328,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
            // Transform the destination bounds to current display coordinates.
            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) {
            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
@@ -1345,7 +1374,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    /**
     * Fades out and removes an overlay surface.
     */
    private void fadeOutAndRemoveOverlay(SurfaceControl surface) {
    private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
            boolean withStartDelay) {
        if (surface == null) {
            return;
        }
@@ -1362,13 +1392,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            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 =
                mSurfaceControlTransactionFactory.getTransaction();
        tx.remove(surface);
        tx.apply();
            }
        });
        animator.start();
        if (callback != null) callback.run();
    }

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

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

@@ -237,7 +240,8 @@ class PinnedTaskController {
     * rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
     * 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 PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
        if (bounds == null && pipTx == null) {
@@ -280,6 +284,16 @@ class PinnedTaskController {
                ? params.getSourceRectHint()
                : null;
        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)
                ? sourceHintRect : areaBounds;
        final int w = contentBounds.width();
+5 −6
Original line number Diff line number Diff line
@@ -3341,7 +3341,7 @@ class Task extends TaskFragment {
        info.positionInParent = getRelativePosition();

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

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

    /**