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>
This commit is contained in:
@@ -81,6 +81,9 @@
|
|||||||
<!-- The width and height of the background for custom action in PiP menu. -->
|
<!-- The width and height of the background for custom action in PiP menu. -->
|
||||||
<dimen name="pip_custom_close_bg_size">32dp</dimen>
|
<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="dismiss_target_x_size">24dp</dimen>
|
||||||
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
|
<dimen name="floating_dismiss_bottom_margin">50dp</dimen>
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ import com.android.wm.shell.pip.PipTransition;
|
|||||||
import com.android.wm.shell.pip.PipTransitionController;
|
import com.android.wm.shell.pip.PipTransitionController;
|
||||||
import com.android.wm.shell.pip.PipTransitionState;
|
import com.android.wm.shell.pip.PipTransitionState;
|
||||||
import com.android.wm.shell.pip.PipUiEventLogger;
|
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.PhonePipMenuController;
|
||||||
import com.android.wm.shell.pip.phone.PipController;
|
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.PipMotionHelper;
|
||||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||||
import com.android.wm.shell.recents.RecentTasksController;
|
import com.android.wm.shell.recents.RecentTasksController;
|
||||||
@@ -320,7 +320,7 @@ public abstract class WMShellModule {
|
|||||||
DisplayController displayController,
|
DisplayController displayController,
|
||||||
PipAppOpsListener pipAppOpsListener,
|
PipAppOpsListener pipAppOpsListener,
|
||||||
PipBoundsAlgorithm pipBoundsAlgorithm,
|
PipBoundsAlgorithm pipBoundsAlgorithm,
|
||||||
PipKeepClearAlgorithm pipKeepClearAlgorithm,
|
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
|
||||||
PipBoundsState pipBoundsState,
|
PipBoundsState pipBoundsState,
|
||||||
PipMotionHelper pipMotionHelper,
|
PipMotionHelper pipMotionHelper,
|
||||||
PipMediaController pipMediaController,
|
PipMediaController pipMediaController,
|
||||||
@@ -357,15 +357,17 @@ public abstract class WMShellModule {
|
|||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
|
static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
|
||||||
return new PipKeepClearAlgorithm();
|
return new PhonePipKeepClearAlgorithm(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
|
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
|
||||||
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
|
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
|
||||||
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
|
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) {
|
||||||
|
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
|
||||||
|
pipKeepClearAlgorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler is used by Icon.loadDrawableAsync
|
// Handler is used by Icon.loadDrawableAsync
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ interface IPip {
|
|||||||
* @param activityInfo ActivityInfo tied to the Activity
|
* @param activityInfo ActivityInfo tied to the Activity
|
||||||
* @param pictureInPictureParams PictureInPictureParams tied to the Activity
|
* @param pictureInPictureParams PictureInPictureParams tied to the Activity
|
||||||
* @param launcherRotation Launcher rotation to calculate the PiP destination bounds
|
* @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
|
* @return destination bounds the PiP window should land into
|
||||||
*/
|
*/
|
||||||
Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
|
Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
|
||||||
in PictureInPictureParams pictureInPictureParams,
|
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
|
* Notifies the swiping Activity to PiP onto home transition is finished
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class PipBoundsAlgorithm {
|
|||||||
|
|
||||||
private final @NonNull PipBoundsState mPipBoundsState;
|
private final @NonNull PipBoundsState mPipBoundsState;
|
||||||
private final PipSnapAlgorithm mSnapAlgorithm;
|
private final PipSnapAlgorithm mSnapAlgorithm;
|
||||||
|
private final PipKeepClearAlgorithm mPipKeepClearAlgorithm;
|
||||||
|
|
||||||
private float mDefaultSizePercent;
|
private float mDefaultSizePercent;
|
||||||
private float mMinAspectRatioForMinSize;
|
private float mMinAspectRatioForMinSize;
|
||||||
@@ -60,9 +61,11 @@ public class PipBoundsAlgorithm {
|
|||||||
protected Point mScreenEdgeInsets;
|
protected Point mScreenEdgeInsets;
|
||||||
|
|
||||||
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
|
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
|
||||||
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
|
@NonNull PipSnapAlgorithm pipSnapAlgorithm,
|
||||||
|
@NonNull PipKeepClearAlgorithm pipKeepClearAlgorithm) {
|
||||||
mPipBoundsState = pipBoundsState;
|
mPipBoundsState = pipBoundsState;
|
||||||
mSnapAlgorithm = pipSnapAlgorithm;
|
mSnapAlgorithm = pipSnapAlgorithm;
|
||||||
|
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
|
||||||
reloadResources(context);
|
reloadResources(context);
|
||||||
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
|
// 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
|
// 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 */);
|
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() {
|
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 PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
|
||||||
|
|
||||||
final Rect destinationBounds = reentryState != null
|
final Rect destinationBounds = reentryState != null
|
||||||
@@ -138,9 +154,10 @@ public class PipBoundsAlgorithm {
|
|||||||
: getDefaultBounds();
|
: getDefaultBounds();
|
||||||
|
|
||||||
final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
|
final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
|
||||||
return transformBoundsToAspectRatioIfValid(destinationBounds,
|
Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds,
|
||||||
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
|
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
|
||||||
useCurrentSize);
|
useCurrentSize);
|
||||||
|
return aspectRatioBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
|
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,6 +88,11 @@ public class PipTransitionState {
|
|||||||
return isInPip(mState);
|
return isInPip(mState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if activity has fully entered PiP mode. */
|
||||||
|
public boolean hasEnteredPip() {
|
||||||
|
return hasEnteredPip(mState);
|
||||||
|
}
|
||||||
|
|
||||||
public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
|
public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
|
||||||
mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
|
mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
|
||||||
}
|
}
|
||||||
@@ -120,6 +125,11 @@ public class PipTransitionState {
|
|||||||
return state >= TASK_APPEARED && state != EXITING_PIP;
|
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 {
|
public interface OnPipTransitionStateChangedListener {
|
||||||
void onPipTransitionStateChanged(@TransitionState int oldState,
|
void onPipTransitionStateChanged(@TransitionState int oldState,
|
||||||
@TransitionState int newState);
|
@TransitionState int newState);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ import android.content.pm.ActivityInfo;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.util.Pair;
|
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.PipAppOpsListener;
|
||||||
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipBoundsState;
|
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.PipMediaController;
|
||||||
import com.android.wm.shell.pip.PipParamsChangedForwarder;
|
import com.android.wm.shell.pip.PipParamsChangedForwarder;
|
||||||
import com.android.wm.shell.pip.PipSnapAlgorithm;
|
import com.android.wm.shell.pip.PipSnapAlgorithm;
|
||||||
@@ -110,6 +112,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
UserChangeListener {
|
UserChangeListener {
|
||||||
private static final String TAG = "PipController";
|
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;
|
private Context mContext;
|
||||||
protected ShellExecutor mMainExecutor;
|
protected ShellExecutor mMainExecutor;
|
||||||
private DisplayController mDisplayController;
|
private DisplayController mDisplayController;
|
||||||
@@ -262,7 +272,17 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
|
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
|
||||||
Set<Rect> unrestricted) {
|
Set<Rect> unrestricted) {
|
||||||
if (mPipBoundsState.getDisplayId() == displayId) {
|
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,
|
private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
|
||||||
PictureInPictureParams pictureInPictureParams,
|
PictureInPictureParams pictureInPictureParams,
|
||||||
int launcherRotation, int shelfHeight) {
|
int launcherRotation, Rect hotseatKeepClearArea) {
|
||||||
setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
|
|
||||||
|
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);
|
onDisplayRotationChangedNotInPip(mContext, launcherRotation);
|
||||||
final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
|
final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
|
||||||
pictureInPictureParams);
|
pictureInPictureParams);
|
||||||
@@ -1059,12 +1087,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
@Override
|
@Override
|
||||||
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
|
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
|
||||||
PictureInPictureParams pictureInPictureParams, int launcherRotation,
|
PictureInPictureParams pictureInPictureParams, int launcherRotation,
|
||||||
int shelfHeight) {
|
Rect keepClearArea) {
|
||||||
Rect[] result = new Rect[1];
|
Rect[] result = new Rect[1];
|
||||||
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
|
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
|
||||||
(controller) -> {
|
(controller) -> {
|
||||||
result[0] = controller.startSwipePipToHome(componentName, activityInfo,
|
result[0] = controller.startSwipePipToHome(componentName, activityInfo,
|
||||||
pictureInPictureParams, launcherRotation, shelfHeight);
|
pictureInPictureParams, launcherRotation, keepClearArea);
|
||||||
}, true /* blocking */);
|
}, true /* blocking */);
|
||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,6 +39,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
|||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.common.DisplayLayout;
|
import com.android.wm.shell.common.DisplayLayout;
|
||||||
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
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.PipSnapAlgorithm;
|
||||||
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
|
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
@@ -63,7 +64,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
|
|||||||
public TvPipBoundsAlgorithm(Context context,
|
public TvPipBoundsAlgorithm(Context context,
|
||||||
@NonNull TvPipBoundsState tvPipBoundsState,
|
@NonNull TvPipBoundsState tvPipBoundsState,
|
||||||
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
|
@NonNull PipSnapAlgorithm pipSnapAlgorithm) {
|
||||||
super(context, tvPipBoundsState, pipSnapAlgorithm);
|
super(context, tvPipBoundsState, pipSnapAlgorithm,
|
||||||
|
new PipKeepClearAlgorithm() {});
|
||||||
this.mTvPipBoundsState = tvPipBoundsState;
|
this.mTvPipBoundsState = tvPipBoundsState;
|
||||||
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
|
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
|
||||||
reloadResources(context);
|
reloadResources(context);
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec
|
|||||||
testSpec.assertLayers {
|
testSpec.assertLayers {
|
||||||
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
|
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
|
||||||
pipLayerList.zipWithNext { previous, current ->
|
pipLayerList.zipWithNext { previous, current ->
|
||||||
current.visibleRegion.coversAtMost(previous.visibleRegion.region)
|
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
|
|||||||
initializeMockResources();
|
initializeMockResources();
|
||||||
mPipBoundsState = new PipBoundsState(mContext);
|
mPipBoundsState = new PipBoundsState(mContext);
|
||||||
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
|
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
|
||||||
new PipSnapAlgorithm());
|
new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
|
||||||
|
|
||||||
mPipBoundsState.setDisplayLayout(
|
mPipBoundsState.setDisplayLayout(
|
||||||
new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
|
new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
|
|||||||
mPipBoundsState = new PipBoundsState(mContext);
|
mPipBoundsState = new PipBoundsState(mContext);
|
||||||
mPipTransitionState = new PipTransitionState();
|
mPipTransitionState = new PipTransitionState();
|
||||||
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
|
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
|
||||||
new PipSnapAlgorithm());
|
new PipSnapAlgorithm(), new PipKeepClearAlgorithm() {});
|
||||||
mMainExecutor = new TestShellExecutor();
|
mMainExecutor = new TestShellExecutor();
|
||||||
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
|
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
|
||||||
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
|
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
|
||||||
|
|||||||
@@ -34,61 +34,61 @@ import org.junit.runner.RunWith;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests against {@link PipKeepClearAlgorithm}.
|
* Unit tests against {@link PhonePipKeepClearAlgorithm}.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
@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);
|
private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
|
mPipKeepClearAlgorithm = new PhonePipKeepClearAlgorithm(mContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() {
|
public void findUnoccludedPosition_withCollidingRestrictedKeepClearArea_movesBounds() {
|
||||||
final Rect inBounds = new Rect(0, 0, 100, 100);
|
final Rect inBounds = new Rect(0, 0, 100, 100);
|
||||||
final Rect keepClearRect = new Rect(50, 50, 150, 150);
|
final Rect keepClearRect = new Rect(50, 50, 150, 150);
|
||||||
|
|
||||||
final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
|
final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds,
|
||||||
Set.of(), DISPLAY_BOUNDS);
|
Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
assertFalse(outBounds.contains(keepClearRect));
|
assertFalse(outBounds.contains(keepClearRect));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
|
public void findUnoccludedPosition_withNonCollidingRestrictedKeepClearArea_boundsUnchanged() {
|
||||||
final Rect inBounds = new Rect(0, 0, 100, 100);
|
final Rect inBounds = new Rect(0, 0, 100, 100);
|
||||||
final Rect keepClearRect = new Rect(100, 100, 150, 150);
|
final Rect keepClearRect = new Rect(100, 100, 150, 150);
|
||||||
|
|
||||||
final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
|
final Rect outBounds = mPipKeepClearAlgorithm.findUnoccludedPosition(inBounds,
|
||||||
Set.of(), DISPLAY_BOUNDS);
|
Set.of(keepClearRect), Set.of(), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
assertEquals(inBounds, outBounds);
|
assertEquals(inBounds, outBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
|
public void findUnoccludedPosition_withCollidingUnrestrictedKeepClearArea_moveBounds() {
|
||||||
// TODO(b/183746978): update this test to accommodate for the updated algorithm
|
// TODO(b/183746978): update this test to accommodate for the updated algorithm
|
||||||
final Rect inBounds = new Rect(0, 0, 100, 100);
|
final Rect inBounds = new Rect(0, 0, 100, 100);
|
||||||
final Rect keepClearRect = new Rect(50, 50, 150, 150);
|
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);
|
Set.of(keepClearRect), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
assertEquals(inBounds, outBounds);
|
assertFalse(outBounds.contains(keepClearRect));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
|
public void findUnoccludedPosition_withNonCollidingUnrestrictedKeepClearArea_boundsUnchanged() {
|
||||||
final Rect inBounds = new Rect(0, 0, 100, 100);
|
final Rect inBounds = new Rect(0, 0, 100, 100);
|
||||||
final Rect keepClearRect = new Rect(100, 100, 150, 150);
|
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);
|
Set.of(keepClearRect), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
assertEquals(inBounds, outBounds);
|
assertEquals(inBounds, outBounds);
|
||||||
@@ -64,6 +64,7 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -85,7 +86,7 @@ public class PipControllerTest extends ShellTestCase {
|
|||||||
@Mock private PhonePipMenuController mMockPhonePipMenuController;
|
@Mock private PhonePipMenuController mMockPhonePipMenuController;
|
||||||
@Mock private PipAppOpsListener mMockPipAppOpsListener;
|
@Mock private PipAppOpsListener mMockPipAppOpsListener;
|
||||||
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
|
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
|
||||||
@Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
|
@Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
|
||||||
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
|
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
|
||||||
@Mock private PipMediaController mMockPipMediaController;
|
@Mock private PipMediaController mMockPipMediaController;
|
||||||
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
|
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
|
||||||
@@ -267,7 +268,20 @@ public class PipControllerTest extends ShellTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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 int displayId = 1;
|
||||||
final Rect keepClearArea = new Rect(0, 0, 10, 10);
|
final Rect keepClearArea = new Rect(0, 0, 10, 10);
|
||||||
when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
|
when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
|
|||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipBoundsState;
|
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.PipSnapAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipTaskOrganizer;
|
import com.android.wm.shell.pip.PipTaskOrganizer;
|
||||||
import com.android.wm.shell.pip.PipTransitionController;
|
import com.android.wm.shell.pip.PipTransitionController;
|
||||||
@@ -87,8 +88,10 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mPipBoundsState = new PipBoundsState(mContext);
|
mPipBoundsState = new PipBoundsState(mContext);
|
||||||
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
|
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
|
||||||
|
final PipKeepClearAlgorithm pipKeepClearAlgorithm =
|
||||||
|
new PipKeepClearAlgorithm() {};
|
||||||
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
|
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
|
||||||
mPipBoundsState, pipSnapAlgorithm);
|
mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
|
||||||
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
|
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
|
||||||
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
|
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
|
||||||
mMockPipTransitionController, mFloatingContentCoordinator);
|
mMockPipTransitionController, mFloatingContentCoordinator);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
|
|||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
import com.android.wm.shell.pip.PipBoundsAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipBoundsState;
|
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.PipSnapAlgorithm;
|
||||||
import com.android.wm.shell.pip.PipTaskOrganizer;
|
import com.android.wm.shell.pip.PipTaskOrganizer;
|
||||||
import com.android.wm.shell.pip.PipTransitionController;
|
import com.android.wm.shell.pip.PipTransitionController;
|
||||||
@@ -104,7 +105,8 @@ public class PipTouchHandlerTest extends ShellTestCase {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mPipBoundsState = new PipBoundsState(mContext);
|
mPipBoundsState = new PipBoundsState(mContext);
|
||||||
mPipSnapAlgorithm = new PipSnapAlgorithm();
|
mPipSnapAlgorithm = new PipSnapAlgorithm();
|
||||||
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm);
|
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
|
||||||
|
new PipKeepClearAlgorithm() {});
|
||||||
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
|
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
|
||||||
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
|
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
|
||||||
mMockPipTransitionController, mFloatingContentCoordinator);
|
mMockPipTransitionController, mFloatingContentCoordinator);
|
||||||
|
|||||||
Reference in New Issue
Block a user