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

Commit 5f5fdaea authored by Jorge Gil's avatar Jorge Gil Committed by Android (Google) Code Review
Browse files

Merge "Add unit tests and cleanup PipBoundsState"

parents bb64b582 8cceeffe
Loading
Loading
Loading
Loading
+65 −77
Original line number Diff line number Diff line
@@ -59,42 +59,39 @@ public final class PipBoundsState {
    private final @NonNull Rect mExpandedBounds = new Rect();
    private final @NonNull Rect mNormalMovementBounds = new Rect();
    private final @NonNull Rect mExpandedMovementBounds = new Rect();
    private final Context mContext;
    private final @NonNull Context mContext;
    private float mAspectRatio;
    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 @Nullable PipReentryState mPipReentryState;
    private @Nullable ComponentName mLastPipComponentName;
    private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo();
    private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
    /** The current minimum edge size of PIP. */
    private int mMinEdgeSize;
    /** The preferred minimum (and default) size specified by apps. */
    private Size mOverrideMinSize;
    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();
    private @Nullable Size mOverrideMinSize;
    private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
    private boolean mIsImeShowing;
    private int mImeHeight;
    private boolean mIsShelfShowing;
    private int mShelfHeight;

    private Runnable mOnMinimalSizeChangeCallback;
    private BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback;
    private @Nullable Runnable mOnMinimalSizeChangeCallback;
    private @Nullable BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback;

    public PipBoundsState(Context context) {
    public PipBoundsState(@NonNull Context context) {
        mContext = context;
        reloadResources();
    }

    /**
     * Reloads the resources.
     */
    /** Reloads the resources. */
    public void onConfigurationChanged() {
        reloadResources();
    }

    private void reloadResources() {
        mStashOffset = mContext.getResources()
                .getDimensionPixelSize(R.dimen.pip_stash_offset);
        mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset);
    }

    /** Set the current PIP bounds. */
@@ -102,12 +99,14 @@ public final class PipBoundsState {
        mBounds.set(bounds);
    }

    /** Get the current PIP bounds. */
    @NonNull
    public Rect getBounds() {
        return new Rect(mBounds);
    }

    /** Returns the current movement bounds. */
    @NonNull
    public Rect getMovementBounds() {
        return mMovementBounds;
    }
@@ -135,28 +134,28 @@ public final class PipBoundsState {
    }

    /** Set the normal movement bounds. */
    public void setNormalMovementBounds(Rect bounds) {
    public void setNormalMovementBounds(@NonNull Rect bounds) {
        mNormalMovementBounds.set(bounds);
    }

    /** Returns the normal movement bounds. */
    @NonNull
    public Rect getNormalMovementBounds() {
        return mNormalMovementBounds;
    }

    /** Set the expanded movement bounds. */
    public void setExpandedMovementBounds(Rect bounds) {
    public void setExpandedMovementBounds(@NonNull Rect bounds) {
        mExpandedMovementBounds.set(bounds);
    }

    /** Returns the expanded movement bounds. */
    @NonNull
    public Rect getExpandedMovementBounds() {
        return mExpandedMovementBounds;
    }

    /**
     * Dictate where PiP currently should be stashed, if at all.
     */
    /** Dictate where PiP currently should be stashed, if at all. */
    public void setStashed(@StashType int stashedState) {
        mStashedState = stashedState;
    }
@@ -169,50 +168,39 @@ public final class PipBoundsState {
        return mStashedState;
    }

    /**
     * Whether PiP is stashed or not.
     */
    /** Whether PiP is stashed or not. */
    public boolean isStashed() {
        return mStashedState != STASH_TYPE_NONE;
    }

    /**
     * Returns the offset from the edge of the screen for PiP stash.
     */
    /** Returns the offset from the edge of the screen for PiP stash. */
    public int getStashOffset() {
        return mStashOffset;
    }

    /** Set the PIP aspect ratio. */
    public void setAspectRatio(float aspectRatio) {
        mAspectRatio = aspectRatio;
    }

    /** Get the PIP aspect ratio. */
    public float getAspectRatio() {
        return mAspectRatio;
    }

    /**
     * Save the reentry state to restore to when re-entering PIP mode.
     *
     * TODO(b/169373982): consider refactoring this so that this class alone can use mBounds and
     * calculate the snap fraction to save for re-entry.
     */
    /** Save the reentry state to restore to when re-entering PIP mode. */
    public void saveReentryState(@NonNull Rect bounds, float fraction) {
        mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction);
    }

    /**
     * Returns the saved reentry state.
     */
    /** Returns the saved reentry state. */
    @Nullable
    public PipReentryState getReentryState() {
        return mPipReentryState;
    }

    /**
     * Set the last {@link ComponentName} to enter PIP mode.
     */
    public void setLastPipComponentName(ComponentName lastPipComponentName) {
    /** Set the last {@link ComponentName} to enter PIP mode. */
    public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) {
        final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
        mLastPipComponentName = lastPipComponentName;
        if (changed) {
@@ -220,41 +208,40 @@ public final class PipBoundsState {
        }
    }

    /** Get the last PIP component name, if any. */
    @Nullable
    public ComponentName getLastPipComponentName() {
        return mLastPipComponentName;
    }

    /** Get the current display info. */
    @NonNull
    public DisplayInfo getDisplayInfo() {
        return mDisplayInfo;
    }

    /**
     * Update the display info.
     */
    /** Update the display info. */
    public void setDisplayInfo(@NonNull DisplayInfo displayInfo) {
        mDisplayInfo.copyFrom(displayInfo);
    }

    /** Set the rotation of the display. */
    public void setDisplayRotation(int rotation) {
        mDisplayInfo.rotation = rotation;
    }

    /**
     * Returns the display's bound.
     */
    /** Returns the display's bounds. */
    @NonNull
    public Rect getDisplayBounds() {
        return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
    }

    /**
     * Update the display layout.
     */
    /** Update the display layout. */
    public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
        mDisplayLayout.set(displayLayout);
    }

    /** Get the display layout. */
    @NonNull
    public DisplayLayout getDisplayLayout() {
        return mDisplayLayout;
@@ -275,10 +262,8 @@ public final class PipBoundsState {
        return mMinEdgeSize;
    }

    /**
     * Sets the preferred size of PIP as specified by the activity in PIP mode.
     */
    public void setOverrideMinSize(Size overrideMinSize) {
    /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
    public void setOverrideMinSize(@Nullable Size overrideMinSize) {
        final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
        mOverrideMinSize = overrideMinSize;
        if (changed && mOnMinimalSizeChangeCallback != null) {
@@ -287,6 +272,7 @@ public final class PipBoundsState {
    }

    /** Returns the preferred minimal size specified by the activity in PIP. */
    @Nullable
    public Size getOverrideMinSize() {
        return mOverrideMinSize;
    }
@@ -297,8 +283,10 @@ public final class PipBoundsState {
        return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
    }

    public AnimatingBoundsState getAnimatingBoundsState() {
        return mAnimatingBoundsState;
    /** Get the state of the bounds in motion. */
    @NonNull
    public MotionBoundsState getMotionBoundsState() {
        return mMotionBoundsState;
    }

    /** Set whether the IME is currently showing and its height. */
@@ -344,41 +332,41 @@ public final class PipBoundsState {
    /**
     * Registers a callback when the minimal size of PIP that is set by the app changes.
     */
    public void setOnMinimalSizeChangeCallback(Runnable onMinimalSizeChangeCallback) {
    public void setOnMinimalSizeChangeCallback(@Nullable Runnable onMinimalSizeChangeCallback) {
        mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback;
    }

    /** Set a callback to be notified when the shelf visibility changes. */
    public void setOnShelfVisibilityChangeCallback(
            BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) {
            @Nullable BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) {
        mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback;
    }

    /** Source of truth for the current animation bounds of PIP. */
    public static class AnimatingBoundsState {
        /** The bounds used when PIP is being dragged or animated. */
        private final Rect mTemporaryBounds = new Rect();
    /** Source of truth for the current bounds of PIP that may be in motion. */
    public static class MotionBoundsState {
        /** The bounds used when PIP is in motion (e.g. during a drag or animation) */
        private final @NonNull Rect mBoundsInMotion = new Rect();
        /** The destination bounds to which PIP is animating. */
        private final Rect mAnimatingToBounds = new Rect();
        private final @NonNull Rect mAnimatingToBounds = new Rect();

        /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */
        public boolean isAnimating() {
            return !mTemporaryBounds.isEmpty();
        public boolean isInMotion() {
            return !mBoundsInMotion.isEmpty();
        }

        /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */
        public void setTemporaryBounds(Rect bounds) {
            mTemporaryBounds.set(bounds);
        public void setBoundsInMotion(@NonNull Rect bounds) {
            mBoundsInMotion.set(bounds);
        }

        /** Set the bounds to which PIP is animating. */
        public void setAnimatingToBounds(Rect bounds) {
        public void setAnimatingToBounds(@NonNull Rect bounds) {
            mAnimatingToBounds.set(bounds);
        }

        /** Called when all ongoing dragging and animation operations have ended. */
        /** Called when all ongoing motion operations have ended. */
        public void onAllAnimationsEnded() {
            mTemporaryBounds.setEmpty();
            mBoundsInMotion.setEmpty();
        }

        /** Called when an ongoing physics animation has ended. */
@@ -386,20 +374,22 @@ public final class PipBoundsState {
            mAnimatingToBounds.setEmpty();
        }

        /** Returns the temporary animation bounds. */
        public Rect getTemporaryBounds() {
            return mTemporaryBounds;
        /** Returns the motion bounds. */
        @NonNull
        public Rect getBoundsInMotion() {
            return mBoundsInMotion;
        }

        /** Returns the destination bounds to which PIP is currently animating. */
        @NonNull
        public Rect getAnimatingToBounds() {
            return mAnimatingToBounds;
        }

        void dump(PrintWriter pw, String prefix) {
            final String innerPrefix = prefix + "  ";
            pw.println(prefix + AnimatingBoundsState.class.getSimpleName());
            pw.println(innerPrefix + "mTemporaryBounds=" + mTemporaryBounds);
            pw.println(prefix + MotionBoundsState.class.getSimpleName());
            pw.println(innerPrefix + "mBoundsInMotion=" + mBoundsInMotion);
            pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds);
        }
    }
@@ -432,9 +422,7 @@ public final class PipBoundsState {
        }
    }

    /**
     * Dumps internal state.
     */
    /** Dumps internal state. */
    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
@@ -461,6 +449,6 @@ public final class PipBoundsState {
        } else {
            mPipReentryState.dump(pw, innerPrefix);
        }
        mAnimatingBoundsState.dump(pw, innerPrefix);
        mMotionBoundsState.dump(pw, innerPrefix);
    }
}
+20 −20
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
            });

    /**
     * PhysicsAnimator instance for animating {@link PipBoundsState#getAnimatingBoundsState()}
     * PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
     * using physics animations.
     */
    private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;
@@ -98,7 +98,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    private MagnetizedObject<Rect> mMagnetizedPip;

    /**
     * Update listener that resizes the PIP to {@link PipBoundsState#getAnimatingBoundsState()}.
     * Update listener that resizes the PIP to {@link PipBoundsState#getMotionBoundsState()}.
     */
    private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;

@@ -172,14 +172,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mFloatingContentCoordinator = floatingContentCoordinator;
        mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
        mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
                mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
                mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
        mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
                mSfAnimationHandlerThreadLocal.get());

        mResizePipUpdateListener = (target, values) -> {
            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
            if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
                        mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), null);
                        mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null);
            }
        };
    }
@@ -187,8 +187,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    @NonNull
    @Override
    public Rect getFloatingBoundsOnScreen() {
        return !mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds().isEmpty()
                ? mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds() : getBounds();
        return !mPipBoundsState.getMotionBoundsState().getAnimatingToBounds().isEmpty()
                ? mPipBoundsState.getMotionBoundsState().getAnimatingToBounds() : getBounds();
    }

    @NonNull
@@ -207,7 +207,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
    void synchronizePinnedStackBounds() {
        cancelPhysicsAnimation();
        mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
        mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();

        if (mPipTaskOrganizer.isInPip()) {
            mFloatingContentCoordinator.onContentMoved(this);
@@ -242,7 +242,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                resizePipUnchecked(toBounds);
                mPipBoundsState.setBounds(toBounds);
            } else {
                mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(toBounds);
                mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds);
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
                        (Rect newBounds) -> {
                            mMainHandler.post(() -> {
@@ -278,8 +278,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

        // If we're already in the dismiss target area, then there won't be a move to set the
        // temporary bounds, so just initialize it to the current bounds.
        if (!mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
        if (!mPipBoundsState.getMotionBoundsState().isInMotion()) {
            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds());
        }
        mTemporaryBoundsPhysicsAnimator
                .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
@@ -396,7 +396,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

        final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;

        final int startValueY = mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds().top;
        final int startValueY = mPipBoundsState.getMotionBoundsState().getBoundsInMotion().top;
        final float estimatedFlingYEndValue =
                PhysicsAnimator.estimateFlingEndValue(startValueY, velocityY, mFlingConfigY);

@@ -411,7 +411,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
        if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
            // Animate from the current bounds if we're not already animating.
            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds());
        }

        mTemporaryBoundsPhysicsAnimator
@@ -492,7 +492,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
    private void cancelPhysicsAnimation() {
        mTemporaryBoundsPhysicsAnimator.cancel();
        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
    }

@@ -565,17 +565,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        if (!mDismissalPending
                && !mSpringingToTouch
                && !mMagnetizedPip.getObjectStuckToTarget()) {
            // All animations (including dragging) have actually finished.
            // All motion operations have actually finished.
            mPipBoundsState.setBounds(
                    mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
            mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
                    mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
            mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
            if (!mDismissalPending) {
                // do not schedule resize if PiP is dismissing, which may cause app re-open to
                // mBounds instead of it's normal bounds.
                mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
            }
        }
        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
        mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
        mDismissalPending = false;
    }
@@ -586,7 +586,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
     */
    private void setAnimatingToBounds(Rect bounds) {
        mPipBoundsState.getAnimatingBoundsState().setAnimatingToBounds(bounds);
        mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(bounds);
        mFloatingContentCoordinator.onContentMoved(this);
    }

@@ -625,7 +625,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    MagnetizedObject<Rect> getMagnetizedPip() {
        if (mMagnetizedPip == null) {
            mMagnetizedPip = new MagnetizedObject<Rect>(
                    mContext, mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(),
                    mContext, mPipBoundsState.getMotionBoundsState().getBoundsInMotion(),
                    FloatProperties.RECT_X, FloatProperties.RECT_Y) {
                @Override
                public float getWidth(@NonNull Rect animatedPipBounds) {
+7 −7
Original line number Diff line number Diff line
@@ -706,7 +706,7 @@ public class PipTouchHandler {
                return;
            }

            Rect bounds = getPossiblyAnimatingBounds();
            Rect bounds = getPossiblyMotionBounds();
            mDelta.set(0f, 0f);
            mStartPosition.set(bounds.left, bounds.top);
            mMovementWithinDismiss = touchState.getDownTouchPosition().y
@@ -745,7 +745,7 @@ public class PipTouchHandler {
                mDelta.x += left - lastX;
                mDelta.y += top - lastY;

                mTmpBounds.set(getPossiblyAnimatingBounds());
                mTmpBounds.set(getPossiblyMotionBounds());
                mTmpBounds.offsetTo((int) left, (int) top);
                mMotionHelper.movePip(mTmpBounds, true /* isDragging */);

@@ -895,12 +895,12 @@ public class PipTouchHandler {
    }

    /**
     * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
     * otherwise.
     * Returns the PIP bounds if we're not in the middle of a motion operation, or the current,
     * temporary motion bounds otherwise.
     */
    Rect getPossiblyAnimatingBounds() {
        return mPipBoundsState.getAnimatingBoundsState().isAnimating()
                ? mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()
    Rect getPossiblyMotionBounds() {
        return mPipBoundsState.getMotionBoundsState().isInMotion()
                ? mPipBoundsState.getMotionBoundsState().getBoundsInMotion()
                : mPipBoundsState.getBounds();
    }

+34 −0
Original line number Diff line number Diff line
@@ -135,4 +135,38 @@ public class PipBoundsStateTest extends ShellTestCase {

        verify(callback, never()).accept(true, 100);
    }

    @Test
    public void testSetOverrideMinSize_changed_callbackInvoked() {
        final Runnable callback = mock(Runnable.class);
        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);

        mPipBoundsState.setOverrideMinSize(new Size(10, 10));

        verify(callback).run();
    }

    @Test
    public void testSetOverrideMinSize_notChanged_callbackNotInvoked() {
        final Runnable callback = mock(Runnable.class);
        mPipBoundsState.setOverrideMinSize(new Size(5, 5));
        mPipBoundsState.setOnMinimalSizeChangeCallback(callback);

        mPipBoundsState.setOverrideMinSize(new Size(5, 5));

        verify(callback, never()).run();
    }

    @Test
    public void testGetOverrideMinEdgeSize() {
        mPipBoundsState.setOverrideMinSize(null);
        assertEquals(0, mPipBoundsState.getOverrideMinEdgeSize());

        mPipBoundsState.setOverrideMinSize(new Size(5, 10));
        assertEquals(5, mPipBoundsState.getOverrideMinEdgeSize());

        mPipBoundsState.setOverrideMinSize(new Size(15, 10));
        assertEquals(10, mPipBoundsState.getOverrideMinEdgeSize());
    }
}