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

Commit 1a2b9135 authored by Tony Huang's avatar Tony Huang
Browse files

Exit split when fold only if user interacted

If user fold the phone but do not interact and unfold later, we
should keep split status which follow current UX design.

Fix: 288331854
Test: manual
Test: pass existing tests
Change-Id: I9f008c3b51e460fcbd25f20bd45e84ca4e7ae6f1
parent f1f94bcd
Loading
Loading
Loading
Loading
+44 −24
Original line number Diff line number Diff line
@@ -219,6 +219,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
    private boolean mIsDropEntering;
    private boolean mIsExiting;
    private boolean mIsRootTranslucent;
    @VisibleForTesting
    int mTopStageAfterFoldDismiss;

    private DefaultMixedHandler mMixedHandler;
    private final Toast mSplitUnsupportedToast;
@@ -1310,6 +1312,24 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
            final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage;
            exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
        }

        // Dismiss split if the flag record any side of stages.
        if (mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
            if (ENABLE_SHELL_TRANSITIONS) {
                // Need manually clear here due to this transition might be aborted due to keyguard
                // on top and lead to no visible change.
                clearSplitPairedInRecents(EXIT_REASON_DEVICE_FOLDED);
                final WindowContainerTransaction wct = new WindowContainerTransaction();
                prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
                mSplitTransitions.startDismissTransition(wct, this,
                        mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
            } else {
                exitSplitScreen(
                        mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
                        EXIT_REASON_DEVICE_FOLDED);
            }
            mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
        }
    }

    void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -1346,15 +1366,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        if (!mMainStage.isActive() || mIsExiting) return;

        onSplitScreenExit();
        clearSplitPairedInRecents(exitReason);

        mRecentTasks.ifPresent(recentTasks -> {
            // Notify recents if we are exiting in a way that breaks the pair, and disable further
            // updates to splits in the recents until we enter split again
            if (shouldBreakPairedTaskInRecents(exitReason) && mShouldUpdateRecents) {
                recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId());
                recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId());
            }
        });
        mShouldUpdateRecents = false;
        mIsDividerRemoteAnimating = false;
        mSplitRequest = null;
@@ -1481,6 +1494,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }
    }

    private void clearSplitPairedInRecents(@ExitReason int exitReason) {
        if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;

        mRecentTasks.ifPresent(recentTasks -> {
            // Notify recents if we are exiting in a way that breaks the pair, and disable further
            // updates to splits in the recents until we enter split again
            mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
            mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId));
        });
    }

    /**
     * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
     * an existing WindowContainerTransaction (rather than applying immediately). This is intended
@@ -2208,7 +2232,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
        }
    }

    void updateSurfaces(SurfaceControl.Transaction transaction) {
    /**
     * Update surfaces of the split screen layout based on the current state
     * @param transaction to write the updates to
     */
    public void updateSurfaces(SurfaceControl.Transaction transaction) {
        updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false);
        mSplitLayout.update(transaction);
    }
@@ -2227,26 +2255,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,

    @VisibleForTesting
    void onFoldedStateChanged(boolean folded) {
        int topStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
        mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
        if (!folded) return;

        if (!mMainStage.isActive()) return;
        if (!isSplitActive() || !isSplitScreenVisible()) return;

        // To avoid split dismiss when user fold the device and unfold to use later, we only
        // record the flag here and try to dismiss on wakeUp callback to ensure split dismiss
        // when user interact on phone folded.
        if (mMainStage.isFocused()) {
            topStageAfterFoldDismiss = STAGE_TYPE_MAIN;
            mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN;
        } else if (mSideStage.isFocused()) {
            topStageAfterFoldDismiss = STAGE_TYPE_SIDE;
        }

        if (ENABLE_SHELL_TRANSITIONS) {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            prepareExitSplitScreen(topStageAfterFoldDismiss, wct);
            mSplitTransitions.startDismissTransition(wct, this,
                    topStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
        } else {
            exitSplitScreen(
                    topStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
                    EXIT_REASON_DEVICE_FOLDED);
            mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE;
        }
    }

+8 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.wm.shell.windowdecor.WindowDecorViewModel;

import java.io.PrintWriter;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
@@ -347,6 +348,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
        wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */);
    }

    void doForAllChildTasks(Consumer<Integer> consumer) {
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
            final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
            consumer.accept(taskInfo.taskId);
        }
    }

    /** Collects all the current child tasks and prepares transaction to evict them to display. */
    void evictAllChildren(WindowContainerTransaction wct) {
        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
+3 −0
Original line number Diff line number Diff line
@@ -704,6 +704,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
        if (mPipHandler != null) {
            mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
        }
        if (mSplitHandler != null && mSplitHandler.isSplitActive()) {
            mSplitHandler.updateSurfaces(startTransaction);
        }
        return mUnfoldHandler.startAnimation(
                mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
    }
+0 −1
Original line number Diff line number Diff line
@@ -270,7 +270,6 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
    @Override
    public void prepareStartTransaction(Transaction transaction) {
        mUnfoldBackgroundController.ensureBackground(transaction);
        mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction);
    }

    @Override
+9 −2
Original line number Diff line number Diff line
@@ -347,13 +347,20 @@ public class StageCoordinatorTests extends ShellTestCase {
    }

    @Test
    public void testExitSplitScreenAfterFolded() {
        when(mMainStage.isActive()).thenReturn(true);
    public void testExitSplitScreenAfterFoldedAndWakeUp() {
        when(mMainStage.isFocused()).thenReturn(true);
        when(mMainStage.getTopVisibleChildTaskId()).thenReturn(INVALID_TASK_ID);
        mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build();
        mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().setVisible(true).build();
        when(mStageCoordinator.isSplitActive()).thenReturn(true);
        when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);

        mStageCoordinator.onFoldedStateChanged(true);

        assertEquals(mStageCoordinator.mTopStageAfterFoldDismiss, STAGE_TYPE_MAIN);

        mStageCoordinator.onFinishedWakingUp();

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            verify(mTaskOrganizer).startNewTransition(eq(TRANSIT_SPLIT_DISMISS), notNull());
        } else {