Merge "Handle split screen accordingly after recent transition finished" into tm-qpr-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<ActivityManager.RunningTaskInfo> predicate) {
|
||||
if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return getChildTaskInfo(predicate) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ActivityManager.RunningTaskInfo getChildTaskInfo(
|
||||
Predicate<ActivityManager.RunningTaskInfo> 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
|
||||
|
||||
Reference in New Issue
Block a user