Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +3 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; Loading Loading @@ -60,7 +62,6 @@ public class SplitDecorManager extends WindowlessWindowManager { private static final String TAG = SplitDecorManager.class.getSimpleName(); private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground"; private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground"; private static final long FADE_DURATION = 133; private final IconProvider mIconProvider; private final SurfaceSession mSurfaceSession; Loading Loading @@ -263,6 +264,7 @@ public class SplitDecorManager extends WindowlessWindowManager { final SurfaceControl.Transaction animT = new SurfaceControl.Transaction(); mScreenshotAnimator = ValueAnimator.ofFloat(1, 0); mScreenshotAnimator.setDuration(FADE_DURATION); mScreenshotAnimator.addUpdateListener(valueAnimator -> { final float progress = (float) valueAnimator.getAnimatedValue(); animT.setAlpha(mScreenshot, progress); Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,11 @@ public class SplitScreenConstants { */ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1; /** * Duration used for every split fade-in or fade-out. */ public static final int FADE_DURATION = 133; @IntDef(prefix = {"SPLIT_POSITION_"}, value = { SPLIT_POSITION_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +65 −79 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; Loading @@ -34,7 +35,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -63,7 +63,7 @@ class SplitScreenTransitions { private final Runnable mOnFinish; DismissSession mPendingDismiss = null; TransitSession mPendingEnter = null; EnterSession mPendingEnter = null; TransitSession mPendingResize = null; private IBinder mAnimatingTransition = null; Loading Loading @@ -120,6 +120,7 @@ class SplitScreenTransitions { @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { // Play some place-holder fade animations final boolean isEnter = isPendingEnter(transition); for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); Loading @@ -142,17 +143,23 @@ class SplitScreenTransitions { change.getEndRelOffset().x, change.getEndRelOffset().y); } } boolean isRootOrSplitSideRoot = change.getParent() == null || topRoot.equals(change.getParent()); boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR; // For enter or exit, we only want to animate side roots and the divider but not the // top-root. if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer()) || isDivider) { continue; } if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer()))) { final boolean isTopRoot = topRoot.equals(change.getContainer()); final boolean isMainRoot = mainRoot.equals(change.getContainer()); final boolean isSideRoot = sideRoot.equals(change.getContainer()); final boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR; final boolean isMainChild = mainRoot.equals(change.getParent()); final boolean isSideChild = sideRoot.equals(change.getParent()); if (isEnter && (isMainChild || isSideChild)) { // Reset child tasks bounds on finish. mFinishTransaction.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); mFinishTransaction.setCrop(leash, null); } else if (isEnter && isTopRoot) { // Ensure top root is visible at start. t.setAlpha(leash, 1.f); t.show(leash); } else if (isEnter && isMainRoot || isSideRoot) { t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top); t.setWindowCrop(leash, change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); Loading @@ -161,10 +168,24 @@ class SplitScreenTransitions { t.setLayer(leash, Integer.MAX_VALUE); t.show(leash); } // These container changes we don't want to animate them. // We should only animate stage root, divider and child tasks are not under stage root. if (isTopRoot || isMainChild || isSideChild || change.getTaskInfo() == null) { continue; } if (isEnter && mPendingEnter.mResizeAnim) { // We will run animation in next transition so skip anim here continue; } else if (isEnter && isMainRoot) { // Main stage already on top so skip fade in animation to reduce flicker. continue; } boolean isOpening = TransitionUtil.isOpeningType(info.getType()); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { // fade in startExampleAnimation(leash, true /* show */); startFadeAnimation(leash, true /* show */); } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { Loading @@ -173,7 +194,7 @@ class SplitScreenTransitions { // and don't animate it so it doesn't pop-in when reparented. t.setAlpha(leash, 0.f); } else { startExampleAnimation(leash, false /* show */); startFadeAnimation(leash, false /* show */); } } } Loading Loading @@ -267,7 +288,7 @@ class SplitScreenTransitions { Transitions.TransitionHandler handler, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, int extraTransitType) { int extraTransitType, boolean resizeAnim) { if (mPendingEnter != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " skip to start enter split transition since it already exist. "); Loading @@ -275,7 +296,7 @@ class SplitScreenTransitions { } final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback, extraTransitType); extraTransitType, resizeAnim); return transition; } Loading @@ -284,9 +305,10 @@ class SplitScreenTransitions { @Nullable RemoteTransition remoteTransition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, int extraTransitType) { mPendingEnter = new TransitSession( transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType); int extraTransitType, boolean resizeAnim) { mPendingEnter = new EnterSession( transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType, resizeAnim); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter split screen"); Loading Loading @@ -409,78 +431,27 @@ class SplitScreenTransitions { } } // TODO(shell-transitions): real animations private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(500); va.setDuration(FADE_DURATION); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); }); final Runnable finisher = () -> { transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); mTransitions.getMainExecutor().execute(() -> { mAnimations.remove(va); onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); } @Override public void onAnimationCancel(Animator animation) { finisher.run(); } }); mAnimations.add(va); mTransitions.getAnimExecutor().execute(va::start); } // TODO(shell-transitions): real animations private void startExampleResizeAnimation(@NonNull SurfaceControl leash, @NonNull Rect startBounds, @NonNull Rect endBounds) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); va.setDuration(500); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setWindowCrop(leash, (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), (int) (startBounds.height() * (1.f - fraction) + endBounds.height() * fraction)); transaction.setPosition(leash, startBounds.left * (1.f - fraction) + endBounds.left * fraction, startBounds.top * (1.f - fraction) + endBounds.top * fraction); transaction.apply(); }); final Runnable finisher = () -> { transaction.setWindowCrop(leash, 0, 0); transaction.setPosition(leash, endBounds.left, endBounds.top); transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); mTransitions.getMainExecutor().execute(() -> { mAnimations.remove(va); onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); } @Override public void onAnimationCancel(Animator animation) { finisher.run(); } }); mAnimations.add(va); Loading Loading @@ -569,6 +540,21 @@ class SplitScreenTransitions { } } /** Bundled information of enter transition. */ class EnterSession extends TransitSession { final boolean mResizeAnim; EnterSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, @Nullable RemoteTransition remoteTransition, int extraTransitType, boolean resizeAnim) { super(transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType); this.mResizeAnim = resizeAnim; } } /** Bundled information of dismiss transition. */ class DismissSession extends TransitSession { final int mReason; Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +32 −45 Original line number Diff line number Diff line Loading @@ -395,7 +395,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, null /* consumedCallback */, null /* finishedCallback */, isSplitScreenVisible() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN); ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); } else { mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -503,7 +504,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(wct, null /* taskInfo */, position); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, null /* consumedCallback */, null /* finishedCallback */, extraTransitType); null /* consumedCallback */, null /* finishedCallback */, extraTransitType, !mIsDropEntering); } /** Launches an activity into split by legacy transition. */ Loading Loading @@ -661,7 +663,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition( TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); setEnterInstanceId(instanceId); } Loading Loading @@ -713,7 +715,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition( TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); setEnterInstanceId(instanceId); } Loading Loading @@ -1494,14 +1496,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void prepareBringSplit(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { StageTaskListener targetStage; if (isSplitScreenVisible()) { // If the split screen is foreground, retrieves target stage based on position. targetStage = startPosition == mSideStagePosition ? mSideStage : mMainStage; } else { targetStage = mSideStage; } if (taskInfo != null) { wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); Loading @@ -1510,13 +1504,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // and evict all tasks current under its. if (!isSplitScreenVisible()) { // Recreate so we need to reset position rather than keep position of background split. mSplitLayout.resetDividerPosition(); updateWindowBounds(mSplitLayout, wct); final StageTaskListener anotherStage = targetStage == mMainStage ? mSideStage : mMainStage; anotherStage.reparentTopTask(wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); mMainStage.reparentTopTask(wct); prepareSplitLayout(wct); } } Loading @@ -1532,8 +1521,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.addTask(taskInfo, wct); } mMainStage.activate(wct, true /* includingTopTask */); prepareSplitLayout(wct); } private void prepareSplitLayout(WindowContainerTransaction wct) { if (mIsDropEntering) { mSplitLayout.resetDividerPosition(); } else { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); } updateWindowBounds(mSplitLayout, wct); if (!mIsDropEntering) { // Reset its smallest width dp to avoid is change layout before it actually resized to // split bounds. wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token, SMALLEST_SCREEN_WIDTH_DP_UNDEFINED); } wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); } Loading @@ -1548,6 +1551,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); t.show(mRootTaskLeash); setSplitsVisible(true); mIsDropEntering = false; updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), Loading Loading @@ -1777,18 +1781,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Handle entering split screen while there is a split pair running in the background. if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive() && mSplitRequest == null) { if (mIsDropEntering) { mSplitLayout.resetDividerPosition(); } else { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); } final WindowContainerTransaction wct = new WindowContainerTransaction(); mMainStage.reparentTopTask(wct); prepareEnterSplitScreen(wct); mMainStage.evictAllChildren(wct); mSideStage.evictOtherChildren(wct, taskId); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -1983,20 +1979,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { mSplitLayout.init(); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (mIsDropEntering) { prepareEnterSplitScreen(wct); } else { // TODO (b/238697912) : Add the validation to prevent entering non-recovered status onSplitScreenEnter(); mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); mMainStage.activate(wct, true /* includingTopTask */); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); } mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -2353,7 +2337,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), null /* consumedCallback */, null /* finishedCallback */, 0 /* extraTransitType */); 0 /* extraTransitType */, !mIsDropEntering); } } return out; Loading Loading @@ -2596,7 +2580,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private boolean startPendingEnterAnimation( @NonNull SplitScreenTransitions.TransitSession enterTransition, @NonNull SplitScreenTransitions.EnterSession enterTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { // First, verify that we actually have opened apps in both splits. Loading Loading @@ -2654,7 +2638,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, + " to have been called with " + sideChild.getTaskInfo().taskId + " before startAnimation()."); } final TransitionInfo.Change finalMainChild = mainChild; final TransitionInfo.Change finalSideChild = sideChild; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { Loading @@ -2664,6 +2647,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (finalSideChild != null) { mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId); } if (enterTransition.mResizeAnim) { mShowDecorImmediately = true; mSplitLayout.flingDividerToCenter(); } }); finishEnterSplitScreen(finishT); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +2 −2 Original line number Diff line number Diff line Loading @@ -182,7 +182,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder transition = mSplitScreenTransitions.startEnterTransition( TRANSIT_OPEN, new WindowContainerTransaction(), new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, Loading Loading @@ -408,7 +408,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder enterTransit = mSplitScreenTransitions.startEnterTransition( TRANSIT_OPEN, new WindowContainerTransaction(), new RemoteTransition(new TestRemoteTransition(), "Test"), mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +3 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; Loading Loading @@ -60,7 +62,6 @@ public class SplitDecorManager extends WindowlessWindowManager { private static final String TAG = SplitDecorManager.class.getSimpleName(); private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground"; private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground"; private static final long FADE_DURATION = 133; private final IconProvider mIconProvider; private final SurfaceSession mSurfaceSession; Loading Loading @@ -263,6 +264,7 @@ public class SplitDecorManager extends WindowlessWindowManager { final SurfaceControl.Transaction animT = new SurfaceControl.Transaction(); mScreenshotAnimator = ValueAnimator.ofFloat(1, 0); mScreenshotAnimator.setDuration(FADE_DURATION); mScreenshotAnimator.addUpdateListener(valueAnimator -> { final float progress = (float) valueAnimator.getAnimatedValue(); animT.setAlpha(mScreenshot, progress); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,11 @@ public class SplitScreenConstants { */ public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1; /** * Duration used for every split fade-in or fade-out. */ public static final int FADE_DURATION = 133; @IntDef(prefix = {"SPLIT_POSITION_"}, value = { SPLIT_POSITION_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +65 −79 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; Loading @@ -34,7 +35,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -63,7 +63,7 @@ class SplitScreenTransitions { private final Runnable mOnFinish; DismissSession mPendingDismiss = null; TransitSession mPendingEnter = null; EnterSession mPendingEnter = null; TransitSession mPendingResize = null; private IBinder mAnimatingTransition = null; Loading Loading @@ -120,6 +120,7 @@ class SplitScreenTransitions { @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { // Play some place-holder fade animations final boolean isEnter = isPendingEnter(transition); for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); Loading @@ -142,17 +143,23 @@ class SplitScreenTransitions { change.getEndRelOffset().x, change.getEndRelOffset().y); } } boolean isRootOrSplitSideRoot = change.getParent() == null || topRoot.equals(change.getParent()); boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR; // For enter or exit, we only want to animate side roots and the divider but not the // top-root. if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer()) || isDivider) { continue; } if (isPendingEnter(transition) && (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer()))) { final boolean isTopRoot = topRoot.equals(change.getContainer()); final boolean isMainRoot = mainRoot.equals(change.getContainer()); final boolean isSideRoot = sideRoot.equals(change.getContainer()); final boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR; final boolean isMainChild = mainRoot.equals(change.getParent()); final boolean isSideChild = sideRoot.equals(change.getParent()); if (isEnter && (isMainChild || isSideChild)) { // Reset child tasks bounds on finish. mFinishTransaction.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); mFinishTransaction.setCrop(leash, null); } else if (isEnter && isTopRoot) { // Ensure top root is visible at start. t.setAlpha(leash, 1.f); t.show(leash); } else if (isEnter && isMainRoot || isSideRoot) { t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top); t.setWindowCrop(leash, change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); Loading @@ -161,10 +168,24 @@ class SplitScreenTransitions { t.setLayer(leash, Integer.MAX_VALUE); t.show(leash); } // These container changes we don't want to animate them. // We should only animate stage root, divider and child tasks are not under stage root. if (isTopRoot || isMainChild || isSideChild || change.getTaskInfo() == null) { continue; } if (isEnter && mPendingEnter.mResizeAnim) { // We will run animation in next transition so skip anim here continue; } else if (isEnter && isMainRoot) { // Main stage already on top so skip fade in animation to reduce flicker. continue; } boolean isOpening = TransitionUtil.isOpeningType(info.getType()); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { // fade in startExampleAnimation(leash, true /* show */); startFadeAnimation(leash, true /* show */); } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { Loading @@ -173,7 +194,7 @@ class SplitScreenTransitions { // and don't animate it so it doesn't pop-in when reparented. t.setAlpha(leash, 0.f); } else { startExampleAnimation(leash, false /* show */); startFadeAnimation(leash, false /* show */); } } } Loading Loading @@ -267,7 +288,7 @@ class SplitScreenTransitions { Transitions.TransitionHandler handler, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, int extraTransitType) { int extraTransitType, boolean resizeAnim) { if (mPendingEnter != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " skip to start enter split transition since it already exist. "); Loading @@ -275,7 +296,7 @@ class SplitScreenTransitions { } final IBinder transition = mTransitions.startTransition(transitType, wct, handler); setEnterTransition(transition, remoteTransition, consumedCallback, finishedCallback, extraTransitType); extraTransitType, resizeAnim); return transition; } Loading @@ -284,9 +305,10 @@ class SplitScreenTransitions { @Nullable RemoteTransition remoteTransition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, int extraTransitType) { mPendingEnter = new TransitSession( transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType); int extraTransitType, boolean resizeAnim) { mPendingEnter = new EnterSession( transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType, resizeAnim); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter split screen"); Loading Loading @@ -409,78 +431,27 @@ class SplitScreenTransitions { } } // TODO(shell-transitions): real animations private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(500); va.setDuration(FADE_DURATION); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); }); final Runnable finisher = () -> { transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); mTransitions.getMainExecutor().execute(() -> { mAnimations.remove(va); onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); } @Override public void onAnimationCancel(Animator animation) { finisher.run(); } }); mAnimations.add(va); mTransitions.getAnimExecutor().execute(va::start); } // TODO(shell-transitions): real animations private void startExampleResizeAnimation(@NonNull SurfaceControl leash, @NonNull Rect startBounds, @NonNull Rect endBounds) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); va.setDuration(500); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setWindowCrop(leash, (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), (int) (startBounds.height() * (1.f - fraction) + endBounds.height() * fraction)); transaction.setPosition(leash, startBounds.left * (1.f - fraction) + endBounds.left * fraction, startBounds.top * (1.f - fraction) + endBounds.top * fraction); transaction.apply(); }); final Runnable finisher = () -> { transaction.setWindowCrop(leash, 0, 0); transaction.setPosition(leash, endBounds.left, endBounds.top); transaction.setAlpha(leash, end); transaction.apply(); mTransactionPool.release(transaction); mTransitions.getMainExecutor().execute(() -> { mAnimations.remove(va); onFinish(null /* wct */, null /* wctCB */); }); }; va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); } @Override public void onAnimationCancel(Animator animation) { finisher.run(); } }); mAnimations.add(va); Loading Loading @@ -569,6 +540,21 @@ class SplitScreenTransitions { } } /** Bundled information of enter transition. */ class EnterSession extends TransitSession { final boolean mResizeAnim; EnterSession(IBinder transition, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishedCallback, @Nullable RemoteTransition remoteTransition, int extraTransitType, boolean resizeAnim) { super(transition, consumedCallback, finishedCallback, remoteTransition, extraTransitType); this.mResizeAnim = resizeAnim; } } /** Bundled information of dismiss transition. */ class DismissSession extends TransitSession { final int mReason; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +32 −45 Original line number Diff line number Diff line Loading @@ -395,7 +395,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, null /* consumedCallback */, null /* finishedCallback */, isSplitScreenVisible() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN); ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); } else { mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -503,7 +504,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(wct, null /* taskInfo */, position); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, null /* consumedCallback */, null /* finishedCallback */, extraTransitType); null /* consumedCallback */, null /* finishedCallback */, extraTransitType, !mIsDropEntering); } /** Launches an activity into split by legacy transition. */ Loading Loading @@ -661,7 +663,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition( TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); setEnterInstanceId(instanceId); } Loading Loading @@ -713,7 +715,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startEnterTransition( TRANSIT_TO_FRONT, wct, remoteTransition, this, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); setEnterInstanceId(instanceId); } Loading Loading @@ -1494,14 +1496,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void prepareBringSplit(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { StageTaskListener targetStage; if (isSplitScreenVisible()) { // If the split screen is foreground, retrieves target stage based on position. targetStage = startPosition == mSideStagePosition ? mSideStage : mMainStage; } else { targetStage = mSideStage; } if (taskInfo != null) { wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); Loading @@ -1510,13 +1504,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // and evict all tasks current under its. if (!isSplitScreenVisible()) { // Recreate so we need to reset position rather than keep position of background split. mSplitLayout.resetDividerPosition(); updateWindowBounds(mSplitLayout, wct); final StageTaskListener anotherStage = targetStage == mMainStage ? mSideStage : mMainStage; anotherStage.reparentTopTask(wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); mMainStage.reparentTopTask(wct); prepareSplitLayout(wct); } } Loading @@ -1532,8 +1521,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.addTask(taskInfo, wct); } mMainStage.activate(wct, true /* includingTopTask */); prepareSplitLayout(wct); } private void prepareSplitLayout(WindowContainerTransaction wct) { if (mIsDropEntering) { mSplitLayout.resetDividerPosition(); } else { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); } updateWindowBounds(mSplitLayout, wct); if (!mIsDropEntering) { // Reset its smallest width dp to avoid is change layout before it actually resized to // split bounds. wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token, SMALLEST_SCREEN_WIDTH_DP_UNDEFINED); } wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); } Loading @@ -1548,6 +1551,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); t.show(mRootTaskLeash); setSplitsVisible(true); mIsDropEntering = false; updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), Loading Loading @@ -1777,18 +1781,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Handle entering split screen while there is a split pair running in the background. if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive() && mSplitRequest == null) { if (mIsDropEntering) { mSplitLayout.resetDividerPosition(); } else { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); } final WindowContainerTransaction wct = new WindowContainerTransaction(); mMainStage.reparentTopTask(wct); prepareEnterSplitScreen(wct); mMainStage.evictAllChildren(wct); mSideStage.evictOtherChildren(wct, taskId); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -1983,20 +1979,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { mSplitLayout.init(); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (mIsDropEntering) { prepareEnterSplitScreen(wct); } else { // TODO (b/238697912) : Add the validation to prevent entering non-recovered status onSplitScreenEnter(); mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); mMainStage.activate(wct, true /* includingTopTask */); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); } mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { Loading Loading @@ -2353,7 +2337,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), null /* consumedCallback */, null /* finishedCallback */, 0 /* extraTransitType */); 0 /* extraTransitType */, !mIsDropEntering); } } return out; Loading Loading @@ -2596,7 +2580,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private boolean startPendingEnterAnimation( @NonNull SplitScreenTransitions.TransitSession enterTransition, @NonNull SplitScreenTransitions.EnterSession enterTransition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { // First, verify that we actually have opened apps in both splits. Loading Loading @@ -2654,7 +2638,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, + " to have been called with " + sideChild.getTaskInfo().taskId + " before startAnimation()."); } final TransitionInfo.Change finalMainChild = mainChild; final TransitionInfo.Change finalSideChild = sideChild; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { Loading @@ -2664,6 +2647,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (finalSideChild != null) { mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId); } if (enterTransition.mResizeAnim) { mShowDecorImmediately = true; mSplitLayout.flingDividerToCenter(); } }); finishEnterSplitScreen(finishT); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +2 −2 Original line number Diff line number Diff line Loading @@ -182,7 +182,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder transition = mSplitScreenTransitions.startEnterTransition( TRANSIT_OPEN, new WindowContainerTransaction(), new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, Loading Loading @@ -408,7 +408,7 @@ public class SplitTransitionTests extends ShellTestCase { IBinder enterTransit = mSplitScreenTransitions.startEnterTransition( TRANSIT_OPEN, new WindowContainerTransaction(), new RemoteTransition(new TestRemoteTransition(), "Test"), mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN); mStageCoordinator, null, null, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false); mMainStage.onTaskAppeared(mMainChild, createMockSurface()); mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, Loading