Merge "Add keep clear area avoidance algorithm for PiP" into tm-qpr-dev am: 2542f63ded
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18606022 Change-Id: I43a6c6a5c48b49547c4599a861037e287b0de6ab Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -63,6 +63,7 @@ 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.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;
|
||||||
@@ -188,7 +189,8 @@ public class WMShellModule {
|
|||||||
@Provides
|
@Provides
|
||||||
static Optional<Pip> providePip(Context context, DisplayController displayController,
|
static Optional<Pip> providePip(Context context, DisplayController displayController,
|
||||||
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
|
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
|
||||||
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
|
PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
|
||||||
|
PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
|
||||||
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
|
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
|
||||||
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
|
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
|
||||||
WindowManagerShellWrapper windowManagerShellWrapper,
|
WindowManagerShellWrapper windowManagerShellWrapper,
|
||||||
@@ -197,7 +199,8 @@ public class WMShellModule {
|
|||||||
Optional<OneHandedController> oneHandedController,
|
Optional<OneHandedController> oneHandedController,
|
||||||
@ShellMainThread ShellExecutor mainExecutor) {
|
@ShellMainThread ShellExecutor mainExecutor) {
|
||||||
return Optional.ofNullable(PipController.create(context, displayController,
|
return Optional.ofNullable(PipController.create(context, displayController,
|
||||||
pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState,
|
pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
|
||||||
|
pipMotionHelper,
|
||||||
pipMediaController, phonePipMenuController, pipTaskOrganizer,
|
pipMediaController, phonePipMenuController, pipTaskOrganizer,
|
||||||
pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
|
pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
|
||||||
taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
|
taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
|
||||||
@@ -215,6 +218,12 @@ public class WMShellModule {
|
|||||||
return new PipSnapAlgorithm();
|
return new PipSnapAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
|
||||||
|
return new PipKeepClearAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
|
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
private PipAppOpsListener mAppOpsListener;
|
private PipAppOpsListener mAppOpsListener;
|
||||||
private PipMediaController mMediaController;
|
private PipMediaController mMediaController;
|
||||||
private PipBoundsAlgorithm mPipBoundsAlgorithm;
|
private PipBoundsAlgorithm mPipBoundsAlgorithm;
|
||||||
|
private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
|
||||||
private PipBoundsState mPipBoundsState;
|
private PipBoundsState mPipBoundsState;
|
||||||
|
private PipMotionHelper mPipMotionHelper;
|
||||||
private PipTouchHandler mTouchHandler;
|
private PipTouchHandler mTouchHandler;
|
||||||
private PipTransitionController mPipTransitionController;
|
private PipTransitionController mPipTransitionController;
|
||||||
private TaskStackListenerImpl mTaskStackListener;
|
private TaskStackListenerImpl mTaskStackListener;
|
||||||
@@ -286,7 +288,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
@Nullable
|
@Nullable
|
||||||
public static Pip create(Context context, DisplayController displayController,
|
public static Pip create(Context context, DisplayController displayController,
|
||||||
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
|
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
|
||||||
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
|
PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
|
||||||
|
PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
|
||||||
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
|
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
|
||||||
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
|
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
|
||||||
WindowManagerShellWrapper windowManagerShellWrapper,
|
WindowManagerShellWrapper windowManagerShellWrapper,
|
||||||
@@ -301,7 +304,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
|
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
|
||||||
pipBoundsState, pipMediaController,
|
pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
|
||||||
phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
|
phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
|
||||||
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
|
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
|
||||||
oneHandedController, mainExecutor)
|
oneHandedController, mainExecutor)
|
||||||
@@ -312,7 +315,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
DisplayController displayController,
|
DisplayController displayController,
|
||||||
PipAppOpsListener pipAppOpsListener,
|
PipAppOpsListener pipAppOpsListener,
|
||||||
PipBoundsAlgorithm pipBoundsAlgorithm,
|
PipBoundsAlgorithm pipBoundsAlgorithm,
|
||||||
|
PipKeepClearAlgorithm pipKeepClearAlgorithm,
|
||||||
@NonNull PipBoundsState pipBoundsState,
|
@NonNull PipBoundsState pipBoundsState,
|
||||||
|
PipMotionHelper pipMotionHelper,
|
||||||
PipMediaController pipMediaController,
|
PipMediaController pipMediaController,
|
||||||
PhonePipMenuController phonePipMenuController,
|
PhonePipMenuController phonePipMenuController,
|
||||||
PipTaskOrganizer pipTaskOrganizer,
|
PipTaskOrganizer pipTaskOrganizer,
|
||||||
@@ -335,7 +340,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
|
|||||||
mWindowManagerShellWrapper = windowManagerShellWrapper;
|
mWindowManagerShellWrapper = windowManagerShellWrapper;
|
||||||
mDisplayController = displayController;
|
mDisplayController = displayController;
|
||||||
mPipBoundsAlgorithm = pipBoundsAlgorithm;
|
mPipBoundsAlgorithm = pipBoundsAlgorithm;
|
||||||
|
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
|
||||||
mPipBoundsState = pipBoundsState;
|
mPipBoundsState = pipBoundsState;
|
||||||
|
mPipMotionHelper = pipMotionHelper;
|
||||||
mPipTaskOrganizer = pipTaskOrganizer;
|
mPipTaskOrganizer = pipTaskOrganizer;
|
||||||
mMainExecutor = mainExecutor;
|
mMainExecutor = mainExecutor;
|
||||||
mMediaController = pipMediaController;
|
mMediaController = pipMediaController;
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,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 PipSnapAlgorithm mMockPipSnapAlgorithm;
|
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
|
||||||
@Mock private PipMediaController mMockPipMediaController;
|
@Mock private PipMediaController mMockPipMediaController;
|
||||||
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
|
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
|
||||||
@@ -101,7 +102,8 @@ public class PipControllerTest extends ShellTestCase {
|
|||||||
}).when(mMockExecutor).execute(any());
|
}).when(mMockExecutor).execute(any());
|
||||||
mPipController = new PipController(mContext, mMockDisplayController,
|
mPipController = new PipController(mContext, mMockDisplayController,
|
||||||
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
|
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
|
||||||
mMockPipBoundsState, mMockPipMediaController,
|
mMockPipKeepClearAlgorithm,
|
||||||
|
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
|
||||||
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
|
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
|
||||||
mMockPipTransitionController, mMockWindowManagerShellWrapper,
|
mMockPipTransitionController, mMockWindowManagerShellWrapper,
|
||||||
mMockTaskStackListener, mPipParamsChangedForwarder,
|
mMockTaskStackListener, mPipParamsChangedForwarder,
|
||||||
@@ -134,7 +136,8 @@ public class PipControllerTest extends ShellTestCase {
|
|||||||
|
|
||||||
assertNull(PipController.create(spyContext, mMockDisplayController,
|
assertNull(PipController.create(spyContext, mMockDisplayController,
|
||||||
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
|
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
|
||||||
mMockPipBoundsState, mMockPipMediaController,
|
mMockPipKeepClearAlgorithm,
|
||||||
|
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
|
||||||
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
|
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
|
||||||
mMockPipTransitionController, mMockWindowManagerShellWrapper,
|
mMockPipTransitionController, mMockWindowManagerShellWrapper,
|
||||||
mMockTaskStackListener, mPipParamsChangedForwarder,
|
mMockTaskStackListener, mPipParamsChangedForwarder,
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests against {@link PipKeepClearAlgorithm}.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
@SmallTest
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
public class PipKeepClearAlgorithmTest extends ShellTestCase {
|
||||||
|
|
||||||
|
private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
|
||||||
|
private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adjust_withCollidingRestrictedKeepClearAreas_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);
|
||||||
|
|
||||||
|
assertFalse(outBounds.contains(keepClearRect));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
|
||||||
|
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);
|
||||||
|
|
||||||
|
assertEquals(inBounds, outBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
|
||||||
|
// 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(),
|
||||||
|
Set.of(keepClearRect), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
|
assertEquals(inBounds, outBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
|
||||||
|
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(),
|
||||||
|
Set.of(keepClearRect), DISPLAY_BOUNDS);
|
||||||
|
|
||||||
|
assertEquals(inBounds, outBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user