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

Commit 1b560d87 authored by Jerry Chang's avatar Jerry Chang
Browse files

Integrate launch-to-side split transition to shell transition

Integrate launch-to-side split transition to shell transition.
Because there is no guarantee for launching activity to split
successfully before actually launching it, trigger enter split
transition after observed new task launching into split.

Bug: 206487881
Test: atest SplitTransitionTests
Test: drag to enter split with shell transition works
Change-Id: Idda7c3b02689753cea1a1fe80264145f1a449f73
parent 5ee0a77a
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -318,8 +318,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
            startIntentLegacy(intent, fillInIntent, position, options);
            return;
        }
        mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
                null /* remote */);

        try {
            options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
                    null /* wct */);
            intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
                    null /* requiredPermission */, options);
        } catch (PendingIntent.CanceledException e) {
            Slog.e(TAG, "Failed to launch task", e);
        }
    }

    private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+47 −66
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonT
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -54,10 +53,8 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
@@ -436,17 +433,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        mTaskOrganizer.applyTransaction(wct);
    }

    public void startIntent(PendingIntent intent, Intent fillInIntent,
            @StageType int stage, @SplitPosition int position,
            @androidx.annotation.Nullable Bundle options,
            @Nullable RemoteTransition remoteTransition) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        options = resolveStartStage(stage, position, options, wct);
        wct.sendPendingIntent(intent, fillInIntent, options);
        mSplitTransitions.startEnterTransition(
                TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
    }

    /**
     * Collects all the current child tasks of a specific split and prepares transaction to evict
     * them to display.
@@ -1158,12 +1144,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                mSplitTransitions.mPendingDismiss = transition;
            }
        } else {
            // Not in split mode, so look for an open into a split stage just so we can whine and
            // complain about how this isn't a supported operation.
            // Not in split mode, so look for an open into a split stage to active split screen.
            if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
                if (getStageOfTask(triggerTask) != null) {
                    throw new IllegalStateException("Entering split implicitly with only one task"
                            + " isn't supported.");
                    // One task is appearing in split, prepare to enter split screen.
                    out = new WindowContainerTransaction();
                    mSplitTransitions.mPendingEnter = transition;
                    mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
                    mSideStage.moveToTop(getSideStageBounds(), out);
                }
            }
        }
@@ -1232,8 +1220,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    private boolean startPendingEnterAnimation(@NonNull IBinder transition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) {
            // First, verify that we actually have opened 2 apps in split.
        // First, verify that we actually have opened apps in both splits.
        TransitionInfo.Change mainChild = null;
        TransitionInfo.Change sideChild = null;
        for (int iC = 0; iC < info.getChanges().size(); ++iC) {
@@ -1256,8 +1243,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,

        // Update local states (before animating).
        setDividerVisibility(true);
            setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
                    null /* wct */);
        setSplitsVisible(true);

        addDividerBarToTransition(info, t, true /* show */);
@@ -1279,10 +1264,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                    + " before startAnimation().");
        }
        return true;
        } else {
            // TODO: other entry method animations
            throw new RuntimeException("Unsupported split-entry");
        }
    }

    private boolean startPendingDismissAnimation(@NonNull IBinder transition,
+51 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;

import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -66,7 +67,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;

import org.junit.Before;
@@ -132,6 +132,40 @@ public class SplitTransitionTests extends ShellTestCase {
                .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
    }

    @Test
    public void testLaunchToSide() {
        ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder()
                .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
        ActivityManager.RunningTaskInfo reparentTask = new TestRunningTaskInfoBuilder()
                .setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();

        // Create a request to start a new task in side stage
        TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, newTask, null);
        IBinder transition = mock(IBinder.class);
        WindowContainerTransaction result =
                mStageCoordinator.handleRequest(transition, request);

        // it should handle the transition to enter split screen.
        assertNotNull(result);
        assertTrue(containsSplitEnter(result));

        // simulate the transition
        TransitionInfo.Change openChange = createChange(TRANSIT_OPEN, newTask);
        TransitionInfo.Change reparentChange = createChange(TRANSIT_CHANGE, reparentTask);

        TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
        info.addChange(openChange);
        info.addChange(reparentChange);
        mSideStage.onTaskAppeared(newTask, createMockSurface());
        mMainStage.onTaskAppeared(reparentTask, createMockSurface());
        boolean accepted = mStageCoordinator.startAnimation(transition, info,
                mock(SurfaceControl.Transaction.class),
                mock(SurfaceControl.Transaction.class),
                mock(Transitions.TransitionFinishCallback.class));
        assertTrue(accepted);
        assertTrue(mStageCoordinator.isSplitScreenVisible());
    }

    @Test
    public void testLaunchPair() {
        TransitionInfo info = createEnterPairInfo();
@@ -324,6 +358,22 @@ public class SplitTransitionTests extends ShellTestCase {
                true /* includingTopTask */);
    }

    private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
        boolean movedMainToFront = false;
        boolean movedSideToFront = false;
        for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
            WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
            if (op.getType() == HIERARCHY_OP_TYPE_REORDER) {
                if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) {
                    movedMainToFront = true;
                } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) {
                    movedSideToFront = true;
                }
            }
        }
        return movedMainToFront && movedSideToFront;
    }

    private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) {
        // reparenting of child tasks to null constitutes exiting split.
        boolean reparentedMain = false;
+2 −1
Original line number Diff line number Diff line
@@ -1672,7 +1672,8 @@ class ActivityStarter {
                }
                if (newTransition != null) {
                    transitionController.requestStartTransition(newTransition,
                            mTargetTask, remoteTransition, null /* displayChange */);
                            mTargetTask == null ? r.getTask() : mTargetTask,
                            remoteTransition, null /* displayChange */);
                } else if (started) {
                    // Make the collecting transition wait until this request is ready.
                    transitionController.setReady(r, false);