Implement split resize shell transition
For reduce jank or flicker when resize ActivityEmbedding in split case, we should apply shell transition to resize case too. Fix: 247678744 Test: manual Test: pass existing tests Change-Id: I8a1aa500d5928284ca7955042dffb617be15635a
This commit is contained in:
@@ -51,8 +51,6 @@ import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.ScreenshotUtils;
|
||||
import com.android.wm.shell.common.SurfaceUtils;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Handles split decor like showing resizing hint for a specific split.
|
||||
*/
|
||||
@@ -72,17 +70,18 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
private SurfaceControl mIconLeash;
|
||||
private SurfaceControl mBackgroundLeash;
|
||||
private SurfaceControl mGapBackgroundLeash;
|
||||
private SurfaceControl mScreenshot;
|
||||
|
||||
private boolean mShown;
|
||||
private boolean mIsResizing;
|
||||
private final Rect mBounds = new Rect();
|
||||
private final Rect mResizingBounds = new Rect();
|
||||
private final Rect mTempRect = new Rect();
|
||||
private ValueAnimator mFadeAnimator;
|
||||
|
||||
private int mIconSize;
|
||||
private int mOffsetX;
|
||||
private int mOffsetY;
|
||||
private int mRunningAnimationCount = 0;
|
||||
|
||||
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
|
||||
SurfaceSession surfaceSession) {
|
||||
@@ -173,7 +172,6 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
mIsResizing = true;
|
||||
mBounds.set(newBounds);
|
||||
}
|
||||
mResizingBounds.set(newBounds);
|
||||
mOffsetX = offsetX;
|
||||
mOffsetY = offsetY;
|
||||
|
||||
@@ -227,33 +225,41 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
t.setVisibility(mBackgroundLeash, show);
|
||||
t.setVisibility(mIconLeash, show);
|
||||
} else {
|
||||
startFadeAnimation(show, null /* finishedConsumer */);
|
||||
startFadeAnimation(show, false, null);
|
||||
}
|
||||
mShown = show;
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops showing resizing hint. */
|
||||
public void onResized(SurfaceControl.Transaction t) {
|
||||
if (!mShown && mIsResizing) {
|
||||
mTempRect.set(mResizingBounds);
|
||||
mTempRect.offsetTo(-mOffsetX, -mOffsetY);
|
||||
final SurfaceControl screenshot = ScreenshotUtils.takeScreenshot(t,
|
||||
mHostLeash, mTempRect, Integer.MAX_VALUE - 1);
|
||||
public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
|
||||
if (mScreenshot != null) {
|
||||
t.setPosition(mScreenshot, mOffsetX, mOffsetY);
|
||||
|
||||
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
|
||||
final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
|
||||
va.addUpdateListener(valueAnimator -> {
|
||||
final float progress = (float) valueAnimator.getAnimatedValue();
|
||||
animT.setAlpha(screenshot, progress);
|
||||
animT.setAlpha(mScreenshot, progress);
|
||||
animT.apply();
|
||||
});
|
||||
va.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mRunningAnimationCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
|
||||
animT.remove(screenshot);
|
||||
mRunningAnimationCount--;
|
||||
animT.remove(mScreenshot);
|
||||
animT.apply();
|
||||
animT.close();
|
||||
mScreenshot = null;
|
||||
|
||||
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
|
||||
animFinishedCallback.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
va.start();
|
||||
@@ -285,10 +291,34 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
if (mShown) {
|
||||
fadeOutDecor(null /* finishedCallback */);
|
||||
fadeOutDecor(animFinishedCallback);
|
||||
} else {
|
||||
// Decor surface is hidden so release it directly.
|
||||
releaseDecor(t);
|
||||
if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
|
||||
animFinishedCallback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Screenshot host leash and attach on it if meet some conditions */
|
||||
public void screenshotIfNeeded(SurfaceControl.Transaction t) {
|
||||
if (!mShown && mIsResizing) {
|
||||
mTempRect.set(mBounds);
|
||||
mTempRect.offsetTo(0, 0);
|
||||
mScreenshot = ScreenshotUtils.takeScreenshot(t, mHostLeash, mTempRect,
|
||||
Integer.MAX_VALUE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Set screenshot and attach on host leash it if meet some conditions */
|
||||
public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) {
|
||||
if (screenshot == null || !screenshot.isValid()) return;
|
||||
|
||||
if (!mShown && mIsResizing) {
|
||||
mScreenshot = screenshot;
|
||||
t.reparent(screenshot, mHostLeash);
|
||||
t.setLayer(screenshot, Integer.MAX_VALUE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,18 +326,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
* directly. */
|
||||
public void fadeOutDecor(Runnable finishedCallback) {
|
||||
if (mShown) {
|
||||
startFadeAnimation(false /* show */, transaction -> {
|
||||
releaseDecor(transaction);
|
||||
if (finishedCallback != null) finishedCallback.run();
|
||||
});
|
||||
startFadeAnimation(false /* show */, true, finishedCallback);
|
||||
mShown = false;
|
||||
} else {
|
||||
if (finishedCallback != null) finishedCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void startFadeAnimation(boolean show,
|
||||
Consumer<SurfaceControl.Transaction> finishedConsumer) {
|
||||
private void startFadeAnimation(boolean show, boolean releaseSurface,
|
||||
Runnable finishedCallback) {
|
||||
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
|
||||
mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
mFadeAnimator.setDuration(FADE_DURATION);
|
||||
@@ -324,6 +351,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(@NonNull Animator animation) {
|
||||
mRunningAnimationCount++;
|
||||
if (show) {
|
||||
animT.show(mBackgroundLeash).show(mIconLeash);
|
||||
}
|
||||
@@ -335,6 +363,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(@NonNull Animator animation) {
|
||||
mRunningAnimationCount--;
|
||||
if (!show) {
|
||||
if (mBackgroundLeash != null) {
|
||||
animT.hide(mBackgroundLeash);
|
||||
@@ -343,11 +372,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
|
||||
animT.hide(mIconLeash);
|
||||
}
|
||||
}
|
||||
if (finishedConsumer != null) {
|
||||
finishedConsumer.accept(animT);
|
||||
if (releaseSurface) {
|
||||
releaseDecor(animT);
|
||||
}
|
||||
animT.apply();
|
||||
animT.close();
|
||||
|
||||
if (mRunningAnimationCount == 0 && finishedCallback != null) {
|
||||
finishedCallback.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
mFadeAnimator.start();
|
||||
|
||||
@@ -47,6 +47,7 @@ import android.window.WindowContainerTransactionCallback;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.transition.OneShotRemoteHandler;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
@@ -64,6 +65,7 @@ class SplitScreenTransitions {
|
||||
DismissTransition mPendingDismiss = null;
|
||||
TransitSession mPendingEnter = null;
|
||||
TransitSession mPendingRecent = null;
|
||||
TransitSession mPendingResize = null;
|
||||
|
||||
private IBinder mAnimatingTransition = null;
|
||||
OneShotRemoteHandler mPendingRemoteHandler = null;
|
||||
@@ -177,6 +179,43 @@ class SplitScreenTransitions {
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
}
|
||||
|
||||
void applyResizeTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
|
||||
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
|
||||
mFinishCallback = finishCallback;
|
||||
mAnimatingTransition = transition;
|
||||
mFinishTransaction = finishTransaction;
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) {
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
startTransaction.setPosition(leash, change.getEndAbsBounds().left,
|
||||
change.getEndAbsBounds().top);
|
||||
startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(),
|
||||
change.getEndAbsBounds().height());
|
||||
|
||||
SplitDecorManager decor = mainRoot.equals(change.getContainer())
|
||||
? mainDecor : sideDecor;
|
||||
ValueAnimator va = new ValueAnimator();
|
||||
mAnimations.add(va);
|
||||
decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
|
||||
decor.onResized(startTransaction, () -> {
|
||||
mTransitions.getMainExecutor().execute(() -> {
|
||||
mAnimations.remove(va);
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startTransaction.apply();
|
||||
onFinish(null /* wct */, null /* wctCB */);
|
||||
}
|
||||
|
||||
boolean isPendingTransition(IBinder transition) {
|
||||
return getPendingTransition(transition) != null;
|
||||
}
|
||||
@@ -193,6 +232,10 @@ class SplitScreenTransitions {
|
||||
return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
|
||||
}
|
||||
|
||||
boolean isPendingResize(IBinder transition) {
|
||||
return mPendingResize != null && mPendingResize.mTransition == transition;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TransitSession getPendingTransition(IBinder transition) {
|
||||
if (isPendingEnter(transition)) {
|
||||
@@ -201,11 +244,14 @@ class SplitScreenTransitions {
|
||||
return mPendingRecent;
|
||||
} else if (isPendingDismiss(transition)) {
|
||||
return mPendingDismiss;
|
||||
} else if (isPendingResize(transition)) {
|
||||
return mPendingResize;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/** Starts a transition to enter split with a remote transition animator. */
|
||||
IBinder startEnterTransition(
|
||||
@WindowManager.TransitionType int transitType,
|
||||
@@ -258,6 +304,21 @@ class SplitScreenTransitions {
|
||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||
}
|
||||
|
||||
IBinder startResizeTransition(WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler,
|
||||
@Nullable TransitionFinishedCallback finishCallback) {
|
||||
IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
|
||||
setResizeTransition(transition, finishCallback);
|
||||
return transition;
|
||||
}
|
||||
|
||||
void setResizeTransition(@NonNull IBinder transition,
|
||||
@Nullable TransitionFinishedCallback finishCallback) {
|
||||
mPendingResize = new TransitSession(transition, null /* consumedCb */, finishCallback);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Resize split screen");
|
||||
}
|
||||
|
||||
void setRecentTransition(@NonNull IBinder transition,
|
||||
@Nullable RemoteTransition remoteTransition,
|
||||
@Nullable TransitionFinishedCallback finishCallback) {
|
||||
@@ -324,6 +385,9 @@ class SplitScreenTransitions {
|
||||
mPendingRecent.onConsumed(aborted);
|
||||
mPendingRecent = null;
|
||||
mPendingRemoteHandler = null;
|
||||
} else if (isPendingResize(transition)) {
|
||||
mPendingResize.onConsumed(aborted);
|
||||
mPendingResize = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +404,9 @@ class SplitScreenTransitions {
|
||||
} else if (isPendingDismiss(mAnimatingTransition)) {
|
||||
mPendingDismiss.onFinished(wct, mFinishTransaction);
|
||||
mPendingDismiss = null;
|
||||
} else if (isPendingResize(mAnimatingTransition)) {
|
||||
mPendingResize.onFinished(wct, mFinishTransaction);
|
||||
mPendingResize = null;
|
||||
}
|
||||
|
||||
mPendingRemoteHandler = null;
|
||||
|
||||
@@ -1670,15 +1670,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
public void onLayoutSizeChanged(SplitLayout layout) {
|
||||
// Reset this flag every time onLayoutSizeChanged.
|
||||
mShowDecorImmediately = false;
|
||||
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
// Only need screenshot for legacy case because shell transition should screenshot
|
||||
// itself during transition.
|
||||
final SurfaceControl.Transaction startT = mTransactionPool.acquire();
|
||||
mMainStage.screenshotIfNeeded(startT);
|
||||
mSideStage.screenshotIfNeeded(startT);
|
||||
mTransactionPool.release(startT);
|
||||
}
|
||||
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
updateWindowBounds(layout, wct);
|
||||
sendOnBoundsChanged();
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
|
||||
mMainStage.onResized(t);
|
||||
mSideStage.onResized(t);
|
||||
});
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
mSplitTransitions.startResizeTransition(wct, this, null /* callback */);
|
||||
} else {
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
|
||||
mMainStage.onResized(t);
|
||||
mSideStage.onResized(t);
|
||||
});
|
||||
}
|
||||
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
|
||||
}
|
||||
|
||||
@@ -2032,6 +2046,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
} else if (mSplitTransitions.isPendingDismiss(transition)) {
|
||||
shouldAnimate = startPendingDismissAnimation(
|
||||
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
|
||||
} else if (mSplitTransitions.isPendingResize(transition)) {
|
||||
mSplitTransitions.applyResizeTransition(transition, info, startTransaction,
|
||||
finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
|
||||
mSideStage.mRootTaskInfo.token, mMainStage.getSplitDecorManager(),
|
||||
mSideStage.getSplitDecorManager());
|
||||
return true;
|
||||
}
|
||||
if (!shouldAnimate) return false;
|
||||
|
||||
|
||||
@@ -292,7 +292,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
|
||||
void onResized(SurfaceControl.Transaction t) {
|
||||
if (mSplitDecorManager != null) {
|
||||
mSplitDecorManager.onResized(t);
|
||||
mSplitDecorManager.onResized(t, null);
|
||||
}
|
||||
}
|
||||
|
||||
void screenshotIfNeeded(SurfaceControl.Transaction t) {
|
||||
if (mSplitDecorManager != null) {
|
||||
mSplitDecorManager.screenshotIfNeeded(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +310,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
}
|
||||
}
|
||||
|
||||
SplitDecorManager getSplitDecorManager() {
|
||||
return mSplitDecorManager;
|
||||
}
|
||||
|
||||
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
|
||||
// Clear overridden bounds and windowing mode to make sure the child task can inherit
|
||||
// windowing mode and bounds from split root.
|
||||
|
||||
@@ -2177,7 +2177,7 @@ class Task extends TaskFragment {
|
||||
}
|
||||
|
||||
private boolean shouldStartChangeTransition(int prevWinMode, @NonNull Rect prevBounds) {
|
||||
if (!isLeafTask() || !canStartChangeTransition()) {
|
||||
if (!(isLeafTask() || mCreatedByOrganizer) || !canStartChangeTransition()) {
|
||||
return false;
|
||||
}
|
||||
final int newWinMode = getWindowingMode();
|
||||
|
||||
Reference in New Issue
Block a user