diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java index bacf2311e6365..a2eadcdf6210a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java @@ -102,7 +102,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { updateGravityOnExpandToggled(Gravity.NO_GRAVITY, true); } mTvPipBoundsState.setTvPipExpanded(isPipExpanded); - return getTvPipPlacement().getBounds(); + return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds()); } /** Returns the current bounds adjusted to the new aspect ratio, if valid. */ @@ -112,7 +112,20 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio); } - return getTvPipPlacement().getBounds(); + return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds()); + } + + Rect adjustBoundsForTemporaryDecor(Rect bounds) { + Rect boundsWithDecor = new Rect(bounds); + Insets decorInset = mTvPipBoundsState.getPipMenuTemporaryDecorInsets(); + Insets pipDecorReverseInsets = Insets.subtract(Insets.NONE, decorInset); + boundsWithDecor.inset(decorInset); + Gravity.apply(mTvPipBoundsState.getTvPipGravity(), + boundsWithDecor.width(), boundsWithDecor.height(), bounds, boundsWithDecor); + + // remove temporary decoration again + boundsWithDecor.inset(pipDecorReverseInsets); + return boundsWithDecor; } /** @@ -152,8 +165,6 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { mKeepClearAlgorithm.setStashOffset(mTvPipBoundsState.getStashOffset()); mKeepClearAlgorithm.setPipPermanentDecorInsets( mTvPipBoundsState.getPipMenuPermanentDecorInsets()); - mKeepClearAlgorithm.setPipTemporaryDecorInsets( - mTvPipBoundsState.getPipMenuTemporaryDecorInsets()); final Placement placement = mKeepClearAlgorithm.calculatePipPosition( pipSize, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java index bb1cb3586f250..3a6ce81821eca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java @@ -62,6 +62,7 @@ public class TvPipBoundsController { private int mResizeAnimationDuration; private int mStashDurationMs; + private Rect mCurrentPlacementBounds; private Rect mPipTargetBounds; private final Runnable mApplyPendingPlacementRunnable = this::applyPendingPlacement; @@ -96,15 +97,16 @@ public class TvPipBoundsController { } /** - * Update the PiP bounds based on the state of the PiP and keep clear areas. - * Unless {@code immediate} is {@code true}, the PiP does not move immediately to its new - * position, but waits for a new position to stay uncontested for + * Update the PiP bounds based on the state of the PiP, decors, and keep clear areas. + * Unless {@code immediate} is {@code true}, the PiP does not move immediately to avoid + * keep clear areas, but waits for a new position to stay uncontested for * {@link #POSITION_DEBOUNCE_TIMEOUT_MILLIS} before moving to it. + * Temporary decor changes are applied immediately. * * @param stayAtAnchorPosition If true, PiP will be placed at the anchor position * @param disallowStashing If true, PiP will not be placed off-screen in a stashed position * @param animationDuration Duration of the animation to the new position - * @param immediate If true, PiP will move immediately + * @param immediate If true, PiP will move immediately to avoid keep clear areas */ @VisibleForTesting void recalculatePipBounds(boolean stayAtAnchorPosition, boolean disallowStashing, @@ -115,15 +117,16 @@ public class TvPipBoundsController { mTvPipBoundsState.setStashed(stashType); if (stayAtAnchorPosition) { cancelScheduledPlacement(); - movePipTo(placement.getAnchorBounds(), animationDuration); + applyPlacementBounds(placement.getAnchorBounds(), animationDuration); } else if (disallowStashing) { cancelScheduledPlacement(); - movePipTo(placement.getUnstashedBounds(), animationDuration); + applyPlacementBounds(placement.getUnstashedBounds(), animationDuration); } else if (immediate) { cancelScheduledPlacement(); - movePipTo(placement.getBounds(), animationDuration); + applyPlacementBounds(placement.getBounds(), animationDuration); scheduleUnstashIfNeeded(placement); } else { + applyPlacementBounds(mCurrentPlacementBounds, animationDuration); schedulePinnedStackPlacement(placement, animationDuration); } } @@ -159,7 +162,7 @@ public class TvPipBoundsController { } if (placement.getUnstashDestinationBounds() != null) { mUnstashRunnable = () -> { - movePipTo(placement.getUnstashDestinationBounds(), + applyPlacementBounds(placement.getUnstashDestinationBounds(), mResizeAnimationDuration); mUnstashRunnable = null; }; @@ -180,10 +183,10 @@ public class TvPipBoundsController { if (mUnstashRunnable != null) { // currently stashed, use stashed pos - movePipTo(mPendingPlacement.getBounds(), + applyPlacementBounds(mPendingPlacement.getBounds(), mPendingPlacementAnimationDuration); } else { - movePipTo(mPendingPlacement.getUnstashedBounds(), + applyPlacementBounds(mPendingPlacement.getUnstashedBounds(), mPendingPlacementAnimationDuration); } } @@ -192,6 +195,7 @@ public class TvPipBoundsController { } void onPipDismissed() { + mCurrentPlacementBounds = null; mPipTargetBounds = null; cancelScheduledPlacement(); } @@ -206,6 +210,16 @@ public class TvPipBoundsController { } } + private void applyPlacementBounds(Rect bounds, int animationDuration) { + if (bounds == null) { + return; + } + + mCurrentPlacementBounds = bounds; + Rect adjustedBounds = mTvPipBoundsAlgorithm.adjustBoundsForTemporaryDecor(bounds); + movePipTo(adjustedBounds, animationDuration); + } + /** Animates the PiP to the given bounds with the given animation duration. */ private void movePipTo(Rect bounds, int animationDuration) { if (Objects.equals(mPipTargetBounds, bounds)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt index 29f34849fd7b3..1e54436ebce92 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt @@ -88,9 +88,6 @@ class TvPipKeepClearAlgorithm() { /** Spaces around the PiP that we should leave space for when placing the PiP. Permanent PiP * decorations are relevant for calculating intersecting keep clear areas */ private var pipPermanentDecorInsets = Insets.NONE - /** Spaces around the PiP that we should leave space for when placing the PiP. Temporary PiP - * decorations are not relevant for calculating intersecting keep clear areas */ - private var pipTemporaryDecorInsets = Insets.NONE /** * Calculates the position the PiP should be placed at, taking into consideration the @@ -120,13 +117,11 @@ class TvPipKeepClearAlgorithm() { val transformedUnrestrictedAreas = transformAndFilterAreas(unrestrictedAreas) val pipSizeWithAllDecors = addDecors(pipSize) - val pipAnchorBoundsWithAllDecors = + val pipAnchorBoundsWithDecors = getNormalPipAnchorBounds(pipSizeWithAllDecors, transformedMovementBounds) - val pipAnchorBoundsWithPermanentDecors = - removeTemporaryDecorsTransformed(pipAnchorBoundsWithAllDecors) val result = calculatePipPositionTransformed( - pipAnchorBoundsWithPermanentDecors, + pipAnchorBoundsWithDecors, transformedRestrictedAreas, transformedUnrestrictedAreas ) @@ -461,10 +456,6 @@ class TvPipKeepClearAlgorithm() { pipPermanentDecorInsets = insets } - fun setPipTemporaryDecorInsets(insets: Insets) { - pipTemporaryDecorInsets = insets - } - /** * @param open Whether this event marks the opening of an occupied segment * @param pos The coordinate of this event @@ -759,7 +750,6 @@ class TvPipKeepClearAlgorithm() { private fun addDecors(size: Size): Size { val bounds = Rect(0, 0, size.width, size.height) bounds.inset(pipPermanentDecorInsets) - bounds.inset(pipTemporaryDecorInsets) return Size(bounds.width(), bounds.height()) } @@ -774,19 +764,6 @@ class TvPipKeepClearAlgorithm() { return bounds } - /** - * Removes the space that was reserved for temporary decorations around the PiP - * @param bounds the bounds (in base case) to remove the insets from - */ - private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect { - if (pipTemporaryDecorInsets == Insets.NONE) return bounds - - val reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets) - val boundsInScreenSpace = fromTransformedSpace(bounds) - boundsInScreenSpace.inset(reverseInsets) - return toTransformedSpace(boundsInScreenSpace) - } - private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) } private fun Rect.intersectsX(other: Rect) = right >= other.left && left <= other.right private fun Rect.intersectsY(other: Rect) = bottom >= other.top && top <= other.bottom diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt index dae5dfdc6d8d3..05e472245b4af 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt @@ -31,6 +31,7 @@ import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.AdditionalAnswers.returnsFirstArg import org.mockito.Mock import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.any @@ -92,6 +93,8 @@ class TvPipBoundsControllerTest { whenever(context.resources).thenReturn(resources) whenever(resources.getInteger(R.integer.config_pipStashDuration)).thenReturn(STASH_DURATION) + whenever(tvPipBoundsAlgorithm.adjustBoundsForTemporaryDecor(any())) + .then(returnsFirstArg()) boundsController = TvPipBoundsController( context, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt index e21b61596dad8..0fcc5cf384c94 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt @@ -453,21 +453,9 @@ class TvPipKeepClearAlgorithmTest { @Test fun test_PipInsets() { - val permInsets = Insets.of(-1, -2, -3, -4) - algorithm.setPipPermanentDecorInsets(permInsets) - testInsetsForAllPositions(permInsets) + val insets = Insets.of(-1, -2, -3, -4) + algorithm.setPipPermanentDecorInsets(insets) - val tempInsets = Insets.of(-4, -3, -2, -1) - algorithm.setPipPermanentDecorInsets(Insets.NONE) - algorithm.setPipTemporaryDecorInsets(tempInsets) - testInsetsForAllPositions(tempInsets) - - algorithm.setPipPermanentDecorInsets(permInsets) - algorithm.setPipTemporaryDecorInsets(tempInsets) - testInsetsForAllPositions(Insets.add(permInsets, tempInsets)) - } - - private fun testInsetsForAllPositions(insets: Insets) { gravity = Gravity.BOTTOM or Gravity.RIGHT testAnchorPositionWithInsets(insets)