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

Commit f37bfeaa authored by Jerry Chang's avatar Jerry Chang
Browse files

Support to merge transition to remote handler while in split screen mode

Support to merge transition to activating remote transition while in
split screen. Like the case openning a normal app from overview during
split screen mode, it'll trigger a new app openning transition which
should be merged back to the remote transition running in launcher side
to finish the recent animation.

If a split dismissing transition was triggered right after recent
transition finished. It might be a no-op transition and thus won't
callback startAnimation. So also reset split screen state and visibility
when finishing transition.

Bug: 206487881
Test: enable shell transition and enter split screen, observed the
      transition switching to normal app won't hang.
Change-Id: I9bac5eab844f7835c69f9aad17a5c50dc01a76fa
parent 1b7cf5a2
Loading
Loading
Loading
Loading
+26 −16
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@ class SplitScreenTransitions {
    IBinder mPendingRecent = null;

    private IBinder mAnimatingTransition = null;
    private OneShotRemoteHandler mRemoteHandler = null;
    private OneShotRemoteHandler mPendingRemoteHandler = null;
    private OneShotRemoteHandler mActiveRemoteHandler = null;

    private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;

@@ -96,10 +97,11 @@ class SplitScreenTransitions {
            @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
        mFinishCallback = finishCallback;
        mAnimatingTransition = transition;
        if (mRemoteHandler != null) {
            mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
                    mRemoteFinishCB);
            mRemoteHandler = null;
        if (mPendingRemoteHandler != null) {
            mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
                    finishTransaction, mRemoteFinishCB);
            mActiveRemoteHandler = mPendingRemoteHandler;
            mPendingRemoteHandler = null;
            return;
        }
        playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
@@ -172,15 +174,14 @@ class SplitScreenTransitions {
    IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
            @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
            @NonNull Transitions.TransitionHandler handler) {
        final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
        mPendingEnter = transition;

        if (remoteTransition != null) {
            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
            mRemoteHandler = new OneShotRemoteHandler(
            mPendingRemoteHandler = new OneShotRemoteHandler(
                    mTransitions.getMainExecutor(), remoteTransition);
        }
        final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
        mPendingEnter = transition;
        if (mRemoteHandler != null) {
            mRemoteHandler.setTransition(transition);
            mPendingRemoteHandler.setTransition(transition);
        }
        return transition;
    }
@@ -211,9 +212,9 @@ class SplitScreenTransitions {

        if (remoteTransition != null) {
            // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
            mRemoteHandler = new OneShotRemoteHandler(
            mPendingRemoteHandler = new OneShotRemoteHandler(
                    mTransitions.getMainExecutor(), remoteTransition);
            mRemoteHandler.setTransition(transition);
            mPendingRemoteHandler.setTransition(transition);
        }

        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  splitTransition "
@@ -221,6 +222,13 @@ class SplitScreenTransitions {
        return transition;
    }

    void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
            IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
        if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
            mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
        }
    }

    void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
        if (!mAnimations.isEmpty()) return;
        mOnFinish.run();
@@ -241,11 +249,13 @@ class SplitScreenTransitions {
        }
        if (mAnimatingTransition == mPendingRecent) {
            // If the wct is not null while finishing recent transition, it indicates it's not
            // returning to home and hence needing the wct to reorder tasks.
            final boolean toHome = wct == null;
            mStageCoordinator.finishRecentAnimation(toHome);
            // dismissing split and thus need to reorder split task so they can be on top again.
            final boolean dismissSplit = wct == null;
            mStageCoordinator.finishRecentAnimation(dismissSplit);
            mPendingRecent = null;
        }
        mPendingRemoteHandler = null;
        mActiveRemoteHandler = null;
        mAnimatingTransition = null;
    }

+33 −17
Original line number Diff line number Diff line
@@ -166,17 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    @StageType
    private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;

    private final Runnable mOnTransitionAnimationComplete = () -> {
        // If still playing, let it finish.
        if (!isSplitScreenVisible()) {
            // Update divider state after animation so that it is still around and positioned
            // properly for the animation itself.
            mSplitLayout.release();
            mSplitLayout.resetDividerPosition();
            mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
        }
    };

    private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
            new SplitWindowManager.ParentContainerCallbacks() {
                @Override
@@ -237,7 +226,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
                new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
        mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                mOnTransitionAnimationComplete, this);
                this::onTransitionAnimationComplete, this);
        mDisplayController.addDisplayWindowListener(this);
        mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
        transitions.addHandler(this);
@@ -267,7 +256,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        mRootTDAOrganizer.registerListener(displayId, this);
        mSplitLayout = splitLayout;
        mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                mOnTransitionAnimationComplete, this);
                this::onTransitionAnimationComplete, this);
        mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
        mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
        mLogger = logger;
@@ -1234,7 +1223,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
        if (triggerTask == null) {
            // Still want to monitor everything while in split-screen, so return non-null.
            return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
            return mMainStage.isActive() ? new WindowContainerTransaction() : null;
        } else if (triggerTask.displayId != mDisplayId) {
            // Skip handling task on the other display.
            return null;
@@ -1250,7 +1239,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
        }

        if (isSplitScreenVisible()) {
        if (mMainStage.isActive()) {
            // Try to handle everything while in split-screen, so return a WCT even if it's empty.
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  split is active so using split"
                            + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -1295,6 +1284,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return out;
    }

    @Override
    public void mergeAnimation(IBinder transition, TransitionInfo info,
            SurfaceControl.Transaction t, IBinder mergeTarget,
            Transitions.TransitionFinishCallback finishCallback) {
        mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
    }

    @Override
    public void onTransitionMerged(@NonNull IBinder transition) {
        // Once the pending enter transition got merged, make sure to bring divider bar visible and
@@ -1375,6 +1371,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return true;
    }

    void onTransitionAnimationComplete() {
        // If still playing, let it finish.
        if (!mMainStage.isActive()) {
            // Update divider state after animation so that it is still around and positioned
            // properly for the animation itself.
            mSplitLayout.release();
            mSplitLayout.resetDividerPosition();
            mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
        }
    }

    private boolean startPendingEnterAnimation(@NonNull IBinder transition,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        // First, verify that we actually have opened apps in both splits.
@@ -1513,8 +1520,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        return true;
    }

    void finishRecentAnimation(boolean toHome) {
        if (toHome) {
    void finishRecentAnimation(boolean dismissSplit) {
        // Exclude the case that the split screen has been dismissed already.
        if (!mMainStage.isActive()) {
            // The latest split dismissing transition might be a no-op transition and thus won't
            // callback startAnimation, update split visibility here to cover this kind of no-op
            // transition case.
            setSplitsVisible(false);
            return;
        }

        if (dismissSplit) {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
            mSplitTransitions.startDismissTransition(null /* transition */, wct, this,