Merge "Move PiP in response to keep clear areas changed events." into tm-qpr-dev

This commit is contained in:
Mateusz Cicheński
2022-09-02 05:19:12 +00:00
committed by Android (Google) Code Review
17 changed files with 321 additions and 136 deletions

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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. */

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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];
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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)
}
}
}

View File

@@ -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));

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);