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

Commit 76def33f authored by jorgegil@google.com's avatar jorgegil@google.com
Browse files

Request enter PIP on WCT#setWindowingMode(PINNED)

Check that the task is able to enter PIP mode and let the app
decide whether they want to enter instead of forcing any
task into PIP mode when the windowing mode is set through a
WindowContainerTransaction.

Bug: 213891603
Test: atest WindowContainerTests; atest WindowOrganizerTests;
atest ActivityTaskManagerServiceTests; manually, set PINNED win
mode through a wct, verify the app is requested to enter PIP
before actually putting the task into PIP mode.

Change-Id: I703b172fdca01124e92cd3cbce7765166b5b4f01
parent 0291c328
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -854,22 +854,22 @@ class ActivityClientController extends IActivityClientController.Stub {
    /**
     * Requests that an activity should enter picture-in-picture mode if possible. This method may
     * be used by the implementation of non-phone form factors.
     *
     * @return false if the activity cannot enter PIP mode.
     */
    void requestPictureInPictureMode(@NonNull ActivityRecord r) {
    boolean requestPictureInPictureMode(@NonNull ActivityRecord r) {
        if (r.inPinnedWindowingMode()) {
            throw new IllegalStateException("Activity is already in PIP mode");
            return false;
        }

        final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
                "requestPictureInPictureMode", /* beforeStopping */ false);
        if (!canEnterPictureInPicture) {
            throw new IllegalStateException(
                    "Requested PIP on an activity that doesn't support it");
            return false;
        }

        if (r.pictureInPictureArgs.isAutoEnterEnabled()) {
            mService.enterPictureInPictureMode(r, r.pictureInPictureArgs);
            return;
            return mService.enterPictureInPictureMode(r, r.pictureInPictureArgs);
        }

        try {
@@ -877,9 +877,11 @@ class ActivityClientController extends IActivityClientController.Stub {
                    r.app.getThread(), r.token);
            transaction.addCallback(EnterPipRequestedItem.obtain());
            mService.getLifecycleManager().scheduleTransaction(transaction);
            return true;
        } catch (Exception e) {
            Slog.w(TAG, "Failed to send enter pip requested item: "
                    + r.intent.getComponent(), e);
            return false;
        }
    }

+29 −0
Original line number Diff line number Diff line
@@ -544,6 +544,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
                        + " windowing mode during locked task mode.");
            }

            if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) {
                // Do not directly put the container into PINNED mode as it may not support it or
                // the app may not want to enter it. Instead, send a signal to request PIP
                // mode to the app if they wish to support it below in #applyTaskChanges.
                return effects;
            }

            final int prevMode = container.getWindowingMode();
            container.setWindowingMode(windowingMode);
            if (prevMode != container.getWindowingMode()) {
@@ -580,6 +587,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
        }

        if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED
                && !tr.inPinnedWindowingMode()) {
            final ActivityRecord activity = tr.getTopNonFinishingActivity();
            if (activity != null) {
                final boolean lastSupportsEnterPipOnTaskSwitch =
                        activity.supportsEnterPipOnTaskSwitch;
                // Temporarily force enable enter PIP on task switch so that PIP is requested
                // regardless of whether the activity is resumed or paused.
                activity.supportsEnterPipOnTaskSwitch = true;
                boolean canEnterPip = activity.checkEnterPictureInPictureState(
                        "applyTaskChanges", true /* beforeStopping */);
                if (canEnterPip) {
                    canEnterPip = mService.mActivityClientController
                            .requestPictureInPictureMode(activity);
                }
                if (!canEnterPip) {
                    // Restore the flag to its previous state when the activity cannot enter PIP.
                    activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch;
                }
            }
        }

        return effects;
    }

+17 −9
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -42,8 +41,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

import android.annotation.Nullable;
@@ -60,7 +59,6 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
@@ -136,7 +134,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
        assertNull(transaction.getLifecycleStateRequest());
    }

    @Test(expected = IllegalStateException.class)
    @Test
    public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
        final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
@@ -146,11 +144,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {

        mAtm.mActivityClientController.requestPictureInPictureMode(activity);

        // Check enter no transactions with enter pip requests are made.
        verify(lifecycleManager, times(0)).scheduleTransaction(any());
        verify(lifecycleManager, atLeast(0))
                .scheduleTransaction(mClientTransactionCaptor.capture());
        final ClientTransaction transaction = mClientTransactionCaptor.getValue();
        // Check that none are enter pip request items.
        transaction.getCallbacks().forEach(clientTransactionItem -> {
            assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
        });
    }

    @Test(expected = IllegalStateException.class)
    @Test
    public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
        final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
@@ -159,8 +162,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {

        mAtm.mActivityClientController.requestPictureInPictureMode(activity);

        // Check that no transactions with enter pip requests are made.
        verify(lifecycleManager, times(0)).scheduleTransaction(any());
        verify(lifecycleManager, atLeast(0))
                .scheduleTransaction(mClientTransactionCaptor.capture());
        final ClientTransaction transaction = mClientTransactionCaptor.getValue();
        // Check that none are enter pip request items.
        transaction.getCallbacks().forEach(clientTransactionItem -> {
            assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
        });
    }

    @Test
+11 −2
Original line number Diff line number Diff line
@@ -515,7 +515,10 @@ public class WindowOrganizerTests extends WindowTestsBase {
        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);

        assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
        assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode());
        // Get the root task from the PIP activity record again, since the PIP root task may have
        // changed when the activity entered PIP mode.
        final Task pipRootTask = record.getRootTask();
        assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode());
    }

    @Test
@@ -1008,6 +1011,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
        final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent,
                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
        record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
        record.setPictureInPictureParams(new PictureInPictureParams.Builder()
                .setAutoEnterEnabled(true).build());
        spyOn(record);
        doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());

@@ -1492,7 +1497,11 @@ public class WindowOrganizerTests extends WindowTestsBase {
        verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();

        clearInvocations(mWm.mAtmService.mRootWindowContainer);
        t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN);
        // The token for the PIP root task may have changed when the task entered PIP mode, so do
        // not reuse the one from above.
        final WindowContainerToken newToken =
                record.getRootTask().mRemoteToken.toWindowContainerToken();
        t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
        verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
    }