diff options
author | 2022-09-02 05:29:23 +0000 | |
---|---|---|
committer | 2022-09-02 05:29:23 +0000 | |
commit | 1568a2211c36c6993ea19902ca5af24c082f11fe (patch) | |
tree | de6be82f697fc3977085fddb2a6b8b91c3483f6c | |
parent | c7e71f79723528cb3862f55823ca74a1d6ecae40 (diff) | |
parent | abcf028aa915c788db0c7306834ccbc2c20be6bc (diff) |
Merge "Move PiP in response to keep clear areas changed events." into tm-qpr-dev am: abcf028aa9
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19271709
Change-Id: Iebb62380b5d73354b20ed3b80f6d75f528534d8d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
17 files changed, 321 insertions, 136 deletions
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index d2dd8d6b56a9..5696b8dfa8e3 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -81,6 +81,9 @@ <!-- The width and height of the background for custom action in PiP menu. --> <dimen name="pip_custom_close_bg_size">32dp</dimen> + <!-- Extra padding between picture-in-picture windows and any registered keep clear areas. --> + <dimen name="pip_keep_clear_areas_padding">16dp</dimen> + <dimen name="dismiss_target_x_size">24dp</dimen> <dimen name="floating_dismiss_bottom_margin">50dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 31596f304cb9..1564f8f4073f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -72,9 +72,9 @@ import com.android.wm.shell.pip.PipTransition; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; +import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipController; -import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasksController; @@ -320,7 +320,7 @@ public abstract class WMShellModule { DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipKeepClearAlgorithm pipKeepClearAlgorithm, + PhonePipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, @@ -357,15 +357,17 @@ public abstract class WMShellModule { @WMSingleton @Provides - static PipKeepClearAlgorithm providePipKeepClearAlgorithm() { - return new PipKeepClearAlgorithm(); + static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) { + return new PhonePipKeepClearAlgorithm(context); } @WMSingleton @Provides static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context, - PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) { - return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm); + PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm, + PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) { + return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm, + pipKeepClearAlgorithm); } // Handler is used by Icon.loadDrawableAsync diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl index e03421dd58ac..4def15db2f52 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl @@ -37,12 +37,13 @@ interface IPip { * @param activityInfo ActivityInfo tied to the Activity * @param pictureInPictureParams PictureInPictureParams tied to the Activity * @param launcherRotation Launcher rotation to calculate the PiP destination bounds - * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds + * @param hotseatKeepClearArea Bounds of Hotseat to avoid used to calculate PiP destination + bounds * @return destination bounds the PiP window should land into */ Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, in PictureInPictureParams pictureInPictureParams, - int launcherRotation, int shelfHeight) = 1; + int launcherRotation, in Rect hotseatKeepClearArea) = 1; /** * Notifies the swiping Activity to PiP onto home transition is finished diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 7397e5273753..cd61dbb5b7d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -47,6 +47,7 @@ public class PipBoundsAlgorithm { private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; + private final PipKeepClearAlgorithm mPipKeepClearAlgorithm; private float mDefaultSizePercent; private float mMinAspectRatioForMinSize; @@ -60,9 +61,11 @@ public class PipBoundsAlgorithm { protected Point mScreenEdgeInsets; public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState, - @NonNull PipSnapAlgorithm pipSnapAlgorithm) { + @NonNull PipSnapAlgorithm pipSnapAlgorithm, + @NonNull PipKeepClearAlgorithm pipKeepClearAlgorithm) { mPipBoundsState = pipBoundsState; mSnapAlgorithm = pipSnapAlgorithm; + mPipKeepClearAlgorithm = pipKeepClearAlgorithm; reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which @@ -129,8 +132,21 @@ public class PipBoundsAlgorithm { return getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */); } - /** Returns the destination bounds to place the PIP window on entry. */ + /** + * Returns the destination bounds to place the PIP window on entry. + * If there are any keep clear areas registered, the position will try to avoid occluding them. + */ public Rect getEntryDestinationBounds() { + Rect entryBounds = getEntryDestinationBoundsIgnoringKeepClearAreas(); + Rect insets = new Rect(); + getInsetBounds(insets); + return mPipKeepClearAlgorithm.findUnoccludedPosition(entryBounds, + mPipBoundsState.getRestrictedKeepClearAreas(), + mPipBoundsState.getUnrestrictedKeepClearAreas(), insets); + } + + /** Returns the destination bounds to place the PIP window on entry. */ + public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() { final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState(); final Rect destinationBounds = reentryState != null @@ -138,9 +154,10 @@ public class PipBoundsAlgorithm { : getDefaultBounds(); final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null; - return transformBoundsToAspectRatioIfValid(destinationBounds, + Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds, mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */, useCurrentSize); + return aspectRatioBounds; } /** Returns the current bounds adjusted to the new aspect ratio, if valid. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java new file mode 100644 index 000000000000..e3495e100c62 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithm.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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 android.graphics.Rect; + +import java.util.Set; + +/** + * Interface for interacting with keep clear algorithm used to move PiP window out of the way of + * keep clear areas. + */ +public interface PipKeepClearAlgorithm { + + /** + * Adjust the position of picture in picture window based on the registered keep clear areas. + * @param pipBoundsState state of the PiP to use for the calculations + * @param pipBoundsAlgorithm algorithm implementation used to get the entry destination bounds + * @return + */ + default Rect adjust(PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm) { + return pipBoundsState.getBounds(); + } + + /** + * Calculate the bounds so that none of the keep clear areas are occluded, while the bounds stay + * within the allowed bounds. If such position is not feasible, return original bounds. + * @param defaultBounds initial bounds used in the calculation + * @param restrictedKeepClearAreas registered restricted keep clear areas + * @param unrestrictedKeepClearAreas registered unrestricted keep clear areas + * @param allowedBounds bounds that define the allowed space for the output, result will always + * be inside those bounds + * @return bounds that don't cover any of the keep clear areas and are within allowed bounds + */ + default Rect findUnoccludedPosition(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas, + Set<Rect> unrestrictedKeepClearAreas, Rect allowedBounds) { + return defaultBounds; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java index 1a4be3b41911..c6b5ce93fd35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java @@ -88,6 +88,11 @@ public class PipTransitionState { return isInPip(mState); } + /** Returns true if activity has fully entered PiP mode. */ + public boolean hasEnteredPip() { + return hasEnteredPip(mState); + } + public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) { mInSwipePipToHomeTransition = inSwipePipToHomeTransition; } @@ -120,6 +125,11 @@ public class PipTransitionState { return state >= TASK_APPEARED && state != EXITING_PIP; } + /** Returns true if activity has fully entered PiP mode. */ + public static boolean hasEnteredPip(@TransitionState int state) { + return state == ENTERED_PIP; + } + public interface OnPipTransitionStateChangedListener { void onPipTransitionStateChanged(@TransitionState int oldState, @TransitionState int newState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java new file mode 100644 index 000000000000..6dd02e46d657 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2022 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.phone; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; +import android.util.ArraySet; +import android.view.Gravity; + +import com.android.wm.shell.R; +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipKeepClearAlgorithm; + +import java.util.Set; + +/** + * Calculates the adjusted position that does not occlude keep clear areas. + */ +public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm { + + protected int mKeepClearAreasPadding; + + public PhonePipKeepClearAlgorithm(Context context) { + reloadResources(context); + } + + private void reloadResources(Context context) { + final Resources res = context.getResources(); + mKeepClearAreasPadding = res.getDimensionPixelSize(R.dimen.pip_keep_clear_areas_padding); + } + + /** + * Adjusts the current position of PiP to avoid occluding keep clear areas. This will push PiP + * towards the closest edge and then apply calculations to avoid occluding keep clear areas. + */ + public Rect adjust(PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm) { + Rect startingBounds = pipBoundsState.getBounds().isEmpty() + ? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas() + : pipBoundsState.getBounds(); + float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds); + int verticalGravity; + int horizontalGravity; + if (snapFraction < 1.5f || snapFraction >= 3.5f) { + verticalGravity = Gravity.NO_GRAVITY; + } else { + verticalGravity = Gravity.BOTTOM; + } + if (snapFraction >= 0.5f && snapFraction < 2.5f) { + horizontalGravity = Gravity.RIGHT; + } else { + horizontalGravity = Gravity.LEFT; + } + // push the bounds based on the gravity + Rect insets = new Rect(); + pipBoundsAlgorithm.getInsetBounds(insets); + if (pipBoundsState.isImeShowing()) { + insets.bottom -= pipBoundsState.getImeHeight(); + } + Rect pushedBounds = new Rect(startingBounds); + if (verticalGravity == Gravity.BOTTOM) { + pushedBounds.offsetTo(pushedBounds.left, + insets.bottom - pushedBounds.height()); + } + if (horizontalGravity == Gravity.RIGHT) { + pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top); + } else { + pushedBounds.offsetTo(insets.left, pushedBounds.top); + } + return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(), + pipBoundsState.getUnrestrictedKeepClearAreas(), insets); + } + + /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */ + public Rect findUnoccludedPosition(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas, + Set<Rect> unrestrictedKeepClearAreas, Rect allowedBounds) { + if (restrictedKeepClearAreas.isEmpty() && unrestrictedKeepClearAreas.isEmpty()) { + return defaultBounds; + } + Set<Rect> keepClearAreas = new ArraySet<>(); + if (!restrictedKeepClearAreas.isEmpty()) { + keepClearAreas.addAll(restrictedKeepClearAreas); + } + if (!unrestrictedKeepClearAreas.isEmpty()) { + keepClearAreas.addAll(unrestrictedKeepClearAreas); + } + Rect outBounds = new Rect(defaultBounds); + for (Rect r : keepClearAreas) { + Rect tmpRect = new Rect(r); + // add extra padding to the keep clear area + tmpRect.inset(-mKeepClearAreasPadding, -mKeepClearAreasPadding); + if (Rect.intersects(r, outBounds)) { + if (tryOffsetUp(outBounds, tmpRect, allowedBounds)) continue; + if (tryOffsetLeft(outBounds, tmpRect, allowedBounds)) continue; + if (tryOffsetDown(outBounds, tmpRect, allowedBounds)) continue; + if (tryOffsetRight(outBounds, tmpRect, allowedBounds)) continue; + } + } + return outBounds; + } + + private static boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) { + return tryOffset(rectToMove, rectToAvoid, allowedBounds, + rectToAvoid.left - rectToMove.right, 0); + } + + private static boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) { + return tryOffset(rectToMove, rectToAvoid, allowedBounds, + rectToAvoid.right - rectToMove.left, 0); + } + + private static boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) { + return tryOffset(rectToMove, rectToAvoid, allowedBounds, + 0, rectToAvoid.top - rectToMove.bottom); + } + + private static boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds) { + return tryOffset(rectToMove, rectToAvoid, allowedBounds, + 0, rectToAvoid.bottom - rectToMove.top); + } + + private static boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect allowedBounds, + int dx, int dy) { + Rect tmp = new Rect(rectToMove); + tmp.offset(dx, dy); + if (!Rect.intersects(rectToAvoid, tmp) && allowedBounds.contains(tmp)) { + rectToMove.offsetTo(tmp.left, tmp.top); + return true; + } + return false; + } +} 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 ac3407dd1ca1..6c9a6b64c864 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 @@ -43,6 +43,7 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.util.Pair; @@ -79,6 +80,7 @@ import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipAppOpsListener; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipKeepClearAlgorithm; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipSnapAlgorithm; @@ -110,6 +112,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb UserChangeListener { private static final String TAG = "PipController"; + private boolean mEnablePipKeepClearAlgorithm = + SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false); + + @VisibleForTesting + void setEnablePipKeepClearAlgorithm(boolean value) { + mEnablePipKeepClearAlgorithm = value; + } + private Context mContext; protected ShellExecutor mMainExecutor; private DisplayController mDisplayController; @@ -262,7 +272,17 @@ public class PipController implements PipTransitionController.PipTransitionCallb public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted, Set<Rect> unrestricted) { if (mPipBoundsState.getDisplayId() == displayId) { - mPipBoundsState.setKeepClearAreas(restricted, unrestricted); + if (mEnablePipKeepClearAlgorithm) { + mPipBoundsState.setKeepClearAreas(restricted, unrestricted); + // only move if already in pip, other transitions account for keep clear + // areas + if (mPipTransitionState.hasEnteredPip()) { + Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState, + mPipBoundsAlgorithm); + mPipTaskOrganizer.scheduleAnimateResizePip(destBounds, + mEnterAnimationDuration, null); + } + } } } }; @@ -759,8 +779,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, - int launcherRotation, int shelfHeight) { - setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); + int launcherRotation, Rect hotseatKeepClearArea) { + + if (mEnablePipKeepClearAlgorithm) { + // pre-emptively add the keep clear area for Hotseat, so that it is taken into account + // when calculating the entry destination bounds of PiP window + mPipBoundsState.getRestrictedKeepClearAreas().add(hotseatKeepClearArea); + } else { + int shelfHeight = hotseatKeepClearArea.height(); + setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); + } onDisplayRotationChangedNotInPip(mContext, launcherRotation); final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams); @@ -1059,12 +1087,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, - int shelfHeight) { + Rect keepClearArea) { Rect[] result = new Rect[1]; executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome", (controller) -> { result[0] = controller.startSwipePipToHome(componentName, activityInfo, - pictureInPictureParams, launcherRotation, shelfHeight); + pictureInPictureParams, launcherRotation, keepClearArea); }, true /* blocking */); return result[0]; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java deleted file mode 100644 index 78084fafe197..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2022 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.phone; - -import android.graphics.Rect; -import android.util.ArraySet; - -import com.android.wm.shell.pip.PipBoundsAlgorithm; -import com.android.wm.shell.pip.PipBoundsState; - -import java.util.Set; - -/** - * Calculates the adjusted position that does not occlude keep clear areas. - */ -public class PipKeepClearAlgorithm { - - /** - * Adjusts the current position of PiP to avoid occluding keep clear areas. If the user has - * moved PiP manually, the unmodified current position will be returned instead. - */ - public Rect adjust(PipBoundsState boundsState, PipBoundsAlgorithm boundsAlgorithm) { - if (boundsState.hasUserResizedPip()) { - return boundsState.getBounds(); - } - return adjust(boundsAlgorithm.getEntryDestinationBounds(), - boundsState.getRestrictedKeepClearAreas(), - boundsState.getUnrestrictedKeepClearAreas(), boundsState.getDisplayBounds()); - } - - /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */ - public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas, - Set<Rect> unrestrictedKeepClearAreas, Rect displayBounds) { - if (restrictedKeepClearAreas.isEmpty()) { - return defaultBounds; - } - Set<Rect> keepClearAreas = new ArraySet<>(); - if (!restrictedKeepClearAreas.isEmpty()) { - keepClearAreas.addAll(restrictedKeepClearAreas); - } - Rect outBounds = new Rect(defaultBounds); - for (Rect r : keepClearAreas) { - if (Rect.intersects(r, outBounds)) { - if (tryOffsetUp(outBounds, r, displayBounds)) continue; - if (tryOffsetLeft(outBounds, r, displayBounds)) continue; - if (tryOffsetDown(outBounds, r, displayBounds)) continue; - if (tryOffsetRight(outBounds, r, displayBounds)) continue; - } - } - return outBounds; - } - - private boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { - return tryOffset(rectToMove, rectToAvoid, displayBounds, - rectToAvoid.left - rectToMove.right, 0); - } - - private boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { - return tryOffset(rectToMove, rectToAvoid, displayBounds, - rectToAvoid.right - rectToMove.left, 0); - } - - private boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { - return tryOffset(rectToMove, rectToAvoid, displayBounds, - 0, rectToAvoid.top - rectToMove.bottom); - } - - private boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { - return tryOffset(rectToMove, rectToAvoid, displayBounds, - 0, rectToAvoid.bottom - rectToMove.top); - } - - private boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect displayBounds, - int dx, int dy) { - Rect tmp = new Rect(rectToMove); - tmp.offset(dx, dy); - if (!Rect.intersects(rectToAvoid, tmp) && displayBounds.contains(tmp)) { - rectToMove.offsetTo(tmp.left, tmp.top); - return true; - } - return false; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java index a2eadcdf6210..ce34d2f9547d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java @@ -39,6 +39,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipKeepClearAlgorithm; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -63,7 +64,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { public TvPipBoundsAlgorithm(Context context, @NonNull TvPipBoundsState tvPipBoundsState, @NonNull PipSnapAlgorithm pipSnapAlgorithm) { - super(context, tvPipBoundsState, pipSnapAlgorithm); + super(context, tvPipBoundsState, pipSnapAlgorithm, + new PipKeepClearAlgorithm() {}); this.mTvPipBoundsState = tvPipBoundsState; this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm(); reloadResources(context); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 9ba51661aa5f..61ac49835185 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -132,7 +132,7 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec testSpec.assertLayers { val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } pipLayerList.zipWithNext { previous, current -> - current.visibleRegion.coversAtMost(previous.visibleRegion.region) + current.visibleRegion.notBiggerThan(previous.visibleRegion.region) } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 0059846c6055..262e4290ef44 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -64,7 +64,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { initializeMockResources(); mPipBoundsState = new PipBoundsState(mContext); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, - new PipSnapAlgorithm()); + new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {}); mPipBoundsState.setDisplayLayout( new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 579638d28311..90880772b25d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -98,7 +98,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { mPipBoundsState = new PipBoundsState(mContext); mPipTransitionState = new PipTransitionState(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, - new PipSnapAlgorithm()); + new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {}); mMainExecutor = new TestShellExecutor(); mPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java index e0f7e35f8d02..4d7e9e450ceb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java @@ -34,61 +34,61 @@ import org.junit.runner.RunWith; import java.util.Set; /** - * Unit tests against {@link PipKeepClearAlgorithm}. + * Unit tests against {@link PhonePipKeepClearAlgorithm}. */ @RunWith(AndroidTestingRunner.class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class PipKeepClearAlgorithmTest extends ShellTestCase { +public class PhonePipKeepClearAlgorithmTest extends ShellTestCase { - private PipKeepClearAlgorithm mPipKeepClearAlgorithm; + private PhonePipKeepClearAlgorithm mPipKeepClearAlgorithm; private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); @Before public void setUp() throws Exception { - mPipKeepClearAlgorithm = new PipKeepClearAlgorithm(); + mPipKeepClearAlgorithm = new PhonePipKeepClearAlgorithm(mContext); } @Test - public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() { + public void findUnoccludedPosition_withCollidingRestrictedKeepClearArea_movesBounds() { final Rect inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(50, 50, 150, 150); - final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), - Set.of(), DISPLAY_BOUNDS); + final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, + Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS); assertFalse(outBounds.contains(keepClearRect)); } @Test - public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() { + public void findUnoccludedPosition_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() { final Rect inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(100, 100, 150, 150); - final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), - Set.of(), DISPLAY_BOUNDS); + final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, + Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS); assertEquals(inBounds, outBounds); } @Test - public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() { // TODO(b/183746978): update this test to accommodate for the updated algorithm final Rect inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(50, 50, 150, 150); - final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, Set.of(), Set.of(keepClearRect), DISPLAY_BOUNDS); - assertEquals(inBounds, outBounds); + assertFalse(outBounds.contains(keepClearRect)); } @Test - public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + public void findUnoccludedPosition_withNonCollidingUnrestrictedKeepClearArea_boundsUnchanged() { final Rect inBounds = new Rect(0, 0, 100, 100); final Rect keepClearRect = new Rect(100, 100, 150, 150); - final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds, Set.of(), Set.of(keepClearRect), DISPLAY_BOUNDS); assertEquals(inBounds, outBounds); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index eb5726bebb74..1b5091f58f26 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -64,6 +64,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Optional; @@ -85,7 +86,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; - @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm; + @Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm; @Mock private PipSnapAlgorithm mMockPipSnapAlgorithm; @Mock private PipMediaController mMockPipMediaController; @Mock private PipTaskOrganizer mMockPipTaskOrganizer; @@ -267,7 +268,20 @@ public class PipControllerTest extends ShellTestCase { } @Test - public void onKeepClearAreasChanged_updatesPipBoundsState() { + public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() { + final int displayId = 1; + final Rect keepClearArea = new Rect(0, 0, 10, 10); + when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId); + + mPipController.mDisplaysChangedListener.onKeepClearAreasChanged( + displayId, Set.of(keepClearArea), Set.of()); + + verify(mMockPipBoundsState, never()).setKeepClearAreas(Mockito.anySet(), Mockito.anySet()); + } + + @Test + public void onKeepClearAreasChanged_featureEnabled_updatesPipBoundsState() { + mPipController.setEnablePipKeepClearAlgorithm(true); final int displayId = 1; final Rect keepClearArea = new Rect(0, 0, 10, 10); when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java index dd10aa7752f5..dba037db72eb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -36,6 +36,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipKeepClearAlgorithm; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; @@ -87,8 +88,10 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mPipBoundsState = new PipBoundsState(mContext); final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm(); + final PipKeepClearAlgorithm pipKeepClearAlgorithm = + new PipKeepClearAlgorithm() {}; final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, - mPipBoundsState, pipSnapAlgorithm); + mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm); final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm, mMockPipTransitionController, mFloatingContentCoordinator); 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 ecefd89d8778..474d6aaf4623 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 @@ -34,6 +34,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipKeepClearAlgorithm; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; @@ -104,7 +105,8 @@ public class PipTouchHandlerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mPipBoundsState = new PipBoundsState(mContext); mPipSnapAlgorithm = new PipSnapAlgorithm(); - mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm); + mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm, + new PipKeepClearAlgorithm() {}); PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm, mMockPipTransitionController, mFloatingContentCoordinator); |