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

Commit dd7a7256 authored by Merissa Mitchell's avatar Merissa Mitchell Committed by Android (Google) Code Review
Browse files

Merge "[PiP on Desktop] Fix multi-activity PiP expand" into main

parents eee0ebec 57cce33e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import com.android.wm.shell.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
import java.util.Optional
@@ -93,4 +94,7 @@ class PipDesktopState(
    /** Returns whether there is a drag-to-desktop transition in progress. */
    fun isDragToDesktopInProgress(): Boolean =
        isDesktopWindowingPipEnabled() && dragToDesktopTransitionHandlerOptional.get().inProgress

    /** Returns the DisplayLayout associated with the display where PiP window is in. */
    fun getCurrentDisplayLayout(): DisplayLayout = pipDisplayLayoutState.displayLayout
}
+7 −3
Original line number Diff line number Diff line
@@ -153,10 +153,12 @@ public abstract class Pip2Module {
            @ShellMainThread ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<DesktopPipTransitionController> desktopPipTransitionController,
            PipDesktopState pipDesktopState,
            DisplayController displayController) {
        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
                splitScreenControllerOptional, pipDesktopState, displayController);
                splitScreenControllerOptional, desktopPipTransitionController, pipDesktopState,
                displayController);
    }

    @WMSingleton
