Improve drag divider dismiss transition.
Before this CL, the split root task will not include in tansition when drag to dismiss. It leads to users could see flicker when the top task bounds changed due to no animation on it. On this CL, it add update bounds too on dismiss transition then split root could be included in transition. And we could use decor to do fade-out animation. Fix: 245472831 Fix: 283045645 Test: manual Test: pass existing tests Change-Id: Ie6be85173d9a5b71603aa89858df117be08ea484
This commit is contained in:
@@ -293,6 +293,9 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
}
|
||||
|
||||
if (mResizingIconView == null) {
|
||||
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
|
||||
animFinishedCallback.accept(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,6 +314,9 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
releaseDecor(finishT);
|
||||
finishT.apply();
|
||||
finishT.close();
|
||||
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
|
||||
animFinishedCallback.accept(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -87,6 +87,14 @@ class SplitScreenTransitions {
|
||||
mStageCoordinator = stageCoordinator;
|
||||
}
|
||||
|
||||
private void initTransition(@NonNull IBinder transition,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
mAnimatingTransition = transition;
|
||||
mFinishTransaction = finishTransaction;
|
||||
mFinishCallback = finishCallback;
|
||||
}
|
||||
|
||||
/** Play animation for enter transition or dismiss transition. */
|
||||
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@@ -94,9 +102,7 @@ class SplitScreenTransitions {
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
|
||||
@NonNull WindowContainerToken topRoot) {
|
||||
mFinishCallback = finishCallback;
|
||||
mAnimatingTransition = transition;
|
||||
mFinishTransaction = finishTransaction;
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
final TransitSession pendingTransition = getPendingTransition(transition);
|
||||
if (pendingTransition != null) {
|
||||
@@ -220,6 +226,45 @@ class SplitScreenTransitions {
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
}
|
||||
|
||||
/** Play animation for drag divider dismiss transition. */
|
||||
void playDragDismissAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
|
||||
@NonNull WindowContainerToken topRoot) {
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
|
||||
if (toTopRoot.equals(change.getContainer())) {
|
||||
startTransaction.setAlpha(leash, 1.f);
|
||||
startTransaction.show(leash);
|
||||
|
||||
ValueAnimator va = new ValueAnimator();
|
||||
mAnimations.add(va);
|
||||
|
||||
toTopDecor.onResized(startTransaction, animated -> {
|
||||
mAnimations.remove(va);
|
||||
if (animated) {
|
||||
mTransitions.getMainExecutor().execute(() -> {
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (topRoot.equals(change.getContainer())) {
|
||||
// Ensure it on top of all changes in transition.
|
||||
startTransaction.setLayer(leash, Integer.MAX_VALUE);
|
||||
startTransaction.setAlpha(leash, 1.f);
|
||||
startTransaction.show(leash);
|
||||
}
|
||||
}
|
||||
startTransaction.apply();
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
}
|
||||
|
||||
/** Play animation for resize transition. */
|
||||
void playResizeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@@ -227,9 +272,7 @@ class SplitScreenTransitions {
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
|
||||
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
|
||||
mFinishCallback = finishCallback;
|
||||
mAnimatingTransition = transition;
|
||||
mFinishTransaction = finishTransaction;
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
|
||||
@@ -1328,8 +1328,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mIsExiting = true;
|
||||
childrenToTop.resetBounds(wct);
|
||||
wct.reorder(childrenToTop.mRootTaskInfo.token, true);
|
||||
wct.setSmallestScreenWidthDp(childrenToTop.mRootTaskInfo.token,
|
||||
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
|
||||
}
|
||||
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
|
||||
false /* reparentLeafTaskIfRelaunch */);
|
||||
@@ -1517,6 +1515,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
|
||||
mSplitLayout.update(t);
|
||||
mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
|
||||
getMainStageBounds());
|
||||
mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash,
|
||||
getSideStageBounds());
|
||||
setDividerVisibility(true, t);
|
||||
// Ensure divider surface are re-parented back into the hierarchy at the end of the
|
||||
// transition. See Transition#buildFinishTransaction for more detail.
|
||||
@@ -1989,13 +1991,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
final boolean mainStageToTop =
|
||||
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
|
||||
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
|
||||
final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, reason);
|
||||
exitSplitScreen(toTopStage, reason);
|
||||
return;
|
||||
}
|
||||
|
||||
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
toTopStage.resetBounds(wct);
|
||||
prepareExitSplitScreen(dismissTop, wct);
|
||||
if (mRootTaskInfo != null) {
|
||||
wct.setDoNotPip(mRootTaskInfo.token);
|
||||
@@ -2531,8 +2535,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
shouldAnimate = startPendingEnterAnimation(
|
||||
mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
|
||||
} else if (mSplitTransitions.isPendingDismiss(transition)) {
|
||||
final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
|
||||
shouldAnimate = startPendingDismissAnimation(
|
||||
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
|
||||
dismiss, info, startTransaction, finishTransaction);
|
||||
if (shouldAnimate && dismiss.mReason == EXIT_REASON_DRAG_DIVIDER) {
|
||||
final StageTaskListener toTopStage =
|
||||
dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage;
|
||||
mSplitTransitions.playDragDismissAnimation(transition, info, startTransaction,
|
||||
finishTransaction, finishCallback, toTopStage.mRootTaskInfo.token,
|
||||
toTopStage.getSplitDecorManager(), mRootTaskInfo.token);
|
||||
return true;
|
||||
}
|
||||
} else if (mSplitTransitions.isPendingResize(transition)) {
|
||||
mSplitTransitions.playResizeAnimation(transition, info, startTransaction,
|
||||
finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
|
||||
@@ -2787,6 +2800,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSplitTransitions.mPendingDismiss = null;
|
||||
return false;
|
||||
}
|
||||
dismissTransition.setFinishedCallback((callbackWct, callbackT) -> {
|
||||
mMainStage.getSplitDecorManager().release(callbackT);
|
||||
mSideStage.getSplitDecorManager().release(callbackT);
|
||||
});
|
||||
|
||||
addDividerBarToTransition(info, false /* show */);
|
||||
return true;
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen;
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
|
||||
@@ -201,7 +202,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
if (mRootTaskInfo.taskId == taskInfo.taskId) {
|
||||
// Inflates split decor view only when the root task is visible.
|
||||
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
|
||||
if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
|
||||
if (taskInfo.isVisible) {
|
||||
mSplitDecorManager.inflate(mContext, mRootLeash,
|
||||
taskInfo.configuration.windowConfiguration.getBounds());
|
||||
@@ -385,6 +386,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
void resetBounds(WindowContainerTransaction wct) {
|
||||
wct.setBounds(mRootTaskInfo.token, null);
|
||||
wct.setAppBounds(mRootTaskInfo.token, null);
|
||||
wct.setSmallestScreenWidthDp(mRootTaskInfo.token, SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
|
||||
}
|
||||
|
||||
void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
|
||||
|
||||
@@ -44,11 +44,15 @@ public class SplitTestUtils {
|
||||
|
||||
static SplitLayout createMockSplitLayout() {
|
||||
final Rect dividerBounds = new Rect(48, 0, 52, 100);
|
||||
final Rect bounds1 = new Rect(0, 0, 40, 100);
|
||||
final Rect bounds2 = new Rect(60, 0, 100, 100);
|
||||
final SurfaceControl leash = createMockSurface();
|
||||
SplitLayout out = mock(SplitLayout.class);
|
||||
doReturn(dividerBounds).when(out).getDividerBounds();
|
||||
doReturn(dividerBounds).when(out).getRefDividerBounds();
|
||||
doReturn(leash).when(out).getDividerLeash();
|
||||
doReturn(bounds1).when(out).getBounds1();
|
||||
doReturn(bounds2).when(out).getBounds2();
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
@@ -72,6 +73,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
@@ -117,13 +119,13 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
|
||||
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
|
||||
mSplitLayout = SplitTestUtils.createMockSplitLayout();
|
||||
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mIconProvider);
|
||||
mIconProvider));
|
||||
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
|
||||
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
|
||||
mIconProvider);
|
||||
mIconProvider));
|
||||
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
|
||||
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
|
||||
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
|
||||
@@ -137,6 +139,8 @@ public class SplitTransitionTests extends ShellTestCase {
|
||||
.setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();
|
||||
mSideChild = new TestRunningTaskInfoBuilder()
|
||||
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
|
||||
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
|
||||
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -68,6 +68,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
|
||||
import com.android.wm.shell.sysui.ShellController;
|
||||
@@ -145,6 +146,8 @@ public class StageCoordinatorTests extends ShellTestCase {
|
||||
|
||||
mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
||||
mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
|
||||
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
|
||||
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user