diff options
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.cpp | 17 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.h | 13 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 127 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 15 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/TouchState.cpp | 2 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/TouchState.h | 5 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/include/InputDispatcherInterface.h | 4 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 109 |
8 files changed, 275 insertions, 17 deletions
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index a19b04f20e..5270b8a107 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -130,6 +130,23 @@ std::string PointerCaptureChangedEntry::getDescription() const { pointerCaptureEnabled ? "true" : "false"); } +// --- DragEntry --- + +// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries +DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, + float x, float y) + : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER), + connectionToken(connectionToken), + isExiting(isExiting), + x(x), + y(y) {} + +DragEntry::~DragEntry() {} + +std::string DragEntry::getDescription() const { + return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y); +} + // --- KeyEntry --- KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ed17e68f9e..e5fb26c177 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -38,6 +38,7 @@ struct EventEntry { MOTION, SENSOR, POINTER_CAPTURE_CHANGED, + DRAG, }; int32_t id; @@ -111,6 +112,18 @@ struct PointerCaptureChangedEntry : EventEntry { virtual ~PointerCaptureChangedEntry(); }; +struct DragEntry : EventEntry { + sp<IBinder> connectionToken; + bool isExiting; + float x, y; + + DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x, + float y); + std::string getDescription() const override; + + ~DragEntry() override; +}; + struct KeyEntry : EventEntry { int32_t deviceId; uint32_t source; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3183a98257..fe46d17493 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -753,6 +753,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { break; } + case EventEntry::Type::DRAG: { + std::shared_ptr<DragEntry> typedEntry = + std::static_pointer_cast<DragEntry>(mPendingEvent); + dispatchDragLocked(currentTime, typedEntry); + done = true; + break; + } + case EventEntry::Type::KEY: { std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent); if (isAppSwitchDue) { @@ -921,7 +929,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::DRAG: { // nothing to do break; } @@ -943,7 +952,8 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets, - bool addPortalWindows) { + bool addPortalWindows, + bool ignoreDragWindow) { if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) { LOG_ALWAYS_FATAL( "Must provide a valid touch state if adding portal windows or outside targets"); @@ -951,6 +961,9 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display // Traverse windows from front to back to find touched window. const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { + if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) { + continue; + } const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { auto flags = windowInfo->flags; @@ -1060,7 +1073,8 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason case EventEntry::Type::SENSOR: { break; } - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::DRAG: { break; } case EventEntry::Type::FOCUS: @@ -1554,6 +1568,35 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr< return true; } +void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle, + bool isExiting, const MotionEntry& motionEntry) { + // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should + // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid. + LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1); + PointerCoords pointerCoords; + pointerCoords.copyFrom(motionEntry.pointerCoords[0]); + pointerCoords.transform(windowHandle->getInfo()->transform); + + std::unique_ptr<DragEntry> dragEntry = + std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime, + windowHandle->getToken(), isExiting, pointerCoords.getX(), + pointerCoords.getY()); + + enqueueInboundEventLocked(std::move(dragEntry)); +} + +void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) { + std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); + if (channel == nullptr) { + return; // Window has gone away + } + InputTarget target; + target.inputChannel = channel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + entry->dispatchInProgress = true; + dispatchEventLocked(currentTime, entry, {target}); +} + void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 @@ -1660,7 +1703,8 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { case EventEntry::Type::FOCUS: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: - case EventEntry::Type::SENSOR: { + case EventEntry::Type::SENSOR: + case EventEntry::Type::DRAG: { ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str()); return ADISPLAY_ID_NONE; } @@ -2017,6 +2061,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( goto Failed; } + addDragEventLocked(entry, tempTouchState); + // Check whether touches should slip outside of the current foreground window. if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 && tempTouchState.isSlippery()) { @@ -2272,6 +2318,38 @@ Failed: return injectionResult; } +void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) { + if (entry.pointerCount != 1 || !state.dragWindow) { + return; + } + + int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK; + int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + if (maskedAction == AMOTION_EVENT_ACTION_MOVE) { + const sp<InputWindowHandle> hoverWindowHandle = + findTouchedWindowAtLocked(entry.displayId, x, y, &state, + false /*addOutsideTargets*/, false /*addPortalWindows*/, + true /*ignoreDragWindow*/); + // enqueue drag exit if needed. + if (hoverWindowHandle != state.dragHoverWindowHandle && + !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) { + if (state.dragHoverWindowHandle != nullptr) { + enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry); + } + state.dragHoverWindowHandle = hoverWindowHandle; + } + // enqueue drag location if needed. + if (hoverWindowHandle != nullptr) { + enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry); + } + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + state.dragWindow = nullptr; + state.dragHoverWindowHandle = nullptr; + } +} + void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { @@ -2543,7 +2621,8 @@ std::string InputDispatcher::getApplicationWindowLabel( void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { if (eventEntry.type == EventEntry::Type::FOCUS || - eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) { + eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED || + eventEntry.type == EventEntry::Type::DRAG) { // Focus or pointer capture changed events are passed to apps, but do not represent user // activity. return; @@ -2585,7 +2664,8 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s events are not user activity", NamedEnum::string(eventEntry.type).c_str()); break; @@ -2799,7 +2879,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio break; } case EventEntry::Type::FOCUS: - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::DRAG: { break; } case EventEntry::Type::SENSOR: { @@ -3022,6 +3103,15 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, break; } + case EventEntry::Type::DRAG: { + const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry); + status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq, + dragEntry.id, dragEntry.x, + dragEntry.y, + dragEntry.isExiting); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { @@ -3319,7 +3409,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( break; } case EventEntry::Type::FOCUS: - case EventEntry::Type::POINTER_CAPTURE_CHANGED: { + case EventEntry::Type::POINTER_CAPTURE_CHANGED: + case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("Canceling %s events is not supported", NamedEnum::string(cancelationEventEntry->type).c_str()); break; @@ -3384,7 +3475,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::POINTER_CAPTURE_CHANGED: - case EventEntry::Type::SENSOR: { + case EventEntry::Type::SENSOR: + case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", NamedEnum::string(downEventEntry->type).c_str()); break; @@ -4348,6 +4440,15 @@ void InputDispatcher::setInputWindowsLocked( ++i; } } + + // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we + // could just clear the state here. + if (state.dragWindow && + std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) == + windowHandles.end()) { + state.dragWindow = nullptr; + state.dragHoverWindowHandle = nullptr; + } } // Release information for windows that are no longer present. @@ -4538,7 +4639,8 @@ void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mod mBlockUntrustedTouchesMode = mode; } -bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { +bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop) { if (fromToken == toToken) { if (DEBUG_FOCUS) { ALOGD("Trivial transfer to same window."); @@ -4582,6 +4684,11 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< InputTarget::FLAG_DISPATCH_AS_IS); state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); + // Store the dragging window. + if (isDragDrop) { + state.dragWindow = toWindowHandle; + } + found = true; goto Found; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 83094c20ba..b2f3625a73 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -120,8 +120,8 @@ public: virtual void setMaximumObscuringOpacityForTouch(float opacity) override; virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, - const sp<IBinder>& toToken) override; + virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop = false) override; virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel( const std::string& name) override; @@ -185,6 +185,9 @@ private: // Enqueues a focus event. void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus, const std::string& reason) REQUIRES(mLock); + // Enqueues a drag event. + void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting, + const MotionEntry& motionEntry) REQUIRES(mLock); // Adds an event to a queue of recent events for debugging purposes. void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock); @@ -204,7 +207,8 @@ private: sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool addOutsideTargets = false, - bool addPortalWindows = false) REQUIRES(mLock); + bool addPortalWindows = false, + bool ignoreDragWindow = false) REQUIRES(mLock); // All registered connections mapped by channel file descriptor. std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock); @@ -387,6 +391,7 @@ private: const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry); @@ -489,10 +494,12 @@ private: std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); - void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState); + // Enqueue a drag event if needed, and update the touch state. + // Uses findTouchedWindowTargetsLocked to make the decision + void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock); struct TouchOcclusionInfo { bool hasBlockingOcclusion; diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 81b3cf025b..4165f4934c 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -49,6 +49,8 @@ void TouchState::copyFrom(const TouchState& other) { windows = other.windows; portalWindows = other.portalWindows; gestureMonitors = other.gestureMonitors; + dragHoverWindowHandle = other.dragHoverWindowHandle; + dragWindow = other.dragWindow; } void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 623c6a824f..d7a561c5ae 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -41,6 +41,11 @@ struct TouchState { std::vector<TouchedMonitor> gestureMonitors; + // The last drag hover window which could receive the drag event. + sp<InputWindowHandle> dragHoverWindowHandle; + // The window being dragged. + sp<InputWindowHandle> dragWindow; + TouchState(); ~TouchState(); void reset(); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 3491893c72..b601dfc8a4 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -149,8 +149,8 @@ public: * * Returns true on success. False if the window did not actually have touch focus. */ - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; - + virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop) = 0; /** * Sets focus on the specified window. */ diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5cdcfaf38e..32f9b69806 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -766,6 +766,9 @@ public: case AINPUT_EVENT_TYPE_CAPTURE: { FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events"; } + case AINPUT_EVENT_TYPE_DRAG: { + FAIL() << "Use 'consumeDragEvent' for DRAG events"; + } default: { FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; } @@ -803,6 +806,23 @@ public: EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled()); } + void consumeDragEvent(bool isExiting, float x, float y) { + const InputEvent* event = consume(); + ASSERT_NE(nullptr, event) << mName.c_str() + << ": consumer should have returned non-NULL event."; + ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType()) + << "Got " << inputEventTypeToString(event->getType()) + << " event instead of DRAG event"; + + EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + const auto& dragEvent = static_cast<const DragEvent&>(*event); + EXPECT_EQ(isExiting, dragEvent.isExiting()); + EXPECT_EQ(x, dragEvent.getX()); + EXPECT_EQ(y, dragEvent.getY()); + } + void assertNoEvents() { InputEvent* event = consume(); if (event == nullptr) { @@ -905,7 +925,7 @@ public: mInfo.frameTop = frame.top; mInfo.frameRight = frame.right; mInfo.frameBottom = frame.bottom; - mInfo.transform.set(frame.left, frame.top); + mInfo.transform.set(-frame.left, -frame.top); mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(frame); } @@ -1003,6 +1023,10 @@ public: expectedFlags); } + void consumeDragEvent(bool isExiting, float x, float y) { + mInputReceiver->consumeDragEvent(isExiting, x, y); + } + std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) { if (mInputReceiver == nullptr) { ADD_FAILURE() << "Invalid receive event on window with no receiver"; @@ -4682,4 +4706,87 @@ TEST_F(InputDispatcherUntrustedTouchesTest, mTouchWindow->consumeAnyMotionDown(); } +class InputDispatcherDragTests : public InputDispatcherTest { +protected: + std::shared_ptr<FakeApplicationHandle> mApp; + sp<FakeWindowHandle> mWindow; + sp<FakeWindowHandle> mSecondWindow; + sp<FakeWindowHandle> mDragWindow; + + void SetUp() override { + InputDispatcherTest::SetUp(); + mApp = std::make_shared<FakeApplicationHandle>(); + mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 100, 100)); + mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + + mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT); + mSecondWindow->setFrame(Rect(100, 0, 200, 100)); + mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}}); + } + + // Start performing drag, we will create a drag window and transfer touch to it. + void performDrag() { + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Window should receive motion event. + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // The drag window covers the entire display + mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}}); + + // Transfer touch focus to the drag window + mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(), + true /* isDragDrop */); + mWindow->consumeMotionCancel(); + mDragWindow->consumeMotionDown(); + } +}; + +TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { + performDrag(); + + // Move on window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + + // Move to another window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {150, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(true, 150, 50); + mSecondWindow->consumeDragEvent(false, 50, 50); + + // Move back to original window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->consumeDragEvent(true, -50, 50); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); +} + } // namespace android::inputdispatcher |