Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +65 −77 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading @@ -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; } Loading @@ -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. */ Loading Loading @@ -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. */ Loading @@ -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); } } Loading Loading @@ -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); Loading @@ -461,6 +449,6 @@ public final class PipBoundsState { } else { mPipReentryState.dump(pw, innerPrefix); } mAnimatingBoundsState.dump(pw, innerPrefix); mMotionBoundsState.dump(pw, innerPrefix); } } libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +20 −20 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } }; } Loading @@ -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 Loading @@ -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); Loading Loading @@ -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(() -> { Loading Loading @@ -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) Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -492,7 +492,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private void cancelPhysicsAnimation() { mTemporaryBoundsPhysicsAnimator.cancel(); mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded(); mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; } Loading Loading @@ -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; } Loading @@ -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); } Loading Loading @@ -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) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +7 −7 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */); Loading Loading @@ -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(); } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -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()); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +65 −77 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading @@ -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; } Loading @@ -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. */ Loading Loading @@ -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. */ Loading @@ -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); } } Loading Loading @@ -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); Loading @@ -461,6 +449,6 @@ public final class PipBoundsState { } else { mPipReentryState.dump(pw, innerPrefix); } mAnimatingBoundsState.dump(pw, innerPrefix); mMotionBoundsState.dump(pw, innerPrefix); } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +20 −20 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } }; } Loading @@ -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 Loading @@ -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); Loading Loading @@ -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(() -> { Loading Loading @@ -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) Loading Loading @@ -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); Loading @@ -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 Loading Loading @@ -492,7 +492,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private void cancelPhysicsAnimation() { mTemporaryBoundsPhysicsAnimator.cancel(); mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded(); mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; } Loading Loading @@ -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; } Loading @@ -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); } Loading Loading @@ -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) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +7 −7 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 */); Loading Loading @@ -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(); } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -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()); } }