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

Commit 7c2a45be authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Add PIP transition support on non-match parent activity" into main

parents 27f9097f 3ed86bdb
Loading
Loading
Loading
Loading
+24 −15
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.shared.pip;

import static android.util.TypedValue.COMPLEX_UNIT_DIP;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
@@ -170,26 +171,34 @@ public abstract class PipContentOverlay {

        private final Context mContext;
        private final int mAppIconSizePx;
        private final Rect mAppBounds;
        /**
         * The bounds of the application window relative to the task leash.
         */
        private final Rect mRelativeAppBounds;
        private final int mOverlayHalfSize;
        private final Matrix mTmpTransform = new Matrix();
        private final float[] mTmpFloat9 = new float[9];

        private Bitmap mBitmap;

        public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
                Drawable appIcon, int appIconSizePx) {
        // TODO(b/356277166): add non-match_parent support on PIP2.
        /**
         * @param context the {@link Context} that contains the icon information
         * @param relativeAppBounds the bounds of the app window frame relative to the task leash
         * @param destinationBounds the bounds for rhe PIP task
         * @param appIcon the app icon {@link Drawable}
         * @param appIconSizePx the icon dimension in pixel
         */
        public PipAppIconOverlay(@NonNull Context context, @NonNull Rect relativeAppBounds,
                @NonNull Rect destinationBounds, @NonNull Drawable appIcon, int appIconSizePx) {
            mContext = context;
            final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
                    MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
            mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);

            final int overlaySize = getOverlaySize(appBounds, destinationBounds);
            final int overlaySize = getOverlaySize(relativeAppBounds, destinationBounds);
            mOverlayHalfSize = overlaySize >> 1;

            // When the activity is in the secondary split, make sure the scaling center is not
            // offset.
            mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
            mRelativeAppBounds = relativeAppBounds;

            mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
            prepareAppIconOverlay(appIcon);
@@ -206,9 +215,9 @@ public abstract class PipContentOverlay {
         * the overlay will be drawn with the max size of the start and end bounds in different
         * rotation.
         */
        public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
            final int appWidth = appBounds.width();
            final int appHeight = appBounds.height();
        public static int getOverlaySize(Rect overlayBounds, Rect destinationBounds) {
            final int appWidth = overlayBounds.width();
            final int appHeight = overlayBounds.height();

            return Math.max(Math.max(appWidth, appHeight),
                    Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
@@ -230,15 +239,15 @@ public abstract class PipContentOverlay {
            mTmpTransform.reset();
            // In order for the overlay to always cover the pip window, the overlay may have a
            // size larger than the pip window. Make sure that app icon is at the center.
            final int appBoundsCenterX = mAppBounds.centerX();
            final int appBoundsCenterY = mAppBounds.centerY();
            final int appBoundsCenterX = mRelativeAppBounds.centerX();
            final int appBoundsCenterY = mRelativeAppBounds.centerY();
            mTmpTransform.setTranslate(
                    appBoundsCenterX - mOverlayHalfSize,
                    appBoundsCenterY - mOverlayHalfSize);
            // Scale back the bitmap with the pivot point at center.
            final float scale = Math.min(
                    (float) mAppBounds.width() / currentBounds.width(),
                    (float) mAppBounds.height() / currentBounds.height());
                    (float) mRelativeAppBounds.width() / currentBounds.width(),
                    (float) mRelativeAppBounds.height() / currentBounds.height());
            mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
            atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
                    .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+61 −18
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.Surface;
@@ -152,7 +153,6 @@ public class PipAnimationController {
        return mCurrentAnimator;
    }

    @SuppressWarnings("unchecked")
    /**
     * Construct and return an animator that animates from the {@param startBounds} to the
     * {@param endBounds} with the given {@param direction}. If {@param direction} is type
@@ -171,6 +171,7 @@ public class PipAnimationController {
     * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
     * rotation change.
     */
    @SuppressWarnings("unchecked")
    @VisibleForTesting
    public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
            Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@@ -566,7 +567,7 @@ public class PipAnimationController {
                    }
                    getSurfaceTransactionHelper()
                            .resetScale(tx, leash, getDestinationBounds())
                            .crop(tx, leash, getDestinationBounds())
                            .cropAndPosition(tx, leash, getDestinationBounds())
                            .round(tx, leash, true /* applyCornerRadius */)
                            .shadow(tx, leash, shouldApplyShadowRadius());
                    tx.show(leash);
@@ -590,18 +591,50 @@ public class PipAnimationController {
            // Just for simplicity we'll interpolate between the source rect hint insets and empty
            // insets to calculate the window crop
            final Rect initialSourceValue;
            final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame;
            final boolean hasNonMatchFrame = mainWindowFrame != null;
            final boolean changeOrientation =
                    rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270;
            final Rect baseBounds = new Rect(baseValue);
            final Rect startBounds = new Rect(startValue);
            final Rect endBounds = new Rect(endValue);
            if (isOutPipDirection) {
                initialSourceValue = new Rect(endValue);
                // TODO(b/356277166): handle rotation change with activity that provides main window
                //  frame.
                if (hasNonMatchFrame && !changeOrientation) {
                    endBounds.set(mainWindowFrame);
                }
                initialSourceValue = new Rect(endBounds);
            } else if (isInPipDirection) {
                if (hasNonMatchFrame) {
                    baseBounds.set(mainWindowFrame);
                    if (startValue.equals(baseValue)) {
                        // If the start value is at initial state as in PIP animation, also override
                        // the start bounds with nonMatchParentBounds.
                        startBounds.set(mainWindowFrame);
                    }
                }
                initialSourceValue = new Rect(baseBounds);
            } else {
                initialSourceValue = new Rect(baseValue);
                // Note that we assume the window bounds always match task bounds in PIP mode.
                initialSourceValue = new Rect(baseBounds);
            }

            final Point leashOffset;
            if (isInPipDirection) {
                leashOffset = new Point(baseValue.left, baseValue.top);
            } else if (isOutPipDirection) {
                leashOffset = new Point(endValue.left, endValue.top);
            } else {
                leashOffset = new Point(baseValue.left, baseValue.top);
            }

            final Rect rotatedEndRect;
            final Rect lastEndRect;
            final Rect initialContainerRect;
            if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
                lastEndRect = new Rect(endValue);
                rotatedEndRect = new Rect(endValue);
            if (changeOrientation) {
                lastEndRect = new Rect(endBounds);
                rotatedEndRect = new Rect(endBounds);
                // Rotate the end bounds according to the rotation delta because the display will
                // be rotated to the same orientation.
                rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
@@ -617,9 +650,9 @@ public class PipAnimationController {
                // Crop a Rect matches the aspect ratio and pivots at the center point.
                // This is done for entering case only.
                if (isInPipDirection(direction)) {
                    final float aspectRatio = endValue.width() / (float) endValue.height();
                    final float aspectRatio = endBounds.width() / (float) endBounds.height();
                    adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
                            startValue, aspectRatio));
                            startBounds, aspectRatio));
                }
            } else {
                adjustedSourceRectHint.set(sourceRectHint);
@@ -644,7 +677,7 @@ public class PipAnimationController {

            // construct new Rect instances in case they are recycled
            return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
                    endBounds, new Rect(baseBounds), new Rect(startBounds), new Rect(endBounds)) {
                private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
                private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());

@@ -668,11 +701,22 @@ public class PipAnimationController {
                    setCurrentValue(bounds);
                    if (inScaleTransition() || adjustedSourceRectHint.isEmpty()) {
                        if (isOutPipDirection) {
                            getSurfaceTransactionHelper().crop(tx, leash, end)
                                    .scale(tx, leash, end, bounds);
                            // Use the bounds relative to the task leash in case the leash does not
                            // start from (0, 0).
                            final Rect relativeEndBounds = new Rect(end);
                            relativeEndBounds.offset(-leashOffset.x, -leashOffset.y);
                            getSurfaceTransactionHelper()
                                    .crop(tx, leash, relativeEndBounds)
                                    .scale(tx, leash, relativeEndBounds, bounds,
                                            false /* shouldOffset */);
                        } else {
                            getSurfaceTransactionHelper().crop(tx, leash, base)
                                    .scale(tx, leash, base, bounds, angle)
                            // TODO(b/356277166): add support to specify sourceRectHint with
                            //  non-match parent activity.
                            // If there's a PIP resize animation, we should offset the bounds to
                            // (0, 0) since the window bounds should match the leash bounds in PIP
                            // mode.
                            getSurfaceTransactionHelper().cropAndPosition(tx, leash, base)
                                    .scale(tx, leash, base, bounds, angle, inScaleTransition())
                                    .round(tx, leash, base, bounds)
                                    .shadow(tx, leash, shouldApplyShadowRadius());
                        }
@@ -680,7 +724,7 @@ public class PipAnimationController {
                        final Rect insets = computeInsets(fraction);
                        getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
                                adjustedSourceRectHint, initialSourceValue, bounds, insets,
                                isInPipDirection, fraction);
                                isInPipDirection, fraction, leashOffset);
                        final Rect sourceBounds = new Rect(initialContainerRect);
                        sourceBounds.inset(insets);
                        getSurfaceTransactionHelper()
@@ -733,8 +777,7 @@ public class PipAnimationController {
                    getSurfaceTransactionHelper()
                            .rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
                                    insets, degree, x, y, isOutPipDirection,
                                    rotationDelta == ROTATION_270 /* clockwise */);
                    getSurfaceTransactionHelper()
                                    rotationDelta == ROTATION_270 /* clockwise */)
                            .round(tx, leash, sourceBounds, bounds)
                            .shadow(tx, leash, shouldApplyShadowRadius());
                    if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) {
@@ -772,7 +815,7 @@ public class PipAnimationController {
                        tx.setPosition(leash, 0, 0);
                        tx.setWindowCrop(leash, 0, 0);
                    } else {
                        getSurfaceTransactionHelper().crop(tx, leash, destBounds);
                        getSurfaceTransactionHelper().cropAndPosition(tx, leash, destBounds);
                    }
                    if (mContentOverlay != null) {
                        clearContentOverlay();
+79 −25
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.wm.shell.pip;

import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Choreographer;
@@ -68,13 +70,28 @@ public class PipSurfaceTransactionHelper {
     * Operates the crop (and position) on a given transaction and leash
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect destinationBounds) {
    public PipSurfaceTransactionHelper cropAndPosition(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect destinationBounds) {
        tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
                .setPosition(leash, destinationBounds.left, destinationBounds.top);
        return this;
    }

    /**
     * Operates {@link SurfaceControl.Transaction#setCrop} on a given transaction and leash.
     *
     * @param tx the transaction to  apply
     * @param leash the leash to crop
     * @param relativeDestinationBounds the bounds to crop, which is relative to the leash
     *                                  coordinate
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper crop(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect relativeDestinationBounds) {
        tx.setCrop(leash, relativeDestinationBounds);
        return this;
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
@@ -82,38 +99,73 @@ public class PipSurfaceTransactionHelper {
    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect sourceBounds, Rect destinationBounds) {
        mTmpDestinationRectF.set(destinationBounds);
        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */);
        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */,
                true /* shouldOffset */);
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect sourceBounds, RectF destinationBounds) {
        return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
            @NonNull RectF destinationBounds) {
        return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */,
                true /* shouldOffset */);
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash
     * Operates the scale (setMatrix) on a given transaction and leash.
     *
     * @param shouldOffset {@code true} to offset the leash to (0, 0)
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect sourceBounds, Rect destinationBounds, float degrees) {
    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
            @NonNull Rect destinationBounds, boolean shouldOffset) {
        mTmpDestinationRectF.set(destinationBounds);
        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */, shouldOffset);
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash.
     *
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
            @NonNull Rect destinationBounds, float degrees) {
        return scale(tx, leash, sourceBounds, destinationBounds, degrees, true /* shouldOffset */);
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash.
     *
     * @param shouldOffset {@code true} to offset the leash to (0, 0)
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
            @NonNull Rect destinationBounds, float degrees, boolean shouldOffset) {
        mTmpDestinationRectF.set(destinationBounds);
        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees);
        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees, shouldOffset);
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
     *
     * @param shouldOffset {@code true} to offset the leash to (0, 0)
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect sourceBounds, RectF destinationBounds, float degrees) {
    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
            @NonNull RectF destinationBounds, float degrees, boolean shouldOffset) {
        mTmpSourceRectF.set(sourceBounds);
        // We want the matrix to position the surface relative to the screen coordinates so offset
        // the source to 0,0
        // the source to (0, 0) if {@code shouldOffset} is true.
        if (shouldOffset) {
            mTmpSourceRectF.offsetTo(0, 0);
        }
        mTmpDestinationRectF.set(destinationBounds);
        mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
        mTmpTransform.postRotate(degrees,
@@ -123,17 +175,19 @@ public class PipSurfaceTransactionHelper {
    }

    /**
     * Operates the scale (setMatrix) on a given transaction and leash
     * Operates the scale (setMatrix) on a given transaction and leash.
     *
     * @param leashOffset the offset of the leash bounds relative to the screen coordinate
     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
     */
    public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
            SurfaceControl leash, Rect sourceRectHint,
            Rect sourceBounds, Rect destinationBounds, Rect insets,
            boolean isInPipDirection, float fraction) {
    public PipSurfaceTransactionHelper scaleAndCrop(@NonNull SurfaceControl.Transaction tx,
            @NonNull SurfaceControl leash, @NonNull Rect sourceRectHint, @NonNull Rect sourceBounds,
            @NonNull Rect destinationBounds, @NonNull Rect insets, boolean isInPipDirection,
            float fraction, @NonNull Point leashOffset) {
        mTmpDestinationRect.set(sourceBounds);
        // Similar to {@link #scale}, we want to position the surface relative to the screen
        // coordinates so offset the bounds to 0,0
        mTmpDestinationRect.offsetTo(0, 0);
        // coordinates so offset the bounds relative to the leash.
        mTmpDestinationRect.offset(-leashOffset.x, -leashOffset.y);
        mTmpDestinationRect.inset(insets);
        // Scale to the bounds no smaller than the destination and offset such that the top/left
        // of the scaled inset source rect aligns with the top/left of the destination bounds
@@ -152,13 +206,13 @@ public class PipSurfaceTransactionHelper {
            scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
                    (float) destinationBounds.height() / sourceBounds.height());
        }
        float left = destinationBounds.left - insets.left * scale;
        float top = destinationBounds.top - insets.top * scale;
        float left = destinationBounds.left - mTmpDestinationRect.left * scale;
        float top = destinationBounds.top - mTmpDestinationRect.top * scale;
        if (scale == 1) {
            // Work around the 1 pixel off error by rounding the position down at very beginning.
            // We noticed such error from flicker tests, not visually.
            left = sourceBounds.left;
            top = sourceBounds.top;
            left = leashOffset.x;
            top = leashOffset.y;
        }
        mTmpTransform.setScale(scale, scale);
        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+4 −4
Original line number Diff line number Diff line
@@ -960,7 +960,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        final SurfaceControl.Transaction boundsChangeTx =
                mSurfaceControlTransactionFactory.getTransaction();
        mSurfaceTransactionHelper
                .crop(boundsChangeTx, mLeash, destinationBounds)
                .cropAndPosition(boundsChangeTx, mLeash, destinationBounds)
                .round(boundsChangeTx, mLeash, true /* applyCornerRadius */);

        mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
@@ -988,7 +988,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        mSurfaceTransactionHelper
                .resetScale(tx, mLeash, destinationBounds)
                .crop(tx, mLeash, destinationBounds)
                .cropAndPosition(tx, mLeash, destinationBounds)
                .round(tx, mLeash, isInPip());
        // The animation is finished in the Launcher and here we directly apply the final touch.
        applyEnterPipSyncTransaction(destinationBounds, () -> {
@@ -1525,7 +1525,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mPipBoundsState.setBounds(toBounds);
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        mSurfaceTransactionHelper
                .crop(tx, mLeash, toBounds)
                .cropAndPosition(tx, mLeash, toBounds)
                .round(tx, mLeash, mPipTransitionState.isInPip());
        if (shouldSyncPipTransactionWithMenu()) {
            mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
@@ -1628,7 +1628,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            Rect destinationBounds) {
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        mSurfaceTransactionHelper
                .crop(tx, mLeash, destinationBounds)
                .cropAndPosition(tx, mLeash, destinationBounds)
                .resetScale(tx, mLeash, destinationBounds)
                .round(tx, mLeash, mPipTransitionState.isInPip());
        return tx;
+16 −8

File changed.

Preview size limit exceeded, changes collapsed.

Loading