Merge "Adding source bounds hint to support better PiP transition."
diff --git a/api/current.txt b/api/current.txt
index 33eb074..24586f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5667,6 +5667,7 @@
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
+ method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 9328420..7e92671 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5859,6 +5859,7 @@
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
+ method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 39663f2..d33c1fa 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5678,6 +5678,7 @@
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
+ method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
}
diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java
index fbdcbf4..0ce5eeb 100644
--- a/core/java/android/app/PictureInPictureArgs.java
+++ b/core/java/android/app/PictureInPictureArgs.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,11 +36,19 @@
private Float mAspectRatio;
/**
- * The set of actions that are associated with this activity when in picture in picture.
+ * The set of actions that are associated with this activity when in picture-in-picture.
*/
@Nullable
private List<RemoteAction> mUserActions;
+ /**
+ * The source bounds hint used when entering picture-in-picture, relative to the window bounds.
+ * We can use this internally for the transition into picture-in-picture to ensure that a
+ * particular source rect is visible throughout the whole transition.
+ */
+ @Nullable
+ private Rect mSourceRectHint;
+
PictureInPictureArgs(Parcel in) {
if (in.readInt() != 0) {
mAspectRatio = in.readFloat();
@@ -48,6 +57,9 @@
mUserActions = new ArrayList<>();
in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
}
+ if (in.readInt() != 0) {
+ mSourceRectHint = Rect.CREATOR.createFromParcel(in);
+ }
}
/**
@@ -79,6 +91,9 @@
if (otherArgs.hasSetActions()) {
mUserActions = otherArgs.mUserActions;
}
+ if (otherArgs.hasSourceBoundsHint()) {
+ mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
+ }
}
/**
@@ -137,9 +152,43 @@
return mUserActions != null;
}
+ /**
+ * Sets the source bounds hint. These bounds are only used when an activity first enters
+ * picture-in-picture, and describe the bounds in window coordinates of activity entering
+ * picture-in-picture that will be visible following the transition. For the best effect, these
+ * bounds should also match the aspect ratio in the arguments.
+ */
+ public void setSourceRectHint(Rect launchBounds) {
+ if (launchBounds == null) {
+ mSourceRectHint = null;
+ } else {
+ mSourceRectHint = new Rect(launchBounds);
+ }
+ }
+
+ /**
+ * @return the launch bounds
+ * @hide
+ */
+ public Rect getSourceRectHint() {
+ return mSourceRectHint;
+ }
+
+ /**
+ * @return whether there are launch bounds set
+ * @hide
+ */
+ public boolean hasSourceBoundsHint() {
+ return mSourceRectHint != null && !mSourceRectHint.isEmpty();
+ }
+
@Override
public PictureInPictureArgs clone() {
- return new PictureInPictureArgs(mAspectRatio, mUserActions);
+ PictureInPictureArgs args = new PictureInPictureArgs(mAspectRatio, mUserActions);
+ if (mSourceRectHint != null) {
+ args.setSourceRectHint(mSourceRectHint);
+ }
+ return args;
}
@Override
@@ -161,6 +210,12 @@
} else {
out.writeInt(0);
}
+ if (mSourceRectHint != null) {
+ out.writeInt(1);
+ mSourceRectHint.writeToParcel(out, 0);
+ } else {
+ out.writeInt(0);
+ }
}
public static final Creator<PictureInPictureArgs> CREATOR =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2be5e77..92a4b78 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7870,10 +7870,11 @@
r.pictureInPictureArgs.copyOnlySet(args);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
- final Rect bounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
+ final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint();
+ final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
aspectRatio);
- mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
- bounds, true /* moveHomeStackToFront */);
+ mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, destBounds,
+ true /* moveHomeStackToFront */, "enterPictureInPictureMode");
final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
@@ -10525,7 +10526,7 @@
}
@Override
- public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode,
+ public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
boolean preserveWindows, boolean animate, int animationDuration) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
long ident = Binder.clearCallingIdentity();
@@ -10535,13 +10536,14 @@
if (stackId == PINNED_STACK_ID) {
final PinnedActivityStack pinnedStack =
mStackSupervisor.getStack(PINNED_STACK_ID);
- pinnedStack.animateResizePinnedStack(bounds, animationDuration);
+ pinnedStack.animateResizePinnedStack(null /* sourceBounds */, destBounds,
+ animationDuration);
} else {
throw new IllegalArgumentException("Stack: " + stackId
+ " doesn't support animated resize.");
}
} else {
- mStackSupervisor.resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
+ mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
allowResizeInDockedMode, !DEFER_RESUME);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5c49dfd..9c19c2b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2794,7 +2794,7 @@
}
}
- boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
+ boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
if (stack == null) {
throw new IllegalArgumentException(
@@ -2815,13 +2815,14 @@
return false;
}
- moveActivityToPinnedStackLocked(r, "moveTopActivityToPinnedStack", bounds,
- true /* moveHomeStackToFront */);
+ moveActivityToPinnedStackLocked(r, null /* sourceBounds */, destBounds,
+ true /* moveHomeStackToFront */, "moveTopActivityToPinnedStack");
return true;
}
- void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds,
- boolean moveHomeStackToFront) {
+ void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, Rect destBounds,
+ boolean moveHomeStackToFront, String reason) {
+
mWindowManager.deferSurfaceLayout();
// Need to make sure the pinned stack exist so we can resize it below...
@@ -2889,7 +2890,7 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- stack.animateResizePinnedStack(bounds, -1 /* animationDuration */);
+ stack.animateResizePinnedStack(sourceBounds, destBounds, -1 /* animationDuration */);
mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName);
}
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 1708fe5..32d3082 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -41,8 +41,9 @@
return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
}
- void animateResizePinnedStack(Rect bounds, int animationDuration) {
- getWindowContainerController().animateResizePinnedStack(bounds, animationDuration);
+ void animateResizePinnedStack(Rect sourceBounds, Rect destBounds, int animationDuration) {
+ getWindowContainerController().animateResizePinnedStack(sourceBounds, destBounds,
+ animationDuration);
}
void setPictureInPictureAspectRatio(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index cd0e6cc..62414e5 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -121,8 +121,8 @@
private final int mFrozenTaskWidth;
private final int mFrozenTaskHeight;
- BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to,
- boolean moveToFullScreen, boolean replacement) {
+ BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen,
+ boolean replacement) {
super();
mTarget = target;
mFrom.set(from);
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 34ccf87..0145454 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -40,29 +40,30 @@
/**
* Animates the pinned stack.
*/
- public void animateResizePinnedStack(Rect bounds, int animationDuration) {
+ public void animateResizePinnedStack(Rect sourceBounds, Rect destBounds,
+ int animationDuration) {
synchronized (mWindowMap) {
if (mContainer == null) {
throw new IllegalArgumentException("Pinned stack container not found :(");
}
// Get non-null fullscreen bounds if the bounds are null
- final boolean moveToFullscreen = bounds == null;
- bounds = getPinnedStackAnimationBounds(bounds);
+ final boolean moveToFullscreen = destBounds == null;
+ destBounds = getPinnedStackAnimationBounds(destBounds);
// If the bounds are truly null, then there was no fullscreen stack at this time, so
// animate this to the full display bounds
final Rect toBounds;
- if (bounds == null) {
+ if (destBounds == null) {
toBounds = new Rect();
mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
} else {
- toBounds = bounds;
+ toBounds = destBounds;
}
final Rect originalBounds = new Rect();
mContainer.getBounds(originalBounds);
- mContainer.setAnimatingBounds(toBounds);
+ mContainer.setAnimatingBounds(sourceBounds, toBounds);
UiThread.getHandler().post(() -> {
if (mContainer == null) {
return;
@@ -82,13 +83,17 @@
return;
}
+ final int displayId = mContainer.getDisplayContent().getDisplayId();
+ final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+ final Rect targetBounds = new Rect();
+ mContainer.getAnimatingBounds(targetBounds);
final PinnedStackController pinnedStackController =
mContainer.getDisplayContent().getPinnedStackController();
if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) {
- final int displayId = mContainer.getDisplayContent().getDisplayId();
- final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
- animateResizePinnedStack(toBounds, -1 /* duration */);
+ if (!toBounds.equals(targetBounds)) {
+ animateResizePinnedStack(null /* sourceBounds */, toBounds, -1 /* duration */);
+ }
pinnedStackController.setAspectRatio(
pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
? aspectRatio : -1f);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 442cd54..dc437ea 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,7 +19,6 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -128,6 +127,7 @@
private boolean mBoundsAnimating = false;
private boolean mBoundsAnimatingToFullscreen = false;
private Rect mBoundsAnimationTarget = new Rect();
+ private Rect mBoundsAnimationSourceBounds = new Rect();
// Temporary storage for the new bounds that should be used after the configuration change.
// Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
@@ -323,15 +323,31 @@
* Sets the bounds animation target bounds. This can't currently be done in onAnimationStart()
* since that is started on the UiThread.
*/
- void setAnimatingBounds(Rect bounds) {
- if (bounds != null) {
- mBoundsAnimationTarget.set(bounds);
+ void setAnimatingBounds(Rect sourceBounds, Rect destBounds) {
+ if (sourceBounds != null) {
+ mBoundsAnimationSourceBounds.set(sourceBounds);
+ } else {
+ mBoundsAnimationSourceBounds.setEmpty();
+ }
+ if (destBounds != null) {
+ mBoundsAnimationTarget.set(destBounds);
} else {
mBoundsAnimationTarget.setEmpty();
}
}
/**
+ * @return the source bounds for the bounds animation.
+ */
+ void getAnimatingSourceBounds(Rect outBounds) {
+ if (mBoundsAnimationSourceBounds != null) {
+ outBounds.set(mBoundsAnimationSourceBounds);
+ return;
+ }
+ outBounds.setEmpty();
+ }
+
+ /**
* @return the bounds that the task stack is currently being animated towards, or the current
* stack bounds if there is no animation in progress.
*/
@@ -1465,7 +1481,6 @@
public void onAnimationEnd() {
synchronized (mService.mWindowMap) {
mBoundsAnimating = false;
- mBoundsAnimationTarget.setEmpty();
mService.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 48de7e4..826fb45 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -67,8 +67,6 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
-import com.android.server.wm.WindowManagerService.H;
-
import java.io.PrintWriter;
import java.io.FileDescriptor;
@@ -156,6 +154,8 @@
Rect mLastClipRect = new Rect();
Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
+ private Rect mTmpAnimatingBounds = new Rect();
+ private Rect mTmpSourceBounds = new Rect();
/**
* This is rectangle of the window's surface that is not covered by
@@ -1282,6 +1282,7 @@
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
+ final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
// We got resized, so block all updates until we got the new surface.
@@ -1290,7 +1291,7 @@
}
mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
- calculateSurfaceBounds(w, w.getAttrs());
+ calculateSurfaceBounds(w, attrs);
mExtraHScale = (float) 1.0;
mExtraVScale = (float) 1.0;
@@ -1329,23 +1330,59 @@
float surfaceHeight = mSurfaceController.getHeight();
if (isForceScaled()) {
- int hInsets = w.getAttrs().surfaceInsets.left + w.getAttrs().surfaceInsets.right;
- int vInsets = w.getAttrs().surfaceInsets.top + w.getAttrs().surfaceInsets.bottom;
+ int hInsets = attrs.surfaceInsets.left + attrs.surfaceInsets.right;
+ int vInsets = attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+ float surfaceContentWidth = surfaceWidth - hInsets;
+ float surfaceContentHeight = surfaceHeight - vInsets;
if (!mForceScaleUntilResize) {
mSurfaceController.forceScaleableInTransaction(true);
}
+ int posX = mTmpSize.left;
+ int posY = mTmpSize.top;
task.mStack.getDimBounds(mTmpStackBounds);
- // We want to calculate the scaling based on the content area, not based on
- // the entire surface, so that we scale in sync with windows that don't have insets.
- mExtraHScale = mTmpStackBounds.width() / (float)(surfaceWidth - hInsets);
- mExtraVScale = mTmpStackBounds.height() / (float)(surfaceHeight - vInsets);
+ task.mStack.getAnimatingSourceBounds(mTmpSourceBounds);
+ if (!mTmpSourceBounds.isEmpty()) {
+ // Get the final target stack bounds, if we are not animating, this is just the
+ // current stack bounds
+ task.mStack.getAnimatingBounds(mTmpAnimatingBounds);
+
+ // Calculate the current progress and interpolate the difference between the target
+ // and source bounds
+ float finalWidth = mTmpAnimatingBounds.width();
+ float initialWidth = mTmpSourceBounds.width();
+ float t = (surfaceContentWidth - mTmpStackBounds.width())
+ / (surfaceContentWidth - mTmpAnimatingBounds.width());
+ mExtraHScale = (initialWidth + t * (finalWidth - initialWidth)) / initialWidth;
+ mExtraVScale = mExtraHScale;
+
+ // Adjust the position to account for the inset bounds
+ posX -= (int) (t * mExtraHScale * mTmpSourceBounds.left);
+ posY -= (int) (t * mExtraVScale * mTmpSourceBounds.top);
+
+ // Always clip to the stack bounds since the surface can be larger with the current
+ // scale
+ clipRect = null;
+ finalClipRect = mTmpStackBounds;
+ } else {
+ // We want to calculate the scaling based on the content area, not based on
+ // the entire surface, so that we scale in sync with windows that don't have insets.
+ mExtraHScale = mTmpStackBounds.width() / surfaceContentWidth;
+ mExtraVScale = mTmpStackBounds.height() / surfaceContentHeight;
+
+ // Since we are scaled to fit in our previously desired crop, we can now
+ // expose the whole window in buffer space, and not risk extending
+ // past where the system would have cropped us
+ clipRect = null;
+ finalClipRect = null;
+ }
// In the case of ForceScaleToStack we scale entire tasks together,
// and so we need to scale our offsets relative to the task bounds
// or parent and child windows would fall out of alignment.
- int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - mExtraHScale));
- int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - mExtraVScale));
+ posX -= (int) (attrs.x * (1 - mExtraHScale));
+ posY -= (int) (attrs.y * (1 - mExtraVScale));
+
// Imagine we are scaling down. As we scale the buffer down, we decrease the
// distance between the surface top left, and the start of the surface contents
// (previously it was surfaceInsets.left pixels in screen space but now it
@@ -1353,17 +1390,11 @@
// non inset content at the same position, we have to shift the whole window
// forward. Likewise for scaling up, we've increased this distance, and we need
// to shift by a negative number to compensate.
- posX += w.getAttrs().surfaceInsets.left * (1 - mExtraHScale);
- posY += w.getAttrs().surfaceInsets.top * (1 - mExtraVScale);
+ posX += attrs.surfaceInsets.left * (1 - mExtraHScale);
+ posY += attrs.surfaceInsets.top * (1 - mExtraVScale);
- mSurfaceController.setPositionInTransaction((float)Math.floor(posX),
- (float)Math.floor(posY), recoveringMemory);
-
- // Since we are scaled to fit in our previously desired crop, we can now
- // expose the whole window in buffer space, and not risk extending
- // past where the system would have cropped us
- clipRect = null;
- finalClipRect = null;
+ mSurfaceController.setPositionInTransaction((float) Math.floor(posX),
+ (float) Math.floor(posY), recoveringMemory);
// Various surfaces in the scaled stack may resize at different times.
// We need to ensure for each surface, that we disable transformation matrix