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

Commit f18859a6 authored by Ikram Gabiyev's avatar Ikram Gabiyev Committed by Android (Google) Code Review
Browse files

Merge "[PiP2] Handle enter PiP and display change in sync" into main

parents b1b30463 ab0b707c
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipInteractionHandler;
@@ -133,6 +134,7 @@ public abstract class Pip2Module {
            PipAppOpsListener pipAppOpsListener,
            PhonePipMenuController pipMenuController,
            PipUiEventLogger pipUiEventLogger,
            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
            @ShellMainThread ShellExecutor mainExecutor) {
        if (!PipUtils.isPip2ExperimentEnabled()) {
            return Optional.empty();
@@ -142,7 +144,7 @@ public abstract class Pip2Module {
                    displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                    pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                    pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
                    pipUiEventLogger, mainExecutor));
                    pipUiEventLogger, pipSurfaceTransactionHelper, mainExecutor));
        }
    }

@@ -288,4 +290,10 @@ public abstract class Pip2Module {
        return new PipInteractionHandler(context, mainHandler,
                InteractionJankMonitor.getInstance());
    }

    @WMSingleton
    @Provides
    static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
        return new PipSurfaceTransactionHelper(context);
    }
}
+67 −7
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
import android.util.Log;
import android.view.SurfaceControl;
import android.window.DesktopExperienceFlags;
@@ -66,6 +65,7 @@ import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -87,6 +87,8 @@ public class PipController implements ConfigurationChangeListener,
    private static final String TAG = PipController.class.getSimpleName();
    private static final String SWIPE_TO_PIP_APP_BOUNDS = "pip_app_bounds";
    private static final String SWIPE_TO_PIP_OVERLAY = "swipe_to_pip_overlay";
    private static final String DISPLAY_CHANGE_PIP_BOUNDS_UPDATE =
            "display_change_pip_bounds_update";

    private final Context mContext;
    private final ShellCommandHandler mShellCommandHandler;
