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

Commit 644ed4e9 authored by Vania Desmonda's avatar Vania Desmonda Committed by Android (Google) Code Review
Browse files

Merge "Fix split screen exit bug on a freeform task." into main

parents dba6fffa 6019b99d
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;

import static com.android.window.flags.Flags.enableFullScreenWindowOnRemovingSplitScreenStageBugfix;
import static com.android.window.flags.Flags.enableNonDefaultDisplaySplit;
import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.Flags.enableFlexibleTwoAppSplit;
@@ -908,6 +909,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }
        options = options != null ? options : new Bundle();
        addActivityOptions(options, null);
        ActivityManager.RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(taskId);
        if (enableFullScreenWindowOnRemovingSplitScreenStageBugfix() && taskInfo != null
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
            prepareTasksForSplitScreen(new int[] {taskId}, wct);
        }
        wct.startTask(taskId, options);
        mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
    }
+97 −67
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -61,6 +62,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
@@ -71,6 +75,8 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.internal.logging.InstanceId;
import com.android.window.flags.Flags;
import com.android.wm.shell.MockToken;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -96,6 +102,7 @@ import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.Transitions;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -111,6 +118,9 @@ import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StageCoordinatorTests extends ShellTestCase {
    @Rule
    public final SetFlagsRule setFlagsRule = new SetFlagsRule();

    @Mock
    private ShellTaskOrganizer mTaskOrganizer;
    @Mock
@@ -141,12 +151,15 @@ public class StageCoordinatorTests extends ShellTestCase {
    private final Rect mBounds1 = new Rect(10, 20, 30, 40);
    private final Rect mBounds2 = new Rect(5, 10, 15, 20);
    private final Rect mRootBounds = new Rect(0, 0, 45, 60);
    private final int mTaskId = 18;

    private SurfaceControl mRootLeash;
    private SurfaceControl mDividerLeash;
    private ActivityManager.RunningTaskInfo mRootTask;
    private StageCoordinator mStageCoordinator;
    private Transitions mTransitions;
    private SplitScreenTransitions mSplitScreenTransitions;
    private SplitScreenListener mSplitScreenListener;
    private IBinder mBinder;
    private ActivityManager.RunningTaskInfo mRunningTaskInfo;
    private RemoteTransition mRemoteTransition;
    private final TestShellExecutor mMainExecutor = new TestShellExecutor();
    private final ShellExecutor mAnimExecutor = new TestShellExecutor();
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@@ -154,19 +167,35 @@ public class StageCoordinatorTests extends ShellTestCase {
            DEFAULT_DISPLAY, 0);
    private final ActivityManager.RunningTaskInfo mMainChildTaskInfo =
            new TestRunningTaskInfoBuilder().setVisible(true).build();
    private final ArgumentCaptor<WindowContainerTransaction> mWctCaptor =
            ArgumentCaptor.forClass(WindowContainerTransaction.class);
    private final WindowContainerTransaction mWct = spy(new WindowContainerTransaction());

    @Before
    @UiThreadTest
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mTransitions = createTestTransitions();
        Transitions transitions = createTestTransitions();
        WindowContainerToken token = mock(WindowContainerToken.class);
        SurfaceControl dividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();

        mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
                mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
                mDisplayInsetsController, mSplitLayout, transitions, mTransactionPool,
                mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
                Optional.empty(), mSplitState, Optional.empty(), mRootTDAOrganizer));

        mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
        mSplitScreenTransitions = spy(mStageCoordinator.getSplitTransitions());
        mSplitScreenListener = mock(SplitScreenListener.class);
        mStageCoordinator.setSplitTransitions(mSplitScreenTransitions);
        mBinder = mock(IBinder.class);
        mRunningTaskInfo = mock(ActivityManager.RunningTaskInfo.class);
        mRemoteTransition = mock(RemoteTransition.class);
        mRunningTaskInfo.token = token;

        when(mRemoteTransition.getDebugName()).thenReturn("");
        when(token.asBinder()).thenReturn(mBinder);
        when(mRunningTaskInfo.getToken()).thenReturn(token);
        when(mTaskOrganizer.getRunningTaskInfo(mTaskId)).thenReturn(mRunningTaskInfo);
        when(mRootTDAOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(mDisplayAreaInfo);

        when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
@@ -174,11 +203,11 @@ public class StageCoordinatorTests extends ShellTestCase {
        when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
        when(mSplitLayout.isLeftRightSplit()).thenReturn(false);
        when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true);
        when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash);
        when(mSplitLayout.getDividerLeash()).thenReturn(dividerLeash);

        mRootTask = new TestRunningTaskInfoBuilder().build();
        mRootLeash = new SurfaceControl.Builder().setName("test").build();
        mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash);
        SurfaceControl rootLeash = new SurfaceControl.Builder().setName("test").build();
        mStageCoordinator.onTaskAppeared(mRootTask, rootLeash);

        mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
        mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
