diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index f8221d5aae244..f7057d454df92 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -68,6 +68,7 @@ class SplitScreenTransitions { private IBinder mAnimatingTransition = null; OneShotRemoteHandler mPendingRemoteHandler = null; private OneShotRemoteHandler mActiveRemoteHandler = null; + private boolean mEnterTransitionMerged; private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish; @@ -229,6 +230,18 @@ class SplitScreenTransitions { } } + void onTransitionMerged(@NonNull IBinder transition) { + // Once a pending enter transition got merged, make sure to append the reset of finishing + // operations to the finish transition. + if (transition == mPendingEnter) { + mFinishTransaction = mTransactionPool.acquire(); + mStageCoordinator.finishEnterSplitScreen(mFinishTransaction); + mPendingEnter = null; + mPendingRemoteHandler = null; + mEnterTransitionMerged = true; + } + } + void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) { if (!mAnimations.isEmpty()) return; if (mAnimatingTransition == mPendingEnter) { @@ -238,18 +251,16 @@ class SplitScreenTransitions { mPendingDismiss = null; } if (mAnimatingTransition == mPendingRecent) { - // If the clean-up wct is null when finishing recent transition, it indicates it's - // returning to home and thus no need to reorder tasks. - final boolean returnToHome = wct == null; - if (returnToHome) { - wct = new WindowContainerTransaction(); + if (!mEnterTransitionMerged) { + if (wct == null) wct = new WindowContainerTransaction(); + mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction); } - mStageCoordinator.onRecentTransitionFinished(returnToHome, wct, mFinishTransaction); mPendingRecent = null; } mPendingRemoteHandler = null; mActiveRemoteHandler = null; mAnimatingTransition = null; + mEnterTransitionMerged = false; mOnFinish.run(); if (mFinishTransaction != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 3cf8a45310ef2..f625387a05d5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -30,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -369,10 +370,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, sideOptions = sideOptions != null ? sideOptions : new Bundle(); setSideStagePosition(sidePosition, wct); + if (mMainStage.isActive()) { + mMainStage.evictAllChildren(wct); + mSideStage.evictAllChildren(wct); + } else { + // 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. + mMainStage.activate(wct, false /* reparent */); + } mSplitLayout.setDivideRatio(splitRatio); - // 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. - mMainStage.activate(wct, false /* reparent */); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); @@ -1507,16 +1513,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onTransitionMerged(@NonNull IBinder transition) { - // Once the pending enter transition got merged, make sure to bring divider bar visible and - // clear the pending transition from cache to prevent mess-up the following state. - if (transition == mSplitTransitions.mPendingEnter) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - finishEnterSplitScreen(t); - mSplitTransitions.mPendingEnter = null; - mSplitTransitions.mPendingRemoteHandler = null; - t.apply(); - mTransactionPool.release(t); - } + mSplitTransitions.onTransitionMerged(transition); } @Override @@ -1748,26 +1745,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - void onRecentTransitionFinished(boolean returnToHome, WindowContainerTransaction wct, + void onRecentTransitionFinished(WindowContainerTransaction wct, SurfaceControl.Transaction finishT) { - // 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; + // Check if the recent transition is finished by returning to the current split so we can + // restore the divider bar. + for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { + final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); + final IBinder container = op.getContainer(); + if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() + && (mMainStage.containsContainer(container) + || mSideStage.containsContainer(container))) { + setDividerVisibility(true, finishT); + return; + } } - if (returnToHome) { - // When returning to home from recent apps, the splitting tasks are already hidden, so - // append the reset of dismissing operations into the clean-up wct. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - setSplitsVisible(false); - logExit(EXIT_REASON_RETURN_HOME); - } else { - setDividerVisibility(true, finishT); - } + // Dismiss the split screen is it's not returning to split. + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); + setSplitsVisible(false); + setDividerVisibility(false, finishT); + logExit(EXIT_REASON_UNKNOWN); } private void addDividerBarToTransition(@NonNull TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index eb513384d8226..f9dd7c96294fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -31,6 +31,7 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.os.IBinder; import android.util.SparseArray; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -47,6 +48,7 @@ import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import java.io.PrintWriter; +import java.util.function.Predicate; /** * Base class that handle common task org. related for split-screen stages. @@ -119,63 +121,53 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } boolean containsToken(WindowContainerToken token) { - if (token.equals(mRootTaskInfo.token)) { - return true; - } + return contains(t -> t.token.equals(token)); + } - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (token.equals(mChildrenTaskInfo.valueAt(i).token)) { - return true; - } - } - - return false; + boolean containsContainer(IBinder binder) { + return contains(t -> t.token.asBinder() == binder); } /** * Returns the top visible child task's id. */ int getTopVisibleChildTaskId() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.isVisible) { - return info.taskId; - } - } - return INVALID_TASK_ID; + final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible); + return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID; } /** * Returns the top activity uid for the top child task. */ int getTopChildTaskUid() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.topActivityInfo == null) { - continue; - } - return info.topActivityInfo.applicationInfo.uid; - } - return 0; + final ActivityManager.RunningTaskInfo taskInfo = + getChildTaskInfo(t -> t.topActivityInfo != null); + return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0; } /** @return {@code true} if this listener contains the currently focused task. */ boolean isFocused() { - if (mRootTaskInfo == null) { - return false; - } + return contains(t -> t.isFocused); + } - if (mRootTaskInfo.isFocused) { + private boolean contains(Predicate predicate) { + if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) { return true; } + return getChildTaskInfo(predicate) != null; + } + + @Nullable + private ActivityManager.RunningTaskInfo getChildTaskInfo( + Predicate predicate) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (mChildrenTaskInfo.valueAt(i).isFocused) { - return true; + final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); + if (predicate.test(taskInfo)) { + return taskInfo; } } - - return false; + return null; } @Override