@@ -111,6 +113,10 @@ public class PipController implements ConfigurationChangeListener,
    // Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
    @Nullable private PipAnimationListener mPipRecentsAnimationListener;

    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;

    private boolean mWaitingToPlayDisplayChangeBoundsUpdate;

    @VisibleForTesting
    interface PipAnimationListener {
        /**
@@ -150,6 +156,7 @@ public class PipController implements ConfigurationChangeListener,
            PipAppOpsListener pipAppOpsListener,
            PhonePipMenuController pipMenuController,
            PipUiEventLogger pipUiEventLogger,
            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
            ShellExecutor mainExecutor) {
        mContext = context;
        mShellCommandHandler = shellCommandHandler;
@@ -168,6 +175,7 @@ public class PipController implements ConfigurationChangeListener,
        mPipAppOpsListener = pipAppOpsListener;
        mPipMenuController = pipMenuController;
        mPipUiEventLogger = pipUiEventLogger;
        mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
        mMainExecutor = mainExecutor;
        mImpl = new PipImpl();

@@ -196,6 +204,7 @@ public class PipController implements ConfigurationChangeListener,
            PipAppOpsListener pipAppOpsListener,
            PhonePipMenuController pipMenuController,
            PipUiEventLogger pipUiEventLogger,
            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
            ShellExecutor mainExecutor) {
        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -206,7 +215,7 @@ public class PipController implements ConfigurationChangeListener,
                displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
                pipUiEventLogger, mainExecutor);
                pipUiEventLogger, pipSurfaceTransactionHelper, mainExecutor);
    }

    public PipImpl getPipImpl() {
@@ -342,8 +351,7 @@ public class PipController implements ConfigurationChangeListener,
            mPipDisplayLayoutState.rotateTo(toRotation);
        }

        if (!mPipTransitionState.isInPip()
                && mPipTransitionState.getState() != PipTransitionState.ENTERING_PIP) {
        if (!shouldUpdatePipStateOnDisplayChange()) {
            // Skip the PiP-relevant updates if we aren't in a valid PiP state.
            if (mPipTransitionState.isInFixedRotation()) {
                ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -372,9 +380,13 @@ public class PipController implements ConfigurationChangeListener,
            mPipBoundsState.setBounds(toBounds);
        }
        if (mPipTransitionState.getPipTaskToken() == null) {
            Log.wtf(TAG, "PipController.onDisplayChange no PiP task token"
                    + " state=" + mPipTransitionState.getState()
                    + " callers=\n" + Debug.getCallers(4, "    "));
            Log.d(TAG, "PipController.onDisplayChange no PiP task token"
                    + " state=" + mPipTransitionState.getState());
            mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
                final Bundle extra = new Bundle();
                extra.putBoolean(DISPLAY_CHANGE_PIP_BOUNDS_UPDATE, true);
                mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
            });
        } else {
            t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
        }
@@ -386,6 +398,14 @@ public class PipController implements ConfigurationChangeListener,
        mPipDisplayLayoutState.setDisplayLayout(layout);
    }

    private boolean shouldUpdatePipStateOnDisplayChange() {
        // We should at least update internal PiP state, such as PiP bounds state or movement bounds
        // if we are either in PiP or about to enter PiP.
        return mPipTransitionState.isInPip()
                || mPipTransitionState.getState() == PipTransitionState.ENTERING_PIP
                || mPipTransitionState.getState() == PipTransitionState.SCHEDULED_ENTER_PIP;
    }

    //
    // IPip Binder stub helpers
    //
@@ -510,7 +530,47 @@ public class PipController implements ConfigurationChangeListener,
                    listener.accept(false /* inPip */);
                }
                break;
            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
                mWaitingToPlayDisplayChangeBoundsUpdate =
                        extra.getBoolean(DISPLAY_CHANGE_PIP_BOUNDS_UPDATE);
                if (mWaitingToPlayDisplayChangeBoundsUpdate) {
                    // If we reach this point, it means display change did not send through a WCT to
                    // update the pinned task bounds in Core. Instead, the local Shell-side
                    // PiP-relevant bounds state and movement bounds were updated.
                    // So schedule a jumpcut animation to those bounds now.
                    mPipScheduler.scheduleAnimateResizePip(mPipBoundsState.getBounds());
                }
                break;
            case PipTransitionState.CHANGING_PIP_BOUNDS:
                if (!mWaitingToPlayDisplayChangeBoundsUpdate) {
                    break;
                }
                mWaitingToPlayDisplayChangeBoundsUpdate = false;
                final SurfaceControl.Transaction startTx = extra.getParcelable(
                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
                final SurfaceControl.Transaction finishTx = extra.getParcelable(
                        PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
                final Rect destinationBounds = extra.getParcelable(
                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
                handleJumpcutBoundsUpdate(startTx, finishTx, destinationBounds);
                break;
        }
    }

    private void handleJumpcutBoundsUpdate(SurfaceControl.Transaction startTx,
            SurfaceControl.Transaction finishTx, Rect destinationBounds) {
        SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();

        startTx.merge(finishTx);
        startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
        mPipSurfaceTransactionHelper.round(startTx, pipLeash, true /* applyCornerRadius */)
                .shadow(startTx, pipLeash, true /* applyShadowRadius */);
        mPipSurfaceTransactionHelper.round(finishTx, pipLeash, true /* applyCornerRadius */)
                .shadow(finishTx, pipLeash, true /* applyShadowRadius */);
        startTx.apply();

        // Signal that the transition is done - should update transition state by default.
        mPipScheduler.scheduleFinishPipBoundsChange(destinationBounds);
    }

    //
+18 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.pip2.phone;

import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Surface.ROTATION_0;
@@ -236,6 +237,7 @@ public class PipTransition extends PipTransitionController implements
            @NonNull TransitionRequestInfo request) {
        if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
            mEnterTransition = transition;
            mPipTransitionState.setState(PipTransitionState.SCHEDULED_ENTER_PIP);
            final WindowContainerTransaction wct = getEnterPipTransaction(transition,
                    request.getPipChange());

@@ -259,6 +261,7 @@ public class PipTransition extends PipTransitionController implements
            outWct.merge(getEnterPipTransaction(transition, request.getPipChange()),
                    true /* transfer */);
            mEnterTransition = transition;
            mPipTransitionState.setState(PipTransitionState.SCHEDULED_ENTER_PIP);
        }
    }