@@ -259,14 +261,16 @@ public abstract class Pip2Module {
    @WMSingleton
    @Provides
    static Optional<DesktopPipTransitionController> provideDesktopPipTransitionController(
            Context context, Optional<DesktopTasksController> desktopTasksControllerOptional,
            Context context, ShellTaskOrganizer shellTaskOrganizer,
            Optional<DesktopTasksController> desktopTasksControllerOptional,
            Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
            PipDesktopState pipDesktopState
    ) {
        if (DesktopModeStatus.canEnterDesktopMode(context)
                && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
            return Optional.of(
                    new DesktopPipTransitionController(desktopTasksControllerOptional.get(),
                    new DesktopPipTransitionController(shellTaskOrganizer,
                            desktopTasksControllerOptional.get(),
                            desktopUserRepositoriesOptional.get(), pipDesktopState));
        }
        return Optional.empty();
+53 −3
Original line number Diff line number Diff line
@@ -17,21 +17,71 @@
package com.android.wm.shell.desktopmode

import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.graphics.Rect
import android.os.IBinder
import android.window.DesktopExperienceFlags
import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.pip.PipDesktopState
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE

/**
 * Controller to perform extra handling to PiP transitions that are entering while in Desktop mode.
 */
/** Controller to perform extra handling to PiP transitions while in Desktop mode. */
class DesktopPipTransitionController(
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val desktopTasksController: DesktopTasksController,
    private val desktopUserRepositories: DesktopUserRepositories,
    private val pipDesktopState: PipDesktopState,
) {
    /**
     * This is called by [PipScheduler#getExitPipViaExpandTransaction] before starting a PiP
     * transition. In the case of multi-activity PiP, we might need to update the parent task's
     * windowing mode and bounds based on whether we are in Desktop Windowing.
     *
     * @param wct WindowContainerTransaction that will apply these changes
     * @param parentTaskId id taken from TaskInfo#lastParentTaskIdBeforePip
     */
    fun maybeUpdateParentInWct(wct: WindowContainerTransaction, parentTaskId: Int) {
        if (!pipDesktopState.isDesktopWindowingPipEnabled()) {
            return
        }

        if (parentTaskId == ActivityTaskManager.INVALID_TASK_ID) {
            logD("maybeUpdateParentInWct: Task is not multi-activity PiP")
            return
        }

        val parentTask = shellTaskOrganizer.getRunningTaskInfo(parentTaskId)
        if (parentTask == null) {
            logW(
                "maybeUpdateParentInWct: Failed to find RunningTaskInfo for parentTaskId %d",
                parentTaskId,
            )
            return
        }

        val defaultFreeformBounds =
            if (parentTask.lastNonFullscreenBounds.isEmpty) {
                calculateDefaultDesktopTaskBounds(pipDesktopState.getCurrentDisplayLayout())
            } else {
                parentTask.lastNonFullscreenBounds
            }

        val newResolvedWinMode =
            if (pipDesktopState.isPipInDesktopMode()) WINDOWING_MODE_FREEFORM
            else WINDOWING_MODE_FULLSCREEN

        if (newResolvedWinMode != parentTask.windowingMode) {
            wct.setWindowingMode(parentTask.token, newResolvedWinMode)
            wct.setBounds(
                parentTask.token,
                if (newResolvedWinMode == WINDOWING_MODE_FREEFORM) defaultFreeformBounds else Rect()
            )
        }
    }

    /**
     * This is called by [PipTransition#handleRequest] when a request for entering PiP is received.
+10 −2
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -72,6 +73,7 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
    private final PipTransitionState mPipTransitionState;
    private final DisplayController mDisplayController;
    private final PipDesktopState mPipDesktopState;
    private final Optional<DesktopPipTransitionController> mDesktopPipTransitionController;
    private final Optional<SplitScreenController> mSplitScreenControllerOptional;
    private PipTransitionController mPipTransitionController;
    private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -89,6 +91,7 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
            ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState,
            Optional<SplitScreenController> splitScreenControllerOptional,
            Optional<DesktopPipTransitionController> desktopPipTransitionController,
            PipDesktopState pipDesktopState,
            DisplayController displayController) {
        mContext = context;
@@ -97,6 +100,7 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this);
        mPipDesktopState = pipDesktopState;
        mDesktopPipTransitionController = desktopPipTransitionController;
        mSplitScreenControllerOptional = splitScreenControllerOptional;
        mDisplayController = displayController;
        mSurfaceControlTransactionFactory =
@@ -118,9 +122,13 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
        WindowContainerTransaction wct = new WindowContainerTransaction();
        // final expanded bounds to be inherited from the parent
        wct.setBounds(pipTaskToken, null);
        // if we are hitting a multi-activity case
        // windowing mode change will reparent to original host task
        wct.setWindowingMode(pipTaskToken, mPipDesktopState.getOutPipWindowingMode());

        // In multi-activity case, windowing mode change will reparent to original host task, so we
        // have to update the parent windowing mode to what is expected.
        mDesktopPipTransitionController.ifPresent(c -> c.maybeUpdateParentInWct(wct,
                mPipTransitionState.getPipTaskInfo().lastParentTaskIdBeforePip));

        return wct;
    }

+55 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

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

import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Surface.ROTATION_0;

import static com.android.wm.shell.pip2.phone.transition.PipTransitionUtils.getChangeByToken;
@@ -191,13 +193,23 @@ public class PipExpandHandler implements Transitions.TransitionHandler {
        PipExpandAnimator animator = mPipExpandAnimatorSupplier.get(mContext, pipLeash,
                startTransaction, finishTransaction, endBounds, startBounds, endBounds,
                sourceRectHint, delta, mPipDesktopState.isPipInDesktopMode());
        animator.setAnimationStartCallback(() -> mPipInteractionHandler.begin(pipLeash,
                PipInteractionHandler.INTERACTION_EXIT_PIP));
        animator.setAnimationStartCallback(() -> {
            mPipInteractionHandler.begin(pipLeash, PipInteractionHandler.INTERACTION_EXIT_PIP);

            if (parentBeforePip != null) {
                setupMultiActivityExpandAnimation(info, startTransaction, pipLeash,
                        parentBeforePip);
            }
        });

        final TransitionInfo.Change finalPipChange = pipChange;
        animator.setAnimationEndCallback(() -> {
            if (parentBeforePip != null) {
                // TODO b/377362511: Animate local leash instead to also handle letterbox case.
                // For multi-activity, set the crop to be null
                finishTransaction.setCrop(pipLeash, null);
                setupMultiActivityAnimationFinalState(finishTransaction, finalPipChange, pipLeash,
                        parentBeforePip);
            }
            finishTransition();
            mPipInteractionHandler.end();
@@ -207,6 +219,47 @@ public class PipExpandHandler implements Transitions.TransitionHandler {
        return true;
    }

    private void setupMultiActivityExpandAnimation(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl pipLeash,
            @NonNull TransitionInfo.Change parentBeforePip) {
        if (!mPipDesktopState.isDesktopWindowingPipEnabled()) {
            return;
        }

        final int rootIndex = info.findRootIndex(mPipDisplayLayoutState.getDisplayId());
        final int parentWindowingMode = parentBeforePip.getTaskInfo().getWindowingMode();
        if (rootIndex != -1 && parentWindowingMode == WINDOWING_MODE_FREEFORM) {
            // Reparent PiP activity to the root leash if it's animating to freeform so that it is
            // not cropped by the parent task.
            SurfaceControl rootLeash = info.getRoot(rootIndex).getLeash();
            startTransaction.reparent(pipLeash, rootLeash);
            startTransaction.setAlpha(parentBeforePip.getLeash(), 0);
        } else if (parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
            // Don't animate the parent task; show it immediately when the PiP animation finishes
            parentBeforePip.setStartAbsBounds(parentBeforePip.getEndAbsBounds());
            startTransaction.setPosition(parentBeforePip.getLeash(),
                    parentBeforePip.getStartAbsBounds().left,
                    parentBeforePip.getStartAbsBounds().top);
            startTransaction.setCrop(parentBeforePip.getLeash(), parentBeforePip.getEndAbsBounds());
        }
    }

    private void setupMultiActivityAnimationFinalState(
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull TransitionInfo.Change pipChange, @NonNull SurfaceControl pipLeash,
            @NonNull TransitionInfo.Change parentBeforePip) {
        if (!mPipDesktopState.isDesktopWindowingPipEnabled()
                || parentBeforePip.getTaskInfo().getWindowingMode() != WINDOWING_MODE_FREEFORM) {
            return;
        }

        // Reparent the PiP activity to the parent task and reset its position
        finishTransaction.reparent(pipLeash, parentBeforePip.getLeash());
        finishTransaction.setPosition(pipLeash, pipChange.getEndRelOffset().x,
                pipChange.getEndRelOffset().y);
        finishTransaction.setAlpha(parentBeforePip.getLeash(), 1);
    }

    private boolean startExpandToSplitAnimation(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
Loading