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

Commit 6ce9c634 authored by Vinit Nayak's avatar Vinit Nayak Committed by Android (Google) Code Review
Browse files

Merge "Check if apps are currently in pip before launching split screen" into main

parents d920ce1a 697949a2
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -261,9 +261,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH
    }

    /** Whether a particular package is same as current pip package. */
    public boolean isInPipPackage(String packageName) {
    public boolean isPackageActiveInPip(String packageName) {
        final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
        return packageName != null && inPipTask != null
        return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
                && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
    }

+91 −18
Original line number Diff line number Diff line
@@ -201,7 +201,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    private final DisplayImeController mDisplayImeController;
    private final DisplayInsetsController mDisplayInsetsController;
    private final TransactionPool mTransactionPool;
    private final SplitScreenTransitions mSplitTransitions;
    private SplitScreenTransitions mSplitTransitions;
    private final SplitscreenEventLogger mLogger;
    private final ShellExecutor mMainExecutor;
    // Cache live tile tasks while entering recents, evict them from stages in finish transaction
@@ -399,6 +399,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return mSplitTransitions;
    }

    @VisibleForTesting
    void setSplitTransitions(SplitScreenTransitions splitScreenTransitions) {
        mSplitTransitions = splitScreenTransitions;
    }

    public boolean isSplitScreenVisible() {
        return mSideStageListener.mVisible && mMainStageListener.mVisible;
    }
@@ -583,7 +588,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
        wct.startTask(taskId, options);
        // If this should be mixed, send the task to avoid split handle transition directly.
        if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(taskId, mTaskOrganizer)) {
        if (mMixedHandler != null && mMixedHandler.isTaskInPip(taskId, mTaskOrganizer)) {
            mTaskOrganizer.applyTransaction(wct);
            return;
        }
@@ -622,7 +627,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        wct.sendPendingIntent(intent, fillInIntent, options);

        // If this should be mixed, just send the intent to avoid split handle transition directly.
        if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) {
        if (mMixedHandler != null && mMixedHandler.isIntentInPip(intent)) {
            mTaskOrganizer.applyTransaction(wct);
            return;
        }
@@ -711,16 +716,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                taskId1, taskId2, splitPosition, snapPosition);
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        if (taskId2 == INVALID_TASK_ID) {
            if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
                prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
            }
            if (mRecentTasks.isPresent()) {
                mRecentTasks.get().removeSplitPair(taskId1);
            }
            options1 = options1 != null ? options1 : new Bundle();
            addActivityOptions(options1, null);
            wct.startTask(taskId1, options1);
            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
            startSingleTask(taskId1, options1, wct, remoteTransition);
            return;
        }

@@ -741,11 +737,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
                pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        if (taskId == INVALID_TASK_ID) {
            options1 = options1 != null ? options1 : new Bundle();
            addActivityOptions(options1, null);
            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
        boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent);
        boolean secondTaskPipped = mMixedHandler.isTaskInPip(taskId, mTaskOrganizer);
        if (taskId == INVALID_TASK_ID || secondTaskPipped) {
            startSingleIntent(pendingIntent, fillInIntent, options1, wct, remoteTransition);
            return;
        }

        if (firstIntentPipped) {
            startSingleTask(taskId, options2, wct, remoteTransition);
            return;
        }

@@ -757,6 +757,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
    }

    /**
     * @param taskId Starts this task in fullscreen, removing it from existing pairs if it was part
     *               of one.
     */
    private void startSingleTask(int taskId, Bundle options, WindowContainerTransaction wct,
            RemoteTransition remoteTransition) {
        if (mMainStage.containsTask(taskId) || mSideStage.containsTask(taskId)) {
            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
        }
        if (mRecentTasks.isPresent()) {
            mRecentTasks.get().removeSplitPair(taskId);
        }
        options = options != null ? options : new Bundle();
        addActivityOptions(options, null);
        wct.startTask(taskId, options);
        mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
    }

    /** Starts a shortcut and a task to a split pair in one transition. */
    void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
            int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
