Merge "Handle split screen accordingly after recent transition finished" into tm-qpr-dev am: c03b26dfed
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18347482 Change-Id: Ic4f64d94ecdcb26e143336db99e3841d6169272d Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -68,6 +68,7 @@ class SplitScreenTransitions {
|
|||||||
private IBinder mAnimatingTransition = null;
|
private IBinder mAnimatingTransition = null;
|
||||||
OneShotRemoteHandler mPendingRemoteHandler = null;
|
OneShotRemoteHandler mPendingRemoteHandler = null;
|
||||||
private OneShotRemoteHandler mActiveRemoteHandler = null;
|
private OneShotRemoteHandler mActiveRemoteHandler = null;
|
||||||
|
private boolean mEnterTransitionMerged;
|
||||||
|
|
||||||
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
|
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) {
|
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
|
||||||
if (!mAnimations.isEmpty()) return;
|
if (!mAnimations.isEmpty()) return;
|
||||||
if (mAnimatingTransition == mPendingEnter) {
|
if (mAnimatingTransition == mPendingEnter) {
|
||||||
@@ -238,18 +251,16 @@ class SplitScreenTransitions {
|
|||||||
mPendingDismiss = null;
|
mPendingDismiss = null;
|
||||||
}
|
}
|
||||||
if (mAnimatingTransition == mPendingRecent) {
|
if (mAnimatingTransition == mPendingRecent) {
|
||||||
// If the clean-up wct is null when finishing recent transition, it indicates it's
|
if (!mEnterTransitionMerged) {
|
||||||
// returning to home and thus no need to reorder tasks.
|
if (wct == null) wct = new WindowContainerTransaction();
|
||||||
final boolean returnToHome = wct == null;
|
mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
|
||||||
if (returnToHome) {
|
|
||||||
wct = new WindowContainerTransaction();
|
|
||||||
}
|
}
|
||||||
mStageCoordinator.onRecentTransitionFinished(returnToHome, wct, mFinishTransaction);
|
|
||||||
mPendingRecent = null;
|
mPendingRecent = null;
|
||||||
}
|
}
|
||||||
mPendingRemoteHandler = null;
|
mPendingRemoteHandler = null;
|
||||||
mActiveRemoteHandler = null;
|
mActiveRemoteHandler = null;
|
||||||
mAnimatingTransition = null;
|
mAnimatingTransition = null;
|
||||||
|
mEnterTransitionMerged = false;
|
||||||
|
|
||||||
mOnFinish.run();
|
mOnFinish.run();
|
||||||
if (mFinishTransaction != null) {
|
if (mFinishTransaction != null) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
|||||||
import static android.view.WindowManager.transitTypeToString;
|
import static android.view.WindowManager.transitTypeToString;
|
||||||
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
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.SplitLayout.PARALLAX_ALIGN_CENTER;
|
||||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
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();
|
sideOptions = sideOptions != null ? sideOptions : new Bundle();
|
||||||
setSideStagePosition(sidePosition, wct);
|
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);
|
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);
|
updateWindowBounds(mSplitLayout, wct);
|
||||||
wct.reorder(mRootTaskInfo.token, true);
|
wct.reorder(mRootTaskInfo.token, true);
|
||||||
|
|
||||||
@@ -1507,16 +1513,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTransitionMerged(@NonNull IBinder transition) {
|
public void onTransitionMerged(@NonNull IBinder transition) {
|
||||||
// Once the pending enter transition got merged, make sure to bring divider bar visible and
|
mSplitTransitions.onTransitionMerged(transition);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1748,26 +1745,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRecentTransitionFinished(boolean returnToHome, WindowContainerTransaction wct,
|
void onRecentTransitionFinished(WindowContainerTransaction wct,
|
||||||
SurfaceControl.Transaction finishT) {
|
SurfaceControl.Transaction finishT) {
|
||||||
// Exclude the case that the split screen has been dismissed already.
|
// Check if the recent transition is finished by returning to the current split so we can
|
||||||
if (!mMainStage.isActive()) {
|
// restore the divider bar.
|
||||||
// The latest split dismissing transition might be a no-op transition and thus won't
|
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
|
||||||
// callback startAnimation, update split visibility here to cover this kind of no-op
|
final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
|
||||||
// transition case.
|
final IBinder container = op.getContainer();
|
||||||
setSplitsVisible(false);
|
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
|
||||||
return;
|
&& (mMainStage.containsContainer(container)
|
||||||
|
|| mSideStage.containsContainer(container))) {
|
||||||
|
setDividerVisibility(true, finishT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnToHome) {
|
// Dismiss the split screen is it's not returning to split.
|
||||||
// When returning to home from recent apps, the splitting tasks are already hidden, so
|
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||||
// append the reset of dismissing operations into the clean-up wct.
|
setSplitsVisible(false);
|
||||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
setDividerVisibility(false, finishT);
|
||||||
setSplitsVisible(false);
|
logExit(EXIT_REASON_UNKNOWN);
|
||||||
logExit(EXIT_REASON_RETURN_HOME);
|
|
||||||
} else {
|
|
||||||
setDividerVisibility(true, finishT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDividerBarToTransition(@NonNull TransitionInfo info,
|
private void addDividerBarToTransition(@NonNull TransitionInfo info,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.app.ActivityManager;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.SurfaceSession;
|
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 com.android.wm.shell.splitscreen.SplitScreen.StageType;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class that handle common task org. related for split-screen stages.
|
* 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) {
|
boolean containsToken(WindowContainerToken token) {
|
||||||
if (token.equals(mRootTaskInfo.token)) {
|
return contains(t -> t.token.equals(token));
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
boolean containsContainer(IBinder binder) {
|
||||||
if (token.equals(mChildrenTaskInfo.valueAt(i).token)) {
|
return contains(t -> t.token.asBinder() == binder);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the top visible child task's id.
|
* Returns the top visible child task's id.
|
||||||
*/
|
*/
|
||||||
int getTopVisibleChildTaskId() {
|
int getTopVisibleChildTaskId() {
|
||||||
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible);
|
||||||
final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
|
return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
|
||||||
if (info.isVisible) {
|
|
||||||
return info.taskId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return INVALID_TASK_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the top activity uid for the top child task.
|
* Returns the top activity uid for the top child task.
|
||||||
*/
|
*/
|
||||||
int getTopChildTaskUid() {
|
int getTopChildTaskUid() {
|
||||||
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
final ActivityManager.RunningTaskInfo taskInfo =
|
||||||
final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
|
getChildTaskInfo(t -> t.topActivityInfo != null);
|
||||||
if (info.topActivityInfo == null) {
|
return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return info.topActivityInfo.applicationInfo.uid;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {@code true} if this listener contains the currently focused task. */
|
/** @return {@code true} if this listener contains the currently focused task. */
|
||||||
boolean isFocused() {
|
boolean isFocused() {
|
||||||
if (mRootTaskInfo == null) {
|
return contains(t -> t.isFocused);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mRootTaskInfo.isFocused) {
|
private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) {
|
||||||
|
if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return getChildTaskInfo(predicate) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ActivityManager.RunningTaskInfo getChildTaskInfo(
|
||||||
|
Predicate<ActivityManager.RunningTaskInfo> predicate) {
|
||||||
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
|
||||||
if (mChildrenTaskInfo.valueAt(i).isFocused) {
|
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
|
||||||
return true;
|
if (predicate.test(taskInfo)) {
|
||||||
|
return taskInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user