Merge "PiP: Introduce PipTransitionState." into sc-v2-dev

This commit is contained in:
Ben Lin
2021-06-10 21:45:25 +00:00
committed by Android (Google) Code Review
6 changed files with 153 additions and 70 deletions

View File

@@ -107,38 +107,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/ */
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
// Not a complete set of states but serves what we want right now.
private enum State {
UNDEFINED(0),
TASK_APPEARED(1),
ENTRY_SCHEDULED(2),
ENTERING_PIP(3),
ENTERED_PIP(4),
EXITING_PIP(5);
private final int mStateValue;
State(int value) {
mStateValue = value;
}
private boolean isInPip() {
return mStateValue >= TASK_APPEARED.mStateValue
&& mStateValue != EXITING_PIP.mStateValue;
}
/**
* Resize request can be initiated in other component, ignore if we are no longer in PIP,
* still waiting for animation or we're exiting from it.
*
* @return {@code true} if the resize request should be blocked/ignored.
*/
private boolean shouldBlockResizeRequest() {
return mStateValue < ENTERING_PIP.mStateValue
|| mStateValue == EXITING_PIP.mStateValue;
}
}
private final Context mContext; private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue; private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState; private final PipBoundsState mPipBoundsState;
@@ -190,7 +158,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
} }
final boolean isExitPipDirection = isOutPipDirection(direction) final boolean isExitPipDirection = isOutPipDirection(direction)
|| isRemovePipDirection(direction); || isRemovePipDirection(direction);
if (mState != State.EXITING_PIP || isExitPipDirection) { if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
|| isExitPipDirection) {
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is // Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation. // the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP // This is necessary in case there was a resize animation ongoing when exit PIP
@@ -233,7 +202,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private ActivityManager.RunningTaskInfo mDeferredTaskInfo; private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken; private WindowContainerToken mToken;
private SurfaceControl mLeash; private SurfaceControl mLeash;
private State mState = State.UNDEFINED; private PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime; private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -278,6 +247,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public PipTaskOrganizer(Context context, public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue, @NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler, @NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController, @NonNull PipMenuController pipMenuController,
@@ -291,6 +261,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
mContext = context; mContext = context;
mSyncTransactionQueue = syncTransactionQueue; mSyncTransactionQueue = syncTransactionQueue;
mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState; mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler; mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController; mPipMenuController = pipMenuController;
@@ -326,14 +297,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
} }
public boolean isInPip() { public boolean isInPip() {
return mState.isInPip(); return mPipTransitionState.isInPip();
} }
/** /**
* Returns whether the entry animation is waiting to be started. * Returns whether the entry animation is waiting to be started.
*/ */
public boolean isEntryScheduled() { public boolean isEntryScheduled() {
return mState == State.ENTRY_SCHEDULED; return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED;
} }
/** /**
@@ -401,9 +372,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* @param animationDurationMs duration in millisecond for the exiting PiP transition * @param animationDurationMs duration in millisecond for the exiting PiP transition
*/ */
public void exitPip(int animationDurationMs) { public void exitPip(int animationDurationMs) {
if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) { if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) {
Log.wtf(TAG, "Not allowed to exitPip in current state" Log.wtf(TAG, "Not allowed to exitPip in current state"
+ " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return; return;
} }
@@ -427,7 +400,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setBoundsChangeTransaction(mToken, tx); wct.setBoundsChangeTransaction(mToken, tx);
// Set the exiting state first so if there is fixed rotation later, the running animation // Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP. // won't be interrupted by alpha animation for existing PiP.
mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.startTransition(destinationBounds, wct); mPipTransitionController.startTransition(destinationBounds, wct);
@@ -470,9 +443,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* Removes PiP immediately. * Removes PiP immediately.
*/ */
public void removePip() { public void removePip() {
if (!mState.isInPip() || mToken == null) { if (!mPipTransitionState.isInPip() || mToken == null) {
Log.wtf(TAG, "Not allowed to removePip in current state" Log.wtf(TAG, "Not allowed to removePip in current state"
+ " mState=" + mState + " mToken=" + mToken); + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return; return;
} }
@@ -486,7 +459,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
animator.setDuration(mExitAnimationDuration); animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT); animator.setInterpolator(Interpolators.ALPHA_OUT);
animator.start(); animator.start();
mState = State.EXITING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
} }
private void removePipImmediately() { private void removePipImmediately() {
@@ -508,7 +481,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Objects.requireNonNull(info, "Requires RunningTaskInfo"); Objects.requireNonNull(info, "Requires RunningTaskInfo");
mTaskInfo = info; mTaskInfo = info;
mToken = mTaskInfo.token; mToken = mTaskInfo.token;
mState = State.TASK_APPEARED; mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED);
mLeash = leash; mLeash = leash;
mPictureInPictureParams = mTaskInfo.pictureInPictureParams; mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
@@ -562,7 +535,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */); null /* updateBoundsCallback */);
mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
mOneShotAnimationType = ANIM_TYPE_BOUNDS; mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -589,7 +562,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
animateResizePip(currentBounds, destinationBounds, sourceHintRect, animateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */); TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */);
mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} }
/** /**
@@ -614,7 +587,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceControlTransactionFactory.getTransaction(); mSurfaceControlTransactionFactory.getTransaction();
tx.setAlpha(mLeash, 0f); tx.setAlpha(mLeash, 0f);
tx.apply(); tx.apply();
mState = State.ENTRY_SCHEDULED; mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
applyEnterPipSyncTransaction(destinationBounds, () -> { applyEnterPipSyncTransaction(destinationBounds, () -> {
mPipAnimationController mPipAnimationController
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
@@ -625,7 +598,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
.start(); .start();
// mState is set right after the animation is kicked off to block any resize // mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition. // requests such as offsetPip that may have been called prior to the transition.
mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}, null /* boundsChangeTransaction */); }, null /* boundsChangeTransaction */);
} }
@@ -672,7 +645,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void sendOnPipTransitionStarted( private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) { @PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) { if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERING_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} }
mPipTransitionController.sendOnPipTransitionStarted(direction); mPipTransitionController.sendOnPipTransitionStarted(direction);
} }
@@ -681,7 +654,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
void sendOnPipTransitionFinished( void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) { @PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) { if (direction == TRANSITION_DIRECTION_TO_PIP) {
mState = State.ENTERED_PIP; mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
} }
mPipTransitionController.sendOnPipTransitionFinished(direction); mPipTransitionController.sendOnPipTransitionFinished(direction);
// Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent. // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
@@ -706,7 +679,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/ */
@Override @Override
public void onTaskVanished(ActivityManager.RunningTaskInfo info) { public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
if (mState == State.UNDEFINED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return; return;
} }
final WindowContainerToken token = info.token; final WindowContainerToken token = info.token;
@@ -718,7 +691,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
clearWaitForFixedRotation(); clearWaitForFixedRotation();
mInSwipePipToHomeTransition = false; mInSwipePipToHomeTransition = false;
mPictureInPictureParams = null; mPictureInPictureParams = null;
mState = State.UNDEFINED; mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
// Re-set the PIP bounds to none. // Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect()); mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null); mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -732,8 +705,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override @Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) { if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP
Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) {
Log.d(TAG, "Defer onTaskInfoChange in current state: "
+ mPipTransitionState.getTransitionState());
// Defer applying PiP parameters if the task is entering PiP to avoid disturbing // Defer applying PiP parameters if the task is entering PiP to avoid disturbing
// the animation. // the animation.
mDeferredTaskInfo = info; mDeferredTaskInfo = info;
@@ -766,7 +741,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mNextRotation = newRotation; mNextRotation = newRotation;
mWaitForFixedRotation = true; mWaitForFixedRotation = true;
if (mState.isInPip()) { if (mPipTransitionState.isInPip()) {
// Fade out the existing PiP to avoid jump cut during seamless rotation. // Fade out the existing PiP to avoid jump cut during seamless rotation.
fadeExistingPip(false /* show */); fadeExistingPip(false /* show */);
} }
@@ -777,7 +752,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (!mWaitForFixedRotation) { if (!mWaitForFixedRotation) {
return; return;
} }
if (mState == State.TASK_APPEARED) { if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
if (mInSwipePipToHomeTransition) { if (mInSwipePipToHomeTransition) {
onEndOfSwipePipToHomeTransition(); onEndOfSwipePipToHomeTransition();
} else { } else {
@@ -785,9 +760,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(), enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(),
mEnterAnimationDuration); mEnterAnimationDuration);
} }
} else if (mState == State.ENTERED_PIP && mHasFadeOut) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP
&& mHasFadeOut) {
fadeExistingPip(true /* show */); fadeExistingPip(true /* show */);
} else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) { } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP
&& mDeferredAnimEndTransaction != null) {
final PipAnimationController.PipTransitionAnimator<?> animator = final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator();
final Rect destinationBounds = animator.getDestinationBounds(); final Rect destinationBounds = animator.getDestinationBounds();
@@ -848,7 +825,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning() if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
final boolean rotatingPip = mState.isInPip() && fromRotation; final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) { if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
// The position will be used by fade-in animation when the fixed rotation is done. // The position will be used by fade-in animation when the fixed rotation is done.
mPipBoundsState.setBounds(destinationBoundsOut); mPipBoundsState.setBounds(destinationBoundsOut);
@@ -981,7 +958,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect, Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs, @PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
if (!mState.isInPip()) { if (!mPipTransitionState.isInPip()) {
// TODO: tend to use shouldBlockResizeRequest here as well but need to consider // TODO: tend to use shouldBlockResizeRequest here as well but need to consider
// the fact that when in exitPip, scheduleAnimateResizePip is executed in the window // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
// container transaction callback and we want to set the mState immediately. // container transaction callback and we want to set the mState immediately.
@@ -1011,7 +988,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds) .crop(tx, mLeash, toBounds)
.round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip());
if (mPipMenuController.isMenuVisible()) { if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds); mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else { } else {
@@ -1089,7 +1066,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void scheduleFinishResizePip(Rect destinationBounds, public void scheduleFinishResizePip(Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction, @PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) {
return; return;
} }
@@ -1106,7 +1083,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds) .crop(tx, mLeash, destinationBounds)
.resetScale(tx, mLeash, destinationBounds) .resetScale(tx, mLeash, destinationBounds)
.round(tx, mLeash, mState.isInPip()); .round(tx, mLeash, mPipTransitionState.isInPip());
return tx; return tx;
} }
@@ -1115,7 +1092,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/ */
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
if (mState.shouldBlockResizeRequest()) { if (mPipTransitionState.shouldBlockResizeRequest()) {
return; return;
} }
if (mWaitForFixedRotation) { if (mWaitForFixedRotation) {
@@ -1373,7 +1350,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mToken=" + mToken pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null)); + " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mLeash=" + mLeash);
pw.println(innerPrefix + "mState=" + mState); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
} }

