Merge "If task dragged to outer disallowed area, position back under the area" into udc-qpr-dev

This commit is contained in:
Maryam Dehaini
2023-06-26 20:49:39 +00:00
committed by Android (Google) Code Review
10 changed files with 169 additions and 83 deletions

View File

@@ -419,4 +419,8 @@
<dimen name="freeform_resize_handle">15dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen>
<!-- The height of the area at the top of the screen where a freeform task will transition to
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
</resources>

View File

@@ -100,6 +100,10 @@ class DesktopTasksController(
}
}
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_transition_area_height)
init {
desktopMode = DesktopModeImpl()
if (DesktopModeStatus.isProto2Enabled()) {
@@ -700,13 +704,12 @@ class DesktopTasksController(
y: Float
) {
if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
val statusBarHeight = getStatusBarHeight(taskInfo)
if (y <= statusBarHeight && visualIndicator == null) {
if (y <= transitionAreaHeight && visualIndicator == null) {
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
rootTaskDisplayAreaOrganizer)
visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
} else if (y > statusBarHeight && visualIndicator != null) {
} else if (y > transitionAreaHeight && visualIndicator != null) {
releaseVisualIndicator()
}
}
@@ -726,8 +729,7 @@ class DesktopTasksController(
y: Float,
windowDecor: DesktopModeWindowDecoration
) {
val statusBarHeight = getStatusBarHeight(taskInfo)
if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
windowDecor.incrementRelayoutBlock()
moveToFullscreenWithAnimation(taskInfo, position)
}
@@ -746,9 +748,9 @@ class DesktopTasksController(
taskSurface: SurfaceControl,
y: Float
) {
// If the motion event is above the status bar, return since we do not need to show the
// visual indicator at this point.
if (y < getStatusBarHeight(taskInfo)) {
// If the motion event is above the status bar and the visual indicator is not yet visible,
// return since we do not need to show the visual indicator at this point.
if (y < getStatusBarHeight(taskInfo) && visualIndicator == null) {
return
}
if (visualIndicator == null) {

View File

@@ -193,7 +193,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
final DragPositioningCallback dragPositioningCallback =
new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController,
null /* disallowedAreaForEndBounds */);
0 /* disallowedAreaForEndBoundsHeight */);
final CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, dragPositioningCallback);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);

View File

@@ -845,7 +845,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.createResizeVeil();
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
windowDecoration, taskInfo);
windowDecoration);
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
@@ -858,24 +858,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
incrementEventReceiverTasks(taskInfo.displayId);
}
private DragPositioningCallback createDragPositioningCallback(
@NonNull DesktopModeWindowDecoration windowDecoration,
@NonNull RunningTaskInfo taskInfo) {
final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width();
final Rect disallowedAreaForEndBounds;
if (DesktopModeStatus.isProto2Enabled()) {
disallowedAreaForEndBounds = new Rect(0, 0, screenWidth,
getStatusBarHeight(taskInfo.displayId));
} else {
disallowedAreaForEndBounds = null;
}
@NonNull DesktopModeWindowDecoration windowDecoration) {
final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.desktop_mode_transition_area_height);
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration,
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
mTransactionFactory);
mDisplayController, mDragStartListener, mTransactionFactory,
transitionAreaHeight);
} else {
return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
mTransitions);
mDisplayController, mDragStartListener, mTransitions,
transitionAreaHeight);
}
}

View File

