summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java72
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java16
-rw-r--r--services/core/java/com/android/server/wm/Task.java11
5 files changed, 130 insertions, 26 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index c46b5590bab6..200af7415eb1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -26,10 +26,14 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.TaskInfo;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -253,6 +257,7 @@ public class PipAnimationController {
mSurfaceControlTransactionFactory;
private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @TransitionDirection int mTransitionDirection;
+ protected SurfaceControl mContentOverlay;
private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
@AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
@@ -331,6 +336,53 @@ public class PipAnimationController {
return false;
}
+ SurfaceControl getContentOverlay() {
+ return mContentOverlay;
+ }
+
+ PipTransitionAnimator<T> setUseContentOverlay(Context context) {
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ if (mContentOverlay != null) {
+ // remove existing content overlay if there is any.
+ tx.remove(mContentOverlay);
+ tx.apply();
+ }
+ mContentOverlay = new SurfaceControl.Builder(new SurfaceSession())
+ .setCallsite("PipAnimation")
+ .setName("PipContentOverlay")
+ .setColorLayer()
+ .build();
+ tx.show(mContentOverlay);
+ tx.setLayer(mContentOverlay, Integer.MAX_VALUE);
+ tx.setColor(mContentOverlay, getContentOverlayColor(context));
+ tx.setAlpha(mContentOverlay, 0f);
+ tx.reparent(mContentOverlay, mLeash);
+ tx.apply();
+ return this;
+ }
+
+ private float[] getContentOverlayColor(Context context) {
+ final TypedArray ta = context.obtainStyledAttributes(new int[] {
+ android.R.attr.colorBackground });
+ try {
+ int colorAccent = ta.getColor(0, 0);
+ return new float[] {
+ Color.red(colorAccent) / 255f,
+ Color.green(colorAccent) / 255f,
+ Color.blue(colorAccent) / 255f };
+ } finally {
+ ta.recycle();
+ }
+ }
+
+ /**
+ * Clears the {@link #mContentOverlay}, this should be done after the content overlay is
+ * faded out, such as in {@link PipTaskOrganizer#fadeOutAndRemoveOverlay}
+ */
+ void clearContentOverlay() {
+ mContentOverlay = null;
+ }
+
@VisibleForTesting
@TransitionDirection public int getTransitionDirection() {
return mTransitionDirection;
@@ -517,6 +569,9 @@ public class PipAnimationController {
final Rect base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
+ if (mContentOverlay != null) {
+ tx.setAlpha(mContentOverlay, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+ }
if (rotatedEndRect != null) {
// Animate the bounds in a different orientation. It only happens when
// switching between PiP and fullscreen.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f367cd608f37..5a506193b8a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -107,6 +107,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
+ /**
+ * The fixed start delay in ms when fading out the content overlay from bounds animation.
+ * This is to overcome the flicker caused by configuration change when rotating from landscape
+ * to portrait PiP in button navigation mode.
+ */
+ private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
+
// Not a complete set of states but serves what we want right now.
private enum State {
UNDEFINED(0),
@@ -176,6 +183,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final int direction = animator.getTransitionDirection();
final int animationType = animator.getAnimationType();
final Rect destinationBounds = animator.getDestinationBounds();
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay*/);
+ }
if (mWaitForFixedRotation && animationType == ANIM_TYPE_BOUNDS
&& direction == TRANSITION_DIRECTION_TO_PIP) {
// Notify the display to continue the deferred orientation change.
@@ -199,17 +210,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
finishResize(tx, destinationBounds, direction, animationType);
sendOnPipTransitionFinished(direction);
}
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- // TODO (b//169221267): Add jank listener for transactions without buffer updates.
- //InteractionJankMonitor.getInstance().end(
- // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
- }
}
@Override
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
- sendOnPipTransitionCancelled(animator.getTransitionDirection());
+ final int direction = animator.getTransitionDirection();
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay */);
+ }
+ sendOnPipTransitionCancelled(direction);
}
};
@@ -640,7 +651,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// Remove the swipe to home overlay
if (swipeToHomeOverlay != null) {
- fadeOutAndRemoveOverlay(swipeToHomeOverlay);
+ fadeOutAndRemoveOverlay(swipeToHomeOverlay,
+ null /* callback */, false /* withStartDelay */);
}
}, tx);
mInSwipePipToHomeTransition = false;
@@ -723,9 +735,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
}
- final PipAnimationController.PipTransitionAnimator animator =
+ final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator();
if (animator != null) {
+ if (animator.getContentOverlay() != null) {
+ removeContentOverlay(animator.getContentOverlay(), animator::clearContentOverlay);
+ }
animator.removeAllUpdateListeners();
animator.removeAllListeners();
animator.cancel();
@@ -1196,7 +1211,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
snapshotDest);
// Start animation to fade out the snapshot.
- fadeOutAndRemoveOverlay(snapshotSurface);
+ fadeOutAndRemoveOverlay(snapshotSurface,
+ null /* callback */, false /* withStartDelay */);
});
} else {
applyFinishBoundsResize(wct, direction);
@@ -1287,15 +1303,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
animator.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setPipTransactionHandler(mPipTransactionHandler)
- .setDuration(durationMs)
- .start();
- if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) {
+ .setDuration(durationMs);
+ if (isInPipDirection(direction)) {
+ // Similar to auto-enter-pip transition, we use content overlay when there is no
+ // source rect hint to enter PiP use bounds animation.
+ if (sourceHintRect == null) animator.setUseContentOverlay(mContext);
// The destination bounds are used for the end rect of animation and the final bounds
// after animation finishes. So after the animation is started, the destination bounds
// can be updated to new rotation (computeRotatedBounds has changed the DisplayLayout
// without affecting the animation.
- animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
+ if (rotationDelta != Surface.ROTATION_0) {
+ animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
+ }
}
+ animator.start();
return animator;
}
@@ -1308,6 +1329,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
// Transform the destination bounds to current display coordinates.
rotateBounds(outDestinationBounds, displayBounds, mNextRotation, mCurrentRotation);
+ // When entering PiP (from button navigation mode), adjust the source rect hint by
+ // display cutout if applicable.
+ if (sourceHintRect != null && mTaskInfo.displayCutoutInsets != null) {
+ if (rotationDelta == Surface.ROTATION_270) {
+ sourceHintRect.offset(mTaskInfo.displayCutoutInsets.left,
+ mTaskInfo.displayCutoutInsets.top);
+ }
+ }
} else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
@@ -1346,7 +1375,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
/**
* Fades out and removes an overlay surface.
*/
- private void fadeOutAndRemoveOverlay(SurfaceControl surface) {
+ private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
+ boolean withStartDelay) {
if (surface == null) {
return;
}
@@ -1363,15 +1393,21 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.remove(surface);
- tx.apply();
+ removeContentOverlay(surface, callback);
}
});
+ animator.setStartDelay(withStartDelay ? CONTENT_OVERLAY_FADE_OUT_DELAY_MS : 0);
animator.start();
}
+ private void removeContentOverlay(SurfaceControl surface, Runnable callback) {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ tx.remove(surface);
+ tx.apply();
+ if (callback != null) callback.run();
+ }
+
/**
* Dumps internal states.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a72d9aa9ec6b..3144c87e8314 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1884,7 +1884,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
forAllWindows(w -> {
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
}, true /* traverseTopToBottom */);
- mPinnedTaskController.startSeamlessRotationIfNeeded(transaction);
+ mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
}
mWmService.mDisplayManagerInternal.performTraversal(transaction);
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 31e2edec2601..7e95e7d2aa8c 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -27,13 +27,16 @@ import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.RotationUtils;
import android.util.Slog;
import android.view.IPinnedTaskListener;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
@@ -237,7 +240,8 @@ class PinnedTaskController {
* rotation of display. The final surface matrix will be replaced by PiPTaskOrganizer after it
* receives the callback of fixed rotation completion.
*/
- void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t) {
+ void startSeamlessRotationIfNeeded(SurfaceControl.Transaction t,
+ int oldRotation, int newRotation) {
final Rect bounds = mDestRotatedBounds;
final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
if (bounds == null && pipTx == null) {
@@ -280,6 +284,16 @@ class PinnedTaskController {
? params.getSourceRectHint()
: null;
Slog.i(TAG, "Seamless rotation PiP bounds=" + bounds + " hintRect=" + sourceHintRect);
+ final int rotationDelta = RotationUtils.deltaRotation(oldRotation, newRotation);
+ // Adjust for display cutout if applicable.
+ if (sourceHintRect != null && rotationDelta == Surface.ROTATION_270) {
+ if (pinnedTask.getDisplayCutoutInsets() != null) {
+ final int rotationBackDelta = RotationUtils.deltaRotation(newRotation, oldRotation);
+ final Rect displayCutoutInsets = RotationUtils.rotateInsets(
+ Insets.of(pinnedTask.getDisplayCutoutInsets()), rotationBackDelta).toRect();
+ sourceHintRect.offset(displayCutoutInsets.left, displayCutoutInsets.top);
+ }
+ }
final Rect contentBounds = sourceHintRect != null && areaBounds.contains(sourceHintRect)
? sourceHintRect : areaBounds;
final int w = contentBounds.width();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a11325422e29..382a2b1b5062 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4105,7 +4105,7 @@ class Task extends WindowContainer<WindowContainer> {
info.positionInParent = getRelativePosition();
info.pictureInPictureParams = getPictureInPictureParams(top);
- info.displayCutoutInsets = getDisplayCutoutInsets(top);
+ info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null;
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
@@ -4140,16 +4140,15 @@ class Task extends WindowContainer<WindowContainer> {
? null : new PictureInPictureParams(topVisibleActivity.pictureInPictureArgs);
}
- private Rect getDisplayCutoutInsets(Task top) {
- if (top == null || top.mDisplayContent == null
- || top.getDisplayInfo().displayCutout == null) return null;
- final WindowState w = top.getTopVisibleAppMainWindow();
+ Rect getDisplayCutoutInsets() {
+ if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
+ final WindowState w = getTopVisibleAppMainWindow();
final int displayCutoutMode = w == null
? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
: w.getAttrs().layoutInDisplayCutoutMode;
return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|| displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
- ? null : top.getDisplayInfo().displayCutout.getSafeInsets();
+ ? null : getDisplayInfo().displayCutout.getSafeInsets();
}
/**