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

Commit 7d0f3e98 authored by Merissa Mitchell's avatar Merissa Mitchell
Browse files

[PiP2 on Desktop] Restore task to freeform bounds when exiting PiP.

Recall: http://recall/clips/ae803031-fcb0-46a3-b56f-0536d019daf3

Implement exiting/expanding PiP to freeform windowing mode if Desktop
Mode session is active on PiP2.

Bug: 378742351
Test: atest PipSchedulerTest
Flag: com.android.window.flags.enable_desktop_windowing_pip
Change-Id: Iba1b639012fa08824934783ec62f9092670237fd
parent 705fe7f3
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;

import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -38,6 +39,7 @@ import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipMotionHelper;
@@ -128,8 +130,11 @@ public abstract class Pip2Module {
    static PipScheduler providePipScheduler(Context context,
            PipBoundsState pipBoundsState,
            @ShellMainThread ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState) {
        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState);
            PipTransitionState pipTransitionState,
            Optional<DesktopRepository> desktopRepositoryOptional,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
                desktopRepositoryOptional, rootTaskDisplayAreaOrganizer);
    }

    @WMSingleton
+57 −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.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
@@ -25,6 +26,7 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -33,13 +35,19 @@ import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

import java.util.Objects;
import java.util.Optional;

/**
 * Scheduler for Shell initiated PiP transitions and animations.
 */
@@ -50,6 +58,8 @@ public class PipScheduler {
    private final PipBoundsState mPipBoundsState;
    private final ShellExecutor mMainExecutor;
    private final PipTransitionState mPipTransitionState;
    private final Optional<DesktopRepository> mDesktopRepositoryOptional;
    private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
    private PipTransitionController mPipTransitionController;
    private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
            mSurfaceControlTransactionFactory;
@@ -61,11 +71,15 @@ public class PipScheduler {
    public PipScheduler(Context context,
            PipBoundsState pipBoundsState,
            ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState) {
            PipTransitionState pipTransitionState,
            Optional<DesktopRepository> desktopRepositoryOptional,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
        mContext = context;
        mPipBoundsState = pipBoundsState;
        mMainExecutor = mainExecutor;
        mPipTransitionState = pipTransitionState;
        mDesktopRepositoryOptional = desktopRepositoryOptional;
        mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;

        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
@@ -87,7 +101,7 @@ public class PipScheduler {
        wct.setBounds(pipTaskToken, null);
        // if we are hitting a multi-activity case
        // windowing mode change will reparent to original host task
        wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
        wct.setWindowingMode(pipTaskToken, getOutPipWindowingMode());
        return wct;
    }

@@ -241,6 +255,47 @@ public class PipScheduler {
        maybeUpdateMovementBounds();
    }

    /** Returns whether the display is in freeform windowing mode. */
    private boolean isDisplayInFreeform() {
        final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
                Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId);
        if (tdaInfo != null) {
            return tdaInfo.configuration.windowConfiguration.getWindowingMode()
                    == WINDOWING_MODE_FREEFORM;
        }
        return false;
    }

    /** Returns whether PiP is exiting while we're in desktop mode. */
    private boolean isPipExitingToDesktopMode() {
        return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent()
                && (mDesktopRepositoryOptional.get().getVisibleTaskCount(
                Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId) > 0
                || isDisplayInFreeform());
    }

    /**
     * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
     * and can be overridden to restore to an alternate windowing mode.
     */
    private int getOutPipWindowingMode() {
        // If we are exiting PiP while the device is in Desktop mode (the task should expand to
        // freeform windowing mode):
        // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will
        //    resolve the windowing mode to the display's windowing mode.
        // 2) If the display windowing mode is not freeform, set windowing mode to freeform.
        if (isPipExitingToDesktopMode()) {
            if (isDisplayInFreeform()) {
                return WINDOWING_MODE_UNDEFINED;
            } else {
                return WINDOWING_MODE_FREEFORM;
            }
        }

        // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
        return WINDOWING_MODE_UNDEFINED;
    }

    @VisibleForTesting
    void setSurfaceControlTransactionFactory(
            @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+8 −1
Original line number Diff line number Diff line
@@ -42,8 +42,10 @@ import android.window.WindowContainerTransaction;

import androidx.test.filters.SmallTest;

import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -56,6 +58,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Optional;

/**
 * Unit test against {@link PipScheduler}
 */
@@ -79,6 +83,8 @@ public class PipSchedulerTest {
    @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
    @Mock private SurfaceControl.Transaction mMockTransaction;
    @Mock private PipAlphaAnimator mMockAlphaAnimator;
    @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository;
    @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;

    @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
    @Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor;
@@ -96,7 +102,8 @@ public class PipSchedulerTest {
                .thenReturn(mMockTransaction);

        mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
                mMockPipTransitionState);
                mMockPipTransitionState, mMockOptionalDesktopRepository,
                mRootTaskDisplayAreaOrganizer);
        mPipScheduler.setPipTransitionController(mMockPipTransitionController);
        mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
        mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) ->