@@ -83,8 +83,6 @@ public class DragPositioningCallbackUtility {
// Make sure the new resizing destination in any direction falls within the stable bounds.
// If not, set the bounds back to the old location that was valid to avoid conflicts with
// some regions such as the gesture area.
displayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
.getStableBounds(stableBounds);
if ((ctrlType & CTRL_TYPE_LEFT) != 0) {
final int candidateLeft = repositionTaskBounds.left + (int) delta.x;
repositionTaskBounds.left = (candidateLeft > stableBounds.left)
@@ -136,7 +134,7 @@ public class DragPositioningCallbackUtility {
repositionTaskBounds.top);
}
static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
private static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
PointF repositionStartPoint, float x, float y) {
final float deltaX = x - repositionStartPoint.x;
final float deltaY = y - repositionStartPoint.y;
@@ -144,6 +142,23 @@ public class DragPositioningCallbackUtility {
repositionTaskBounds.offset((int) deltaX, (int) deltaY);
}
/**
* Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If
* the bounds are outside of the stable bounds, they are shifted to place task at the top of the
* stable bounds.
*/
static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds,
PointF repositionStartPoint, float x, float y) {
updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint,
x, y);
// If task is outside of stable bounds (in the status bar area), shift the task down.
if (stableBounds.top > repositionTaskBounds.top) {
final int yShift = stableBounds.top - repositionTaskBounds.top;
repositionTaskBounds.offset(0, yShift);
}
}
/**
* Apply a bounds change to a task.
* @param windowDecoration decor of task we are changing bounds for

View File

@@ -21,8 +21,6 @@ import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -42,28 +40,31 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
// If a task move (not resize) finishes in this region, the positioner will not attempt to
// If a task move (not resize) finishes with the positions y less than this value, do not
// finalize the bounds there using WCT#setBounds
private final Rect mDisallowedAreaForEndBounds;
private final int mDisallowedAreaForEndBoundsHeight;
private boolean mHasDragResized;
private int mCtrlType;
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds) {
this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
dragStartListener -> {}, SurfaceControl.Transaction::new);
DisplayController displayController, int disallowedAreaForEndBoundsHeight) {
this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {},
SurfaceControl.Transaction::new, disallowedAreaForEndBoundsHeight);
}
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds,
DisplayController displayController,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Supplier<SurfaceControl.Transaction> supplier) {
Supplier<SurfaceControl.Transaction> supplier,
int disallowedAreaForEndBoundsHeight) {
mTaskOrganizer = taskOrganizer;
mWindowDecoration = windowDecoration;
mDisplayController = displayController;
mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds);
mDragStartListener = dragStartListener;
mTransactionSupplier = supplier;
mDisallowedAreaForEndBoundsHeight = disallowedAreaForEndBoundsHeight;
mDisplayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
@Override
@@ -121,10 +122,10 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
}
mTaskOrganizer.applyTransaction(wct);
} else if (mCtrlType == CTRL_TYPE_UNDEFINED
&& !mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
&& y > mDisallowedAreaForEndBoundsHeight) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTaskOrganizer.applyTransaction(wct);
}

View File

@@ -53,33 +53,35 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
// If a task move (not resize) finishes in this region, the positioner will not attempt to
// If a task move (not resize) finishes with the positions y less than this value, do not
// finalize the bounds there using WCT#setBounds
private final Rect mDisallowedAreaForEndBounds;
private final int mDisallowedAreaForEndBoundsHeight;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private int mCtrlType;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
Rect disallowedAreaForEndBounds,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Transitions transitions) {
this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
dragStartListener, SurfaceControl.Transaction::new, transitions);
Transitions transitions,
int disallowedAreaForEndBoundsHeight) {
this(taskOrganizer, windowDecoration, displayController, dragStartListener,
SurfaceControl.Transaction::new, transitions, disallowedAreaForEndBoundsHeight);
}
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
Rect disallowedAreaForEndBounds,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
Supplier<SurfaceControl.Transaction> supplier, Transitions transitions) {
Supplier<SurfaceControl.Transaction> supplier, Transitions transitions,
int disallowedAreaForEndBoundsHeight) {
mTaskOrganizer = taskOrganizer;
mDesktopWindowDecoration = windowDecoration;
mDisplayController = displayController;
mDragStartListener = dragStartListener;
mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds);
mTransactionSupplier = supplier;
mTransitions = transitions;
mDisallowedAreaForEndBoundsHeight = disallowedAreaForEndBoundsHeight;
mDisplayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
@Override
@@ -110,8 +112,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
} else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
final SurfaceControl.Transaction t = mTransactionSupplier.get();
DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t,
x, y);
mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
t.apply();
}
}
@@ -138,9 +139,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
// won't be called.
mDesktopWindowDecoration.hideResizeVeil();
}
} else if (!mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
} else if (y > mDisallowedAreaForEndBoundsHeight) {
DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(),
mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer);
}

View File

@@ -90,6 +90,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
@Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
@Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
@Mock private SurfaceControl.Transaction mTransaction;
@Mock private Display mDisplay;
private final List<InputManager> mMockInputManagers = new ArrayList<>();
private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
@@ -126,6 +127,9 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
inputChannels[0].dispose();
when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
mDesktopModeWindowDecoration.mDisplay = mDisplay;
doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
}
@Test

View File

@@ -22,6 +22,7 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFI
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
@@ -71,16 +72,6 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
taskPositioner =
FluidResizeTaskPositioner(
mockShellTaskOrganizer,
mockWindowDecoration,
mockDisplayController,
DISALLOWED_AREA_FOR_END_BOUNDS,
mockDragStartListener,
mockTransactionFactory
)
whenever(taskToken.asBinder()).thenReturn(taskBinder)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
@@ -101,6 +92,15 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
taskPositioner = FluidResizeTaskPositioner(
mockShellTaskOrganizer,
mockWindowDecoration,
mockDisplayController,
mockDragStartListener,
mockTransactionFactory,
DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT
)
}
@Test
@@ -544,7 +544,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.right.toFloat() + 5
val newY = STARTING_BOUNDS.top.toFloat() + 5
val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -614,6 +614,38 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
})
}
@Test
fun testDragResize_drag_taskPositionedInStableBounds() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
val newX = STARTING_BOUNDS.left.toFloat()
val newY = STABLE_BOUNDS.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
)
verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
taskPositioner.onDragPositioningEnd(
newX,
newY
)
// Verify task's top bound is set to stable bounds top since dragged outside stable bounds
// but not in disallowed end bounds area.
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
STABLE_BOUNDS.top
}
})
}
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -622,10 +654,11 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
private const val NAVBAR_HEIGHT = 50
private const val CAPTION_HEIGHT = 50
private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
private val STABLE_INSETS = Rect(0, 50, 0, 0)
private val DISALLOWED_AREA_FOR_END_BOUNDS = Rect(0, 0, 300, 300)
private val DISALLOWED_RESIZE_AREA = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
@@ -633,7 +666,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
DISPLAY_BOUNDS.bottom)
private val STABLE_BOUNDS = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)

View File

@@ -89,17 +89,6 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
taskPositioner =
VeiledResizeTaskPositioner(
mockShellTaskOrganizer,
mockDesktopWindowDecoration,
mockDisplayController,
DISALLOWED_AREA_FOR_END_BOUNDS,
mockDragStartListener,
mockTransactionFactory,
mockTransitions
)
whenever(taskToken.asBinder()).thenReturn(taskBinder)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
@@ -119,6 +108,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
}
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
taskPositioner =
VeiledResizeTaskPositioner(
mockShellTaskOrganizer,
mockDesktopWindowDecoration,
mockDisplayController,
mockDragStartListener,
mockTransactionFactory,
mockTransitions,
DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT
)
}
@Test
@@ -269,7 +269,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.left.toFloat() + 5
val newY = STARTING_BOUNDS.top.toFloat() + 5
val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -334,6 +334,38 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
})
}
@Test
fun testDragResize_drag_taskPositionedInStableBounds() {
taskPositioner.onDragPositioningStart(
CTRL_TYPE_UNDEFINED, // drag
STARTING_BOUNDS.left.toFloat(),
STARTING_BOUNDS.top.toFloat()
)
val newX = STARTING_BOUNDS.left.toFloat()
val newY = STABLE_BOUNDS.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
)
verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
taskPositioner.onDragPositioningEnd(
newX,
newY
)
// Verify task's top bound is set to stable bounds top since dragged outside stable bounds
// but not in disallowed end bounds area.
verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
return@argThat wct.changes.any { (token, change) ->
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
STABLE_BOUNDS.top
}
})
}
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -342,12 +374,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
private const val NAVBAR_HEIGHT = 50
private const val CAPTION_HEIGHT = 50
private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
private val DISALLOWED_AREA_FOR_END_BOUNDS = Rect(0, 0, 50, 50)
private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
private val STABLE_BOUNDS = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)