From 86c660c7e8edb01bfc5e25861cb74a6c441d1ce2 Mon Sep 17 00:00:00 2001 From: Robert Horvath Date: Tue, 3 May 2022 18:02:29 +0200 Subject: [PATCH] Apply temporary PiP decor separately from overall placement This change moves temporary decor handling out of the KeepClearAlgorithm as it does not factor into keep clear area avoidance, and separating it allows us to animate out the edu text without re-calculating the overall PiP position. The PiP does not move immediately in response to keep clear area changes, as it waits for the PiP position to stabilize - but changes to temporary decor, such as edu text, should still be applied immediately. Bug: 226583836 Test: test TvPipBoundsControllerTest TvPipKeepClearAlgorithmTest Change-Id: I49d32c18af13978a8a31b9a573273905fce934fd --- .../wm/shell/pip/tv/TvPipBoundsAlgorithm.java | 19 ++++++++--- .../shell/pip/tv/TvPipBoundsController.java | 34 +++++++++++++------ .../shell/pip/tv/TvPipKeepClearAlgorithm.kt | 27 ++------------- .../shell/pip/tv/TvPipBoundsControllerTest.kt | 3 ++ .../pip/tv/TvPipKeepClearAlgorithmTest.kt | 16 ++------- 5 files changed, 46 insertions(+), 53 deletions(-) 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)