From 5b8d244061e3b6fe775dd71af570a91edeaea90b Mon Sep 17 00:00:00 2001 From: "jorgegil@google.com" Date: Fri, 9 Oct 2020 17:28:20 -0700 Subject: [PATCH] Move reentry state into PipBoundsState Bug: 169373982 Test: existing tests pass, reentry works Change-Id: I45af27637f24ad0822e4bdf65cd3a93d4a2442dd --- .../wm/shell/pip/PipBoundsHandler.java | 81 +++------- .../android/wm/shell/pip/PipBoundsState.java | 80 ++++++++++ .../wm/shell/pip/PipTaskOrganizer.java | 13 +- .../wm/shell/pip/phone/PipController.java | 11 +- .../wm/shell/pip/PipBoundsHandlerTest.java | 151 ++++++------------ .../wm/shell/pip/PipBoundsStateTest.java | 110 +++++++++++++ .../pip/{phone => }/PipTaskOrganizerTest.java | 8 +- .../shell/pip/phone/PipTouchHandlerTest.java | 2 +- .../android/systemui/wmshell/TvPipModule.java | 5 +- .../systemui/wmshell/WMShellModule.java | 5 +- 10 files changed, 290 insertions(+), 176 deletions(-) create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java rename libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/{phone => }/PipTaskOrganizerTest.java (88%) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java index de3261baf9b7e..2ab087cf245d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java @@ -22,9 +22,9 @@ import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; +import android.annotation.NonNull; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; -import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -52,14 +52,11 @@ public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); private static final float INVALID_SNAP_FRACTION = -1f; + private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); private DisplayLayout mDisplayLayout; - private ComponentName mLastPipComponentName; - private float mReentrySnapFraction = INVALID_SNAP_FRACTION; - private Size mReentrySize; - private float mDefaultAspectRatio; private float mMinAspectRatio; private float mMaxAspectRatio; @@ -75,7 +72,8 @@ public class PipBoundsHandler { private boolean mIsShelfShowing; private int mShelfHeight; - public PipBoundsHandler(Context context) { + public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) { + mPipBoundsState = pipBoundsState; mSnapAlgorithm = new PipSnapAlgorithm(context); mDisplayLayout = new DisplayLayout(); reloadResources(context); @@ -174,40 +172,6 @@ public class PipBoundsHandler { displayInfo.copyFrom(mDisplayInfo); } - /** - * Responds to IPinnedStackListener on saving reentry snap fraction and size - * for a given {@link ComponentName}. - */ - public void onSaveReentryBounds(ComponentName componentName, Rect bounds) { - mReentrySnapFraction = getSnapFraction(bounds); - mReentrySize = new Size(bounds.width(), bounds.height()); - mLastPipComponentName = componentName; - } - - /** - * Responds to IPinnedStackListener on resetting reentry snap fraction and size - * for a given {@link ComponentName}. - */ - public void onResetReentryBounds(ComponentName componentName) { - if (componentName.equals(mLastPipComponentName)) { - onResetReentryBoundsUnchecked(); - } - } - - private void onResetReentryBoundsUnchecked() { - mReentrySnapFraction = INVALID_SNAP_FRACTION; - mReentrySize = null; - mLastPipComponentName = null; - } - - /** - * Returns ture if there's a valid snap fraction. This is used with {@link EXTRA_IS_FIRST_ENTRY} - * to see if this is the first time user has entered PIP for the component. - */ - public boolean hasSaveReentryBounds() { - return mReentrySnapFraction != INVALID_SNAP_FRACTION; - } - /** * The {@link PipSnapAlgorithm} is couple on display bounds * @return {@link PipSnapAlgorithm}. @@ -250,37 +214,43 @@ public class PipBoundsHandler { } /** - * See {@link #getDestinationBounds(ComponentName, float, Rect, Size, boolean)} + * See {@link #getDestinationBounds(float, Rect, Size, boolean)} */ - public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds, - Size minimalSize) { - return getDestinationBounds(componentName, aspectRatio, bounds, minimalSize, + public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) { + return getDestinationBounds(aspectRatio, bounds, minimalSize, false /* useCurrentMinEdgeSize */); } /** * @return {@link Rect} of the destination PiP window bounds. */ - public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds, + public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) { - if (!componentName.equals(mLastPipComponentName)) { - onResetReentryBoundsUnchecked(); - mLastPipComponentName = componentName; - } + boolean isReentryBounds = false; final Rect destinationBounds; if (bounds == null) { - final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); - destinationBounds = new Rect(defaultBounds); - if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) { + // Calculating initial entry bounds + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + + final Rect defaultBounds; + if (state != null) { + // Restore to reentry bounds. + defaultBounds = getDefaultBounds(state.getSnapFraction(), state.getSize()); + isReentryBounds = true; + } else { + // Get actual default bounds. + defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */); mOverrideMinimalSize = minimalSize; } + + destinationBounds = new Rect(defaultBounds); } else { + // Just adjusting bounds (e.g. on aspect ratio changed). destinationBounds = new Rect(bounds); } if (isValidPictureInPictureAspectRatio(aspectRatio)) { - boolean useCurrentSize = bounds == null && mReentrySize != null; transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize, - useCurrentSize); + isReentryBounds); } mAspectRatio = aspectRatio; return destinationBounds; @@ -533,9 +503,6 @@ public class PipBoundsHandler { public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); - pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); - pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction); - pw.println(innerPrefix + "mReentrySize=" + mReentrySize); pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo); pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio); pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index 10e5c3d33def8..2625f16fac463 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -17,9 +17,15 @@ package com.android.wm.shell.pip; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; import android.graphics.Rect; +import android.util.Size; + +import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.util.Objects; /** * Singleton source of truth for the current state of PIP bounds. @@ -28,6 +34,8 @@ public final class PipBoundsState { private static final String TAG = PipBoundsState.class.getSimpleName(); private final @NonNull Rect mBounds = new Rect(); + private PipReentryState mPipReentryState; + private ComponentName mLastPipComponentName; void setBounds(@NonNull Rect bounds) { mBounds.set(bounds); @@ -38,6 +46,72 @@ public final class PipBoundsState { return new Rect(mBounds); } + /** + * 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. + */ + public void saveReentryState(@NonNull Rect bounds, float fraction) { + mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction); + } + + /** + * 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) { + final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName); + mLastPipComponentName = lastPipComponentName; + if (changed) { + clearReentryState(); + } + } + + public ComponentName getLastPipComponentName() { + return mLastPipComponentName; + } + + @VisibleForTesting + void clearReentryState() { + mPipReentryState = null; + } + + static final class PipReentryState { + private static final String TAG = PipReentryState.class.getSimpleName(); + + private final @NonNull Size mSize; + private final float mSnapFraction; + + PipReentryState(@NonNull Size size, float snapFraction) { + mSize = size; + mSnapFraction = snapFraction; + } + + @NonNull + Size getSize() { + return mSize; + } + + float getSnapFraction() { + return mSnapFraction; + } + + void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mSize=" + mSize); + pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction); + } + } + /** * Dumps internal state. */ @@ -45,5 +119,11 @@ public final class PipBoundsState { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mBounds=" + mBounds); + pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); + if (mPipReentryState == null) { + pw.println(innerPrefix + "mPipReentryState=null"); + } else { + mPipReentryState.dump(pw, innerPrefix); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 15fd4245a8193..22dc0841d88d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -329,7 +329,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, PictureInPictureParams pictureInPictureParams) { mShouldIgnoreEnteringPipTransition = true; mState = State.ENTERING_PIP; - return mPipBoundsHandler.getDestinationBounds(componentName, + mPipBoundsState.setLastPipComponentName(componentName); + return mPipBoundsHandler.getDestinationBounds( getAspectRatioOrDefault(pictureInPictureParams), null /* bounds */, getMinimalSize(activityInfo)); } @@ -465,6 +466,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mPipBoundsState.setLastPipComponentName(mTaskInfo.topActivity); mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); @@ -491,7 +493,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); @@ -686,13 +688,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); + mPipBoundsState.setLastPipComponentName(info.topActivity); final PictureInPictureParams newParams = info.pictureInPictureParams; if (newParams == null || !applyPictureInPictureParams(newParams)) { Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams); return; } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - info.topActivity, getAspectRatioOrDefault(newParams), + getAspectRatioOrDefault(newParams), mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo), true /* userCurrentMinEdgeSize */); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); @@ -709,7 +712,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onFixedRotationFinished(int displayId) { if (mShouldDeferEnteringPip && mState.isInPip()) { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); // schedule a regular animation to ensure all the callbacks are still being sent enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); @@ -783,7 +786,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); if (newDestinationBounds.equals(currentDestinationBounds)) return; if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 13f5ac3edc605..a3d21f2d84666 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -173,7 +173,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onActivityHidden(ComponentName componentName) { - mHandler.post(() -> mPipBoundsHandler.onResetReentryBounds(componentName)); + mHandler.post(() -> { + if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { + // The activity was removed, we don't want to restore to the reentry state + // saved for this component anymore. + mPipBoundsState.setLastPipComponentName(null); + } + }); } @Override @@ -384,7 +390,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry bounds to restore to when re-entering. updateReentryBounds(pipBounds); - mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds); + final float snapFraction = mPipBoundsHandler.getSnapFraction(mReentryBounds); + mPipBoundsState.saveReentryState(mReentryBounds, snapFraction); } // Disable touches while the animation is running mTouchHandler.setTouchEnabled(false); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java index d9e3148913b32..e0ac8e24756db 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.content.ComponentName; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -58,15 +57,13 @@ public class PipBoundsHandlerTest extends PipTestCase { private PipBoundsHandler mPipBoundsHandler; private DisplayInfo mDefaultDisplayInfo; - private ComponentName mTestComponentName1; - private ComponentName mTestComponentName2; + private PipBoundsState mPipBoundsState; @Before public void setUp() throws Exception { initializeMockResources(); - mPipBoundsHandler = new PipBoundsHandler(mContext); - mTestComponentName1 = new ComponentName(mContext, "component1"); - mTestComponentName2 = new ComponentName(mContext, "component2"); + mPipBoundsState = new PipBoundsState(); + mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo); } @@ -126,8 +123,8 @@ public class PipBoundsHandlerTest extends PipTestCase { (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2 }; for (float aspectRatio : aspectRatios) { - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); assertEquals("Destination bounds matches the given aspect ratio", @@ -142,8 +139,8 @@ public class PipBoundsHandlerTest extends PipTestCase { MAX_ASPECT_RATIO * 2 }; for (float aspectRatio : invalidAspectRatios) { - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); assertEquals("Destination bounds fallbacks to default aspect ratio", @@ -158,8 +155,8 @@ public class PipBoundsHandlerTest extends PipTestCase { final Rect currentBounds = new Rect(0, 0, 0, 100); currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + currentBounds, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); @@ -182,8 +179,8 @@ public class PipBoundsHandlerTest extends PipTestCase { for (int i = 0; i < aspectRatios.length; i++) { final float aspectRatio = aspectRatios[i]; final Size minimalSize = minimalSizes[i]; - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, minimalSize); assertTrue("Destination bounds is no smaller than minimal requirement", (destinationBounds.width() == minimalSize.getWidth() && destinationBounds.height() >= minimalSize.getHeight()) @@ -203,8 +200,8 @@ public class PipBoundsHandlerTest extends PipTestCase { currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2); - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, currentBounds, minSize); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + currentBounds, minSize); assertTrue("Destination bounds ignores minimal size", destinationBounds.width() > minSize.getWidth() @@ -212,28 +209,44 @@ public class PipBoundsHandlerTest extends PipTestCase { } @Test - public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() { - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + public void getDestinationBounds_reentryStateExists_restoreLastSize() { + final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + reentryBounds.scale(1.25f); + final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds); - oldPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition); + mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + assertEquals(reentryBounds.width(), destinationBounds.width()); + assertEquals(reentryBounds.height(), destinationBounds.height()); + } - assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition); + @Test + public void getDestinationBounds_reentryStateExists_restoreLastPosition() { + final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + reentryBounds.offset(0, -100); + final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds); + + mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction); + + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + + assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds); } @Test public void setShelfHeight_offsetBounds() { final int shelfHeight = 100; - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); mPipBoundsHandler.setShelfHeight(true, shelfHeight); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); oldPosition.offset(0, -shelfHeight); assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition); @@ -242,92 +255,30 @@ public class PipBoundsHandlerTest extends PipTestCase { @Test public void onImeVisibilityChanged_offsetBounds() { final int imeHeight = 100; - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); oldPosition.offset(0, -imeHeight); assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition); } @Test - public void onSaveReentryBounds_restoreLastPosition() { - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + public void getDestinationBounds_noReentryState_useDefaultBounds() { + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - oldPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition); + mPipBoundsState.clearReentryState(); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition); - } - - @Test - public void onSaveReentryBounds_restoreLastSize() { - final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - oldSize.scale(1.25f); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize); - - final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertEquals(oldSize.width(), newSize.width()); - assertEquals(oldSize.height(), newSize.height()); - } - - @Test - public void onResetReentryBounds_useDefaultBounds() { - final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - final Rect newBounds = new Rect(defaultBounds); - newBounds.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName1); - final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } - @Test - public void onResetReentryBounds_componentMismatch_restoreLastPosition() { - final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - final Rect newBounds = new Rect(defaultBounds); - newBounds.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName2); - final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds); - } - - @Test - public void onSaveReentryBounds_componentMismatch_restoreLastSize() { - final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - oldSize.scale(1.25f); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName2); - final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertEquals(oldSize.width(), newSize.width()); - assertEquals(oldSize.height(), newSize.height()); - } - private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { final Rect expectedWithMargin = new Rect(expected); expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java new file mode 100644 index 0000000000000..dc9399edaa3b6 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.ComponentName; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.Size; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link PipBoundsState}. + */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class PipBoundsStateTest extends PipTestCase { + + private static final Rect DEFAULT_BOUNDS = new Rect(0, 0, 10, 10); + private static final float DEFAULT_SNAP_FRACTION = 1.0f; + + private PipBoundsState mPipBoundsState; + private ComponentName mTestComponentName1; + private ComponentName mTestComponentName2; + + @Before + public void setUp() { + mPipBoundsState = new PipBoundsState(); + mTestComponentName1 = new ComponentName(mContext, "component1"); + mTestComponentName2 = new ComponentName(mContext, "component2"); + } + + @Test + public void testSetBounds() { + final Rect bounds = new Rect(0, 0, 100, 100); + mPipBoundsState.setBounds(bounds); + + assertEquals(bounds, mPipBoundsState.getBounds()); + } + + @Test + public void testSetReentryState() { + final Rect bounds = new Rect(0, 0, 100, 100); + final float snapFraction = 0.5f; + + mPipBoundsState.saveReentryState(bounds, snapFraction); + + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + assertEquals(new Size(100, 100), state.getSize()); + assertEquals(snapFraction, state.getSnapFraction(), 0.01); + } + + @Test + public void testClearReentryState() { + final Rect bounds = new Rect(0, 0, 100, 100); + final float snapFraction = 0.5f; + + mPipBoundsState.saveReentryState(bounds, snapFraction); + mPipBoundsState.clearReentryState(); + + assertNull(mPipBoundsState.getReentryState()); + } + + @Test + public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION); + + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + assertNotNull(state); + assertEquals(new Size(DEFAULT_BOUNDS.width(), DEFAULT_BOUNDS.height()), state.getSize()); + assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01); + } + + @Test + public void testSetLastPipComponentName_changed_clearReentryState() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION); + + mPipBoundsState.setLastPipComponentName(mTestComponentName2); + + assertNull(mPipBoundsState.getReentryState()); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java similarity index 88% rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 46ebbf3846dfc..2f7faafbc95ee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.pip.phone; +package com.android.wm.shell.pip; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -27,12 +27,6 @@ import android.testing.TestableLooper; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.PipBoundsHandler; -import com.android.wm.shell.pip.PipBoundsState; -import com.android.wm.shell.pip.PipSurfaceTransactionHelper; -import com.android.wm.shell.pip.PipTaskOrganizer; -import com.android.wm.shell.pip.PipTestCase; -import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index 4713142118a89..3f60cc01f20b5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -93,7 +93,7 @@ public class PipTouchHandlerTest extends PipTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mPipBoundsState = new PipBoundsState(); - mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 28343ed5d1f60..0d3aa4538247c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -85,8 +85,9 @@ public abstract class TvPipModule { @SysUISingleton @Provides - static PipBoundsHandler providePipBoundsHandler(Context context) { - return new PipBoundsHandler(context); + static PipBoundsHandler providePipBoundsHandler(Context context, + PipBoundsState pipBoundsState) { + return new PipBoundsHandler(context, pipBoundsState); } @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 81cb1f487ed8b..ad908b2babc1d 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -98,8 +98,9 @@ public class WMShellModule { @SysUISingleton @Provides - static PipBoundsHandler providePipBoundsHandler(Context context) { - return new PipBoundsHandler(context); + static PipBoundsHandler providesPipBoundsHandler(Context context, + PipBoundsState pipBoundsState) { + return new PipBoundsHandler(context, pipBoundsState); } @SysUISingleton