View File

@@ -52,19 +52,23 @@ import com.android.wm.shell.transition.Transitions;
*/ */
public class PipTransition extends PipTransitionController { public class PipTransition extends PipTransitionController {
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration; private final int mEnterExitAnimationDuration;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback; private Transitions.TransitionFinishCallback mFinishCallback;
private Rect mExitDestinationBounds = new Rect(); private Rect mExitDestinationBounds = new Rect();
public PipTransition(Context context, public PipTransition(Context context,
PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController, PipAnimationController pipAnimationController,
Transitions transitions, Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) { @NonNull ShellTaskOrganizer shellTaskOrganizer) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer); pipAnimationController, transitions, shellTaskOrganizer);
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources() mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration); .getInteger(R.integer.config_pipResizeAnimationDuration);
} }
@@ -85,6 +89,7 @@ public class PipTransition extends PipTransitionController {
if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) { if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
final TransitionInfo.Change change = info.getChanges().get(0); final TransitionInfo.Change change = info.getChanges().get(0);
mFinishCallback = finishCallback; mFinishCallback = finishCallback;
startTransaction.apply();
boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
new Rect(mExitDestinationBounds)); new Rect(mExitDestinationBounds));
mExitDestinationBounds.setEmpty(); mExitDestinationBounds.setEmpty();
@@ -114,6 +119,7 @@ public class PipTransition extends PipTransitionController {
startTransaction.setAlpha(wallpaper.getLeash(), 1.f); startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
} }
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
mFinishCallback = finishCallback; mFinishCallback = finishCallback;
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
startTransaction, finishTransaction); startTransaction, finishTransaction);
@@ -130,6 +136,9 @@ public class PipTransition extends PipTransitionController {
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction, @PipAnimationController.TransitionDirection int direction,
SurfaceControl.Transaction tx) { SurfaceControl.Transaction tx) {
if (isInPipDirection(direction)) {
mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
WindowContainerTransaction wct = new WindowContainerTransaction(); WindowContainerTransaction wct = new WindowContainerTransaction();
prepareFinishResizeTransaction(taskInfo, destinationBounds, prepareFinishResizeTransaction(taskInfo, destinationBounds,
direction, tx, wct); direction, tx, wct);

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.pip;
import android.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
* {@link PipTransition}.
*/
public class PipTransitionState {
public static final int UNDEFINED = 0;
public static final int TASK_APPEARED = 1;
public static final int ENTRY_SCHEDULED = 2;
public static final int ENTERING_PIP = 3;
public static final int ENTERED_PIP = 4;
public static final int EXITING_PIP = 5;
// Not a complete set of states but serves what we want right now.
@IntDef(prefix = { "TRANSITION_STATE_" }, value = {
UNDEFINED,
TASK_APPEARED,
ENTRY_SCHEDULED,
ENTERING_PIP,
ENTERED_PIP,
EXITING_PIP
})
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionState {}
private @TransitionState int mState;
public PipTransitionState() {
mState = UNDEFINED;
}
public void setTransitionState(@TransitionState int state) {
mState = state;
}
public @TransitionState int getTransitionState() {
return mState;
}
public boolean isInPip() {
return mState >= TASK_APPEARED
&& mState != EXITING_PIP;
}
/**
* Resize request can be initiated in other component, ignore if we are no longer in PIP,
* still waiting for animation or we're exiting from it.
*
* @return {@code true} if the resize request should be blocked/ignored.
*/
public boolean shouldBlockResizeRequest() {
return mState < ENTERING_PIP
|| mState == EXITING_PIP;
}
}

View File

@@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor; private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState; private PipBoundsState mPipBoundsState;
private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1; private ComponentName mComponent1;
@@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mComponent1 = new ComponentName(mContext, "component1"); mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2"); mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext); mPipBoundsState = new PipBoundsState(mContext);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm()); new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor(); mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipBoundsState, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController, mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,

View File

@@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController; import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -142,12 +143,19 @@ public abstract class TvPipModule {
return new PipAnimationController(pipSurfaceTransactionHelper); return new PipAnimationController(pipSurfaceTransactionHelper);
} }
@WMSingleton
@Provides
static PipTransitionState providePipTransitionState() {
return new PipTransitionState();
}
@WMSingleton @WMSingleton
@Provides @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context, static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController, TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue, SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState, PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController, PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController, PipTransitionController pipTransitionController,
@@ -157,7 +165,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, return new PipTaskOrganizer(context,
syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor); shellTaskOrganizer, mainExecutor);

View File

@@ -48,6 +48,7 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition; import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -182,10 +183,17 @@ public class WMShellModule {
floatingContentCoordinator, pipUiEventLogger, mainExecutor); floatingContentCoordinator, pipUiEventLogger, mainExecutor);
} }
@WMSingleton
@Provides
static PipTransitionState providePipTransitionState() {
return new PipTransitionState();
}
@WMSingleton @WMSingleton
@Provides @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context, static PipTaskOrganizer providePipTaskOrganizer(Context context,
SyncTransactionQueue syncTransactionQueue, SyncTransactionQueue syncTransactionQueue,
PipTransitionState pipTransitionState,
PipBoundsState pipBoundsState, PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController, PhonePipMenuController menuPhoneController,
@@ -197,7 +205,7 @@ public class WMShellModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, return new PipTaskOrganizer(context,
syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper, menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor); shellTaskOrganizer, mainExecutor);
@@ -215,8 +223,9 @@ public class WMShellModule {
static PipTransitionController providePipTransitionController(Context context, static PipTransitionController providePipTransitionController(Context context,
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) { PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
return new PipTransition(context, pipBoundsState, pipMenuController, PhonePipMenuController pipMenuController) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
} }