@@ -196,14 +225,12 @@ public class StageCoordinatorTests extends ShellTestCase {
    public void testMoveToStage_splitActiveBackground() {
        when(mStageCoordinator.isSplitActive()).thenReturn(true);

        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
        final WindowContainerTransaction wct = spy(new WindowContainerTransaction());
        mStageCoordinator.moveToStage(mRootTask, SPLIT_POSITION_BOTTOM_OR_RIGHT, mWct);

        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
        // TODO(b/349828130) Address this once we remove index_undefined called
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(mWct), eq(mRootTask),
                eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
        verify(mMainStage).reparentTopTask(eq(wct));
        verify(mMainStage).reparentTopTask(eq(mWct));
        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
    }
@@ -215,25 +242,21 @@ public class StageCoordinatorTests extends ShellTestCase {
        // Assume current side stage is top or left.
        mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);

        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mStageCoordinator.moveToStage(mRootTask, SPLIT_POSITION_BOTTOM_OR_RIGHT, mWct);

        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
        // TODO(b/349828130) Address this once we remove index_undefined called
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(mWct), eq(mRootTask),
                eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
        assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition());
    }

    @Test
    public void testMoveToStage_splitInctive() {
        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
    public void testMoveToStage_splitInactive() {
        mStageCoordinator.moveToStage(mRootTask, SPLIT_POSITION_BOTTOM_OR_RIGHT, mWct);

        mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
        // TODO(b/349828130) Address this once we remove index_undefined called
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task),
        verify(mStageCoordinator).prepareEnterSplitScreen(eq(mWct), eq(mRootTask),
                eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED));
        assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
    }
@@ -248,25 +271,23 @@ public class StageCoordinatorTests extends ShellTestCase {
    @Test
    public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
        mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
        final SplitScreenListener listener = mock(SplitScreenListener.class);
        mStageCoordinator.registerSplitScreenListener(listener);
        clearInvocations(listener);
        mStageCoordinator.registerSplitScreenListener(mSplitScreenListener);
        clearInvocations(mSplitScreenListener);

        mStageCoordinator.onLayoutSizeChanged(mSplitLayout);

        verify(listener).onSplitBoundsChanged(mRootBounds, mBounds2, mBounds1);
        verify(mSplitScreenListener).onSplitBoundsChanged(mRootBounds, mBounds2, mBounds1);
    }

    @Test
    public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
        mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
        final SplitScreenListener listener = mock(SplitScreenListener.class);
        mStageCoordinator.registerSplitScreenListener(listener);
        clearInvocations(listener);
        mStageCoordinator.registerSplitScreenListener(mSplitScreenListener);
        clearInvocations(mSplitScreenListener);

        mStageCoordinator.onLayoutSizeChanged(mSplitLayout);

        verify(listener).onSplitBoundsChanged(mRootBounds, mBounds1, mBounds2);
        verify(mSplitScreenListener).onSplitBoundsChanged(mRootBounds, mBounds1, mBounds2);
    }

    @Test