@@ -645,10 +648,22 @@ public class PipTransition extends PipTransitionController implements
        }
        mFinishCallback = finishCallback;

        Rect destinationBounds = pipChange.getEndAbsBounds();
        SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
        final Rect destinationBounds = pipChange.getEndAbsBounds();
        if (pipChange.getEndRotation() != ROTATION_UNDEFINED
                && pipChange.getStartRotation() != pipChange.getEndRotation()) {
            // If we are playing an enter PiP animation with display change collected together
            // in the same transition, then PipController#onDisplayChange() must have already
            // updated the PiP bounds state to reflect the final desired destination bounds.
            // This might not be in the WM state yet as PiP task token might have been null then.
            // WM state will be updated via a follow-up bounds change transition after.
            destinationBounds.set(mPipBoundsState.getBounds());
        }

        final SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
        Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");

        // Note that fixed rotation is different from the same transition display change rotation;
        // with fixed rotation, we expect a follow-up async rotation transition after this one.
        final int delta = getFixedRotationDelta(info, pipChange, mPipDisplayLayoutState);
        if (delta != ROTATION_0) {
            updatePipChangesForFixedRotation(info, pipChange,
@@ -675,6 +690,7 @@ public class PipTransition extends PipTransitionController implements
            finishTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
        } else {
            startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
            finishTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
        }

        PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+12 −7
Original line number Diff line number Diff line
@@ -76,27 +76,30 @@ public class PipTransitionState {
    // State for Launcher animating the swipe PiP to home animation.
    public static final int SWIPING_TO_PIP = 1;

    // State for scheduling enter PiP transition; could be after SWIPING_TO_PIP
    public static final int SCHEDULED_ENTER_PIP = 2;

    // State for Shell animating enter PiP or jump-cutting to PiP mode after Launcher animation.
    public static final int ENTERING_PIP = 2;
    public static final int ENTERING_PIP = 3;

    // State for app finishing drawing in PiP mode as a final step in enter PiP flow.
    public static final int ENTERED_PIP = 3;
    public static final int ENTERED_PIP = 4;

    // State to indicate we have scheduled a PiP bounds change transition.
    public static final int SCHEDULED_BOUNDS_CHANGE = 4;
    public static final int SCHEDULED_BOUNDS_CHANGE = 5;

    // State for the start of playing a transition to change PiP bounds. At this point, WM Core
    // is aware of the new PiP bounds, but Shell might still be continuing animating.
    public static final int CHANGING_PIP_BOUNDS = 5;
    public static final int CHANGING_PIP_BOUNDS = 6;

    // State for finishing animating into new PiP bounds after resize is complete.
    public static final int CHANGED_PIP_BOUNDS = 6;
    public static final int CHANGED_PIP_BOUNDS = 7;

    // State for starting exiting PiP.
    public static final int EXITING_PIP = 7;
    public static final int EXITING_PIP = 8;

    // State for finishing exit PiP flow.
    public static final int EXITED_PIP = 8;
    public static final int EXITED_PIP = 9;

    private static final int FIRST_CUSTOM_STATE = 1000;

@@ -105,6 +108,7 @@ public class PipTransitionState {
    @IntDef(prefix = { "TRANSITION_STATE_" }, value =  {
            UNDEFINED,
            SWIPING_TO_PIP,
            SCHEDULED_ENTER_PIP,
            ENTERING_PIP,
            ENTERED_PIP,
            SCHEDULED_BOUNDS_CHANGE,
@@ -421,6 +425,7 @@ public class PipTransitionState {
        switch (state) {
            case UNDEFINED: return "undefined";
            case SWIPING_TO_PIP: return "swiping_to_pip";
            case SCHEDULED_ENTER_PIP: return "scheduled_enter_pip";
            case ENTERING_PIP: return "entering-pip";
            case ENTERED_PIP: return "entered-pip";
            case SCHEDULED_BOUNDS_CHANGE: return "scheduled_bounds_change";