@@ -844,6 +862,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            return;
        }

        boolean handledForPipSplitLaunch = handlePippedSplitIntentsLaunch(
                pendingIntent1,
                pendingIntent2,
                options1,
                options2,
                shortcutInfo1,
                shortcutInfo2,
                wct,
                fillInIntent1,
                fillInIntent2,
                remoteTransition);
        if (handledForPipSplitLaunch) {
            return;
        }

        if (!mMainStage.isActive()) {
            // Build a request WCT that will launch both apps such that task 0 is on the main stage
            // while task 1 is on the side stage.
@@ -878,6 +911,46 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        setEnterInstanceId(instanceId);
    }

    /**
     * Checks if either of the apps in the desired split launch is currently in Pip. If so, it will
     * launch the non-pipped app as a fullscreen app, otherwise no-op.
     */
    private boolean handlePippedSplitIntentsLaunch(PendingIntent pendingIntent1,
            PendingIntent pendingIntent2, Bundle options1, Bundle options2,
            ShortcutInfo shortcutInfo1, ShortcutInfo shortcutInfo2, WindowContainerTransaction wct,
            Intent fillInIntent1, Intent fillInIntent2, RemoteTransition remoteTransition) {
        // If one of the split apps to start is in Pip, only launch the non-pip app in fullscreen
        boolean firstIntentPipped = mMixedHandler.isIntentInPip(pendingIntent1);
        boolean secondIntentPipped = mMixedHandler.isIntentInPip(pendingIntent2);
        if (firstIntentPipped || secondIntentPipped) {
            Bundle options = secondIntentPipped ? options1 : options2;
            options = options == null ? new Bundle() : options;
            addActivityOptions(options, null);
            if (shortcutInfo1 != null || shortcutInfo2 != null) {
                ShortcutInfo infoToLaunch = secondIntentPipped ? shortcutInfo1 : shortcutInfo2;
                wct.startShortcut(mContext.getPackageName(), infoToLaunch, options);
                mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
            } else {
                PendingIntent intentToLaunch = secondIntentPipped ? pendingIntent1 : pendingIntent2;
                Intent fillInIntentToLaunch = secondIntentPipped ? fillInIntent1 : fillInIntent2;
                startSingleIntent(intentToLaunch, fillInIntentToLaunch, options, wct,
                        remoteTransition);
            }
            return true;
        }
        return false;
    }

    /** @param pendingIntent Starts this intent in fullscreen */
    private void startSingleIntent(PendingIntent pendingIntent, Intent fillInIntent, Bundle options,
            WindowContainerTransaction wct,
            RemoteTransition remoteTransition) {
        Bundle optionsToLaunch = options != null ? options : new Bundle();
        addActivityOptions(optionsToLaunch, null);
        wct.sendPendingIntent(pendingIntent, fillInIntent, optionsToLaunch);
        mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
    }

    /** Starts a pair of tasks using legacy transition. */
    void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
            int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
+5 −4
Original line number Diff line number Diff line
@@ -563,22 +563,23 @@ public class DefaultMixedHandler implements MixedTransitionHandler,

    /** Use to when split use intent to enter, check if this enter transition should be mixed or
     * not.*/
    public boolean shouldSplitEnterMixed(PendingIntent intent) {
    public boolean isIntentInPip(PendingIntent intent) {
        // Check if this intent package is same as pip one or not, if true we want let the pip
        // task enter split.
        if (mPipHandler != null) {
            return mPipHandler.isInPipPackage(SplitScreenUtils.getPackageName(intent.getIntent()));
            return mPipHandler
                    .isPackageActiveInPip(SplitScreenUtils.getPackageName(intent.getIntent()));
        }
        return false;
    }

    /** Use to when split use taskId to enter, check if this enter transition should be mixed or
     * not.*/
    public boolean shouldSplitEnterMixed(int taskId, ShellTaskOrganizer shellTaskOrganizer) {
    public boolean isTaskInPip(int taskId, ShellTaskOrganizer shellTaskOrganizer) {
        // Check if this intent package is same as pip one or not, if true we want let the pip
        // task enter split.
        if (mPipHandler != null) {
            return mPipHandler.isInPipPackage(
            return mPipHandler.isPackageActiveInPip(
                    SplitScreenUtils.getPackageName(taskId, shellTaskOrganizer));
        }
        return false;
+96 −0
Original line number Diff line number Diff line
@@ -40,10 +40,12 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -51,6 +53,7 @@ import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -74,6 +77,7 @@ import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.HomeTransitionObserver;
import com.android.wm.shell.transition.Transitions;

@@ -111,6 +115,8 @@ public class StageCoordinatorTests extends ShellTestCase {
    private TransactionPool mTransactionPool;
    @Mock
    private LaunchAdjacentController mLaunchAdjacentController;
    @Mock
    private DefaultMixedHandler mDefaultMixedHandler;

    private final Rect mBounds1 = new Rect(10, 20, 30, 40);
    private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -370,6 +376,96 @@ 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(
                pendingIntent,
                null /*fillInIntent*/,
                null /*option1*/,
                taskId,
                null /*option2*/,
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(1))
                .startFullscreenTransition(any(), any());

        // Test launching first intent fullscreen
        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false);
        when(mDefaultMixedHandler.isTaskInPip(taskId, mTaskOrganizer)).thenReturn(true);
        mStageCoordinator.startIntentAndTask(
                pendingIntent,
                null /*fillInIntent*/,
                null /*option1*/,
                taskId,
                null /*option2*/,
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, 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(
                pendingIntent,
                null /*fillInIntent*/,
                null /*shortcutInfo1*/,
                new Bundle(),
                pendingIntent2,
                null /*fillInIntent2*/,
                null /*shortcutInfo1*/,
                new Bundle(),
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(1))
                .startFullscreenTransition(any(), any());

        // Test launching first intent fullscreen
        when(mDefaultMixedHandler.isIntentInPip(pendingIntent)).thenReturn(false);
        when(mDefaultMixedHandler.isIntentInPip(pendingIntent2)).thenReturn(true);
        mStageCoordinator.startIntents(
                pendingIntent,
                null /*fillInIntent*/,
                null /*shortcutInfo1*/,
                new Bundle(),
                pendingIntent2,
                null /*fillInIntent2*/,
                null /*shortcutInfo1*/,
                new Bundle(),
                0 /*splitPosition*/,
                1 /*snapPosition*/,
                remoteTransition /*remoteTransition*/,
                null /*instanceId*/);
        verify(splitScreenTransitions, times(2))
                .startFullscreenTransition(any(), any());
    }

    private Transitions createTestTransitions() {
        ShellInit shellInit = new ShellInit(mMainExecutor);
        final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),