Merge "Improve enter split transition" into tm-qpr-dev am: 9ab7dedcdd

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19458153

Change-Id: I0ece5d1456a72e94188732b16a7cd6abdad907e3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Tony Huang
2022-08-23 02:35:39 +00:00
committed by Automerger Merge Worker
3 changed files with 81 additions and 28 deletions

View File

@@ -80,7 +80,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public static final int PARALLAX_DISMISSING = 1; public static final int PARALLAX_DISMISSING = 1;
public static final int PARALLAX_ALIGN_CENTER = 2; public static final int PARALLAX_ALIGN_CENTER = 2;
private static final int FLING_ANIMATION_DURATION = 250; private static final int FLING_RESIZE_DURATION = 250;
private static final int FLING_SWITCH_DURATION = 350;
private static final int FLING_ENTER_DURATION = 350;
private static final int FLING_EXIT_DURATION = 350;
private final int mDividerWindowWidth; private final int mDividerWindowWidth;
private final int mDividerInsets; private final int mDividerInsets;
@@ -93,6 +96,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final Rect mBounds1 = new Rect(); private final Rect mBounds1 = new Rect();
// Bounds2 final position should be always at bottom or right // Bounds2 final position should be always at bottom or right
private final Rect mBounds2 = new Rect(); private final Rect mBounds2 = new Rect();
// The temp bounds outside of display bounds for side stage when split screen inactive to avoid
// flicker next time active split screen.
private final Rect mInvisibleBounds = new Rect();
private final Rect mWinBounds1 = new Rect(); private final Rect mWinBounds1 = new Rect();
private final Rect mWinBounds2 = new Rect(); private final Rect mWinBounds2 = new Rect();
private final SplitLayoutHandler mSplitLayoutHandler; private final SplitLayoutHandler mSplitLayoutHandler;
@@ -141,6 +147,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
resetDividerPosition(); resetDividerPosition();
mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide); mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide);
mInvisibleBounds.set(mRootBounds);
mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
isLandscape() ? 0 : mRootBounds.bottom);
} }
private int getDividerInsets(Resources resources, Display display) { private int getDividerInsets(Resources resources, Display display) {
@@ -239,6 +249,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
rect.offset(-mRootBounds.left, -mRootBounds.top); rect.offset(-mRootBounds.left, -mRootBounds.top);
} }
/** Gets bounds size equal to root bounds but outside of screen, used for position side stage
* when split inactive to avoid flicker when next time active. */
public void getInvisibleBounds(Rect rect) {
rect.set(mInvisibleBounds);
}
/** Returns leash of the current divider bar. */ /** Returns leash of the current divider bar. */
@Nullable @Nullable
public SurfaceControl getDividerLeash() { public SurfaceControl getDividerLeash() {
@@ -284,6 +300,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
initDividerPosition(mTempRect); initDividerPosition(mTempRect);
mInvisibleBounds.set(mRootBounds);
mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
isLandscape() ? 0 : mRootBounds.bottom);
return true; return true;
} }
@@ -405,6 +425,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mFreezeDividerWindow = freezeDividerWindow; mFreezeDividerWindow = freezeDividerWindow;
} }
/** Update current layout as divider put on start or end position. */
public void setDividerAtBorder(boolean start) {
final int pos = start ? mDividerSnapAlgorithm.getDismissStartTarget().position
: mDividerSnapAlgorithm.getDismissEndTarget().position;
setDividePosition(pos, false /* applyLayoutChange */);
}
/** /**
* Updates bounds with the passing position. Usually used to update recording bounds while * Updates bounds with the passing position. Usually used to update recording bounds while
* performing animation or dragging divider bar to resize the splits. * performing animation or dragging divider bar to resize the splits.
@@ -449,17 +476,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) { public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) { switch (snapTarget.flag) {
case FLAG_DISMISS_START: case FLAG_DISMISS_START:
flingDividePosition(currentPosition, snapTarget.position, flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */, () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER)); EXIT_REASON_DRAG_DIVIDER));
break; break;
case FLAG_DISMISS_END: case FLAG_DISMISS_END:
flingDividePosition(currentPosition, snapTarget.position, flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */, () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER)); EXIT_REASON_DRAG_DIVIDER));
break; break;
default: default:
flingDividePosition(currentPosition, snapTarget.position, flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> setDividePosition(snapTarget.position, true /* applyLayoutChange */)); () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break; break;
} }
@@ -516,12 +543,20 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public void flingDividerToDismiss(boolean toEnd, int reason) { public void flingDividerToDismiss(boolean toEnd, int reason) {
final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position
: mDividerSnapAlgorithm.getDismissStartTarget().position; : mDividerSnapAlgorithm.getDismissStartTarget().position;
flingDividePosition(getDividePosition(), target, flingDividePosition(getDividePosition(), target, FLING_EXIT_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason)); () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason));
} }
/** Fling divider from current position to center position. */
public void flingDividerToCenter() {
final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
flingDividePosition(getDividePosition(), pos, FLING_ENTER_DURATION,
() -> setDividePosition(pos, true /* applyLayoutChange */));
}
@VisibleForTesting @VisibleForTesting
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) { void flingDividePosition(int from, int to, int duration,
@Nullable Runnable flingFinishedCallback) {
if (from == to) { if (from == to) {
// No animation run, still callback to stop resizing. // No animation run, still callback to stop resizing.
mSplitLayoutHandler.onLayoutSizeChanged(this); mSplitLayoutHandler.onLayoutSizeChanged(this);
@@ -531,7 +566,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
} }
ValueAnimator animator = ValueAnimator ValueAnimator animator = ValueAnimator
.ofInt(from, to) .ofInt(from, to)
.setDuration(FLING_ANIMATION_DURATION); .setDuration(duration);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addUpdateListener( animator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue())); animation -> updateDivideBounds((int) animation.getAnimatedValue()));
@@ -588,7 +623,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
AnimatorSet set = new AnimatorSet(); AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2, animator3); set.playTogether(animator1, animator2, animator3);
set.setDuration(FLING_ANIMATION_DURATION); set.setDuration(FLING_SWITCH_DURATION);
set.addListener(new AnimatorListenerAdapter() { set.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {

View File

@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -488,13 +489,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct); options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
// If split still not active, apply windows bounds first to avoid surface reset to
// wrong pos by SurfaceAnimator from wms.
// TODO(b/223325631): check is it still necessary after improve enter transition done.
if (!mMainStage.isActive()) {
updateWindowBounds(mSplitLayout, wct);
}
wct.sendPendingIntent(intent, fillInIntent, options); wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
} }
@@ -641,7 +635,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.startTask(sideTaskId, sideOptions); wct.startTask(sideTaskId, sideOptions);
} }
// Using legacy transitions, so we can't use blast sync since it conflicts. // Using legacy transitions, so we can't use blast sync since it conflicts.
mTaskOrganizer.applyTransaction(wct); mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> { mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t); setDividerVisibility(true, t);
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
@@ -893,10 +887,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mShouldUpdateRecents = false; mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = false; mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null) { if (childrenToTop == null) {
mSideStage.removeAllTasks(wct, false /* toTop */); mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */); mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */); wct.reorder(mRootTaskInfo.token, false /* onTop */);
wct.setForceTranslucent(mRootTaskInfo.token, true);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
onTransitionAnimationComplete(); onTransitionAnimationComplete();
} else { } else {
// Expand to top side split as full screen for fading out decor animation and dismiss // Expand to top side split as full screen for fading out decor animation and dismiss
@@ -907,27 +904,32 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
? mSideStage : mMainStage; ? mSideStage : mMainStage;
tempFullStage.resetBounds(wct); tempFullStage.resetBounds(wct);
wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token, wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token,
mRootTaskInfo.configuration.smallestScreenWidthDp); SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
dismissStage.dismiss(wct, false /* toTop */); dismissStage.dismiss(wct, false /* toTop */);
} }
mSyncQueue.queue(wct); mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> { mSyncQueue.runInSync(t -> {
t.setWindowCrop(mMainStage.mRootLeash, null) t.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null); .setWindowCrop(mSideStage.mRootLeash, null);
t.setPosition(mMainStage.mRootLeash, 0, 0)
.setPosition(mSideStage.mRootLeash, 0, 0);
t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer); t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t); setDividerVisibility(false, t);
// In this case, exit still under progress, fade out the split decor after first WCT if (childrenToTop == null) {
// done and do remaining WCT after animation finished. t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
if (childrenToTop != null) { } else {
// In this case, exit still under progress, fade out the split decor after first WCT
// done and do remaining WCT after animation finished.
childrenToTop.fadeOutDecor(() -> { childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false; mIsExiting = false;
childrenToTop.dismiss(finishedWCT, true /* toTop */); childrenToTop.dismiss(finishedWCT, true /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */); finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
mTaskOrganizer.applyTransaction(finishedWCT); finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(finishedWCT);
mSyncQueue.runInSync(at -> {
at.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
});
onTransitionAnimationComplete(); onTransitionAnimationComplete();
}); });
} }
@@ -996,6 +998,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.activate(wct, true /* includingTopTask */); mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct); updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true); wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
} }
void finishEnterSplitScreen(SurfaceControl.Transaction t) { void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1221,7 +1224,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Make the stages adjacent to each other so they occlude what's behind them. // Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct); wct.setForceTranslucent(mRootTaskInfo.token, true);
mSplitLayout.getInvisibleBounds(mTempRect1);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
});
} }
private void onRootTaskVanished() { private void onRootTaskVanished() {
@@ -1377,10 +1386,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// TODO (b/238697912) : Add the validation to prevent entering non-recovered status // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init(); mSplitLayout.init();
prepareEnterSplitScreen(wct); mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
mMainStage.activate(wct, true /* includingTopTask */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
mSyncQueue.queue(wct); mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> mSyncQueue.runInSync(t -> {
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
mSplitLayout.flingDividerToCenter();
});
} }
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true; mShouldUpdateRecents = true;
@@ -1822,6 +1838,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// properly for the animation itself. // properly for the animation itself.
mSplitLayout.release(); mSplitLayout.release();
mSplitLayout.resetDividerPosition(); mSplitLayout.resetDividerPosition();
mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
} }
} }

View File

@@ -159,7 +159,8 @@ public class SplitLayoutTests extends ShellTestCase {
} }
private void waitDividerFlingFinished() { private void waitDividerFlingFinished() {
verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture()); verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(),
mRunnableCaptor.capture());
mRunnableCaptor.getValue().run(); mRunnableCaptor.getValue().run();
} }