@@ -367,13 +388,8 @@ public class StageCoordinatorTests extends ShellTestCase {
    @Test
    public void testSplitIntentAndTaskWithPippedApp_launchFullscreen() {
        int taskId = 9;
        SplitScreenTransitions splitScreenTransitions =
                spy(mStageCoordinator.getSplitTransitions());
        mStageCoordinator.setSplitTransitions(splitScreenTransitions);
        mStageCoordinator.setMixedHandler(mDefaultMixedHandler);
        PendingIntent pendingIntent = mock(PendingIntent.class);
        RemoteTransition remoteTransition = mock(RemoteTransition.class);
        when(remoteTransition.getDebugName()).thenReturn("");
        // Test launching second task full screen
        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true);
        mStageCoordinator.startIntentAndTask(
@@ -384,9 +400,9 @@ public class StageCoordinatorTests extends ShellTestCase {
                null /*option2*/,
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                mRemoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(1))
        verify(mSplitScreenTransitions, times(1))
                .startFullscreenTransition(any(), any());

        // Test launching first intent fullscreen
@@ -400,22 +416,17 @@ public class StageCoordinatorTests extends ShellTestCase {
                null /*option2*/,
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                mRemoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(2))
        verify(mSplitScreenTransitions, times(2))
                .startFullscreenTransition(any(), any());
    }

    @Test
    public void testSplitIntentsWithPippedApp_launchFullscreen() {
        SplitScreenTransitions splitScreenTransitions =
                spy(mStageCoordinator.getSplitTransitions());
        mStageCoordinator.setSplitTransitions(splitScreenTransitions);
        mStageCoordinator.setMixedHandler(mDefaultMixedHandler);
        PendingIntent pendingIntent = mock(PendingIntent.class);
        PendingIntent pendingIntent2 = mock(PendingIntent.class);
        RemoteTransition remoteTransition = mock(RemoteTransition.class);
        when(remoteTransition.getDebugName()).thenReturn("");
        // Test launching second task full screen
        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(true);
        mStageCoordinator.startIntents(
@@ -429,9 +440,9 @@ public class StageCoordinatorTests extends ShellTestCase {
                new Bundle(),
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                mRemoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(1))
        verify(mSplitScreenTransitions, times(1))
                .startFullscreenTransition(any(), any());

        // Test launching first intent fullscreen
@@ -448,35 +459,54 @@ public class StageCoordinatorTests extends ShellTestCase {
                new Bundle(),
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                mRemoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(2))
        verify(mSplitScreenTransitions, times(2))
                .startFullscreenTransition(any(), any());
    }


    @Test
    public void startTask_ensureWindowingModeCleared() {
        SplitScreenTransitions splitScreenTransitions =
                spy(mStageCoordinator.getSplitTransitions());
        mStageCoordinator.setSplitTransitions(splitScreenTransitions);
        ArgumentCaptor<WindowContainerTransaction> wctCaptor =
                ArgumentCaptor.forClass(WindowContainerTransaction.class);
        int taskId = 18;
        IBinder binder = mock(IBinder.class);
        ActivityManager.RunningTaskInfo rti = mock(ActivityManager.RunningTaskInfo.class);
        WindowContainerToken mockToken = mock(WindowContainerToken.class);
        when(mockToken.asBinder()).thenReturn(binder);
        when(rti.getToken()).thenReturn(mockToken);
        when(mTaskOrganizer.getRunningTaskInfo(taskId)).thenReturn(rti);
        mStageCoordinator.startTask(taskId, SPLIT_POSITION_TOP_OR_LEFT, null /*options*/,
        mStageCoordinator.startTask(mTaskId, SPLIT_POSITION_TOP_OR_LEFT, null /*options*/,
                null, SPLIT_INDEX_UNDEFINED);
        verify(splitScreenTransitions).startEnterTransition(anyInt(),
                wctCaptor.capture(), any(), any(), anyInt(), anyBoolean());
        verify(mSplitScreenTransitions).startEnterTransition(anyInt(),
                mWctCaptor.capture(), any(), any(), anyInt(), anyBoolean());

        int windowingMode = mWctCaptor.getValue().getChanges().get(mBinder).getWindowingMode();
        assertEquals(windowingMode, WINDOWING_MODE_UNDEFINED);
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_FULL_SCREEN_WINDOW_ON_REMOVING_SPLIT_SCREEN_STAGE_BUGFIX)
    public void startTasksOnSingleFreeformWindow_ensureWindowingModeClearedAndLaunchFullScreen() {
        mDisplayAreaInfo.configuration.windowConfiguration.setWindowingMode(
                WINDOWING_MODE_FREEFORM);
        when(mRunningTaskInfo.getWindowingMode()).thenReturn(WINDOWING_MODE_FREEFORM);

        mStageCoordinator.startTasks(mTaskId, null, INVALID_TASK_ID, null,
                SPLIT_POSITION_TOP_OR_LEFT, SNAP_TO_2_50_50, mRemoteTransition,
                InstanceId.fakeInstanceId(0));

        int windowingMode = wctCaptor.getValue().getChanges().get(binder).getWindowingMode();
        verify(mSplitScreenTransitions).startFullscreenTransition(mWctCaptor.capture(), any());
        int windowingMode = mWctCaptor.getValue().getChanges().get(mBinder).getWindowingMode();
        assertEquals(windowingMode, WINDOWING_MODE_UNDEFINED);
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_FULL_SCREEN_WINDOW_ON_REMOVING_SPLIT_SCREEN_STAGE_BUGFIX)
    public void startTasksOnSingleFreeformWindow_flagDisabled_noChangeToWindowingModeInWct() {
        mDisplayAreaInfo.configuration.windowConfiguration.setWindowingMode(
                WINDOWING_MODE_FREEFORM);
        when(mRunningTaskInfo.getWindowingMode()).thenReturn(WINDOWING_MODE_FREEFORM);

        mStageCoordinator.startTasks(mTaskId, null, INVALID_TASK_ID, null,
                SPLIT_POSITION_TOP_OR_LEFT, SNAP_TO_2_50_50, mRemoteTransition,
                InstanceId.fakeInstanceId(0));

        verify(mSplitScreenTransitions).startFullscreenTransition(mWctCaptor.capture(), any());
        assertThat(mWctCaptor.getValue().getChanges()).isEmpty();
    }

    @Test
    public void testDismiss_freeformDisplay() {
        mDisplayAreaInfo.configuration.windowConfiguration.setWindowingMode(