Merge "Add a DefaultMixedHandler handler combined transitions" into tm-qpr-dev
This commit is contained in:
@@ -34,6 +34,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler;
|
|||||||
import com.android.wm.shell.recents.RecentTasksController;
|
import com.android.wm.shell.recents.RecentTasksController;
|
||||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||||
|
import com.android.wm.shell.transition.DefaultMixedHandler;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||||
|
|
||||||
@@ -131,6 +132,13 @@ public class ShellInitImpl {
|
|||||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
|
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||||
mTransitions.register(mShellTaskOrganizer);
|
mTransitions.register(mShellTaskOrganizer);
|
||||||
mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init);
|
mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init);
|
||||||
|
if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
|
||||||
|
final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
|
||||||
|
mPipTouchHandlerOptional.get().getTransitionHandler(),
|
||||||
|
mSplitScreenOptional.get().getTransitionHandler());
|
||||||
|
// Added at end so that it has highest priority.
|
||||||
|
mTransitions.addHandler(mixedHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/181599115): This should really be the pip controller, but until we can provide the
|
// TODO(b/181599115): This should really be the pip controller, but until we can provide the
|
||||||
|
|||||||
@@ -295,6 +295,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
|
|||||||
displayController.addDisplayWindowListener(this);
|
displayController.addDisplayWindowListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipTransitionController getTransitionController() {
|
||||||
|
return mPipTransitionController;
|
||||||
|
}
|
||||||
|
|
||||||
public Rect getCurrentOrAnimatingBounds() {
|
public Rect getCurrentOrAnimatingBounds() {
|
||||||
PipAnimationController.PipTransitionAnimator animator =
|
PipAnimationController.PipTransitionAnimator animator =
|
||||||
mPipAnimationController.getCurrentAnimator();
|
mPipAnimationController.getCurrentAnimator();
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_OPEN;
|
|||||||
import static android.view.WindowManager.TRANSIT_PIP;
|
import static android.view.WindowManager.TRANSIT_PIP;
|
||||||
import static android.view.WindowManager.transitTypeToString;
|
import static android.view.WindowManager.transitTypeToString;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
|
||||||
|
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
|
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
|
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
|
||||||
@@ -52,6 +51,7 @@ import android.graphics.Rect;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.window.TransitionInfo;
|
import android.window.TransitionInfo;
|
||||||
import android.window.TransitionRequestInfo;
|
import android.window.TransitionRequestInfo;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
@@ -217,8 +217,9 @@ public class PipTransition extends PipTransitionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Entering PIP.
|
// Entering PIP.
|
||||||
if (isEnteringPip(info, mCurrentPipTaskToken)) {
|
if (isEnteringPip(info)) {
|
||||||
return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
|
startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For transition that we don't animate, but contains the PIP leash, we need to update the
|
// For transition that we don't animate, but contains the PIP leash, we need to update the
|
||||||
@@ -245,22 +246,31 @@ public class PipTransition extends PipTransitionController {
|
|||||||
@Override
|
@Override
|
||||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
@NonNull TransitionRequestInfo request) {
|
@NonNull TransitionRequestInfo request) {
|
||||||
if (request.getType() == TRANSIT_PIP) {
|
if (requestHasPipEnter(request)) {
|
||||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
augmentRequest(transition, request, wct);
|
||||||
mRequestedEnterTransition = transition;
|
|
||||||
mRequestedEnterTask = request.getTriggerTask().token;
|
|
||||||
wct.setActivityWindowingMode(request.getTriggerTask().token,
|
|
||||||
WINDOWING_MODE_UNDEFINED);
|
|
||||||
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
|
||||||
wct.setBounds(request.getTriggerTask().token, destinationBounds);
|
|
||||||
}
|
|
||||||
return wct;
|
return wct;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void augmentRequest(@NonNull IBinder transition,
|
||||||
|
@NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
|
||||||
|
if (!requestHasPipEnter(request)) {
|
||||||
|
throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
|
||||||
|
}
|
||||||
|
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||||
|
mRequestedEnterTransition = transition;
|
||||||
|
mRequestedEnterTask = request.getTriggerTask().token;
|
||||||
|
outWCT.setActivityWindowingMode(request.getTriggerTask().token,
|
||||||
|
WINDOWING_MODE_UNDEFINED);
|
||||||
|
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
||||||
|
outWCT.setBounds(request.getTriggerTask().token, destinationBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleRotateDisplay(int startRotation, int endRotation,
|
public boolean handleRotateDisplay(int startRotation, int endRotation,
|
||||||
WindowContainerTransaction wct) {
|
WindowContainerTransaction wct) {
|
||||||
@@ -559,92 +569,94 @@ public class PipTransition extends PipTransitionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
|
/** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
|
||||||
private static boolean isEnteringPip(@NonNull TransitionInfo info,
|
private boolean isEnteringPip(@NonNull TransitionInfo info) {
|
||||||
@Nullable WindowContainerToken currentPipTaskToken) {
|
|
||||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||||
if (change.getTaskInfo() != null
|
if (isEnteringPip(change, info.getType())) return true;
|
||||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
|
|
||||||
&& !change.getContainer().equals(currentPipTaskToken)) {
|
|
||||||
// We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
|
|
||||||
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
|
|
||||||
if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// This can happen if the request to enter PIP happens when we are collecting for
|
|
||||||
// another transition, such as TRANSIT_CHANGE (display rotation).
|
|
||||||
if (info.getType() == TRANSIT_CHANGE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Please file a bug to handle the unexpected transition type.
|
|
||||||
throw new IllegalStateException("Entering PIP with unexpected transition type="
|
|
||||||
+ transitTypeToString(info.getType()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startEnterAnimation(@NonNull TransitionInfo info,
|
/** Whether a particular change is a window that is entering pip. */
|
||||||
|
@Override
|
||||||
|
public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
|
||||||
|
@WindowManager.TransitionType int transitType) {
|
||||||
|
if (change.getTaskInfo() != null
|
||||||
|
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
|
||||||
|
&& !change.getContainer().equals(mCurrentPipTaskToken)) {
|
||||||
|
// We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
|
||||||
|
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
|
||||||
|
if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// This can happen if the request to enter PIP happens when we are collecting for
|
||||||
|
// another transition, such as TRANSIT_CHANGE (display rotation).
|
||||||
|
if (transitType == TRANSIT_CHANGE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Please file a bug to handle the unexpected transition type.
|
||||||
|
throw new IllegalStateException("Entering PIP with unexpected transition type="
|
||||||
|
+ transitTypeToString(transitType));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startEnterAnimation(@NonNull TransitionInfo info,
|
||||||
@NonNull SurfaceControl.Transaction startTransaction,
|
@NonNull SurfaceControl.Transaction startTransaction,
|
||||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
// Search for an Enter PiP transition (along with a show wallpaper one)
|
// Search for an Enter PiP transition
|
||||||
TransitionInfo.Change enterPip = null;
|
TransitionInfo.Change enterPip = null;
|
||||||
TransitionInfo.Change wallpaper = null;
|
|
||||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||||
if (change.getTaskInfo() != null
|
if (change.getTaskInfo() != null
|
||||||
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
|
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||||
enterPip = change;
|
enterPip = change;
|
||||||
} else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
|
|
||||||
wallpaper = change;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (enterPip == null) {
|
if (enterPip == null) {
|
||||||
return false;
|
throw new IllegalStateException("Trying to start PiP animation without a pip"
|
||||||
}
|
+ "participant");
|
||||||
// Keep track of the PIP task.
|
|
||||||
mCurrentPipTaskToken = enterPip.getContainer();
|
|
||||||
mHasFadeOut = false;
|
|
||||||
|
|
||||||
if (mFinishCallback != null) {
|
|
||||||
callFinishCallback(null /* wct */);
|
|
||||||
mFinishTransaction = null;
|
|
||||||
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the wallpaper if there is a wallpaper change.
|
|
||||||
if (wallpaper != null) {
|
|
||||||
startTransaction.show(wallpaper.getLeash());
|
|
||||||
startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
|
|
||||||
}
|
|
||||||
// Make sure other open changes are visible as entering PIP. Some may be hidden in
|
// Make sure other open changes are visible as entering PIP. Some may be hidden in
|
||||||
// Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
|
// Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
|
||||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||||
if (change == enterPip || change == wallpaper) {
|
if (change == enterPip) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isOpeningType(change.getMode())) {
|
if (isOpeningType(change.getMode())) {
|
||||||
final SurfaceControl leash = change.getLeash();
|
final SurfaceControl leash = change.getLeash();
|
||||||
startTransaction.show(leash).setAlpha(leash, 1.f);
|
startTransaction.show(leash).setAlpha(leash, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
|
||||||
|
@NonNull final SurfaceControl.Transaction startTransaction,
|
||||||
|
@NonNull final SurfaceControl.Transaction finishTransaction,
|
||||||
|
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
if (mFinishCallback != null) {
|
||||||
|
callFinishCallback(null /* wct */);
|
||||||
|
mFinishTransaction = null;
|
||||||
|
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of the PIP task and animation.
|
||||||
|
mCurrentPipTaskToken = pipChange.getContainer();
|
||||||
|
mHasFadeOut = false;
|
||||||
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
|
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
|
||||||
mFinishCallback = finishCallback;
|
mFinishCallback = finishCallback;
|
||||||
mFinishTransaction = finishTransaction;
|
mFinishTransaction = finishTransaction;
|
||||||
final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
|
|
||||||
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
|
|
||||||
startTransaction, finishTransaction, enterPip.getStartRotation(),
|
|
||||||
endRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
|
final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo();
|
||||||
final SurfaceControl.Transaction startTransaction,
|
final SurfaceControl leash = pipChange.getLeash();
|
||||||
final SurfaceControl.Transaction finishTransaction,
|
final int startRotation = pipChange.getStartRotation();
|
||||||
final int startRotation, final int endRotation) {
|
final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation();
|
||||||
|
|
||||||
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
|
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
|
||||||
taskInfo.topActivityInfo);
|
taskInfo.topActivityInfo);
|
||||||
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
|
||||||
@@ -657,7 +669,6 @@ public class PipTransition extends PipTransitionController {
|
|||||||
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
|
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
|
||||||
destinationBounds, sourceHintRect);
|
destinationBounds, sourceHintRect);
|
||||||
}
|
}
|
||||||
PipAnimationController.PipTransitionAnimator animator;
|
|
||||||
// Set corner radius for entering pip.
|
// Set corner radius for entering pip.
|
||||||
mSurfaceTransactionHelper
|
mSurfaceTransactionHelper
|
||||||
.crop(finishTransaction, leash, destinationBounds)
|
.crop(finishTransaction, leash, destinationBounds)
|
||||||
@@ -694,7 +705,7 @@ public class PipTransition extends PipTransitionController {
|
|||||||
null /* callback */, false /* withStartDelay */);
|
null /* callback */, false /* withStartDelay */);
|
||||||
}
|
}
|
||||||
mPipTransitionState.setInSwipePipToHomeTransition(false);
|
mPipTransitionState.setInSwipePipToHomeTransition(false);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rotationDelta != Surface.ROTATION_0) {
|
if (rotationDelta != Surface.ROTATION_0) {
|
||||||
@@ -702,6 +713,12 @@ public class PipTransition extends PipTransitionController {
|
|||||||
tmpTransform.postRotate(rotationDelta);
|
tmpTransform.postRotate(rotationDelta);
|
||||||
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
|
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
|
||||||
}
|
}
|
||||||
|
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||||
|
startTransaction.setAlpha(leash, 0f);
|
||||||
|
}
|
||||||
|
startTransaction.apply();
|
||||||
|
|
||||||
|
PipAnimationController.PipTransitionAnimator animator;
|
||||||
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
||||||
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
|
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
|
||||||
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
|
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
|
||||||
@@ -712,7 +729,6 @@ public class PipTransition extends PipTransitionController {
|
|||||||
animator.setUseContentOverlay(mContext);
|
animator.setUseContentOverlay(mContext);
|
||||||
}
|
}
|
||||||
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||||
startTransaction.setAlpha(leash, 0f);
|
|
||||||
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
|
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
|
||||||
0f, 1f);
|
0f, 1f);
|
||||||
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
|
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
|
||||||
@@ -720,7 +736,6 @@ public class PipTransition extends PipTransitionController {
|
|||||||
throw new RuntimeException("Unrecognized animation type: "
|
throw new RuntimeException("Unrecognized animation type: "
|
||||||
+ mOneShotAnimationType);
|
+ mOneShotAnimationType);
|
||||||
}
|
}
|
||||||
startTransaction.apply();
|
|
||||||
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
|
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
|
||||||
.setPipAnimationCallback(mPipAnimationCallback)
|
.setPipAnimationCallback(mPipAnimationCallback)
|
||||||
.setDuration(mEnterExitAnimationDuration);
|
.setDuration(mEnterExitAnimationDuration);
|
||||||
@@ -731,8 +746,6 @@ public class PipTransition extends PipTransitionController {
|
|||||||
animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
|
animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
|
||||||
}
|
}
|
||||||
animator.start();
|
animator.start();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Computes destination bounds in old rotation and updates source hint rect if available. */
|
/** Computes destination bounds in old rotation and updates source hint rect if available. */
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.wm.shell.pip;
|
package com.android.wm.shell.pip;
|
||||||
|
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
|
import static android.view.WindowManager.TRANSIT_PIP;
|
||||||
|
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
|
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
|
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
|
||||||
@@ -28,10 +29,16 @@ import android.content.ComponentName;
|
|||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.window.TransitionInfo;
|
||||||
|
import android.window.TransitionRequestInfo;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
@@ -206,6 +213,30 @@ public abstract class PipTransitionController implements Transitions.TransitionH
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return whether the transition-request represents a pip-entry. */
|
||||||
|
public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) {
|
||||||
|
return request.getType() == TRANSIT_PIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether a particular change is a window that is entering pip. */
|
||||||
|
public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
|
||||||
|
@WindowManager.TransitionType int transitType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add PiP-related changes to `outWCT` for the given request. */
|
||||||
|
public void augmentRequest(@NonNull IBinder transition,
|
||||||
|
@NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
|
||||||
|
throw new IllegalStateException("Request isn't entering PiP");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Play a transition animation for entering PiP on a specific PiP change. */
|
||||||
|
public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
|
||||||
|
@NonNull final SurfaceControl.Transaction startTransaction,
|
||||||
|
@NonNull final SurfaceControl.Transaction finishTransaction,
|
||||||
|
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback interface for PiP transitions (both from and to PiP mode)
|
* Callback interface for PiP transitions (both from and to PiP mode)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import com.android.wm.shell.pip.PipAnimationController;
|
|||||||
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipBoundsState;
|
import com.android.wm.shell.pip.PipBoundsState;
|
||||||
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.PipUiEventLogger;
|
import com.android.wm.shell.pip.PipUiEventLogger;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
|
|
||||||
@@ -249,6 +250,10 @@ public class PipTouchHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipTransitionController getTransitionHandler() {
|
||||||
|
return mPipTaskOrganizer.getTransitionController();
|
||||||
|
}
|
||||||
|
|
||||||
private void reloadResources() {
|
private void reloadResources() {
|
||||||
final Resources res = mContext.getResources();
|
final Resources res = mContext.getResources();
|
||||||
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
|
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
|
static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
|
||||||
static final int EXIT_REASON_SCREEN_LOCKED = 7;
|
static final int EXIT_REASON_SCREEN_LOCKED = 7;
|
||||||
static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
|
static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
|
||||||
static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
|
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
|
||||||
@IntDef(value = {
|
@IntDef(value = {
|
||||||
EXIT_REASON_UNKNOWN,
|
EXIT_REASON_UNKNOWN,
|
||||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
|
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
|
||||||
@@ -198,6 +198,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mStageCoordinator.isSplitScreenVisible();
|
return mStageCoordinator.isSplitScreenVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StageCoordinator getTransitionHandler() {
|
||||||
|
return mStageCoordinator;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
||||||
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
|
|||||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||||
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
|
||||||
|
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||||
@@ -58,9 +57,6 @@ import java.util.ArrayList;
|
|||||||
class SplitScreenTransitions {
|
class SplitScreenTransitions {
|
||||||
private static final String TAG = "SplitScreenTransitions";
|
private static final String TAG = "SplitScreenTransitions";
|
||||||
|
|
||||||
/** Flag applied to a transition change to identify it as a divider bar for animation. */
|
|
||||||
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
|
|
||||||
|
|
||||||
private final TransactionPool mTransactionPool;
|
private final TransactionPool mTransactionPool;
|
||||||
private final Transitions mTransitions;
|
private final Transitions mTransitions;
|
||||||
private final Runnable mOnFinish;
|
private final Runnable mOnFinish;
|
||||||
@@ -187,27 +183,28 @@ class SplitScreenTransitions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Starts a transition to dismiss split. */
|
/** Starts a transition to dismiss split. */
|
||||||
IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
|
IBinder startDismissTransition(WindowContainerTransaction wct,
|
||||||
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
|
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
|
||||||
@SplitScreenController.ExitReason int reason) {
|
@SplitScreenController.ExitReason int reason) {
|
||||||
final int type = reason == EXIT_REASON_DRAG_DIVIDER
|
final int type = reason == EXIT_REASON_DRAG_DIVIDER
|
||||||
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
|
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
|
||||||
if (transition == null) {
|
IBinder transition = mTransitions.startTransition(type, wct, handler);
|
||||||
transition = mTransitions.startTransition(type, wct, handler);
|
setDismissTransition(transition, dismissTop, reason);
|
||||||
}
|
return transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets a transition to dismiss split. */
|
||||||
|
void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop,
|
||||||
|
@SplitScreenController.ExitReason int reason) {
|
||||||
mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
|
mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
|
||||||
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||||
+ " deduced Dismiss due to %s. toTop=%s",
|
+ " deduced Dismiss due to %s. toTop=%s",
|
||||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||||
return transition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
|
void setRecentTransition(@NonNull IBinder transition,
|
||||||
Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
|
@Nullable RemoteTransition remoteTransition) {
|
||||||
if (transition == null) {
|
|
||||||
transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
|
|
||||||
}
|
|
||||||
mPendingRecent = transition;
|
mPendingRecent = transition;
|
||||||
|
|
||||||
if (remoteTransition != null) {
|
if (remoteTransition != null) {
|
||||||
@@ -219,7 +216,6 @@ class SplitScreenTransitions {
|
|||||||
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
|
||||||
+ " deduced Enter recent panel");
|
+ " deduced Enter recent panel");
|
||||||
return transition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
|
|||||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||||
import static android.view.WindowManager.transitTypeToString;
|
import static android.view.WindowManager.transitTypeToString;
|
||||||
|
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
||||||
|
|
||||||
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
|
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
|
||||||
@@ -47,7 +48,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
|
|||||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
|
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
|
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
|
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
|
|
||||||
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
|
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
|
||||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
|
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
|
||||||
@@ -126,12 +126,15 @@ import javax.inject.Provider;
|
|||||||
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
|
||||||
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
|
||||||
*/
|
*/
|
||||||
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||||
DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
|
DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
|
||||||
ShellTaskOrganizer.TaskListener {
|
ShellTaskOrganizer.TaskListener {
|
||||||
|
|
||||||
private static final String TAG = StageCoordinator.class.getSimpleName();
|
private static final String TAG = StageCoordinator.class.getSimpleName();
|
||||||
|
|
||||||
|
/** Flag applied to a transition change to identify it as a divider bar for animation. */
|
||||||
|
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
|
||||||
|
|
||||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||||
|
|
||||||
private final MainStage mMainStage;
|
private final MainStage mMainStage;
|
||||||
@@ -619,7 +622,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
if (ENABLE_SHELL_TRANSITIONS) {
|
if (ENABLE_SHELL_TRANSITIONS) {
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
|
prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
|
||||||
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
|
mSplitTransitions.startDismissTransition(wct, this,
|
||||||
mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
|
mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
|
||||||
} else {
|
} else {
|
||||||
exitSplitScreen(
|
exitSplitScreen(
|
||||||
@@ -649,8 +652,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
prepareExitSplitScreen(dismissTop, wct);
|
prepareExitSplitScreen(dismissTop, wct);
|
||||||
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
|
mSplitTransitions.startDismissTransition(wct, this, dismissTop,
|
||||||
dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
|
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -712,10 +715,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
.setWindowCrop(mSideStage.mRootLeash, null);
|
.setWindowCrop(mSideStage.mRootLeash, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hide divider and reset its position.
|
onTransitionAnimationComplete();
|
||||||
mSplitLayout.resetDividerPosition();
|
|
||||||
mSplitLayout.release();
|
|
||||||
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
|
|
||||||
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
|
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
|
||||||
// Log the exit
|
// Log the exit
|
||||||
if (childrenToTop != null) {
|
if (childrenToTop != null) {
|
||||||
@@ -1111,8 +1111,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
prepareExitSplitScreen(dismissTop, wct);
|
prepareExitSplitScreen(dismissTop, wct);
|
||||||
mSplitTransitions.startDismissTransition(
|
mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
|
||||||
null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1301,7 +1300,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
@Nullable TransitionRequestInfo request) {
|
@Nullable TransitionRequestInfo request) {
|
||||||
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
||||||
if (triggerTask == null) {
|
if (triggerTask == null) {
|
||||||
if (mMainStage.isActive()) {
|
if (isSplitActive()) {
|
||||||
|
// Check if the display is rotating.
|
||||||
final TransitionRequestInfo.DisplayChange displayChange =
|
final TransitionRequestInfo.DisplayChange displayChange =
|
||||||
request.getDisplayChange();
|
request.getDisplayChange();
|
||||||
if (request.getType() == TRANSIT_CHANGE && displayChange != null
|
if (request.getType() == TRANSIT_CHANGE && displayChange != null
|
||||||
@@ -1328,7 +1328,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
|
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMainStage.isActive()) {
|
if (isSplitActive()) {
|
||||||
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
|
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
|
||||||
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
|
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
|
||||||
@@ -1343,7 +1343,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
|
int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
|
||||||
: STAGE_TYPE_MAIN;
|
: STAGE_TYPE_MAIN;
|
||||||
prepareExitSplitScreen(dismissTop, out);
|
prepareExitSplitScreen(dismissTop, out);
|
||||||
mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
|
mSplitTransitions.setDismissTransition(transition, dismissTop,
|
||||||
EXIT_REASON_APP_FINISHED);
|
EXIT_REASON_APP_FINISHED);
|
||||||
}
|
}
|
||||||
} else if (isOpening && inFullscreen) {
|
} else if (isOpening && inFullscreen) {
|
||||||
@@ -1353,12 +1353,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
} else if (activityType == ACTIVITY_TYPE_HOME
|
} else if (activityType == ACTIVITY_TYPE_HOME
|
||||||
|| activityType == ACTIVITY_TYPE_RECENTS) {
|
|| activityType == ACTIVITY_TYPE_RECENTS) {
|
||||||
// Enter overview panel, so start recent transition.
|
// Enter overview panel, so start recent transition.
|
||||||
mSplitTransitions.startRecentTransition(transition, out, this,
|
mSplitTransitions.setRecentTransition(transition,
|
||||||
request.getRemoteTransition());
|
request.getRemoteTransition());
|
||||||
} else {
|
} else {
|
||||||
// Occluded by the other fullscreen task, so dismiss both.
|
// Occluded by the other fullscreen task, so dismiss both.
|
||||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
|
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
|
||||||
mSplitTransitions.startDismissTransition(transition, out, this,
|
mSplitTransitions.setDismissTransition(transition,
|
||||||
STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
|
STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1373,6 +1373,33 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used for mixed scenarios. For such scenarios, just make sure to include exiting
|
||||||
|
* split or entering split when appropriate.
|
||||||
|
*/
|
||||||
|
public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
|
||||||
|
@NonNull WindowContainerTransaction outWCT) {
|
||||||
|
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
|
||||||
|
if (triggerTask != null && triggerTask.displayId != mDisplayId) {
|
||||||
|
// Skip handling task on the other display.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final @WindowManager.TransitionType int type = request.getType();
|
||||||
|
if (isSplitActive() && !isOpeningType(type)
|
||||||
|
&& (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " One of the splits became "
|
||||||
|
+ "empty during a mixed transition (one not handled by split),"
|
||||||
|
+ " so make sure split-screen state is cleaned-up. "
|
||||||
|
+ "mainStageCount=%d sideStageCount=%d", mMainStage.getChildCount(),
|
||||||
|
mSideStage.getChildCount());
|
||||||
|
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSplitActive() {
|
||||||
|
return mMainStage.isActive();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||||
SurfaceControl.Transaction t, IBinder mergeTarget,
|
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||||
@@ -1465,7 +1492,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTransitionAnimationComplete() {
|
/** Called to clean-up state and do house-keeping after the animation is done. */
|
||||||
|
public void onTransitionAnimationComplete() {
|
||||||
// If still playing, let it finish.
|
// If still playing, let it finish.
|
||||||
if (!mMainStage.isActive()) {
|
if (!mMainStage.isActive()) {
|
||||||
// Update divider state after animation so that it is still around and positioned
|
// Update divider state after animation so that it is still around and positioned
|
||||||
@@ -1529,8 +1557,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startPendingDismissAnimation(
|
/** Synchronize split-screen state with transition and make appropriate preparations. */
|
||||||
@NonNull SplitScreenTransitions.DismissTransition dismissTransition,
|
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
|
||||||
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
|
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
|
||||||
@NonNull SurfaceControl.Transaction finishT) {
|
@NonNull SurfaceControl.Transaction finishT) {
|
||||||
// Make some noise if things aren't totally expected. These states shouldn't effect
|
// Make some noise if things aren't totally expected. These states shouldn't effect
|
||||||
@@ -1563,7 +1591,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mRecentTasks.ifPresent(recentTasks -> {
|
mRecentTasks.ifPresent(recentTasks -> {
|
||||||
// Notify recents if we are exiting in a way that breaks the pair, and disable further
|
// Notify recents if we are exiting in a way that breaks the pair, and disable further
|
||||||
// updates to splits in the recents until we enter split again
|
// updates to splits in the recents until we enter split again
|
||||||
if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
|
if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) {
|
||||||
for (TransitionInfo.Change change : info.getChanges()) {
|
for (TransitionInfo.Change change : info.getChanges()) {
|
||||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||||
if (taskInfo != null
|
if (taskInfo != null
|
||||||
@@ -1580,30 +1608,38 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
// Wait until after animation to update divider
|
// Wait until after animation to update divider
|
||||||
|
|
||||||
// Reset crops so they don't interfere with subsequent launches
|
// Reset crops so they don't interfere with subsequent launches
|
||||||
t.setWindowCrop(mMainStage.mRootLeash, null);
|
t.setCrop(mMainStage.mRootLeash, null);
|
||||||
t.setWindowCrop(mSideStage.mRootLeash, null);
|
t.setCrop(mSideStage.mRootLeash, null);
|
||||||
|
|
||||||
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
|
if (toStage == STAGE_TYPE_UNDEFINED) {
|
||||||
logExit(dismissTransition.mReason);
|
logExit(dismissReason);
|
||||||
// TODO: Have a proper remote for this. Until then, though, reset state and use the
|
|
||||||
// normal animation stuff (which falls back to the normal launcher remote).
|
|
||||||
mSplitLayout.release(t);
|
|
||||||
mSplitTransitions.mPendingDismiss = null;
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
logExitToStage(dismissTransition.mReason,
|
logExitToStage(dismissReason, toStage == STAGE_TYPE_MAIN);
|
||||||
dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addDividerBarToTransition(info, t, false /* show */);
|
addDividerBarToTransition(info, t, false /* show */);
|
||||||
// We're dismissing split by moving the other one to fullscreen.
|
|
||||||
// Since we don't have any animations for this yet, just use the internal example
|
|
||||||
// animations.
|
|
||||||
|
|
||||||
// Hide divider and dim layer on transition finished.
|
// Hide divider and dim layer on transition finished.
|
||||||
setDividerVisibility(false, finishT);
|
setDividerVisibility(false, finishT);
|
||||||
finishT.hide(mMainStage.mDimLayer);
|
finishT.hide(mMainStage.mDimLayer);
|
||||||
finishT.hide(mSideStage.mDimLayer);
|
finishT.hide(mSideStage.mDimLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startPendingDismissAnimation(
|
||||||
|
@NonNull SplitScreenTransitions.DismissTransition dismissTransition,
|
||||||
|
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
|
||||||
|
@NonNull SurfaceControl.Transaction finishT) {
|
||||||
|
prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
|
||||||
|
t, finishT);
|
||||||
|
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
|
||||||
|
// TODO: Have a proper remote for this. Until then, though, reset state and use the
|
||||||
|
// normal animation stuff (which falls back to the normal launcher remote).
|
||||||
|
t.hide(mSplitLayout.getDividerLeash());
|
||||||
|
mSplitLayout.release(t);
|
||||||
|
mSplitTransitions.mPendingDismiss = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1776,7 +1812,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
|
||||||
mSplitTransitions.startDismissTransition(null /* transition */, wct,
|
mSplitTransitions.startDismissTransition(wct,
|
||||||
StageCoordinator.this, STAGE_TYPE_UNDEFINED,
|
StageCoordinator.this, STAGE_TYPE_UNDEFINED,
|
||||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
|
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,13 +268,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
mChildrenTaskInfo.remove(taskId);
|
mChildrenTaskInfo.remove(taskId);
|
||||||
mChildrenLeashes.remove(taskId);
|
mChildrenLeashes.remove(taskId);
|
||||||
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
|
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
|
||||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
|
|
||||||
mCallbacks.onChildTaskEnterPip(taskId);
|
|
||||||
}
|
|
||||||
if (ENABLE_SHELL_TRANSITIONS) {
|
if (ENABLE_SHELL_TRANSITIONS) {
|
||||||
// Status is managed/synchronized by the transition lifecycle.
|
// Status is managed/synchronized by the transition lifecycle.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||||
|
mCallbacks.onChildTaskEnterPip(taskId);
|
||||||
|
}
|
||||||
sendStatusChanged();
|
sendStatusChanged();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.transition;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||||
|
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||||
|
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||||
|
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
|
||||||
|
import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.window.TransitionInfo;
|
||||||
|
import android.window.TransitionRequestInfo;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
import com.android.wm.shell.pip.PipTransitionController;
|
||||||
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
|
import com.android.wm.shell.splitscreen.StageCoordinator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handler for dealing with transitions involving multiple other handlers. For example: an
|
||||||
|
* activity in split-screen going into PiP.
|
||||||
|
*/
|
||||||
|
public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||||
|
|
||||||
|
private final Transitions mPlayer;
|
||||||
|
private final PipTransitionController mPipHandler;
|
||||||
|
private final StageCoordinator mSplitHandler;
|
||||||
|
|
||||||
|
private static class MixedTransition {
|
||||||
|
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
|
||||||
|
|
||||||
|
final int mType;
|
||||||
|
final IBinder mTransition;
|
||||||
|
|
||||||
|
Transitions.TransitionFinishCallback mFinishCallback = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixed transitions are made up of multiple "parts". This keeps track of how many
|
||||||
|
* parts are currently animating.
|
||||||
|
*/
|
||||||
|
int mInFlightSubAnimations = 0;
|
||||||
|
|
||||||
|
MixedTransition(int type, IBinder transition) {
|
||||||
|
mType = type;
|
||||||
|
mTransition = transition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
|
||||||
|
|
||||||
|
public DefaultMixedHandler(@NonNull Transitions player,
|
||||||
|
@NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
|
||||||
|
mPlayer = player;
|
||||||
|
mPipHandler = pipHandler;
|
||||||
|
mSplitHandler = splitHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||||
|
@NonNull TransitionRequestInfo request) {
|
||||||
|
if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
|
||||||
|
+ "Split-Screen is active, so treat it as Mixed.");
|
||||||
|
if (request.getRemoteTransition() != null) {
|
||||||
|
throw new IllegalStateException("Unexpected remote transition in"
|
||||||
|
+ "pip-enter-from-split request");
|
||||||
|
}
|
||||||
|
mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT,
|
||||||
|
transition));
|
||||||
|
|
||||||
|
WindowContainerTransaction out = new WindowContainerTransaction();
|
||||||
|
mPipHandler.augmentRequest(transition, request, out);
|
||||||
|
mSplitHandler.addEnterOrExitIfNeeded(request, out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransitionInfo subCopy(@NonNull TransitionInfo info,
|
||||||
|
@WindowManager.TransitionType int newType) {
|
||||||
|
final TransitionInfo out = new TransitionInfo(newType, info.getFlags());
|
||||||
|
for (int i = 0; i < info.getChanges().size(); ++i) {
|
||||||
|
out.getChanges().add(info.getChanges().get(i));
|
||||||
|
}
|
||||||
|
out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
|
||||||
|
out.setAnimationOptions(info.getAnimationOptions());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
|
||||||
|
return change.getTaskInfo() != null
|
||||||
|
&& change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWallpaper(@NonNull TransitionInfo.Change change) {
|
||||||
|
return (change.getFlags() & FLAG_IS_WALLPAPER) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction startTransaction,
|
||||||
|
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
MixedTransition mixed = null;
|
||||||
|
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||||
|
if (mActiveTransitions.get(i).mTransition != transition) continue;
|
||||||
|
mixed = mActiveTransitions.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mixed == null) return false;
|
||||||
|
|
||||||
|
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
|
||||||
|
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
|
||||||
|
finishCallback);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
|
||||||
|
+ mixed.mType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
|
||||||
|
@NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction startTransaction,
|
||||||
|
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
|
||||||
|
+ "entering PIP while Split-Screen is active.");
|
||||||
|
TransitionInfo.Change pipChange = null;
|
||||||
|
TransitionInfo.Change wallpaper = null;
|
||||||
|
final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK);
|
||||||
|
boolean homeIsOpening = false;
|
||||||
|
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||||
|
TransitionInfo.Change change = info.getChanges().get(i);
|
||||||
|
if (mPipHandler.isEnteringPip(change, info.getType())) {
|
||||||
|
if (pipChange != null) {
|
||||||
|
throw new IllegalStateException("More than 1 pip-entering changes in one"
|
||||||
|
+ " transition? " + info);
|
||||||
|
}
|
||||||
|
pipChange = change;
|
||||||
|
// going backwards, so remove-by-index is fine.
|
||||||
|
everythingElse.getChanges().remove(i);
|
||||||
|
} else if (isHomeOpening(change)) {
|
||||||
|
homeIsOpening = true;
|
||||||
|
} else if (isWallpaper(change)) {
|
||||||
|
wallpaper = change;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pipChange == null) {
|
||||||
|
// um, something probably went wrong.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final boolean isGoingHome = homeIsOpening;
|
||||||
|
mixed.mFinishCallback = finishCallback;
|
||||||
|
Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
|
||||||
|
--mixed.mInFlightSubAnimations;
|
||||||
|
if (mixed.mInFlightSubAnimations > 0) return;
|
||||||
|
if (isGoingHome) {
|
||||||
|
mSplitHandler.onTransitionAnimationComplete();
|
||||||
|
}
|
||||||
|
mixed.mFinishCallback.onTransitionFinished(wct, wctCB);
|
||||||
|
};
|
||||||
|
if (isGoingHome) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
|
||||||
|
+ "since entering-PiP caused us to leave split and return home.");
|
||||||
|
// We need to split the transition into 2 parts: the pip part (animated by pip)
|
||||||
|
// and the dismiss-part (animated by launcher).
|
||||||
|
mixed.mInFlightSubAnimations = 2;
|
||||||
|
// immediately make the wallpaper visible (so that we don't see it pop-in during
|
||||||
|
// the time it takes to start recents animation (which is remote).
|
||||||
|
if (wallpaper != null) {
|
||||||
|
startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f);
|
||||||
|
}
|
||||||
|
// make a new startTransaction because pip's startEnterAnimation "consumes" it so
|
||||||
|
// we need a separate one to send over to launcher.
|
||||||
|
SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
|
||||||
|
// Let split update internal state for dismiss.
|
||||||
|
mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED,
|
||||||
|
EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
|
||||||
|
finishTransaction);
|
||||||
|
|
||||||
|
// We are trying to accommodate launcher's close animation which can't handle the
|
||||||
|
// divider-bar, so if split-handler is closing the divider-bar, just hide it and remove
|
||||||
|
// from transition info.
|
||||||
|
for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) {
|
||||||
|
if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR) != 0) {
|
||||||
|
everythingElse.getChanges().remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
|
||||||
|
finishCB);
|
||||||
|
// Dispatch the rest of the transition normally. This will most-likely be taken by
|
||||||
|
// recents or default handler.
|
||||||
|
mPlayer.dispatchTransition(mixed.mTransition, everythingElse, otherStartT,
|
||||||
|
finishTransaction, finishCB, this);
|
||||||
|
} else {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
|
||||||
|
+ "forward animation to Pip-Handler.");
|
||||||
|
// This happens if the pip-ing activity is in a multi-activity task (and thus a
|
||||||
|
// new pip task is spawned). In this case, we don't actually exit split so we can
|
||||||
|
// just let pip transition handle the animation verbatim.
|
||||||
|
mixed.mInFlightSubAnimations = 1;
|
||||||
|
mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
|
||||||
|
finishCB);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||||
|
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransitionMerged(@NonNull IBinder transition) {
|
||||||
|
MixedTransition mixed = null;
|
||||||
|
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
|
||||||
|
if (mActiveTransitions.get(i).mTransition != transition) continue;
|
||||||
|
mixed = mActiveTransitions.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mixed == null) return;
|
||||||
|
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
|
||||||
|
mPipHandler.onTransitionMerged(transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -435,33 +435,42 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
|
playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
|
private void playTransition(@NonNull ActiveTransition active) {
|
||||||
return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
|
|
||||||
(wct, cb) -> onFinish(active.mToken, wct, cb));
|
|
||||||
}
|
|
||||||
|
|
||||||
void playTransition(@NonNull ActiveTransition active) {
|
|
||||||
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
|
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
|
||||||
|
|
||||||
// If a handler already chose to run this animation, try delegating to it first.
|
// If a handler already chose to run this animation, try delegating to it first.
|
||||||
if (active.mHandler != null) {
|
if (active.mHandler != null) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
|
||||||
active.mHandler);
|
active.mHandler);
|
||||||
if (startAnimation(active, active.mHandler)) {
|
boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo,
|
||||||
|
active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb));
|
||||||
|
if (consumed) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise give every other handler a chance (in order)
|
// Otherwise give every other handler a chance
|
||||||
|
active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
|
||||||
|
active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives every handler (in order) a chance to animate until one consumes the transition.
|
||||||
|
* @return the handler which consumed the transition.
|
||||||
|
*/
|
||||||
|
TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
|
@NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
|
||||||
|
@NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) {
|
||||||
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
for (int i = mHandlers.size() - 1; i >= 0; --i) {
|
||||||
if (mHandlers.get(i) == active.mHandler) continue;
|
if (mHandlers.get(i) == skip) continue;
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
|
||||||
mHandlers.get(i));
|
mHandlers.get(i));
|
||||||
if (startAnimation(active, mHandlers.get(i))) {
|
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
|
||||||
|
finishCB);
|
||||||
|
if (consumed) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
|
||||||
mHandlers.get(i));
|
mHandlers.get(i));
|
||||||
active.mHandler = mHandlers.get(i);
|
return mHandlers.get(i);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||||
info.addChange(mainChange);
|
info.addChange(mainChange);
|
||||||
info.addChange(sideChange);
|
info.addChange(sideChange);
|
||||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
IBinder transition = mSplitScreenTransitions.startDismissTransition(
|
||||||
new WindowContainerTransaction(), mStageCoordinator,
|
new WindowContainerTransaction(), mStageCoordinator,
|
||||||
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
|
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
|
||||||
boolean accepted = mStageCoordinator.startAnimation(transition, info,
|
boolean accepted = mStageCoordinator.startAnimation(transition, info,
|
||||||
@@ -355,7 +355,7 @@ public class SplitTransitionTests extends ShellTestCase {
|
|||||||
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
|
||||||
info.addChange(mainChange);
|
info.addChange(mainChange);
|
||||||
info.addChange(sideChange);
|
info.addChange(sideChange);
|
||||||
IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
|
IBinder transition = mSplitScreenTransitions.startDismissTransition(
|
||||||
new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
|
new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
|
||||||
STAGE_TYPE_SIDE);
|
STAGE_TYPE_SIDE);
|
||||||
mMainStage.onTaskVanished(mMainChild);
|
mMainStage.onTaskVanished(mMainChild);
|
||||||
|
|||||||
Reference in New Issue
Block a user