summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java5
-rw-r--r--services/core/java/com/android/server/wm/DragState.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java85
3 files changed, 113 insertions, 22 deletions
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 3c60d8296577..db058cafe5fe 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -215,7 +215,8 @@ class DragDropController {
mDragState.mOriginalAlpha = alpha;
mDragState.mAnimatedScale = callingWin.mGlobalScale;
mDragState.mToken = dragToken;
- mDragState.mDisplayContent = displayContent;
+ mDragState.mStartDragDisplayContent = displayContent;
+ mDragState.mCurrentDisplayContent = displayContent;
mDragState.mData = data;
mDragState.mCallingTaskIdToHide = shouldMoveCallingTaskToBack(callingWin,
flags);
@@ -273,7 +274,7 @@ class DragDropController {
InputManagerGlobal.getInstance().setPointerIcon(
PointerIcon.getSystemIcon(
mService.mContext, PointerIcon.TYPE_GRABBING),
- mDragState.mDisplayContent.getDisplayId(), touchDeviceId,
+ mDragState.mCurrentDisplayContent.getDisplayId(), touchDeviceId,
touchPointerId, mDragState.getInputToken());
}
// remember the thumb offsets for later
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b3e9244d108d..4a4e2461c2ca 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -129,10 +129,17 @@ class DragState {
*/
volatile boolean mAnimationCompleted = false;
/**
+ * The display on which the drag originally started. Note that it's possible for either/both
+ * mStartDragDisplayContent and mCurrentDisplayContent to be invalid if DisplayTopology was
+ * changed or removed in the middle of the drag. In this case, drag will also be cancelled as
+ * soon as listener is notified.
+ */
+ DisplayContent mStartDragDisplayContent;
+ /**
* The display on which the drag is happening. If it goes into a different display this will
* be updated.
*/
- DisplayContent mDisplayContent;
+ DisplayContent mCurrentDisplayContent;
@Nullable private ValueAnimator mAnimator;
private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
@@ -181,7 +188,7 @@ class DragState {
.setContainerLayer()
.setName("Drag and Drop Input Consumer")
.setCallsite("DragState.showInputSurface")
- .setParent(mDisplayContent.getOverlayLayer())
+ .setParent(mCurrentDisplayContent.getOverlayLayer())
.build();
}
final InputWindowHandle h = getInputWindowHandle();
@@ -549,7 +556,7 @@ class DragState {
PointF relativeToWindowCoords = new PointF(newWin.translateToWindowX(touchX),
newWin.translateToWindowY(touchY));
if (Flags.enableConnectedDisplaysDnd()
- && mDisplayContent.getDisplayId() != newWin.getDisplayId()) {
+ && mCurrentDisplayContent.getDisplayId() != newWin.getDisplayId()) {
// Currently DRAG_STARTED coords are sent relative to the window target in **px**
// coordinates. However, this cannot be extended to connected displays scenario,
// as there's only global **dp** coordinates and no global **px** coordinates.
@@ -720,6 +727,20 @@ class DragState {
mCurrentDisplayX = displayX;
mCurrentDisplayY = displayY;
+ final DisplayContent lastSetDisplayContent = mCurrentDisplayContent;
+ boolean cursorMovedToDifferentDisplay = false;
+ // Keep latest display up-to-date even when drag has stopped.
+ if (Flags.enableConnectedDisplaysDnd() && mCurrentDisplayContent.mDisplayId != displayId) {
+ final DisplayContent newDisplay = mService.mRoot.getDisplayContent(displayId);
+ if (newDisplay == null) {
+ Slog.e(TAG_WM, "Target displayId=" + displayId + " was not found, ending drag.");
+ endDragLocked(false /* dropConsumed */,
+ false /* relinquishDragSurfaceToDropTarget */);
+ return;
+ }
+ cursorMovedToDifferentDisplay = true;
+ mCurrentDisplayContent = newDisplay;
+ }
if (!keepHandling) {
return;
}
@@ -728,6 +749,24 @@ class DragState {
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
}
+ if (cursorMovedToDifferentDisplay) {
+ mAnimatedScale = mAnimatedScale * mCurrentDisplayContent.mBaseDisplayDensity
+ / lastSetDisplayContent.mBaseDisplayDensity;
+ mThumbOffsetX = mThumbOffsetX * mCurrentDisplayContent.mBaseDisplayDensity
+ / lastSetDisplayContent.mBaseDisplayDensity;
+ mThumbOffsetY = mThumbOffsetY * mCurrentDisplayContent.mBaseDisplayDensity
+ / lastSetDisplayContent.mBaseDisplayDensity;
+ mTransaction.reparent(mSurfaceControl, mCurrentDisplayContent.getSurfaceControl());
+ mTransaction.setScale(mSurfaceControl, mAnimatedScale, mAnimatedScale);
+
+ final InputWindowHandle inputWindowHandle = getInputWindowHandle();
+ if (inputWindowHandle == null) {
+ Slog.w(TAG_WM, "Drag is in progress but there is no drag window handle.");
+ return;
+ }
+ inputWindowHandle.displayId = displayId;
+ mTransaction.setInputWindowInfo(mInputSurface, inputWindowHandle);
+ }
mTransaction.setPosition(mSurfaceControl, displayX - mThumbOffsetX,
displayY - mThumbOffsetY).apply();
ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: displayId=%d, pos=(%d,%d)", mSurfaceControl,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 23dcb65eb30f..63c745ea6cd0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -43,6 +43,7 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -92,6 +93,7 @@ import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Tests for the {@link DragDropController} class.
@@ -255,7 +257,7 @@ public class DragDropControllerTests extends WindowTestsBase {
iwindow.setDragEventJournal(dragEvents);
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
// Verify the start-drag event is sent for invisible windows
final DragEvent dragEvent = dragEvents.get(0);
assertTrue(dragEvent.getAction() == ACTION_DRAG_STARTED);
@@ -297,7 +299,7 @@ public class DragDropControllerTests extends WindowTestsBase {
globalInterceptIWindow.setDragEventJournal(globalInterceptWindowDragEvents);
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
- createClipDataForActivity(null, mock(UserHandle.class)), () -> {
+ createClipDataForActivity(null, mock(UserHandle.class)), (unused) -> {
// Verify the start-drag event is sent for the local and global intercept window
// but not the other window
assertTrue(nonLocalWindowDragEvents.isEmpty());
@@ -340,7 +342,7 @@ public class DragDropControllerTests extends WindowTestsBase {
iwindow.setDragEventJournal(dragEvents);
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
// Verify the start-drag event has the drag flags
final DragEvent dragEvent = dragEvents.get(0);
assertTrue(dragEvent.getAction() == ACTION_DRAG_STARTED);
@@ -386,7 +388,7 @@ public class DragDropControllerTests extends WindowTestsBase {
iwindow2.setDragEventJournal(dragEvents2);
startDrag(dragStartX, dragStartY, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
// Verify the start-drag event is sent as-is for the drag origin window.
final DragEvent dragEvent = dragEvents.get(0);
assertEquals(ACTION_DRAG_STARTED, dragEvent.getAction());
@@ -441,7 +443,7 @@ public class DragDropControllerTests extends WindowTestsBase {
iwindow2.setDragEventJournal(dragEvents2);
startDrag(dragStartX, dragStartY, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
// Verify the start-drag event is sent as-is for the drag origin window.
final DragEvent dragEvent = dragEvents.get(0);
assertEquals(ACTION_DRAG_STARTED, dragEvent.getAction());
@@ -477,6 +479,56 @@ public class DragDropControllerTests extends WindowTestsBase {
});
}
+ @Test
+ public void testDragMove() {
+ startDrag(0, 0, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
+ ClipData.newPlainText("label", "text"), (surface) -> {
+ int dragMoveX = mWindow.getBounds().centerX();
+ int dragMoveY = mWindow.getBounds().centerY();
+ final SurfaceControl.Transaction transaction =
+ mSystemServicesTestRule.mTransaction;
+ clearInvocations(transaction);
+
+ mTarget.handleMotionEvent(true, mWindow.getDisplayId(), dragMoveX, dragMoveY);
+ verify(transaction).setPosition(surface, dragMoveX, dragMoveY);
+
+ // Clean-up.
+ mTarget.reportDropWindow(mWindow.mInputChannelToken, 0, 0);
+ mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(), 0,
+ 0);
+ mToken = mWindow.mClient.asBinder();
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_DND)
+ public void testConnectedDisplaysDragMoveToOtherDisplay() {
+ final float testDensityMultiplier = 1.5f;
+ final DisplayContent testDisplay = createMockSimulatedDisplay();
+ testDisplay.mBaseDisplayDensity =
+ (int) (mDisplayContent.mBaseDisplayDensity * testDensityMultiplier);
+ WindowState testWindow = createDropTargetWindow("App drag test window", testDisplay);
+
+ // Test starts from mWindow which is on default display.
+ startDrag(0, 0, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
+ ClipData.newPlainText("label", "text"), (surface) -> {
+ final SurfaceControl.Transaction transaction =
+ mSystemServicesTestRule.mTransaction;
+ clearInvocations(transaction);
+ mTarget.handleMotionEvent(true, testWindow.getDisplayId(), 0, 0);
+
+ verify(transaction).reparent(surface, testDisplay.getSurfaceControl());
+ verify(transaction).setScale(surface, testDensityMultiplier,
+ testDensityMultiplier);
+
+ // Clean-up.
+ mTarget.reportDropWindow(mWindow.mInputChannelToken, 0, 0);
+ mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(), 0,
+ 0);
+ mToken = mWindow.mClient.asBinder();
+ });
+ }
+
private DragEvent last(ArrayList<DragEvent> list) {
return list.get(list.size() - 1);
}
@@ -645,7 +697,7 @@ public class DragDropControllerTests extends WindowTestsBase {
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ
| View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
assertTrue(dragEvents.get(0).getAction() == ACTION_DRAG_STARTED);
// Verify after consuming that the drag surface is relinquished
@@ -676,7 +728,7 @@ public class DragDropControllerTests extends WindowTestsBase {
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ
| View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION,
- ClipData.newPlainText("label", "text"), () -> {
+ ClipData.newPlainText("label", "text"), (unused) -> {
assertTrue(dragEvents.get(0).getAction() == ACTION_DRAG_STARTED);
// Verify after consuming that the drag surface is relinquished
@@ -713,7 +765,7 @@ public class DragDropControllerTests extends WindowTestsBase {
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
- ClipData.newPlainText("label", "Test"), () -> {
+ ClipData.newPlainText("label", "Test"), (unused) -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mWindow.mInputChannelToken, invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(),
@@ -738,7 +790,7 @@ public class DragDropControllerTests extends WindowTestsBase {
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
- ClipData.newPlainText("label", "Test"), () -> {
+ ClipData.newPlainText("label", "Test"), (unused) -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(),
@@ -761,7 +813,7 @@ public class DragDropControllerTests extends WindowTestsBase {
doReturn(mock(Binder.class)).when(listener).asBinder();
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
- startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
+ startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), (unused) -> {
// Trigger an unhandled drop and verify the global drag listener was not called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */, mDisplayContent.getDisplayId(),
@@ -784,7 +836,7 @@ public class DragDropControllerTests extends WindowTestsBase {
mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG,
- ClipData.newPlainText("label", "Test"), () -> {
+ ClipData.newPlainText("label", "Test"), (unused) -> {
// Trigger an unhandled drop and verify the global drag listener was called
mTarget.reportDropWindow(mock(IBinder.class), invalidXY, invalidXY);
mTarget.handleMotionEvent(false /* keepHandling */,
@@ -805,7 +857,7 @@ public class DragDropControllerTests extends WindowTestsBase {
}
private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
- startDrag(flags, data, () -> {
+ startDrag(flags, data, (unused) -> {
mTarget.reportDropWindow(mWindow.mInputChannelToken, dropX, dropY);
mTarget.handleMotionEvent(false /* keepHandling */, mWindow.getDisplayId(), dropX,
dropY);
@@ -816,27 +868,26 @@ public class DragDropControllerTests extends WindowTestsBase {
/**
* Starts a drag with the given parameters, calls Runnable `r` after drag is started.
*/
- private void startDrag(int flag, ClipData data, Runnable r) {
- startDrag(0, 0, flag, data, r);
+ private void startDrag(int flag, ClipData data, Consumer<SurfaceControl> c) {
+ startDrag(0, 0, flag, data, c);
}
/**
* Starts a drag with the given parameters, calls Runnable `r` after drag is started.
*/
private void startDrag(float startInWindowX, float startInWindowY, int flag, ClipData data,
- Runnable r) {
+ Consumer<SurfaceControl> c) {
final SurfaceSession appSession = new SurfaceSession();
try {
final SurfaceControl surface = new SurfaceControl.Builder(appSession).setName(
"drag surface").setBufferSize(100, 100).setFormat(
PixelFormat.TRANSLUCENT).build();
-
assertTrue(mWm.mInputManager.startDragAndDrop(new Binder(), new Binder()));
mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, flag, surface, 0, 0, 0,
startInWindowX, startInWindowY, 0, 0, data);
assertNotNull(mToken);
- r.run();
+ c.accept(surface);
} finally {
appSession.kill();
}