Offset src-rect by display cutouts if needed
For PiP2, we need to make sure source rect hint is adjusted
by display cutouts if needed to avoid flickers when entering.
Also refactored code portions handling fixed rotation in PiP2.
Bug: 377957432
Flag: com.android.wm.shell.enable_pip2
Test: enter fixed rotation PiP2 from ROTATION_90 on felix apidemos
Change-Id: I813245950213db4289e3b076bd7301be2be8847c
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index ea783e9..3caad09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
@@ -230,6 +231,11 @@
// If there is no PiP change, exit this transition handler and potentially try others.
if (pipChange == null) return false;
+ // Other targets might have default transforms applied that are not relevant when
+ // playing PiP transitions, so reset those transforms if needed.
+ prepareOtherTargetTransforms(info, startTransaction, finishTransaction);
+
+ // Update the PipTransitionState while supplying the PiP leash and token to be cached.
Bundle extra = new Bundle();
extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer());
extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash());
@@ -341,17 +347,21 @@
(destinationBounds.height() - overlaySize) / 2f);
}
- final int startRotation = pipChange.getStartRotation();
- final int endRotation = mPipDisplayLayoutState.getRotation();
- final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
- : startRotation - endRotation;
+ final int delta = getFixedRotationDelta(info, pipChange);
if (delta != ROTATION_0) {
- mPipTransitionState.setInFixedRotation(true);
- handleBoundsEnterFixedRotation(pipChange, pipActivityChange, endRotation);
+ // Update transition target changes in place to prepare for fixed rotation.
+ handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
}
+ // Update the src-rect-hint in params in place, to set up initial animator transform.
+ Rect sourceRectHint = getAdjustedSourceRectHint(info, pipChange, pipActivityChange);
+ pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint().set(sourceRectHint);
+
+ // Config-at-end transitions need to have their activities transformed before starting
+ // the animation; this makes the buffer seem like it's been updated to final size.
prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
pipActivityChange);
+
startTransaction.merge(finishTransaction);
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
startTransaction, finishTransaction, destinationBounds, delta);
@@ -387,55 +397,36 @@
return false;
}
+ final SurfaceControl pipLeash = getLeash(pipChange);
final Rect startBounds = pipChange.getStartAbsBounds();
final Rect endBounds = pipChange.getEndAbsBounds();
-
final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
- final float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
- final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
- endBounds);
- final Rect adjustedSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint)
- : PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio);
+ final Rect adjustedSourceRectHint = getAdjustedSourceRectHint(info, pipChange,
+ pipActivityChange);
- final SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
-
- // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
- // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
- // by the Transitions framework to simplify Task opening transitions.
- if (TransitionUtil.isOpeningType(info.getType())) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getLeash() == null) continue;
- if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
- startTransaction.setAlpha(change.getLeash(), 1f);
- }
- }
- }
-
- final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- final int startRotation = pipChange.getStartRotation();
- final int endRotation = fixedRotationChange != null
- ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
- final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
- : startRotation - endRotation;
-
+ final int delta = getFixedRotationDelta(info, pipChange);
if (delta != ROTATION_0) {
- mPipTransitionState.setInFixedRotation(true);
- handleBoundsEnterFixedRotation(pipChange, pipActivityChange,
- fixedRotationChange.getEndFixedRotation());
+ // Update transition target changes in place to prepare for fixed rotation.
+ handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
}
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
startTransaction, finishTransaction, endBounds, delta);
- if (sourceRectHint == null) {
- // update the src-rect-hint in params in place, to set up initial animator transform.
- params.getSourceRectHint().set(adjustedSourceRectHint);
+ if (PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, endBounds) == null) {
+ // If app provided src-rect-hint is invalid, use app icon overlay.
animator.setAppIconContentOverlay(
mContext, startBounds, endBounds, pipChange.getTaskInfo().topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
}
+ // Update the src-rect-hint in params in place, to set up initial animator transform.
+ params.getSourceRectHint().set(adjustedSourceRectHint);
+
+ // Config-at-end transitions need to have their activities transformed before starting
+ // the animation; this makes the buffer seem like it's been updated to final size.
prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
pipActivityChange);
+
animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
animator.setAnimationEndCallback(() -> {
if (animator.getContentOverlayLeash() != null) {
@@ -457,11 +448,22 @@
animator.start();
}
- private void handleBoundsEnterFixedRotation(TransitionInfo.Change pipTaskChange,
- TransitionInfo.Change pipActivityChange, int endRotation) {
- final Rect endBounds = pipTaskChange.getEndAbsBounds();
- final Rect endActivityBounds = pipActivityChange.getEndAbsBounds();
- int startRotation = pipTaskChange.getStartRotation();
+ private void handleBoundsEnterFixedRotation(TransitionInfo info,
+ TransitionInfo.Change outPipTaskChange,
+ TransitionInfo.Change outPipActivityChange) {
+ final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ final Rect endBounds = outPipTaskChange.getEndAbsBounds();
+ final Rect endActivityBounds = outPipActivityChange.getEndAbsBounds();
+ int startRotation = outPipTaskChange.getStartRotation();
+ int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation();
+
+ if (startRotation == endRotation) {
+ return;
+ }
+
+ // This is used by display change listeners to respond properly to fixed rotation.
+ mPipTransitionState.setInFixedRotation(true);
// Cache the task to activity offset to potentially restore later.
Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left,
@@ -490,15 +492,15 @@
endBounds.top + activityEndOffset.y);
}
- private void handleExpandFixedRotation(TransitionInfo.Change pipTaskChange, int endRotation) {
- final Rect endBounds = pipTaskChange.getEndAbsBounds();
+ private void handleExpandFixedRotation(TransitionInfo.Change outPipTaskChange, int delta) {
+ final Rect endBounds = outPipTaskChange.getEndAbsBounds();
final int width = endBounds.width();
final int height = endBounds.height();
final int left = endBounds.left;
final int top = endBounds.top;
int newTop, newLeft;
- if (endRotation == Surface.ROTATION_90) {
+ if (delta == Surface.ROTATION_90) {
newLeft = top;
newTop = -(left + width);
} else {
@@ -585,15 +587,11 @@
final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, endBounds,
startBounds);
- final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- final int startRotation = pipChange.getStartRotation();
- final int endRotation = fixedRotationChange != null
- ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
- final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
- : endRotation - startRotation;
-
+ // We define delta = startRotation - endRotation, so we need to flip the sign.
+ final int delta = -getFixedRotationDelta(info, pipChange);
if (delta != ROTATION_0) {
- handleExpandFixedRotation(pipChange, endRotation);
+ // Update PiP target change in place to prepare for fixed rotation;
+ handleExpandFixedRotation(pipChange, delta);
}
PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash,
@@ -661,6 +659,72 @@
return null;
}
+ @NonNull
+ private Rect getAdjustedSourceRectHint(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change pipTaskChange,
+ @NonNull TransitionInfo.Change pipActivityChange) {
+ final Rect startBounds = pipTaskChange.getStartAbsBounds();
+ final Rect endBounds = pipTaskChange.getEndAbsBounds();
+ final PictureInPictureParams params = pipTaskChange.getTaskInfo().pictureInPictureParams;
+
+ // Get the source-rect-hint provided by the app and check its validity; null if invalid.
+ final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
+ endBounds);
+
+ final Rect adjustedSourceRectHint = new Rect();
+ if (sourceRectHint != null) {
+ adjustedSourceRectHint.set(sourceRectHint);
+ // If multi-activity PiP, use the parent task before PiP to retrieve display cutouts;
+ // then, offset the valid app provided source rect hint by the cutout insets.
+ // For single-activity PiP, just use the pinned task to get the cutouts instead.
+ TransitionInfo.Change parentBeforePip = pipActivityChange.getLastParent() != null
+ ? getChangeByToken(info, pipActivityChange.getLastParent()) : null;
+ Rect cutoutInsets = parentBeforePip != null
+ ? parentBeforePip.getTaskInfo().displayCutoutInsets
+ : pipTaskChange.getTaskInfo().displayCutoutInsets;
+ if (cutoutInsets != null
+ && getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) {
+ adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top);
+ }
+ } else {
+ // For non-valid app provided src-rect-hint, calculate one to crop into during
+ // app icon overlay animation.
+ float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
+ adjustedSourceRectHint.set(
+ PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio));
+ }
+ return adjustedSourceRectHint;
+ }
+
+ @Surface.Rotation
+ private int getFixedRotationDelta(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change pipChange) {
+ TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ int startRotation = pipChange.getStartRotation();
+ int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation();
+ int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+ : startRotation - endRotation;
+ return delta;
+ }
+
+ private void prepareOtherTargetTransforms(TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ // For opening type transitions, if there is a change of mode TO_FRONT/OPEN,
+ // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f
+ // by the Transitions framework to simplify Task opening transitions.
+ if (TransitionUtil.isOpeningType(info.getType())) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getLeash() == null) continue;
+ if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+ startTransaction.setAlpha(change.getLeash(), 1f);
+ }
+ }
+ }
+
+ }
+
private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
// cache the original task token to check for multi-activity case later