diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java index 4a9e28e8d9ff8..7c69c0cf3d67a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java @@ -256,7 +256,8 @@ public class PipBoundsHandler { // Calculate the snap fraction of the current stack along the old movement bounds final Rect postChangeStackBounds = new Rect(oldBounds); - final float snapFraction = getSnapFraction(postChangeStackBounds); + final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds, + getMovementBounds(postChangeStackBounds), mPipBoundsState.getStashedState()); // Update the display layout mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation); @@ -273,7 +274,8 @@ public class PipBoundsHandler { final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds, false /* adjustForIme */); mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, - snapFraction); + snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(), + mPipBoundsState.getDisplayBounds()); getInsetBounds(outInsetBounds); outBounds.set(postChangeStackBounds); @@ -321,7 +323,7 @@ public class PipBoundsHandler { boolean useCurrentMinEdgeSize, boolean useCurrentSize) { // Save the snap fraction and adjust the size based on the new aspect ratio. final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds, - getMovementBounds(stackBounds)); + getMovementBounds(stackBounds), mPipBoundsState.getStashedState()); final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize; final Size size; if (useCurrentMinEdgeSize || useCurrentSize) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index c4ce2cf84dd22..57867d0b08488 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -16,34 +16,70 @@ package com.android.wm.shell.pip; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; +import android.content.Context; import android.graphics.Rect; import android.util.Size; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** * Singleton source of truth for the current state of PIP bounds. */ public final class PipBoundsState { + public static final int STASH_TYPE_NONE = 0; + public static final int STASH_TYPE_LEFT = 1; + public static final int STASH_TYPE_RIGHT = 2; + + @IntDef(prefix = { "STASH_TYPE_" }, value = { + STASH_TYPE_NONE, + STASH_TYPE_LEFT, + STASH_TYPE_RIGHT + }) + @Retention(RetentionPolicy.SOURCE) + @interface StashType {} + private static final String TAG = PipBoundsState.class.getSimpleName(); private final @NonNull Rect mBounds = new Rect(); + private final Context mContext; private float mAspectRatio; - private boolean mIsStashed; + private int mStashedState = STASH_TYPE_NONE; + private int mStashOffset; private PipReentryState mPipReentryState; private ComponentName mLastPipComponentName; private final DisplayInfo mDisplayInfo = new DisplayInfo(); private final DisplayLayout mDisplayLayout = new DisplayLayout(); private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState(); + public PipBoundsState(Context context) { + mContext = context; + reloadResources(); + } + + /** + * Reloads the resources. + */ + public void onConfigurationChanged() { + reloadResources(); + } + + private void reloadResources() { + mStashOffset = mContext.getResources() + .getDimensionPixelSize(R.dimen.pip_stash_offset); + } + /** * Set the current PIP bounds. */ @@ -57,17 +93,32 @@ public final class PipBoundsState { } /** - * Dictate where PiP currently should be stashed or not. + * Dictate where PiP currently should be stashed, if at all. */ - public void setStashed(boolean isStashed) { - mIsStashed = isStashed; + public void setStashed(@StashType int stashedState) { + mStashedState = stashedState; + } + + /** + * Return where the PiP is stashed, if at all. + * @return {@code STASH_NONE}, {@code STASH_LEFT} or {@code STASH_RIGHT}. + */ + public @StashType int getStashedState() { + return mStashedState; } /** * Whether PiP is stashed or not. */ public boolean isStashed() { - return mIsStashed; + return mStashedState != STASH_TYPE_NONE; + } + + /** + * Returns the offset from the edge of the screen for PiP stash. + */ + public int getStashOffset() { + return mStashOffset; } public void setAspectRatio(float aspectRatio) { @@ -245,7 +296,8 @@ public final class PipBoundsState { pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo); pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout); - pw.println(innerPrefix + "mIsStashed=" + mIsStashed); + pw.println(innerPrefix + "mStashedState=" + mStashedState); + pw.println(innerPrefix + "mStashOffset=" + mStashOffset); if (mPipReentryState == null) { pw.println(innerPrefix + "mPipReentryState=null"); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java index 820930c463f26..71060752df09e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java @@ -16,6 +16,10 @@ package com.android.wm.shell.pip; +import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; +import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE; +import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; + import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; @@ -41,10 +45,21 @@ public class PipSnapAlgorithm { mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } + /** + * Returns a fraction that describes where the PiP bounds is. + * See {@link #getSnapFraction(Rect, Rect, int)}. + */ + public float getSnapFraction(Rect stackBounds, Rect movementBounds) { + return getSnapFraction(stackBounds, movementBounds, STASH_TYPE_NONE); + } + /** * @return returns a fraction that describes where along the {@param movementBounds} the * {@param stackBounds} are. If the {@param stackBounds} are not currently on the * {@param movementBounds} exactly, then they will be snapped to the movement bounds. + * stashType dictates whether the PiP is stashed (off-screen) or not. If + * that's the case, we will have to do some math to calculate the snap fraction + * correctly. * * The fraction is defined in a clockwise fashion against the {@param movementBounds}: * @@ -54,9 +69,10 @@ public class PipSnapAlgorithm { * 3 +---+ 2 * 3 2 */ - public float getSnapFraction(Rect stackBounds, Rect movementBounds) { + public float getSnapFraction(Rect stackBounds, Rect movementBounds, + @PipBoundsState.StashType int stashType) { final Rect tmpBounds = new Rect(); - snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds); + snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds, stashType); final float widthFraction = (float) (tmpBounds.left - movementBounds.left) / movementBounds.width(); final float heightFraction = (float) (tmpBounds.top - movementBounds.top) / @@ -103,6 +119,22 @@ public class PipSnapAlgorithm { } } + /** + * Same as {@link #applySnapFraction(Rect, Rect, float)}, but take stash state into + * consideration. + */ + public void applySnapFraction(Rect stackBounds, Rect movementBounds, float snapFraction, + @PipBoundsState.StashType int stashType, int stashOffset, Rect displayBounds) { + applySnapFraction(stackBounds, movementBounds, snapFraction); + + if (stashType != STASH_TYPE_NONE) { + stackBounds.offsetTo(stashType == STASH_TYPE_LEFT + ? stashOffset - stackBounds.width() + : displayBounds.right - stashOffset, + stackBounds.top); + } + } + /** * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given * {@param stackBounds}. @@ -178,17 +210,24 @@ public class PipSnapAlgorithm { * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes * the new bounds out to {@param boundsOut}. */ - public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) { + public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, + @PipBoundsState.StashType int stashType) { + int leftEdge = stackBounds.left; + if (stashType == STASH_TYPE_LEFT) { + leftEdge = movementBounds.left; + } else if (stashType == STASH_TYPE_RIGHT) { + leftEdge = movementBounds.right; + } final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right, - stackBounds.left)); + leftEdge)); final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom, stackBounds.top)); boundsOut.set(stackBounds); // Otherwise, just find the closest edge - final int fromLeft = Math.abs(stackBounds.left - movementBounds.left); + final int fromLeft = Math.abs(leftEdge - movementBounds.left); final int fromTop = Math.abs(stackBounds.top - movementBounds.top); - final int fromRight = Math.abs(movementBounds.right - stackBounds.left); + final int fromRight = Math.abs(movementBounds.right - leftEdge); final int fromBottom = Math.abs(movementBounds.bottom - stackBounds.top); final int shortest = Math.min(Math.min(fromLeft, fromRight), Math.min(fromTop, fromBottom)); if (shortest == fromLeft) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 37a5919b1c9ea..b68cf4f883d7a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -196,6 +196,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mMainExecutor.execute(() -> { mPipBoundsHandler.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); + mPipBoundsState.onConfigurationChanged(); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 83cd63c149c50..2e3db06455eea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -16,6 +16,9 @@ package com.android.wm.shell.pip.phone; +import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; +import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; @@ -33,7 +36,6 @@ import androidx.dynamicanimation.animation.AnimationHandler; import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler; import androidx.dynamicanimation.animation.SpringForce; -import com.android.wm.shell.R; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -79,8 +81,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** The region that all of PIP must stay within. */ private final Rect mFloatingAllowedArea = new Rect(); - private int mStashOffset = 0; - /** Coordinator instance for resolving conflicts with other floating content. */ private FloatingContentCoordinator mFloatingContentCoordinator; @@ -179,8 +179,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( mSfAnimationHandlerThreadLocal.get()); - reloadResources(); - mResizePipUpdateListener = (target, values) -> { if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) { mPipTaskOrganizer.scheduleUserResizePip(getBounds(), @@ -189,11 +187,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }; } - void reloadResources() { - mStashOffset = mContext.getResources() - .getDimensionPixelSize(R.dimen.pip_stash_offset); - } - @NonNull @Override public Rect getFloatingBoundsOnScreen() { @@ -380,6 +373,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ void stashToEdge( float velocityX, float velocityY, @Nullable Runnable endAction) { + mPipBoundsState.setStashed(velocityX < 0 ? STASH_TYPE_LEFT : STASH_TYPE_RIGHT); movetoTarget(velocityX, velocityY, endAction, true /* isStash */); } @@ -399,9 +393,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) .withEndActions(endAction); - final float leftEdge = isStash ? mStashOffset - mPipBoundsState.getBounds().width() + final float leftEdge = isStash + ? mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width() : mMovementBounds.left; - final float rightEdge = isStash ? mPipBoundsState.getDisplayBounds().right - mStashOffset + final float rightEdge = isStash + ? mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset() : mMovementBounds.right; final float xEndValue = velocityX < 0 ? leftEdge : rightEdge; @@ -471,9 +467,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()), - currentMovementBounds); + currentMovementBounds, mPipBoundsState.getStashedState()); } - mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction); + + mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction, + mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(), + mPipBoundsState.getDisplayBounds()); if (immediate) { movePip(normalBounds); @@ -512,8 +511,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mFlingConfigY = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom); mStashConfigX = new PhysicsAnimator.FlingConfig( - DEFAULT_FRICTION, mStashOffset - mPipBoundsState.getBounds().width(), - mPipBoundsState.getDisplayBounds().right - mStashOffset); + DEFAULT_FRICTION, + mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width(), + mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset()); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index c04e7e8a59357..9ba46723081f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -221,7 +221,6 @@ public class PipTouchHandler { R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); mPipDismissTargetHandler.updateMagneticTargetSize(); - mMotionHelper.reloadResources(); } private boolean shouldShowResizeHandle() { @@ -730,7 +729,7 @@ public class PipTouchHandler { } if (touchState.startedDragging()) { - mPipBoundsState.setStashed(false); + mPipBoundsState.setStashed(PipBoundsState.STASH_TYPE_NONE); mSavedSnapFraction = -1f; mPipDismissTargetHandler.showDismissTargetMaybe(); } @@ -789,7 +788,6 @@ public class PipTouchHandler { if (mEnableStash && (animatingBounds.right > mPipBoundsState.getDisplayBounds().right || animatingBounds.left < mPipBoundsState.getDisplayBounds().left)) { - mPipBoundsState.setStashed(true); mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */); } else { mMotionHelper.flingToSnapTarget(vel.x, vel.y, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java index 5169243f02d60..a3eaac4a31d99 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java @@ -61,7 +61,7 @@ public class PipBoundsHandlerTest extends ShellTestCase { @Before public void setUp() throws Exception { initializeMockResources(); - mPipBoundsState = new PipBoundsState(); + mPipBoundsState = new PipBoundsState(mContext); mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java index 844f82d00dad9..5e11de7c72d73 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -51,7 +51,7 @@ public class PipBoundsStateTest extends ShellTestCase { @Before public void setUp() { - mPipBoundsState = new PipBoundsState(); + mPipBoundsState = new PipBoundsState(mContext); mTestComponentName1 = new ComponentName(mContext, "component1"); mTestComponentName2 = new ComponentName(mContext, "component2"); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index efe553a1a3a4a..f4143f6ee3e0a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -79,7 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); - mPipBoundsState = new PipBoundsState(); + mPipBoundsState = new PipBoundsState(mContext); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index f6dcec23626b8..94f30511ebbf8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -88,7 +88,7 @@ public class PipTouchHandlerTest extends ShellTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mPipBoundsState = new PipBoundsState(); + mPipBoundsState = new PipBoundsState(mContext); mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index a59c876526326..5caab6ab0e7ee 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -95,8 +95,8 @@ public abstract class TvPipModule { @WMSingleton @Provides - static PipBoundsState providePipBoundsState() { - return new PipBoundsState(); + static PipBoundsState providePipBoundsState(Context context) { + return new PipBoundsState(context); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 0f8fb7bbd0ec0..25ca7ade60776 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -93,8 +93,8 @@ public class WMShellModule { @WMSingleton @Provides - static PipBoundsState providePipBoundsState() { - return new PipBoundsState(); + static PipBoundsState providePipBoundsState(Context context) { + return new PipBoundsState(context); } @WMSingleton