Reset the pip icon overlay scale and offset
Make sure we properly reset the scale and offset
of the content overlay for PiP, as we reset the task scale
and set a new crop.
We also need to use different bounds to make overlay
calulations if in fixed rotation.
Test: swipe up to enter PiP with srcRectHint=null
Bug: 314010733
Change-Id: I38a465009497a60685f7bab73ddae2f389c4451f
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index 850b06a0..a70a072 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -193,19 +193,12 @@
MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
- final int appWidth = appBounds.width();
- final int appHeight = appBounds.height();
-
- // In order to have the overlay always cover the pip window during the transition, the
- // overlay will be drawn with the max size of the start and end bounds in different
- // rotation.
- final int overlaySize = Math.max(Math.max(appWidth, appHeight),
- Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+ final int overlaySize = getOverlaySize(appBounds, destinationBounds);
mOverlayHalfSize = overlaySize >> 1;
// When the activity is in the secondary split, make sure the scaling center is not
// offset.
- mAppBounds = new Rect(0, 0, appWidth, appHeight);
+ mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
prepareAppIconOverlay(appIcon);
@@ -215,6 +208,21 @@
.build();
}
+ /**
+ * Returns the size of the app icon overlay.
+ *
+ * In order to have the overlay always cover the pip window during the transition,
+ * the overlay will be drawn with the max size of the start and end bounds in different
+ * rotation.
+ */
+ public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
+ final int appWidth = appBounds.width();
+ final int appHeight = appBounds.height();
+
+ return Math.max(Math.max(appWidth, appHeight),
+ Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+ }
+
@Override
public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
tx.show(mLeash);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fe4980a..395db68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -43,6 +43,7 @@
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
@@ -76,6 +77,8 @@
import com.android.wm.shell.util.TransitionUtil;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Optional;
/**
@@ -86,6 +89,18 @@
private static final String TAG = PipTransition.class.getSimpleName();
+ /** No fixed rotation, or fixed rotation state is undefined. */
+ private static final int FIXED_ROTATION_UNDEFINED = 0;
+ /**
+ * Fixed rotation detected via callbacks (see PipController#startSwipePipToHome());
+ * this is used in the swipe PiP to home case, since the transitions itself isn't supposed to
+ * see the fixed rotation.
+ */
+ private static final int FIXED_ROTATION_CALLBACK = 1;
+
+ /** Fixed rotation detected in the incoming transition. */
+ private static final int FIXED_ROTATION_TRANSITION = 2;
+
private final Context mContext;
private final PipTransitionState mPipTransitionState;
private final PipDisplayLayoutState mPipDisplayLayoutState;
@@ -106,17 +121,28 @@
/** The Task window that is currently in PIP windowing mode. */
@Nullable
private WindowContainerToken mCurrentPipTaskToken;
- /** Whether display is in fixed rotation. */
- private boolean mInFixedRotation;
+
+ @IntDef(prefix = { "FIXED_ROTATION_" }, value = {
+ FIXED_ROTATION_UNDEFINED,
+ FIXED_ROTATION_CALLBACK,
+ FIXED_ROTATION_TRANSITION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FixedRotationState {}
+
+ /** Fixed rotation state of the display. */
+ private @FixedRotationState int mFixedRotationState = FIXED_ROTATION_UNDEFINED;
/**
* The rotation that the display will apply after expanding PiP to fullscreen. This is only
- * meaningful if {@link #mInFixedRotation} is true.
+ * meaningful if {@link #mFixedRotationState} is {@link #FIXED_ROTATION_TRANSITION}.
*/
@Surface.Rotation
private int mEndFixedRotation;
/** Whether the PIP window has fade out for fixed rotation. */
private boolean mHasFadeOut;
+ private Rect mInitBounds = new Rect();
+
/** Used for setting transform to a transaction from animator. */
private final PipAnimationController.PipTransactionHandler mTransactionConsumer =
new PipAnimationController.PipTransactionHandler() {
@@ -181,8 +207,16 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final TransitionInfo.Change currentPipTaskChange = findCurrentPipTaskChange(info);
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
- mInFixedRotation = fixedRotationChange != null;
- mEndFixedRotation = mInFixedRotation
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
+ // If we are just about to process potential fixed rotation information,
+ // then fixed rotation state should either be UNDEFINED or CALLBACK.
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "%s: startAnimation() should start with clear fixed rotation state", TAG);
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
+ }
+ mFixedRotationState = fixedRotationChange != null
+ ? FIXED_ROTATION_TRANSITION : mFixedRotationState;
+ mEndFixedRotation = mFixedRotationState == FIXED_ROTATION_TRANSITION
? fixedRotationChange.getEndFixedRotation()
: ROTATION_UNDEFINED;
@@ -347,6 +381,10 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
+ // Transition either finished pre-emptively, got merged, or aborted,
+ // so update fixed rotation state to default.
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
+
if (transition != mExitTransition) {
return;
}
@@ -408,7 +446,8 @@
// done at the start. But if it is running fixed rotation, there will be a seamless
// display transition later. So the last rotation transform needs to be kept to
// avoid flickering, and then the display transition will reset the transform.
- if (!mInFixedRotation && mFinishTransaction != null) {
+ if (mFixedRotationState != FIXED_ROTATION_TRANSITION
+ && mFinishTransaction != null) {
mFinishTransaction.merge(tx);
}
} else {
@@ -426,12 +465,27 @@
mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
.resetScale(tx, leash, destinationBounds)
.round(tx, leash, true /* applyCornerRadius */);
+ if (mPipOrganizer.mSwipePipToHomeOverlay != null && !mInitBounds.isEmpty()) {
+ // Resetting the scale for pinned task while re-adjusting its crop,
+ // also scales the overlay. So we need to update the overlay leash too.
+ Rect overlayBounds = new Rect(destinationBounds);
+ final int overlaySize = PipContentOverlay.PipAppIconOverlay
+ .getOverlaySize(mInitBounds, destinationBounds);
+
+ overlayBounds.offsetTo(
+ (destinationBounds.width() - overlaySize) / 2,
+ (destinationBounds.height() - overlaySize) / 2);
+ mSurfaceTransactionHelper.resetScale(tx,
+ mPipOrganizer.mSwipePipToHomeOverlay, overlayBounds);
+ }
}
+ mInitBounds.setEmpty();
wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
final int displayRotation = taskInfo.getConfiguration().windowConfiguration
.getDisplayRotation();
- if (enteringPip && mInFixedRotation && mEndFixedRotation != displayRotation
+ if (enteringPip && mFixedRotationState == FIXED_ROTATION_TRANSITION
+ && mEndFixedRotation != displayRotation
&& hasValidLeash) {
// Launcher may update the Shelf height during the animation, which will update the
// destination bounds. Because this is in fixed rotation, We need to make sure the
@@ -451,6 +505,8 @@
mFinishTransaction = null;
callFinishCallback(wct);
}
+ // This is the end of transition on the Shell side so update the fixed rotation state.
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
finishResizeForMenu(destinationBounds);
}
@@ -467,6 +523,7 @@
// mFinishCallback might be null with an outdated mCurrentPipTaskToken
// for example, when app crashes while in PiP and exit transition has not started
mCurrentPipTaskToken = null;
+ mFixedRotationState = FIXED_ROTATION_UNDEFINED;
if (mFinishCallback == null) return;
mFinishCallback.onTransitionFinished(null /* wct */);
mFinishCallback = null;
@@ -475,6 +532,9 @@
@Override
public void onFixedRotationStarted() {
+ if (mFixedRotationState == FIXED_ROTATION_UNDEFINED) {
+ mFixedRotationState = FIXED_ROTATION_CALLBACK;
+ }
fadeEnteredPipIfNeed(false /* show */);
}
@@ -656,7 +716,7 @@
// Check if it is fixed rotation.
final int rotationDelta;
- if (mInFixedRotation) {
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
final int startRotation = pipChange.getStartRotation();
final int endRotation = mEndFixedRotation;
rotationDelta = deltaRotation(startRotation, endRotation);
@@ -873,11 +933,13 @@
final int startRotation = pipChange.getStartRotation();
// Check again in case some callers use startEnterAnimation directly so the flag was not
// set in startAnimation, e.g. from DefaultMixedHandler.
- if (!mInFixedRotation) {
+ if (mFixedRotationState != FIXED_ROTATION_TRANSITION) {
mEndFixedRotation = pipChange.getEndFixedRotation();
- mInFixedRotation = mEndFixedRotation != ROTATION_UNDEFINED;
+ mFixedRotationState = mEndFixedRotation != ROTATION_UNDEFINED
+ ? FIXED_ROTATION_TRANSITION : mFixedRotationState;
}
- final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation();
+ final int endRotation = mFixedRotationState == FIXED_ROTATION_TRANSITION
+ ? mEndFixedRotation : pipChange.getEndRotation();
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
@@ -888,10 +950,15 @@
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = pipChange.getStartAbsBounds();
+
+ // Cache the start bounds for overlay manipulations as a part of finishCallback.
+ mInitBounds.set(currentBounds);
+
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
- if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+ if (rotationDelta != Surface.ROTATION_0
+ && mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// Need to get the bounds of new rotation in old rotation for fixed rotation,
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
destinationBounds, sourceHintRect);
@@ -958,7 +1025,8 @@
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration);
- if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
+ if (rotationDelta != Surface.ROTATION_0
+ && mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// For fixed rotation, the animation destination bounds is in old rotation coordinates.
// Set the destination bounds to new coordinates after the animation is finished.
// ComputeRotatedBounds has changed the DisplayLayout without affecting the animation.
@@ -997,13 +1065,17 @@
@NonNull SurfaceControl leash, @Nullable Rect sourceHintRect,
@NonNull Rect destinationBounds,
@NonNull ActivityManager.RunningTaskInfo pipTaskInfo) {
- if (mInFixedRotation) {
+ if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
// If rotation changes when returning to home, the transition should contain both the
// entering PiP and the display change (PipController#startSwipePipToHome has updated
// the display layout to new rotation). So it is not expected to see fixed rotation.
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"%s: SwipePipToHome should not use fixed rotation %d", TAG, mEndFixedRotation);
}
+ Rect appBounds = pipTaskInfo.configuration.windowConfiguration.getAppBounds();
+ if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) {
+ mInitBounds.set(appBounds);
+ }
final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
if (swipePipToHomeOverlay != null) {
// Launcher fade in the overlay on top of the fullscreen Task. It is possible we