Save PiP activity position on screen for next re-entry into PiP.
- Keep the last known PiP position when the user expands the activity to
fullscreen until the activity is next hidden or destroyed.
Bug: 36520745
Test: android.server.cts.ActivityManagerPinnedStackTests
Test: #testEnterPictureInPictureSavePosition
Test: #testEnterPictureInPictureDiscardSavedPositionOnFinish
Change-Id: I32e2bec8baa000b854981a4492f44c636f083680
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0a42aa9..21085fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3116,6 +3116,10 @@
// Need to make sure the pinned stack exist so we can resize it below...
stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+ // Calculate the target bounds here before the task is reparented back into pinned windowing
+ // mode (which will reset the saved bounds)
+ final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
try {
final TaskRecord task = r.getTask();
// Resize the pinned stack to match the current size of the task the activity we are
@@ -3154,11 +3158,6 @@
mWindowManager.continueSurfaceLayout();
}
- // Calculate the default bounds (don't use existing stack bounds as we may have just created
- // the stack, and schedule the start of the animation into PiP (the bounds animator that
- // is triggered by this is posted on another thread)
- final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
true /* fromFullscreen */);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 44d7948..9f9ca8f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -53,6 +56,7 @@
import android.annotation.CallSuper;
import android.app.Activity;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -1211,6 +1215,30 @@
}
@Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ final int prevWinMode = getWindowingMode();
+ super.onConfigurationChanged(newParentConfig);
+ final int winMode = getWindowingMode();
+
+ if (prevWinMode == winMode) {
+ return;
+ }
+
+ if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
+ // Entering PiP from fullscreen, reset the snap fraction
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+ } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED) {
+ // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
+ // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
+ final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ if (pinnedStack != null) {
+ mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this,
+ pinnedStack.mPreAnimationBounds);
+ }
+ }
+ }
+
+ @Override
void checkAppWindowsReadyToShow() {
if (allDrawn == mLastAllDrawn) {
return;
@@ -1837,6 +1865,11 @@
@Override
void setHidden(boolean hidden) {
super.setHidden(hidden);
+
+ if (hidden) {
+ // Once the app window is hidden, reset the last saved PiP snap fraction
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+ }
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index d8726bf..69cbe46 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -48,6 +48,7 @@
import com.android.server.UiThread;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -71,6 +72,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
+ public static final float INVALID_SNAP_FRACTION = -1f;
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Handler mHandler = UiThread.getHandler();
@@ -101,6 +103,8 @@
private float mDefaultAspectRatio;
private Point mScreenEdgeInsets;
private int mCurrentMinSize;
+ private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
+ private WeakReference<AppWindowToken> mLastPipActivity = null;
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
@@ -113,6 +117,7 @@
private final Rect mTmpAnimatingBoundsRect = new Rect();
private final Point mTmpDisplaySize = new Point();
+
/**
* The callback object passed to listeners for them to notify the controller of state changes.
*/
@@ -250,9 +255,35 @@
}
/**
+ * Saves the current snap fraction for re-entry of the current activity into PiP.
+ */
+ void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) {
+ mReentrySnapFraction = getSnapFraction(stackBounds);
+ mLastPipActivity = new WeakReference<>(token);
+ }
+
+ /**
+ * Resets the last saved snap fraction so that the default bounds will be returned.
+ */
+ void resetReentrySnapFraction(AppWindowToken token) {
+ if (mLastPipActivity != null && mLastPipActivity.get() == token) {
+ mReentrySnapFraction = INVALID_SNAP_FRACTION;
+ mLastPipActivity = null;
+ }
+ }
+
+ /**
* @return the default bounds to show the PIP when there is no active PIP.
*/
- Rect getDefaultBounds() {
+ Rect getDefaultOrLastSavedBounds() {
+ return getDefaultBounds(mReentrySnapFraction);
+ }
+
+ /**
+ * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
+ * will apply the default bounds to the provided snap fraction.
+ */
+ Rect getDefaultBounds(float snapFraction) {
synchronized (mService.mWindowMap) {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
@@ -260,8 +291,14 @@
final Rect defaultBounds = new Rect();
final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
- 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+ if (snapFraction != INVALID_SNAP_FRACTION) {
+ defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
+ final Rect movementBounds = getMovementBounds(defaultBounds);
+ mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
+ } else {
+ Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+ 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+ }
return defaultBounds;
}
}
@@ -299,9 +336,7 @@
final Rect postChangeStackBounds = mTmpRect;
// Calculate the snap fraction of the current stack along the old movement bounds
- final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
- final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
- preChangeMovementBounds);
+ final float snapFraction = getSnapFraction(postChangeStackBounds);
mDisplayInfo.copyFrom(displayInfo);
// Calculate the stack bounds in the new orientation to the same same fraction along the
@@ -414,7 +449,7 @@
try {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds();
+ final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
transformBoundsToAspectRatio(normalBounds, mAspectRatio,
false /* useCurrentMinEdgeSize */);
@@ -486,6 +521,14 @@
}
/**
+ * @return the default snap fraction to apply instead of the default gravity when calculating
+ * the default stack bounds when first entering PiP.
+ */
+ private float getSnapFraction(Rect stackBounds) {
+ return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
+ }
+
+ /**
* @return the pixels for a given dp value.
*/
private int dpToPx(float dpValue, DisplayMetrics dm) {
@@ -494,7 +537,8 @@
void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "PinnedStackController");
- pw.print(prefix + " defaultBounds="); getDefaultBounds().printShortString(pw);
+ pw.print(prefix + " defaultBounds=");
+ getDefaultBounds(INVALID_SNAP_FRACTION).printShortString(pw);
pw.println();
mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw);
@@ -516,7 +560,7 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS);
+ getDefaultBounds(INVALID_SNAP_FRACTION).writeToProto(proto, DEFAULT_BOUNDS);
mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect);
getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS);
proto.end(token);
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index b021a72..02fbfba 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -61,7 +61,7 @@
displayContent.getPinnedStackController();
if (stackBounds == null) {
// Calculate the aspect ratio bounds from the default bounds
- stackBounds = pinnedStackController.getDefaultBounds();
+ stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
}
if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
@@ -173,7 +173,7 @@
* from fullscreen to non-fullscreen bounds.
*/
public boolean deferScheduleMultiWindowModeChanged() {
- synchronized(mWindowMap) {
+ synchronized (mWindowMap) {
return mContainer.deferScheduleMultiWindowModeChanged();
}
}