Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86c660c7 authored by Robert Horvath's avatar Robert Horvath
Browse files

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
parent 5996d908
Loading
Loading
Loading
Loading
+15 −4
Original line number Diff line number Diff line
@@ -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,
+24 −10
Original line number Diff line number Diff line
@@ -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)) {
+2 −25
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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<Rect>())

        boundsController = TvPipBoundsController(
                context,
+2 −14
Original line number Diff line number Diff line
@@ -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)